/* Copyright (C) 1999 Beau Kuiper
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "ftpd.h"
void file_becomeuser(FTPSTATE *peer)
{
if (config->rootmode)
{
if (setegid(peer->gidt_asgid) == -1)
log_giveentry(MYLOG_INFO, NULL, safe_snprintf("setegid failed: %s", strerror(errno)));
if (seteuid(peer->uidt_asuid) == -1)
log_giveentry(MYLOG_INFO, NULL, safe_snprintf("seteuid failed: %s", strerror(errno)));
}
}
void file_becomeroot(FTPSTATE *peer)
{
if (config->rootmode)
{
if (seteuid((uid_t)0) == -1)
log_giveentry(MYLOG_INFO, NULL, safe_snprintf("seteuid failed: %s", strerror(errno)));
if (setegid((gid_t)0) == -1)
log_giveentry(MYLOG_INFO, NULL, safe_snprintf("setegid failed: %s", strerror(errno)));
}
}
int checkdir(char *dir)
{
struct stat check;
int result = stat(dir, &check);
if (result == 0)
if (S_ISDIR(check.st_mode))
return(TRUE);
return(FALSE);
}
int checkfile(char *file)
{
struct stat check;
int result = stat(file, &check);
if (result == 0)
if (!S_ISDIR(check.st_mode))
return(TRUE);
return(FALSE);
}
int checkchdir(FTPSTATE *peer, char *dir)
{
int result;
char *dir2 = safe_snprintf("%s/", dir);
result = (check_acl(peer, dir2, ACL_CHDIR) == 0);
if (result)
result = (chdir(dir) == 0);
freewrapper(dir2);
return(result);
}
/* this needs explaining, send this function a pointer to nine '-' in a string ie "---------"
and the file mode of the file, and it will send back the permissions for that file in that
string, eg "-rw-r--r--" */
/* this version was inspired by how GNU fileutils does it */
void convertperms(char *permstr, int mode)
{
int mcpy = mode;
int cnt;
/* work out the 3 sets of rwx's */
for(cnt = 6; cnt > -1; cnt -= 3)
{
permstr[cnt+1] = (mcpy & S_IROTH) ? 'r' : '-';
permstr[cnt+2] = (mcpy & S_IWOTH) ? 'w' : '-';
permstr[cnt+3] = (mcpy & S_IXOTH) ? 'x' : '-';
mcpy = (mcpy >> 3);
}
/* work out special bits. Setuid the setgid the sticky bit */
if (mode & S_ISUID) permstr[3] = (mode & S_IXUSR) ? 's' : 'S';
if (mode & S_ISGID) permstr[6] = (mode & S_IXGRP) ? 's' : 'S';
if (mode & S_ISVTX) permstr[9] = (mode & S_IXOTH) ? 't' : 'T';
/* check for regular files first, better performace! */
if (S_ISREG(mode))
permstr[0] = '-';
else if (S_ISLNK(mode))
permstr[0] = 'l';
else if (S_ISDIR(mode))
permstr[0] = 'd';
else if (S_ISBLK(mode))
permstr[0] = 'b';
else if (S_ISCHR(mode))
permstr[0] = 'c';
else if (S_ISFIFO(mode))
permstr[0] = 'p';
#ifdef S_ISSOCK
else if (S_ISSOCK(mode))
permstr[0] = 's';
#endif
else
permstr[0] = '?';
}
char *file_expand(FTPSTATE *peer, char *filename)
{
char *newfile = strdupwrapper(peer->pwd);
dir_combine(peer, &newfile, filename);
return(newfile);
}
int file_isfdregularfile(int fd)
{
struct stat buf;
if (fstat(fd, &buf) == 0)
return(S_ISREG(buf.st_mode));
else
return(TRUE); /* assume yes! */
}
int file_isfdadir(int fd)
{
struct stat buf;
if (fstat(fd, &buf) == 0)
return(S_ISDIR(buf.st_mode));
else
return(TRUE); /* assume yes, but there is no good reason fstat
won't work! */
}
/* opens a unique filename, in peers virtual space, returns the real name in
rname for logging */
int file_ustoreopen(FTPSTATE *peer, char *infile, int *nounique, char **rname)
{
char *filename = file_expand(peer, infile);
int checknext = TRUE;
int pos = 0;
int filefd = -1;
int epos = strlen(filename);
*nounique = FALSE;
reallocwrapper(epos + 5, (void **)&filename);
if (check_acl(peer, filename, ACL_ADD) == 0)
{
filefd = open(filename, O_CREAT | O_EXCL | O_WRONLY, 0666 & ~peer->umask);
checknext = ((filefd == -1) && (errno == EEXIST));
}
while((checknext) && (pos < 1000))
{
filename[epos] = '.';
filename[epos+1] = (pos / 100) + '0';
filename[epos+2] = (pos % 100) / 10 + '0';
filename[epos+3] = (pos % 10) + '0';
filename[epos+4] = 0;
pos++;
if (check_acl(peer, filename, ACL_ADD) == 0)
{
filefd = open(filename, O_CREAT | O_EXCL | O_WRONLY, 0666 & ~peer->umask);
checknext = ((filefd == -1) && (errno == EEXIST));
}
}
if (pos >= 1000)
*nounique = TRUE;
*rname = filename;
return(filefd);
}
/* opens a file for storing in peers virtual space, returns the real name for logging in rname */
int file_storeopen(FTPSTATE *peer, char *infile, char **rname)
{
char *filename = file_expand(peer, infile);
int newfd = -1;
int openflags = FALSE;
int canoverwrite = FALSE;
int canadd;
canoverwrite = (check_acl(peer, filename, ACL_REPLACE) == 0);
canadd = (check_acl(peer, filename, ACL_ADD) == 0);
/* if the user can add files, then allow creation */
if (canadd)
{
openflags |= O_CREAT;
/* if the user cannot overwrite files */
if (!canoverwrite)
openflags |= O_EXCL;
}
/* try opening the file, or no-op if no acl perms available */
if (canoverwrite || canadd)
newfd = open(filename, openflags | O_WRONLY, 0666 & ~peer->umask);
else
errno = EACCES;
/* now test it against device file access settings */
if ((errno == 0) && (!peer->accessdevices))
if (!file_isfdregularfile(newfd))
{
close(newfd);
errno = EACCES;
newfd = -1;
}
*rname = filename;
return(newfd);
}
int file_readopen(FTPSTATE *peer, char *infile, char **rname)
{
int filefd = -1;
char *filename = file_expand(peer, infile);
check_acl(peer, filename, ACL_READ);
if (errno == 0)
filefd = open(filename, O_RDONLY);
if (errno == 0)
if (file_isfdadir(filefd))
{
close(filefd);
errno = EISDIR;
filefd = -1;
}
if ((errno == 0) && (!peer->accessdevices))
if (!file_isfdregularfile(filefd))
{
close(filefd);
errno = EACCES;
filefd = -1;
}
*rname = filename;
return(filefd);
}
NEWFILE *file_nfopen(FTPSTATE *peer, char *filename)
{
char *rname;
int fd = file_readopen(peer, filename, &rname);
NEWFILE *result = NULL;
if (fd > 0)
result = nfdopen(fd);
freewrapper(rname);
return(result);
}
MYGLOBDATA *file_glob(FTPSTATE *peer, char *dir, char *pattern, int allfiles)
{
int dirlen = strlen(dir);
MYGLOBDATA *mg = NULL;
char *pdir = mallocwrapper(dirlen + 2);
memcpy(pdir, dir, dirlen);
if (pdir[dirlen-1] == '/')
dirlen--;
pdir[dirlen] = '/';
pdir[dirlen+1] = 0;
check_acl(peer, pdir, ACL_LIST);
if (errno == 0)
mg = myglob(pdir, pattern, allfiles, TRUE);
freewrapper(pdir);
return(mg);
}
int file_unlink(FTPSTATE *peer, char *infile)
{
int result = -1;
char *filename = file_expand(peer, infile);
check_acl(peer, filename, ACL_DELETE);
if (errno == 0)
result = unlink(filename);
freewrapper(filename);
return(result);
}
/* there is a race here, where the user can get to overwrite a file if it created between
checkfile and rename. It appears to be very difficult to prevent */
char *file_rename(FTPSTATE *peer, char *infile, char *infile2)
{
char *filename = file_expand(peer, infile);
char *filename2 = file_expand(peer, infile2);
char *badfile = NULL;
if (checkfile(filename2))
{
if (check_acl(peer, filename2, ACL_DELETE) == -1)
badfile = infile2;
}
else
errno = 0;
if (errno == 0)
if (check_acl(peer, filename2, ACL_ADD) == -1)
badfile = infile2;
if (errno == 0)
if (check_acl(peer, filename, ACL_DELETE) == -1)
badfile = infile;
if (errno == 0)
{
if (rename(filename, filename2) == -1)
badfile = infile;
}
freewrapper(filename);
freewrapper(filename2);
return(badfile);
}
int file_stat(FTPSTATE *peer, char *infile, struct stat *output)
{
int result = -1;
char *filename = file_expand(peer, infile);
check_acl(peer, filename, ACL_READ);
if (errno == 0)
result = stat(filename, output);
freewrapper(filename);
return(result);
}
int file_mkdir(FTPSTATE *peer, char *infile)
{
int result = -1;
char *filename = file_expand(peer, infile);
check_acl(peer, filename, ACL_MKDIR);
if (errno == 0)
result = mkdir(filename, 0777 & ~peer->umask);
freewrapper(filename);
return(result);
}
int file_rmdir(FTPSTATE *peer, char *infile)
{
int result = -1;
char *filename = file_expand(peer, infile);
check_acl(peer, filename, ACL_RMDIR);
if (errno == 0)
result = rmdir(filename);
freewrapper(filename);
return(result);
}
int file_chmod(FTPSTATE *peer, char *infile, int mode)
{
int result = -1;
char *filename = file_expand(peer, infile);
check_acl(peer, filename, ACL_CHMOD);
if (errno == 0)
/* never allow sticky bit or setgid or setuid to be set */
result = chmod(filename, mode & 0777);
freewrapper(filename);
return(result);
}
syntax highlighted by Code2HTML, v. 0.9.1