X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=cmds.c;h=cd8cfbc610aeff1b4755832f696a4065516b0b94;hb=2e06cf84e003a95b6640a98c6da2348f93e6b6c6;hp=131df3403bf9bf32e6050da978da4e55dab1baf2;hpb=4b83f8e50792b459dfd8a6ffe470c2fccb524e7b;p=betaftpd diff --git a/cmds.c b/cmds.c index 131df34..cd8cfbc 100644 --- a/cmds.c +++ b/cmds.c @@ -2,7 +2,7 @@ Copyright (C) 1999-2000 Steinar H. Gunderson This program is is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License, version 2 if the + it under the terms of the GNU General Public License, version 2 of the License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, @@ -21,14 +21,14 @@ #include #endif -#if HAVE_SYS_TYPES_H -#include -#endif - #if HAVE_STROPTS_H #include #endif +#if HAVE_SYS_TYPES_H +#include +#endif + #if HAVE_SYS_CONF_H #include #endif @@ -77,10 +77,6 @@ #include #endif -#if HAVE_PWD_H -#include -#endif - #if HAVE_GRP_H #include #endif @@ -89,10 +85,6 @@ #include #endif -#if HAVE_SYS_SOCKET_H -#include -#endif - #if HAVE_SYS_STAT_H #include #endif @@ -109,10 +101,6 @@ #include #endif -#if HAVE_NETINET_IN_H -#include -#endif - #if HAVE_SHADOW_H #include #endif @@ -141,6 +129,10 @@ #include #include +#if WANT_DCACHE +#include +#endif + #define lstat stat extern struct conn *first_conn; @@ -317,6 +309,7 @@ int cmd_pass(struct conn * const c) c->auth = 0; } else { c->uid = p->pw_uid; + c->gid = p->pw_gid; strncpy(c->curr_dir, p->pw_dir, 254); c->curr_dir[254] = 0; } @@ -336,11 +329,15 @@ int cmd_pass(struct conn * const c) ) { c->auth = 0; } else { - c->auth = 3; + c->auth = 4; } } #endif /* !WANT_NONROOT */ + /* root should not be allowed to FTP */ + if (c->uid == 0) { + c->auth = 0; + } if (c->auth == 0) { numeric(c, 530, "Login incorrect."); } else { @@ -348,6 +345,7 @@ int cmd_pass(struct conn * const c) chdir(c->curr_dir); dump_file(c, 230, "welcome.msg"); #endif + /* Have a different message for anonymous users? */ numeric(c, 230, "User logged in."); } return 1; @@ -414,9 +412,11 @@ int cmd_port(struct conn * const c) #if !WANT_NONROOT /* need root privilegies for a short while */ seteuid(getuid()); + setegid(getgid()); #endif bind(sock, (struct sockaddr *)&sin, sizeof(sin)); #if !WANT_NONROOT + setegid(c->gid); seteuid(c->uid); #endif @@ -802,7 +802,7 @@ int cmd_rnfr(struct conn * const c) snprintf(c->rename_from, 256, "%s/%s", cwd, fname); /* Just check that the file exists. */ - TRAP_ERROR(lstat(c->rename_from, &buf) == -1, 550, return 1); + TRAP_ERROR(lstat(c->rename_from, &buf) == -1, 550, c->rename_from[0] = '\0'; return 1); numeric(c, 350, "File exists, send RNTO."); return 1; @@ -821,9 +821,10 @@ int cmd_rnto(struct conn * const c) return 1; } - TRAP_ERROR(rename(c->rename_from, fname) == -1, 550, return 1); + TRAP_ERROR(rename(c->rename_from, fname) == -1, 550, c->rename_from[0] = '\0'; return 1); + c->rename_from[0] = '\0'; - numeric(c, 250, "File renamed successfulyy."); + numeric(c, 250, "File renamed successfully."); return 1; } @@ -908,7 +909,7 @@ char conn_state[5][27] = { "Waiting for e-mail address", "Waiting for password", "Logged in", - "Waiting for password", /* actually non-existant user */ + "Logged in", /* non-anonymous */ }; char ftran_state[6][42] = { @@ -932,11 +933,19 @@ int cmd_stat(struct conn * const c) " BetaFTPD version " VERSION " (http://members.xoom.com/sneeze/betaftpd.html)\r\n" " Connected to %s\r\n" " Control connection state: %s\r\n" +#if WANT_ASCII + " TYPE: %s; STRUcture: File; transfer MODE: Stream\r\n" +#else " TYPE: Image; STRUcture: File; transfer MODE: Stream\r\n" +#endif " Data connection state: %s\r\n" "211 End of status\r\n", inet_ntoa(((struct sockaddr_in *)(&(c->addr)))->sin_addr), - conn_state[c->auth], (f) ? ftran_state[f->state] : ftran_state[0]); + conn_state[c->auth], +#if WANT_ASCII + (c->ascii_mode == 1) ? "ASCII, FORM: Nonprint" : "Image", +#endif + (f) ? ftran_state[f->state] : ftran_state[0]); i = strlen(buf); @@ -972,9 +981,14 @@ int _mwrite(const char * const buf, const struct ftran * const f, /* * mwrite: This is a short_hand define, making calls to _mwrite() very - * similiar to calls to write(). + * similiar to calls to write(). It works both with and without + * mmap(). */ +#if HAVE_MMAP #define mwrite(buf, count) pos = _mwrite((buf), (f), (pos), (count), (size)); +#else +#define mwrite(buf, count) write(f->local_file, buf, count); +#endif /* * long_listing(): @@ -1040,14 +1054,14 @@ int long_listing(char * const retbuf, const char * const pathname, const int do_ #if WANT_NONROOT char rights[16]; - if (nr_check_permission(0, pathname, 0, (buf.st_mode & S_IFDIR), rights) == -1) { + if (nr_check_permission(0, pathname, 0, (S_ISDIR(buf.st_mode)), rights) == -1) { /* no permission to even see this file */ return 0; } - i = snprintf(temp, 1024, "%c%s %3u %-8s %-8s %8lu %12s %s\r\n", + snprintf(temp, 1024, "%c%s %3u %-8s %-8s %8lu %12s %s\r\n", #else - i = snprintf(temp, 1024, "%c%c%c%c%c%c%c%c%c%c %3u %-8s %-8s %8lu %12s %s", + snprintf(temp, 1024, "%c%c%c%c%c%c%c%c%c%c %3u %-8s %-8s %8lu %12s %s", #endif decode_mode(buf.st_mode), #if WANT_NONROOT @@ -1065,7 +1079,8 @@ int long_listing(char * const retbuf, const char * const pathname, const int do_ #endif buf.st_nlink, username, groupname, (unsigned long)(buf.st_size), newd, pathname); - + i = strlen(temp); + #if 0 /* * vim needs this extra character for some reason... It's too @@ -1096,7 +1111,7 @@ int cmd_list(struct conn * const c) { struct list_options lo; -/* lo.recursive = 0; */ + lo.recursive = 0; lo.long_listing = 1; lo.classify = 0; @@ -1115,7 +1130,7 @@ int cmd_nlst(struct conn * const c) { struct list_options lo; -/* lo.recursive = 0; */ + lo.recursive = 0; lo.long_listing = 0; lo.classify = 0; @@ -1164,30 +1179,17 @@ void do_listing(struct conn * const c, struct list_options * const lo) #if WANT_DCACHE { - struct dcache *d = NULL, *next = first_dcache->next_dcache; - struct stat buf; + struct dcache *d = find_dcache(cwd, ptr, lo); + if (d != NULL) { + d->use_count++; + f->dir_cache = d; + f->file_data = d->dir_data; + f->size = d->dir_size; + f->dir_listing = 1; + f->pos = 0; - if (stat(cwd, &buf) > -1) { - /* run through the linked list */ - while (next != NULL) { - d = next; - next = d->next_dcache; - - if (buf.st_mtime <= d->generated && - strcmp(d->dir_name, cwd) == 0 && - strcmp(d->pattern, ptr) == 0 && - memcmp(&(d->lo), lo, - sizeof(struct list_options)) == 0) { - d->use_count++; - f->dir_cache = d; - f->file_data = d->dir_data; - f->size = d->dir_size; - f->dir_listing = 1; - f->pos = 0; - prepare_for_transfer(f); - return; - } - } + prepare_for_transfer(f); + return; } } #endif @@ -1200,30 +1202,14 @@ void do_listing(struct conn * const c, struct list_options * const lo) size = num_files * 160; f->file_data = malloc(size + 1); TRAP_ERROR(f->file_data == NULL, 550, return); - list_core(c, ptr, lo, size); + list_core(c, ptr, "", lo, size, 0); } #else - list_core(c, ptr, lo); + list_core(c, ptr, "", lo); #endif #if WANT_DCACHE - /* populate the directory listing cache */ - { - struct stat buf; - struct dcache *d = alloc_new_dcache(); - if (d != NULL && stat(cwd, &buf) > -1) { - d->use_count++; - f->dir_cache = d; - d->dir_data = f->file_data; - d->dir_size = f->size; - d->generated = buf.st_mtime; - - strcpy(d->dir_name, cwd); - strncpy(d->pattern, ptr, 255); - d->pattern[255] = 0; - d->lo = *lo; - } - } + populate_dcache(f, cwd, ptr, lo); #endif #if HAVE_MMAP @@ -1260,8 +1246,8 @@ int get_num_files(struct conn * const c, const char * const pathname, return -1; } -#if 0 /* the rest of the code doesn't support recursion yet */ if (lo->recursive) { + int i; for (i = 0; i < pglob.gl_pathc; i++) { char *temp = pglob.gl_pathv[i]; struct stat buf; @@ -1274,7 +1260,8 @@ int get_num_files(struct conn * const c, const char * const pathname, } } } -#endif + + globfree(&pglob); return num_files; } @@ -1289,20 +1276,20 @@ int get_num_files(struct conn * const c, const char * const pathname, * under 80 for long listings, and a little under 160 for * short listings), the list will be truncated. Fix... * + * The return value only makes sense if mmap()'ing, since it + * returns the number of bytes written into the buffer. + * * This function is rather long. */ -void list_core(struct conn * const c, const char * const pathname, - struct list_options * const lo +int list_core(struct conn * const c, const char * const pathname, + char * const disp_pathname, struct list_options * const lo #if HAVE_MMAP - , const int size + , const int size, int pos #endif ) { int i; glob_t pglob; -#if HAVE_MMAP - int pos = 0; -#endif struct ftran * const f = c->transfer; /* @@ -1314,12 +1301,28 @@ void list_core(struct conn * const c, const char * const pathname, #ifdef GLOB_NOMATCH case GLOB_NOMATCH: #endif - break; + break; /* note: break, not return */ default: numeric(c, 550, strerror(EACCES)); - return; +#if HAVE_MMAP + return pos; +#else + return 0; +#endif } + if (lo->recursive) { + if (disp_pathname[0] == '\0') { + mwrite(".:\r\n", 4); + } else { + char temp[1024]; + int i; + + snprintf(temp, 1024, "%s:\r\n", disp_pathname); + i = strlen(temp); + mwrite(temp, i); + } + } if (lo->long_listing) { /* FIX: we may get too high total number if we are running nonroot! */ struct stat buf; @@ -1331,12 +1334,9 @@ void list_core(struct conn * const c, const char * const pathname, total += buf.st_blocks; } } - i = snprintf(temp, 1024, "total %lu\r\n", total >> 1); -#if HAVE_MMAP + snprintf(temp, 1024, "total %lu\r\n", total >> 1); + i = strlen(temp); mwrite(temp, i); -#else - write(f->local_file, temp, i); -#endif } for (i = 0; i < pglob.gl_pathc; i++) { @@ -1367,15 +1367,43 @@ void list_core(struct conn * const c, const char * const pathname, } } - /* support recursion here some day... */ - -#if HAVE_MMAP mwrite(buf, strlen(buf)); mwrite("\r\n", 2); -#else - write(f->local_file, buf, strlen(buf)); - write(f->local_file, "\r\n", 2); + } + + /* + * If recursion is on, dive into any subdirectories now -- note + * that each entry is stat()'ed twice, hopefully the OS will manage, + * and we've got our own dcache anyways -- this could be fixed at + * the expense of some memory, consider for later inclusion. + */ + if (lo->recursive) { + for (i = 0; i < pglob.gl_pathc; i++) { + struct stat buf; + const char * const temp = pglob.gl_pathv[i]; + + /* don't dive into `.' or `..' */ + if (lstat(temp, &buf) != -1 && S_ISDIR(buf.st_mode) && + (temp[0] != '.' || (temp[1] != '.' && temp[1] != '\0'))) { + char tmp2[1024]; + + mwrite("\r\n", 2); + + /* attach the pathname to the end of the displayed path */ + if (disp_pathname[0] == '\0') { + snprintf(tmp2, 1024, "%s", temp); + } else { + snprintf(tmp2, 1024, "%s/%s", disp_pathname, temp); + } + + chdir(temp); + pos = list_core(c, "*", tmp2, lo, +#if HAVE_MMAP + size, pos); #endif + chdir(".."); + } + } } #if HAVE_MMAP @@ -1385,6 +1413,11 @@ void list_core(struct conn * const c, const char * const pathname, #endif globfree(&pglob); +#if HAVE_MMAP + return pos; +#else + return 0; +#endif } /* @@ -1429,22 +1462,30 @@ int cmd_type(struct conn * const c) } /* - * cmd_mode(): Handles the MODE command. We always use stream mode, - * so the argument is ignored. + * cmd_mode(): Handles the MODE command. Only stream mode is supported. */ int cmd_mode(struct conn * const c) { - numeric(c, 200, "MODE ignored (always S)"); + c->recv_buf[0] &= (255-32); /* convert to upper case */ + if (c->recv_buf[0] == 'S') { + numeric(c, 200, "Mode is STREAM."); + } else { + numeric(c, 504, "Unknown mode."); + } return 1; } /* - * cmd_stru(): Handles the STRU command. We always use file mode, - * so the argument is ignored. + * cmd_stru(): Handles the STRU command. Only file mode is supported. */ int cmd_stru(struct conn * const c) { - numeric(c, 200, "STRU ignored (always F)"); + c->recv_buf[0] &= (255-32); /* convert to upper case */ + if (c->recv_buf[0] == 'F') { + numeric(c, 200, "Structure is FILE."); + } else { + numeric(c, 504, "Unknown structure."); + } return 1; } @@ -1518,7 +1559,7 @@ int cmd_rein(struct conn * const c) * down without clearing any sockets etc. In other words: * Don't use it on a production site. */ -void cmd_exit(struct conn * const c) +int cmd_exit(struct conn * const c) { while (first_conn->next_conn) destroy_conn(first_conn->next_conn); @@ -1569,8 +1610,10 @@ void parse_command(struct conn *c) #if !WANT_NONROOT if (h->do_setuid) { seteuid(c->uid); + setegid(c->gid); } else { - seteuid(0); + seteuid(getuid()); + setegid(getgid()); } #endif @@ -1588,7 +1631,10 @@ void parse_command(struct conn *c) if (h->callback(c)) { c->recv_buf[cmlen] = schar; #if !WANT_NONROOT - if (h->do_setuid) seteuid(getuid()); + if (h->do_setuid) { + seteuid(getuid()); + setegid(getgid()); + } #endif remove_bytes(c, cmlen); } @@ -1652,13 +1698,13 @@ void prepare_for_transfer(struct ftran *f) * The most common cases are put first, for speed :-) */ char decode_mode(mode_t mode) { - if (mode & S_IFREG) return '-'; - if (mode & S_IFDIR) return 'd'; - if (mode & S_IFLNK) return 'l'; - if (mode & S_IFBLK) return 'b'; - if (mode & S_IFCHR) return 'c'; - if (mode & S_IFSOCK) return 's'; - if (mode & S_IFIFO) return 'f'; + if (S_ISREG(mode)) return '-'; + if (S_ISDIR(mode)) return 'd'; + if (S_ISLNK(mode)) return 'l'; + if (S_ISBLK(mode)) return 'b'; + if (S_ISCHR(mode)) return 'c'; + if (S_ISSOCK(mode)) return 's'; + if (S_ISFIFO(mode)) return 'f'; return '-'; } @@ -1733,9 +1779,9 @@ int do_openfile(struct conn * const c, char * const path, #if WANT_UPLOAD if ((flags & O_CREAT) == 0) { #endif - stat(ptr, &buf); + TRAP_ERROR(stat(ptr, &buf) == -1, 550, return -2); if (!S_ISREG(buf.st_mode)) { - numeric(c, 550, "%s: Not a plain file.", ptr); + numeric(c, 550, "Not a plain file.", ptr); return -2; } #if WANT_UPLOAD @@ -1790,11 +1836,9 @@ int prepare_for_listing(struct conn * const c, char ** const ptr, if (optr != NULL) { while (*++optr) { switch (*optr & (255-32)) { /* uppercase */ -#if 0 case 'R': /* actually case sensitive... */ lo->recursive = 1; break; -#endif case 'L': lo->long_listing = 1; break; @@ -1825,7 +1869,16 @@ int prepare_for_listing(struct conn * const c, char ** const ptr, } /* if no argument, choose all files */ - if (fptr == NULL || fptr[0] == 0) fptr = "*"; + if (fptr == NULL || fptr[0] == 0) { + fptr = "*"; + } else { + /* we need to check if the last part is a directory (no -d switch) */ + struct stat buf; + if (stat(fptr, &buf) == 0 && S_ISDIR(buf.st_mode)) { + TRAP_ERROR(chdir(fptr) == -1, 550, return -1); + fptr = "*"; + } + } *ptr = fptr; #if WANT_NONROOT