pure-quotacheck.c

来自「功能强大的ftp服务器源代码」· C语言 代码 · 共 398 行

C
398
字号
#include <config.h>#include "ftpd.h"#include "quotas.h"#ifndef HAVE_GETOPT_LONG# include "bsd-getopt_long.h"#else# include <getopt.h>#endif#ifdef WITH_DMALLOC# include <dmalloc.h>#endifstatic uid_t uid;static gid_t gid;static const char *startpath;static unsigned long long total_size;static unsigned long long total_files;static signed char isroot;/* * To avoid races/loop attacks, we keep track of inode and * device numbers of every directory to avoid scanning them * twice. It's stupidly paranoid and slow. But it's safe. */typedef struct Node_ {    ino_t inode;    dev_t device;} Node;Node *nodes;size_t nodes_size;static void oom(void){    fputs("Out of memory error!\n", stderr);    exit(EXIT_FAILURE);}static int traversal(const char * const s){    DIR *d;    struct dirent *de;    struct stat st;    size_t slen;    Node *nodes_pnt = nodes;    size_t nodes_cnt = nodes_size;    int fd;    if ((fd = open(s, O_RDONLY | O_DIRECTORY)) == -1) {        if (errno != EACCES) {            return -1;        }        if (fstat(fd, &st) != 0 || !S_ISDIR(st.st_mode) || st.st_uid != uid) {            close(fd);            return -1;        }        (void) fchmod(fd, st.st_mode | 0500);        close(fd);        if ((fd = open(s, O_RDONLY | O_DIRECTORY)) == -1) {            return -1;        }    }    if (fstat(fd, &st) != 0 || !S_ISDIR(st.st_mode)) {        close(fd);        return -1;    }    if ((st.st_mode & 0500) != 0500 && st.st_uid == uid) {        (void) fchmod(fd, (mode_t) (st.st_mode | 0500)); /* if it fails, try anyway */    }    close(fd);    while (nodes_cnt > (size_t) 0U) {        if (nodes_pnt->inode == st.st_ino && nodes_pnt->device == st.st_dev) {            return -1;        }        nodes_pnt++;        nodes_cnt -= sizeof *nodes_pnt;    }    if (nodes == NULL) {        if ((nodes = malloc(sizeof *nodes)) == NULL) {            oom();        }    } else {        Node *new_nodes;        if ((new_nodes = realloc(nodes, nodes_size + sizeof *nodes_pnt))            == NULL) {            oom();        }        nodes = new_nodes;    }    {        Node * const node = (Node *) (((unsigned char *) nodes) + nodes_size);        node->inode = st.st_ino;        node->device = st.st_dev;    }    nodes_size += sizeof *nodes_pnt;    if ((d = opendir(s)) == NULL) {        return -1;    }    slen = strlen(s) + (size_t) 2U;    while ((de = readdir(d)) != NULL) {        char *alloca_buf;        size_t sizeof_buf;        if ((de->d_name[0] == '.' && de->d_name[1] == 0) ||            (de->d_name[0] == '.' && de->d_name[1] == '.' &&             de->d_name[2] == 0)) {            continue;        }        if (strcmp(de->d_name, QUOTA_FILE) == 0) {            continue;        }        sizeof_buf = slen + strlen(de->d_name);        if ((alloca_buf = ALLOCA(sizeof_buf)) == NULL) {            continue;        }        snprintf(alloca_buf, sizeof_buf, "%s/%s", s, de->d_name);        if (stat(alloca_buf, &st) == 0) {            if (S_ISDIR(st.st_mode)) {                if (traversal(alloca_buf) == 0) {                    total_files++;                }            } else if (S_ISREG(st.st_mode)) {                total_size += (unsigned long long) st.st_size;                total_files++;            }        }        ALLOCA_FREE(alloca_buf);    }    closedir(d);    return 0;}static void help(void){    puts("\nUsage:\n\n"         "pure-quotacheck -u <user> -d <directory> [-g <group>]\n\n"         "-d <directory>: start from this directory\n"         "-g <group/gid>: scan the directory under this gid\n"         "-h: help\n"         "-u <user/uid>: scan the directory under this uid\n");    exit(EXIT_SUCCESS);}static int doinitsupgroups(const char *user, const uid_t uid, const gid_t gid){#ifndef NON_ROOT_FTP# ifdef HAVE_SETGROUPS    if (setgroups(1U, &gid) != 0) {        return -1;    }# else    (void) gid;# endif# ifdef HAVE_INITGROUPS                if (user == NULL) {        const struct passwd * const lpwd = getpwuid(uid);                if (lpwd != NULL && lpwd->pw_name != NULL) {            user = lpwd->pw_name;        } else {                    return -1;        }    }    initgroups(user, gid);# else    (void) user;    (void) uid;# endif#else    (void) user;    (void) uid;    (void) gid;    #endif    return 0;}static int changeuidgid(void){    if (setgid(gid) || setegid(gid) ||        setuid(uid) || seteuid(uid) || chdir("/")) {        return -1;    }    return 0;}static int writequota(const char * const quota_file){    int err = -1;    int fd;    struct flock lock;    ssize_t towrite;    ssize_t written;    struct stat st;    char buf[84];    const char *bufpnt = buf;    if ((fd = open("/", O_RDONLY | O_DIRECTORY)) == -1) {        return -1;    }    if (fstat(fd, &st) != 0 || !S_ISDIR(st.st_mode)) {        close(fd);        return -1;    }    if ((st.st_mode & 0700) != 0700 && st.st_uid == uid) {        (void) fchmod(fd, st.st_mode | 0700);    }    close(fd);    if ((fd = open(quota_file, O_RDWR | O_CREAT | O_NOFOLLOW,                   (mode_t) 0600)) == -1) {        return -1;    }    lock.l_whence = SEEK_SET;    lock.l_start = (off_t) 0;    lock.l_len = (off_t) 0;    lock.l_pid = getpid();    lock.l_type = F_WRLCK;    while (fcntl(fd, F_SETLKW, &lock) < 0) {        if (errno != EINTR) {            goto byenounlock;        }    }    if (SNCHECK(snprintf(buf, sizeof buf, "%llu %llu\n",                         total_files, total_size), sizeof buf) ||        ftruncate(fd, (off_t) 0) != 0) {        goto bye;    }    towrite = (ssize_t) strlen(buf);    bufpnt = buf;    while (towrite > (ssize_t) 0) {        for (;;) {            if ((written = write(fd, bufpnt,                                 (size_t) towrite)) <= (ssize_t) 0) {                if (written == (ssize_t) 0 ||                    (errno != EAGAIN && errno != EINTR)) {                    (void) ftruncate(fd, (off_t) 0);                    goto bye;                } else {                    continue;                }            }            break;        }        bufpnt += written;        towrite -=written;    }    err = 0;    bye:    lock.l_type = F_UNLCK;    while (fcntl(fd, F_SETLK, &lock) < 0 && errno == EINTR);    byenounlock:    close(fd);    return err;}int main(int argc, char *argv[]){    int fodder;    if (geteuid() == (uid_t) 0) {        isroot = 1;    } else {        uid = geteuid();        gid = getegid();    }        if (argc < 0) {        return -1;    }    if (argc < 2) {        help();    }    #ifdef HAVE_SETLOCALE# ifdef LC_MESSAGES    (void) setlocale(LC_MESSAGES, "");# endif# ifdef LC_CTYPE    (void) setlocale(LC_CTYPE, "");# endif# ifdef LC_COLLATE    (void) setlocale(LC_COLLATE, "");# endif#endif                   while ((fodder = getopt(argc, argv, "d:g:u:h")) != -1) {        switch(fodder) {        case 'h':            help();            /* doesn't return */        case 'd':            if (startpath != NULL) {                fprintf(stderr, "Already one startpath: [%s]\n", startpath);                return -1;            }            startpath = strdup(optarg);	    if (startpath == NULL) {                oom();            }            break;        case 'g':            {                struct group *gr;                if (gid > (gid_t) 0) {                    fprintf(stderr, "You already gave a gid\n");                    return -1;                }                if ((gr = getgrnam(optarg)) != NULL) {                    gid = gr->gr_gid;                } else {                    gid = (gid_t) strtoul(optarg, NULL, 10);                }            }            break;        case 'u':            {                struct passwd *pw;                if (uid > (uid_t) 0) {                    fprintf(stderr, "You already gave an uid\n");                    return -1;                }                if ((pw = getpwnam(optarg)) != NULL) {                    uid = pw->pw_uid;                    if (gid == (gid_t) 0) {                        gid = pw->pw_gid;                    }                } else {                    uid = (uid_t) strtoul(optarg, NULL, 10);                }            }            break;        case '?':            help();        }    }    if (startpath == NULL) {        fprintf(stderr, "Missing path\n");        return -1;    }    if (uid <= (uid_t) 0) {        fprintf(stderr, "Invalid/insecure uid - must be > 0\n");        return -2;    }    if (gid <= (gid_t) 0) {        fprintf(stderr, "Invalid/insecure gid - must be > 0\n");        return -2;    }    if (isroot != 0) {        if (doinitsupgroups(NULL, uid, gid) #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)                        & 0#endif            != 0 ||            chdir(startpath) != 0 ||             chroot(startpath) != 0 || chdir("/") != 0) {            fprintf(stderr, "Can't chroot to [%s]: [%s]\n",                    startpath, strerror(errno));            return -3;        }        if (changeuidgid() < 0) {            fprintf(stderr, "Can't switch uid/gid: [%s]\n", strerror(errno));            return -3;        }            } else if (chdir(startpath) != 0) {        fprintf(stderr, "Can't enter directory [%s]: [%s]\n",                startpath, strerror(errno));        return -3;    }    if (traversal(isroot != 0 ? "/" : "./") < 0) {        fprintf(stderr, "Unable to traverse [%s]: [%s]\n",                startpath, strerror(errno));        free(nodes);                return -4;    }    free(nodes);    if (isroot != 0) {        if (writequota("/" QUOTA_FILE) < 0) {            err_writequota:            fprintf(stderr, "Unable to update the quota file ("                    QUOTA_FILE "): [%s]\n", strerror(errno));            return -5;        }    } else if (chdir(startpath) != 0 || writequota(QUOTA_FILE) < 0) {        goto err_writequota;    }    return 0;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?