X-Git-Url: https://git.sesse.net/?p=betaftpd;a=blobdiff_plain;f=cmds.c;h=2e38823ab9d4716ae5c2f6aae5805e513c819fca;hp=c17fef9508dfe79c25ec6340ba781fdd7326e82c;hb=HEAD;hpb=e782726047aa73c6dc592981d75ea21e1adf3d3d diff --git a/cmds.c b/cmds.c index c17fef9..2e38823 100644 --- a/cmds.c +++ b/cmds.c @@ -149,7 +149,7 @@ extern fd_set master_fds, master_send_fds; struct handler { char cmd_name[6]; char add_cmlen; /* =1 if the command takes an argument */ - int (*callback)(struct conn * const); + void (*callback)(struct conn * const); char min_auth; #if !WANT_NONROOT char do_setuid; /* =1 if root is not *really* needed */ @@ -263,7 +263,7 @@ int do_chdir(struct conn * const c, const char * const newd) * authentication work. User names are limited to 16 * characters, by force... */ -int cmd_user(struct conn * const c) +void cmd_user(struct conn * const c) { strncpy(c->username, c->recv_buf, 16); c->username[16] = 0; @@ -278,7 +278,6 @@ int cmd_user(struct conn * const c) numeric(c, 331, "Password required for %s.", c->username); c->auth = 2; } - return 1; } /* @@ -289,7 +288,7 @@ int cmd_user(struct conn * const c) * don't even support PAM or real shadow passwords (with * expiry etc) yet... */ -int cmd_pass(struct conn * const c) +void cmd_pass(struct conn * const c) { #if WANT_NONROOT c->auth = nr_userinfo(c->username, &c->uid, c->curr_dir, c->root_dir, @@ -329,7 +328,7 @@ int cmd_pass(struct conn * const c) ) { c->auth = 0; } else { - c->auth = 3; + c->auth = 4; } } #endif /* !WANT_NONROOT */ @@ -345,9 +344,9 @@ 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; } /* @@ -361,10 +360,9 @@ int cmd_pass(struct conn * const c) * 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) +void cmd_acct(struct conn * const c) { numeric(c, 202, "ACCT ignored OK -- not applicable on this system."); - return 1; } /* @@ -374,7 +372,7 @@ int cmd_acct(struct conn * const c) * the whole way), in case there are some weird overflows * somewhere. */ -int cmd_port(struct conn * const c) +void cmd_port(struct conn * const c) { short int a0, a1, a2, a3, p0, p1; int i, sock, err; @@ -383,11 +381,11 @@ int cmd_port(struct conn * const c) if ((c->transfer != NULL) && (c->transfer->state >= 4)) { numeric(c, 500, "Sorry, only one transfer at a time."); - return 1; + return; } sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - TRAP_ERROR(sock == -1, 500, return 1); + TRAP_ERROR(sock == -1, 500, return); destroy_ftran(c->transfer); c->transfer = f = alloc_new_ftran(sock, c); @@ -402,7 +400,7 @@ int cmd_port(struct conn * const c) /* 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); + TRAP_ERROR(err == -1, 500, return); sin.sin_port = FTP_PORT - 1; numeric(c, 200, "PORT command OK."); @@ -415,8 +413,8 @@ int cmd_port(struct conn * const c) #endif bind(sock, (struct sockaddr *)&sin, sizeof(sin)); #if !WANT_NONROOT - seteuid(c->uid); setegid(c->gid); + seteuid(c->uid); #endif f->sin.sin_family = AF_INET; @@ -434,14 +432,13 @@ int cmd_port(struct conn * const c) 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) +void cmd_pasv(struct conn * const c) { struct ftran *f; int tmp, sock, err; @@ -450,14 +447,14 @@ int cmd_pasv(struct conn * const c) if ((c->transfer != NULL) && (c->transfer->state >= 4)) { numeric(c, 503, "Sorry, only one transfer at once."); - return 1; + return; } destroy_ftran(c->transfer); sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - TRAP_ERROR(sock == -1, 500, return 1); + TRAP_ERROR(sock == -1, 500, return); err = add_fd(sock, POLLIN); - TRAP_ERROR(err != 0, 501, return 1); + TRAP_ERROR(err != 0, 501, return); c->transfer = f = alloc_new_ftran(sock, c); @@ -466,18 +463,18 @@ int cmd_pasv(struct conn * const c) /* setup socket */ tmp = sizeof(addr); err = getsockname(c->sock, (struct sockaddr *)&addr, &tmp); - TRAP_ERROR(err == -1, 500, return 1); + TRAP_ERROR(err == -1, 500, return); addr.sin_port = 0; /* let the system choose */ err = bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr)); - TRAP_ERROR(err == -1, 500, return 1); + TRAP_ERROR(err == -1, 500, return); tmp = sizeof(addr); err = getsockname(sock, (struct sockaddr *)&addr, &tmp); - TRAP_ERROR(err == -1, 500, return 1); + TRAP_ERROR(err == -1, 500, return); err = listen(f->sock, 1); - TRAP_ERROR(err == -1, 500, return 1); + TRAP_ERROR(err == -1, 500, return); f->state = 1; numeric(c, 227, "Entering passive mode (%u,%u,%u,%u,%u,%u)", @@ -487,7 +484,6 @@ int cmd_pasv(struct conn * const c) (htonl(addr.sin_addr.s_addr) & 0x000000ff), (htons(addr.sin_port) & 0xff00) >> 8, (htons(addr.sin_port) & 0x00ff)); - return 1; } /* @@ -498,7 +494,7 @@ int cmd_pasv(struct conn * const c) * /betaftpd.users file, if you use nonroot. If not, it's a bug. * Try to get it _reproducible_, and mail it to me. */ -int cmd_pwd(struct conn * const c) +void cmd_pwd(struct conn * const c) { char temp[512], *cdir = NULL; @@ -506,7 +502,6 @@ int cmd_pwd(struct conn * const c) if (cdir != NULL) { numeric(c, 257, "\"%s\" is current working directory.", cdir); } - return 1; } /* @@ -521,6 +516,7 @@ char *do_pwd(struct conn * const c, char * const retbuf, const char * const dir) char *cdir = NULL; strcpy(retbuf, dir); + if (strncmp(retbuf, c->root_dir, strlen(c->root_dir)) != 0) { numeric(c, 550, "curr_dir is outside root_dir, please contact site administrator."); return NULL; @@ -540,10 +536,9 @@ char *do_pwd(struct conn * const c, char * const retbuf, const char * const dir) * cmd_cwd(): Handles CWD command (change working directory). Uses * cmd_cwd_internal() (see below). */ -int cmd_cwd(struct conn * const c) +void cmd_cwd(struct conn * const c) { cmd_cwd_internal(c, c->recv_buf); - return 1; } /* @@ -556,10 +551,9 @@ int cmd_cwd(struct conn * const c) * an error, instead of just staying in the root directory (as * the OS and thus wu-ftpd does). */ -int cmd_cdup(struct conn * const c) +void cmd_cdup(struct conn * const c) { cmd_cwd_internal(c, ".."); - return 1; } /* @@ -594,11 +588,10 @@ void cmd_cwd_internal(struct conn * const c, const char * const newd) * sending functions to start at the correct number. We should * perhaps add some better error checking to this? */ -int cmd_rest(struct conn * const c) +void cmd_rest(struct conn * const c) { c->rest_pos = abs(atoi(c->recv_buf)); numeric(c, 350, "Setting resume at %u bytes.", c->rest_pos); - return 1; } /* @@ -608,19 +601,19 @@ int cmd_rest(struct conn * const c) * connection occurs (or succeeds, if we're using PORT mode), * the actual file transfer begins. */ -int cmd_retr(struct conn * const c) +void cmd_retr(struct conn * const c) { struct ftran *f = c->transfer; if ((f == NULL) || ((f->state != 1) && (f->state != 3))) { numeric(c, 425, "No data connection set up; please use PASV or PORT."); - return 1; + return; } #if WANT_ASCII if ((c->rest_pos > 0) && (c->ascii_mode == 1)) { numeric(c, 500, "Cannot resume while in ASCII mode."); - return 1; + return; } #endif @@ -643,7 +636,6 @@ int cmd_retr(struct conn * const c) #endif prepare_for_transfer(f); } - return 1; } #if WANT_UPLOAD @@ -651,20 +643,18 @@ int cmd_retr(struct conn * const c) * cmd_stor(): Handles the STOR command (upload file). Pushes the * work down to do_store(), below. */ -int cmd_stor(struct conn * const c) +void cmd_stor(struct conn * const c) { do_store(c, 0); - return 1; } /* * cmd_appe(): Handles the APPE command (append to file). Pushes * the work down to do_store(), below. */ -int cmd_appe(struct conn * const c) +void cmd_appe(struct conn * const c) { do_store(c, 1); - return 1; } /* @@ -721,22 +711,21 @@ void do_store(struct conn * const c, const int append) * size of all the files in the directory, rather how * much space the directory inode uses. */ -int cmd_size(struct conn * const c) +void cmd_size(struct conn * const c) { #if WANT_ASCII if (c->ascii_mode) { numeric(c, 550, "SIZE not available in ASCII mode."); - return 1; + return; } #endif { const char * const fname = translate_path(c, c->recv_buf); struct stat buf; - TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return 1); + TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return); numeric(c, 213, "%lu", (unsigned long)(buf.st_size)); - return 1; } } @@ -745,86 +734,81 @@ int cmd_size(struct conn * const c) * date/time of a file. See the comments on cmd_size(), * above. */ -int cmd_mdtm(struct conn * const c) +void cmd_mdtm(struct conn * const c) { const char * const fname = translate_path(c, c->recv_buf); struct stat buf; struct tm *m; - TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return 1); + TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return); m = gmtime(&(buf.st_mtime)); /* at least wu-ftpd does it in GMT */ numeric(c, 213, "%u%02u%02u%02u%02u%02u", m->tm_year + 1900, m->tm_mon + 1, m->tm_mday, m->tm_hour, m->tm_min, m->tm_sec); - return 1; } /* * cmd_abor(): Handle the ABOR command (abort a file transfer). This should * be clean enough, but isn't tested extensively. */ -int cmd_abor(struct conn * const c) +void cmd_abor(struct conn * const c) { if (c->transfer != NULL) { numeric(c, 426, "File transfer aborted."); destroy_ftran(c->transfer); } numeric(c, 226, "ABOR command processed OK."); - return 1; } /* * cmd_dele(): Handle the DELE command (delete a file). */ -int cmd_dele(struct conn * const c) +void cmd_dele(struct conn * const c) { const char * const fname = translate_path(c, c->recv_buf); - TRAP_ERROR(fname == NULL || unlink(fname) == -1, 550, return 1); + TRAP_ERROR(fname == NULL || unlink(fname) == -1, 550, return); numeric(c, 250, "File deleted OK."); - return 1; } /* * cmd_rnfr(): Handle the RNFR command (take a filename to rename from). */ -int cmd_rnfr(struct conn * const c) +void cmd_rnfr(struct conn * const c) { const char * const fname = translate_path(c, c->recv_buf); char cwd[256]; struct stat buf; c->rename_from[0] = '\0'; - if (fname == NULL) return 1; + if (fname == NULL) return; getcwd(cwd, 256); snprintf(c->rename_from, 256, "%s/%s", cwd, fname); /* Just check that the file exists. */ - TRAP_ERROR(lstat(c->rename_from, &buf) == -1, 550, c->rename_from[0] = '\0'; return 1); + TRAP_ERROR(lstat(c->rename_from, &buf) == -1, 550, c->rename_from[0] = '\0'; return); numeric(c, 350, "File exists, send RNTO."); - return 1; } /* * cmd_rnto(): Handle the RNTO command (do the actual renaming). */ -int cmd_rnto(struct conn * const c) +void cmd_rnto(struct conn * const c) { const char * const fname = translate_path(c, c->recv_buf); - if (fname == NULL) return 1; + if (fname == NULL) return; if (c->rename_from[0] == '\0') { numeric(c, 503, "Please send RNFR first."); - return 1; + return; } - TRAP_ERROR(rename(c->rename_from, fname) == -1, 550, c->rename_from[0] = '\0'; return 1); + TRAP_ERROR(rename(c->rename_from, fname) == -1, 550, c->rename_from[0] = '\0'; return); c->rename_from[0] = '\0'; numeric(c, 250, "File renamed successfully."); - return 1; } /* @@ -841,18 +825,20 @@ int cmd_rnto(struct conn * const c) * easy :-) (This code isn't quite easy to understand, because * temp2 is used twice, in two different roles.) */ -int cmd_mkd(struct conn * const c) +void cmd_mkd(struct conn * const c) { const char * const fname = translate_path(c, c->recv_buf); char temp[512], temp2[1024], *cdir; int i, j; - TRAP_ERROR(fname == NULL || mkdir(fname, 0755) == -1, 550, return 1); + TRAP_ERROR(fname == NULL || mkdir(fname, 0755) == -1, 550, return); chdir(fname); getcwd(temp2, 512); cdir = do_pwd(c, temp, temp2); + if (do_pwd == NULL) return; + /* double the quotes in the output */ for (i = 0, j = 0; i <= strlen(cdir); i++, j++) { temp2[j] = cdir[i]; @@ -861,20 +847,18 @@ int cmd_mkd(struct conn * const c) } } numeric(c, 257, "\"%s\" created.", temp2); - return 1; } /* * cmd_rmd(): Handle the RMD/XRMD command. Works just like DELE, only for * directories. */ -int cmd_rmd(struct conn * const c) +void cmd_rmd(struct conn * const c) { const char * const fname = translate_path(c, c->recv_buf); - TRAP_ERROR(fname == NULL || rmdir(fname) == -1, 550, return 1); + TRAP_ERROR(fname == NULL || rmdir(fname) == -1, 550, return); numeric(c, 250, "Directory deleted."); - return 1; } /* @@ -888,10 +872,9 @@ int cmd_rmd(struct conn * const c) * to the full-screen mode, but close to no FTP clients send this * command, and it would touch too much code. */ -int cmd_allo(struct conn * const c) +void cmd_allo(struct conn * const c) { numeric(c, 202, "No storage allocation necessary."); - return 1; } /* @@ -908,7 +891,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] = { @@ -921,7 +904,7 @@ char ftran_state[6][42] = { }; #endif -int cmd_stat(struct conn * const c) +void cmd_stat(struct conn * const c) { #if WANT_STAT char buf[1024]; @@ -950,13 +933,11 @@ int cmd_stat(struct conn * const c) err = send(c->sock, buf, i, 0); if (err == -1 && errno == EPIPE) { - destroy_conn(c); - return 0; + this->free_me = 1; } #else numeric(c, 502, "STAT command disabled for security reasons."); #endif - return 1; } #if HAVE_MMAP @@ -1106,7 +1087,7 @@ int long_listing(char * const retbuf, const char * const pathname, const int do_ * long listing (of type `ls -l'). The listing work is * done by do_listing(), below. */ -int cmd_list(struct conn * const c) +void cmd_list(struct conn * const c) { struct list_options lo; @@ -1115,7 +1096,6 @@ int cmd_list(struct conn * const c) lo.classify = 0; do_listing(c, &lo); - return 1; } /* @@ -1125,7 +1105,7 @@ int cmd_list(struct conn * const c) * FTP clients don't have a clue about what they send out). * The listing work is done by do_listing(), below. */ -int cmd_nlst(struct conn * const c) +void cmd_nlst(struct conn * const c) { struct list_options lo; @@ -1134,7 +1114,6 @@ int cmd_nlst(struct conn * const c) lo.classify = 0; do_listing(c, &lo); - return 1; } /* @@ -1423,25 +1402,23 @@ int list_core(struct conn * const c, const char * const pathname, * cmd_noop(): Handles the NOOP command. Does nothing, doesn't even * reset the timeout. */ -int cmd_noop(struct conn * const c) +void cmd_noop(struct conn * const c) { numeric(c, 200, "NOOP command successful."); - return 1; } /* * cmd_syst(): Handles the SYST command. Returns the system identification. */ -int cmd_syst(struct conn * const c) +void cmd_syst(struct conn * const c) { numeric(c, 215, "UNIX Type: L%u", NBBY); - return 1; } /* * cmd_type(): Handles the TYPE command. */ -int cmd_type(struct conn * const c) +void cmd_type(struct conn * const c) { #if WANT_ASCII c->recv_buf[0] &= (255-32); /* convert to upper case */ @@ -1457,13 +1434,12 @@ int cmd_type(struct conn * const c) #else numeric(c, 200, "TYPE ignored (always I)"); #endif - return 1; } /* * cmd_mode(): Handles the MODE command. Only stream mode is supported. */ -int cmd_mode(struct conn * const c) +void cmd_mode(struct conn * const c) { c->recv_buf[0] &= (255-32); /* convert to upper case */ if (c->recv_buf[0] == 'S') { @@ -1471,13 +1447,12 @@ int cmd_mode(struct conn * const c) } else { numeric(c, 504, "Unknown mode."); } - return 1; } /* * cmd_stru(): Handles the STRU command. Only file mode is supported. */ -int cmd_stru(struct conn * const c) +void cmd_stru(struct conn * const c) { c->recv_buf[0] &= (255-32); /* convert to upper case */ if (c->recv_buf[0] == 'F') { @@ -1485,7 +1460,6 @@ int cmd_stru(struct conn * const c) } else { numeric(c, 504, "Unknown structure."); } - return 1; } /* @@ -1505,21 +1479,19 @@ int cmd_stru(struct conn * const c) * like an error, since the error code is intended for helpful * messages? :-) */ -int cmd_help(struct conn * const c) +void cmd_help(struct conn * const c) { numeric(c, 414, "Sorry, no detailed help; use standard FTP commands."); - return 1; } /* * cmd_quit(): Handles the QUIT command, which shuts down the control * and data sockets. */ -int cmd_quit(struct conn * const c) +void cmd_quit(struct conn * const c) { numeric(c, 221, "Have a nice day!"); - destroy_conn(c); - return 0; + c->free_me = 1; } /* @@ -1528,7 +1500,7 @@ int cmd_quit(struct conn * const c) * copied directly from alloc_new_conn() -- perhaps we should * modularize this? */ -int cmd_rein(struct conn * const c) +void cmd_rein(struct conn * const c) { destroy_ftran(c->transfer); c->buf_len = c->auth = c->rest_pos = 0; @@ -1543,8 +1515,6 @@ int cmd_rein(struct conn * const c) time(&(c->last_transfer)); numeric(c, 220, "BetaFTPD " VERSION " ready."); - - return 1; } #if DOING_PROFILING @@ -1608,8 +1578,8 @@ void parse_command(struct conn *c) #if !WANT_NONROOT if (h->do_setuid) { - seteuid(c->uid); setegid(c->gid); + seteuid(c->uid); } else { seteuid(getuid()); setegid(getgid()); @@ -1625,9 +1595,18 @@ void parse_command(struct conn *c) schar = c->recv_buf[cmlen]; c->recv_buf[cmlen] = 0; - - /* result of zero means the connection is freed */ - if (h->callback(c)) { + + message_buf[0] = '\0'; + h->callback(c); + if (message_buf[0] != '\0') { + /* send any feedback we might have */ + int err = send(c->sock, message_buf, strlen(message_buf), 0); + if (err == -1 && errno == EPIPE) { + c->free_me = 1; + } + } + + if (!c->free_me) { c->recv_buf[cmlen] = schar; #if !WANT_NONROOT if (h->do_setuid) { @@ -1642,8 +1621,12 @@ void parse_command(struct conn *c) } } while ((++h)->callback != NULL); - numeric(c, 500, "Sorry, no such command."); remove_bytes(c, cmlen); + { + char error[] = "500 Sorry, no such command.\r\n"; + if (send(c->sock, error, strlen(error), 0) == -1 && errno == EPIPE) + destroy_conn(c); + } } /* @@ -1844,7 +1827,7 @@ int prepare_for_listing(struct conn * const c, char ** const ptr, case 'F': lo->classify = 1; break; - case ' ': + case '\0': fptr = optr + 1; *(optr--) = 0; break; @@ -1855,7 +1838,7 @@ int prepare_for_listing(struct conn * const c, char ** const ptr, } else { fptr = c->recv_buf; } - + /* then we chdir to the dir in fptr (if any) */ tmp = fptr ? strrchr(fptr, '/') : NULL; if (tmp != NULL) {