📄 cmds.c
字号:
/* cmds.c: BetaFTPD command handlers 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 License as published by the Free Software Foundation. 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/#define _GNU_SOURCE#if HAVE_CONFIG_H#include <config.h>#endif#if HAVE_STROPTS_H#include <stropts.h>#endif#if HAVE_SYS_CONF_H#include <sys/conf.h>#endif#if HAVE_DIRENT_H#include <dirent.h>#endif#if HAVE_CRYPT_H#include <crypt.h>#endif#if HAVE_ERRNO_H#include <errno.h>#endif#if HAVE_GLOB_H#include <glob.h>#endif#if HAVE_STDIO_H#include <stdio.h>#endif#if HAVE_STDLIB_H#include <stdlib.h>#endif#if HAVE_STRING_H#include <string.h>#endif#if HAVE_STRINGS_H#include <strings.h>#endif#if HAVE_UNISTD_H#include <unistd.h>#endif#if HAVE_TIME_H#include <time.h>#endif#if HAVE_FCNTL_H#include <fcntl.h>#endif#if HAVE_GRP_H#include <grp.h>#endif#if HAVE_SYS_IOCTL_H#include <sys/ioctl.h>#endif#if HAVE_SYS_STAT_H#include <sys/stat.h>#endif#if HAVE_SYS_PARAM_H#include <sys/param.h>#endif#if HAVE_STROPTS_H#include <stropts.h>#endif#if HAVE_SYS_CONF_H#include <sys/conf.h>#endif#if HAVE_SHADOW_H#include <shadow.h>#endif#if HAVE_SYS_FILIO_H#include <sys/filio.h>#endif#if HAVE_SYS_POLL_H#include <sys/poll.h>#endif#if WANT_NONROOT#define NO_SETUID#define DO_SETUID#else#define NO_SETUID ,0#define DO_SETUID ,1#endif#ifndef NBBY#define NBBY 8#endif#include <ftpd.h>#include <cmds.h>#include <nonroot.h>#if WANT_DCACHE#include <dcache.h>#endif#define lstat statextern struct conn *first_conn;#if WANT_DCACHEextern struct dcache *first_dcache;#endif#if HAVE_POLLextern struct pollfd fds[];#elseextern fd_set master_fds, master_send_fds;#endifstruct handler { char cmd_name[6]; char add_cmlen; /* =1 if the command takes an argument */ int (*callback)(struct conn * const); char min_auth;#if !WANT_NONROOT char do_setuid; /* =1 if root is not *really* needed */#endif};static const struct handler handler_table[] = { { "user ", 1, cmd_user, 0 NO_SETUID }, { "pass ", 1, cmd_pass, 1 NO_SETUID }, { "retr ", 1, cmd_retr, 3 DO_SETUID }, { "acct ", 1, cmd_acct, 0 NO_SETUID }, { "port ", 1, cmd_port, 3 DO_SETUID }, { "pasv" , 0, cmd_pasv, 3 DO_SETUID }, { "pwd" , 0, cmd_pwd, 3 DO_SETUID }, { "cwd " , 1, cmd_cwd, 3 DO_SETUID }, { "cdup" , 0, cmd_cdup, 3 DO_SETUID }, { "rest ", 1, cmd_rest, 3 DO_SETUID }, { "list" , 0, cmd_list, 3 DO_SETUID }, { "nlst" , 0, cmd_nlst, 3 DO_SETUID }, { "type ", 1, cmd_type, 3 DO_SETUID }, { "mode ", 1, cmd_mode, 3 DO_SETUID }, { "stru ", 1, cmd_stru, 3 DO_SETUID }, { "size ", 1, cmd_size, 3 DO_SETUID }, { "mdtm ", 1, cmd_mdtm, 3 DO_SETUID }, { "abor" , 0, cmd_abor, 3 DO_SETUID }, { "dele ", 1, cmd_dele, 3 DO_SETUID }, { "rnfr ", 1, cmd_rnfr, 3 DO_SETUID }, { "rnto ", 1, cmd_rnto, 3 DO_SETUID }, { "mkd " , 1, cmd_mkd, 3 DO_SETUID }, { "rmd " , 1, cmd_rmd, 3 DO_SETUID }, { "allo ", 1, cmd_allo, 3 DO_SETUID }, { "stat" , 0, cmd_stat, 0 NO_SETUID }, { "noop" , 0, cmd_noop, 0 DO_SETUID }, { "syst" , 0, cmd_syst, 0 DO_SETUID }, { "help" , 0, cmd_help, 0 NO_SETUID }, { "quit" , 0, cmd_quit, 0 DO_SETUID }, { "rein" , 0, cmd_rein, 0 DO_SETUID }, /* deprecated forms */ { "xcup" , 0, cmd_cdup, 3 DO_SETUID }, { "xcwd ", 1, cmd_cwd, 3 DO_SETUID }, { "xpwd" , 0, cmd_pwd, 3 DO_SETUID }, { "xmkd ", 1, cmd_mkd, 3 DO_SETUID }, { "xrmd ", 1, cmd_rmd, 3 DO_SETUID },#if WANT_UPLOAD { "stor ", 1, cmd_stor, 3 DO_SETUID }, { "appe ", 1, cmd_appe, 3 DO_SETUID },#endif#if DOING_PROFILING#warning Use DOING_PROFILING with caution, and NEVER on a production server! :-) { "exit", 0, cmd_exit, 0 NO_SETUID },#endif { "" , 0, NULL, 0 NO_SETUID }};/* * do_chdir(): Does a chdir() to newd on c, staying inside the * limits of root_dir. Use this instead of a chdir() whenever * you can, and possibly even when you can't :-) * * This command quirks around some problems in the rest of * the code (namely translate_path()), so a blank newdir is * interpreted as the root directory. */int do_chdir(struct conn * const c, const char * const newd){ char chd[512], temp[512]; TRAP_ERROR(chdir(c->curr_dir) == -1, 550, return -1); /* handle `absolute' paths */ if (newd[0] == '/' || newd[0] == '\0') { strcpy(temp, c->root_dir); /* * is this the root directory? if not, remove the trailing `/' * and concatenate the new directory on */ if (newd[1] != '\0' && newd[0] != '\0') { temp[strlen(temp) - 1] = 0; strcat(temp, newd); } } else { strcpy(temp, newd); }#if WANT_NONROOT if (nr_check_permission(c->uid, temp, 1, 1, NULL) == -1) { numeric(c, 550, "Permission denied"); return -1; }#endif TRAP_ERROR(chdir(temp) == -1, 550, return -1); getcwd(chd, 254); if (chd[strlen(chd) - 1] != '/') { strcat(chd, "/"); } if (strncmp(chd, c->root_dir, strlen(c->root_dir)) != 0) { numeric(c, 550, "No such file or directory."); return -1; } return 0;}/* * cmd_user(): Handles the USER command, and does most of the initial * authentication work. User names are limited to 16 * characters, by force... */int cmd_user(struct conn * const c){ strncpy(c->username, c->recv_buf, 16); c->username[16] = 0; if (strcasecmp(c->username, "anonymous") == 0) { strcpy(c->username, "ftp"); } if (strcasecmp(c->username, "ftp") == 0) { numeric(c, 331, "Login OK, send password (your e-mail)."); c->auth = 1; } else { numeric(c, 331, "Password required for %s.", c->username); c->auth = 2; } return 1;}/* * cmd_pass(): Handles the PASS command, and checks the password. * This function is rather long and complicated, mostly * because there are so many ways of doing users * (including my nonroot system) out there... And we * don't even support PAM or real shadow passwords (with * expiry etc) yet... */int cmd_pass(struct conn * const c){#if WANT_NONROOT c->auth = nr_userinfo(c->username, &c->uid, c->curr_dir, c->root_dir, c->recv_buf);#else /* !WANT_NONROOT */#if WANT_SHADOW && HAVE_SHADOW_H struct spwd *s;#endif struct passwd *p; p = getpwnam(c->username);#if WANT_SHADOW && HAVE_SHADOW_H s = getspnam(c->username);#endif if (p == NULL) { c->auth = 0; } else { c->uid = p->pw_uid; strncpy(c->curr_dir, p->pw_dir, 254); c->curr_dir[254] = 0; } if (c->auth == 1) { if (c->curr_dir[strlen(c->curr_dir) - 1] != '/') { strcat(c->curr_dir, "/"); } strcpy(c->root_dir, c->curr_dir); c->auth = 3; } else if (c->auth != 0) { strcpy(c->root_dir, "/"); if (strcmp(crypt(c->recv_buf, p->pw_passwd), p->pw_passwd) != 0#if WANT_SHADOW && HAVE_SHADOW_H && (s == NULL || strcmp(crypt(c->recv_buf, s->sp_pwdp), s->sp_pwdp) != 0)#endif ) { c->auth = 0; } else { c->auth = 3; } }#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 {#if WANT_MESSAGE chdir(c->curr_dir); dump_file(c, 230, "welcome.msg");#endif numeric(c, 230, "User logged in."); } return 1;}/* * cmd_acct(): Handle (ignore) the ACCT command. I don't see how we * could make use of this command... wu-ftpd doesn't, either. * However, wu-ftpd (at least the version I have here) uses * 502, which isn't a legal error code according to RFC959. * 202, on the other hand, is, and seems to be applicable. * * I'm unsure if this one should require setuid or not, but * I feel that the RFC959 intention is having it _before_ * USER/PASS. Therefore, this one runs with root privilegies :-) */int cmd_acct(struct conn * const c){ numeric(c, 202, "ACCT ignored OK -- not applicable on this system."); return 1;}/* * cmd_port(): Handles the PORT command, and sets up the data socket. * Making a brief uid=0 (root) appearance (to bind the socket) -- * I feel it's safer that way (instead of running as root * the whole way), in case there are some weird overflows * somewhere. */int cmd_port(struct conn * const c){ short int a0, a1, a2, a3, p0, p1; int i, sock, err; struct ftran *f; struct sockaddr_in sin; if ((c->transfer != NULL) && (c->transfer->state >= 4)) { numeric(c, 500, "Sorry, only one transfer at a time."); return 1; } sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); TRAP_ERROR(sock == -1, 500, return 1); destroy_ftran(c->transfer); c->transfer = f = alloc_new_ftran(sock, c); i = sscanf(c->recv_buf, "%3hu,%3hu,%3hu,%3hu,%3hu,%3hu", &a0, &a1, &a2, &a3, &p0, &p1); if (i < 6) { numeric(c, 501, "Parse error."); } else { const int one = 1; int tmp; /* bind to own address, port 20 (FTP data) */ tmp = sizeof(sin); err = getsockname(c->sock, (struct sockaddr *)&sin, &tmp); TRAP_ERROR(err == -1, 500, return 1); sin.sin_port = FTP_PORT - 1; numeric(c, 200, "PORT command OK."); /* note that bind() might well fail, so we don't error check */#if !WANT_NONROOT /* need root privilegies for a short while */ seteuid(getuid());#endif bind(sock, (struct sockaddr *)&sin, sizeof(sin));#if !WANT_NONROOT seteuid(c->uid);#endif f->sin.sin_family = AF_INET; f->sin.sin_addr.s_addr = htonl( ((unsigned char)(a0) << 24) + ((unsigned char)(a1) << 16) + ((unsigned char)(a2) << 8) + ((unsigned char)(a3) )); f->sin.sin_port = htons( ((unsigned char)(p0) << 8) + ((unsigned char)(p1) )); f->sock = sock; f->state = 3; i = 1; ioctl(f->sock, FIONBIO, &one); } return 1;}/* * cmd_pasv(): Handles the PASV command, and sets up the data socket. * Uses port numbers > 1024, since it doesn't run as root. */int cmd_pasv(struct conn * const c){ struct ftran *f; int tmp, sock, err; unsigned int one = 1; struct sockaddr_in addr; if ((c->transfer != NULL) && (c->transfer->state >= 4)) { numeric(c, 503, "Sorry, only one transfer at once."); return 1; } destroy_ftran(c->transfer); sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); TRAP_ERROR(sock == -1, 500, return 1); err = add_fd(sock, POLLIN); TRAP_ERROR(err != 0, 501, return 1); c->transfer = f = alloc_new_ftran(sock, c); ioctl(sock, FIONBIO, &one); /* setup socket */ tmp = sizeof(addr); err = getsockname(c->sock, (struct sockaddr *)&addr, &tmp); TRAP_ERROR(err == -1, 500, return 1); addr.sin_port = 0; /* let the system choose */ err = bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr)); TRAP_ERROR(err == -1, 500, return 1); tmp = sizeof(addr); err = getsockname(sock, (struct sockaddr *)&addr, &tmp); TRAP_ERROR(err == -1, 500, return 1); err = listen(f->sock, 1); TRAP_ERROR(err == -1, 500, return 1); f->state = 1; numeric(c, 227, "Entering passive mode (%u,%u,%u,%u,%u,%u)", (htonl(addr.sin_addr.s_addr) & 0xff000000) >> 24, (htonl(addr.sin_addr.s_addr) & 0x00ff0000) >> 16, (htonl(addr.sin_addr.s_addr) & 0x0000ff00) >> 8, (htonl(addr.sin_addr.s_addr) & 0x000000ff),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -