Merge branch 'freebsd/current/main' into hardened/current/master

This commit is contained in:
HardenedBSD Sync Services 2024-07-05 18:01:24 -06:00
commit ccd44b020a
No known key found for this signature in database

View File

@ -680,28 +680,27 @@ find_next_name(char *filename, int *fd)
int int
validate_access(int peer, char **filep, int mode) validate_access(int peer, char **filep, int mode)
{ {
struct stat stbuf;
int fd;
int error;
struct dirlist *dirp;
static char pathname[MAXPATHLEN]; static char pathname[MAXPATHLEN];
struct stat sb;
struct dirlist *dirp;
char *filename = *filep; char *filename = *filep;
int err, fd;
/* /*
* Prevent tricksters from getting around the directory restrictions * Prevent tricksters from getting around the directory restrictions
*/ */
if (strstr(filename, "/../")) if (strncmp(filename, "../", 3) == 0 ||
strstr(filename, "/../") != NULL)
return (EACCESS); return (EACCESS);
if (*filename == '/') { if (*filename == '/') {
/* /*
* Allow the request if it's in one of the approved locations. * Absolute file name: allow the request if it's in one of the
* Special case: check the null prefix ("/") by looking * approved locations.
* for length = 1 and relying on the arg. processing that
* it's a /.
*/ */
for (dirp = dirs; dirp->name != NULL; dirp++) { for (dirp = dirs; dirp->name != NULL; dirp++) {
if (dirp->len == 1) if (dirp->len == 1)
/* Only "/" can have len 1 */
break; break;
if (strncmp(filename, dirp->name, dirp->len) == 0 && if (strncmp(filename, dirp->name, dirp->len) == 0 &&
filename[dirp->len] == '/') filename[dirp->len] == '/')
@ -710,30 +709,20 @@ validate_access(int peer, char **filep, int mode)
/* If directory list is empty, allow access to any file */ /* If directory list is empty, allow access to any file */
if (dirp->name == NULL && dirp != dirs) if (dirp->name == NULL && dirp != dirs)
return (EACCESS); return (EACCESS);
if (stat(filename, &stbuf) < 0) if (stat(filename, &sb) != 0)
return (errno == ENOENT ? ENOTFOUND : EACCESS); return (errno == ENOENT ? ENOTFOUND : EACCESS);
if ((stbuf.st_mode & S_IFMT) != S_IFREG) if (!S_ISREG(sb.st_mode))
return (ENOTFOUND); return (ENOTFOUND);
if (mode == RRQ) { if (mode == RRQ) {
if ((stbuf.st_mode & S_IROTH) == 0) if ((sb.st_mode & S_IROTH) == 0)
return (EACCESS); return (EACCESS);
} else { } else {
if (check_woth && ((stbuf.st_mode & S_IWOTH) == 0)) if (check_woth && (sb.st_mode & S_IWOTH) == 0)
return (EACCESS); return (EACCESS);
} }
} else { } else {
int err;
/* /*
* Relative file name: search the approved locations for it. * Relative file name: search the approved locations for it.
* Don't allow write requests that avoid directory
* restrictions.
*/
if (!strncmp(filename, "../", 3))
return (EACCESS);
/*
* If the file exists in one of the directories and isn't * If the file exists in one of the directories and isn't
* readable, continue looking. However, change the error code * readable, continue looking. However, change the error code
* to give an indication that the file exists. * to give an indication that the file exists.
@ -741,18 +730,20 @@ validate_access(int peer, char **filep, int mode)
err = ENOTFOUND; err = ENOTFOUND;
for (dirp = dirs; dirp->name != NULL; dirp++) { for (dirp = dirs; dirp->name != NULL; dirp++) {
snprintf(pathname, sizeof(pathname), "%s/%s", snprintf(pathname, sizeof(pathname), "%s/%s",
dirp->name, filename); dirp->name, filename);
if (stat(pathname, &stbuf) == 0 && if (stat(pathname, &sb) != 0)
(stbuf.st_mode & S_IFMT) == S_IFREG) { continue;
if (mode == RRQ) { if (!S_ISREG(sb.st_mode))
if ((stbuf.st_mode & S_IROTH) != 0) continue;
break; err = EACCESS;
} else { if (mode == RRQ) {
if (!check_woth || ((stbuf.st_mode & S_IWOTH) != 0)) if ((sb.st_mode & S_IROTH) == 0)
break; continue;
} } else {
err = EACCESS; if (check_woth && (sb.st_mode & S_IWOTH) == 0)
continue;
} }
break;
} }
if (dirp->name != NULL) if (dirp->name != NULL)
*filep = filename = pathname; *filep = filename = pathname;
@ -766,27 +757,27 @@ validate_access(int peer, char **filep, int mode)
* This option is handled here because it (might) require(s) the * This option is handled here because it (might) require(s) the
* size of the file. * size of the file.
*/ */
option_tsize(peer, NULL, mode, &stbuf); option_tsize(peer, NULL, mode, &sb);
if (mode == RRQ) if (mode == RRQ) {
fd = open(filename, O_RDONLY); fd = open(filename, O_RDONLY);
else { } else if (create_new) {
if (create_new) { if (increase_name) {
if (increase_name) { err = find_next_name(filename, &fd);
error = find_next_name(filename, &fd); if (err > 0)
if (error > 0) return (err + 100);
return (error + 100); } else {
} else fd = open(filename,
fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT,
O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP |
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
S_IWGRP | S_IROTH | S_IWOTH ); }
} else } else {
fd = open(filename, O_WRONLY | O_TRUNC); fd = open(filename, O_WRONLY | O_TRUNC);
} }
if (fd < 0) if (fd < 0)
return (errno + 100); return (errno + 100);
file = fdopen(fd, (mode == RRQ)? "r":"w"); file = fdopen(fd, mode == RRQ ? "r" : "w");
if (file == NULL) { if (file == NULL) {
close(fd); close(fd);
return (errno + 100); return (errno + 100);