ftpd.c
上传用户:zibowangxu
上传日期:2007-01-04
资源大小:331k
文件大小:183k
- if (strcmp(realdir, "*") == 0 || strcmp(realdir + strlen(realdir) - 2, "/*") == 0)
- isDir = 2;
- else {
- if (lstat(realdir, &s) == 0) {
- if (S_ISDIR(s.st_mode)) {
- strcat(realdir, "/*");
- isDir = 2;
- }
- }
- }
- if (isDir == 0) {
- if (ls_l) {
- lsentry = ls_file(realdir, 0, 0, ls_F);
- if (lsentry != NULL) {
- if (draconian_FILE != NULL) {
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- fputs(lsentry, out);
- }
- free(lsentry);
- }
- }
- else {
- if (draconian_FILE != NULL) {
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- fputs(realdir, out);
- }
- }
- free(realdir);
- }
- else {
- if (ls_R) {
- numSubdirs = 0;
- subdirs = (char **) malloc(200 * sizeof(char *));
- memset(subdirs, 0, 200 * sizeof(char *));
- }
- dl_size = 65536;
- dirlist = (char *) malloc(65536);
- memset(dirlist, 0, 65536);
- dl_used = 0;
- total = 0;
- memset(&g, 0, sizeof(g));
- if (ls_a) {
- #ifdef GLOB_PERIOD
- if (glob(realdir, GLOB_ERR | GLOB_PERIOD, NULL, &g) != 0)
- g.gl_pathc = 0;
- #else
- dperiod = (char *) malloc(strlen(realdir) + 2);
- memset(dperiod, 0, strlen(realdir) + 2);
- strcpy(dperiod, ".");
- strcat(dperiod, realdir);
- if (glob(dperiod, GLOB_ERR, NULL, &g) != 0)
- g.gl_pathc = 0;
- glob(realdir, GLOB_ERR | GLOB_APPEND, NULL, &g);
- free(dperiod);
- #endif
- }
- else if (glob(realdir, GLOB_ERR, NULL, &g) != 0)
- g.gl_pathc = 0;
- free(realdir);
- for (i = 0; i < g.gl_pathc; i++) {
- c = g.gl_pathv[i];
- if (lstat(c, &s) != -1) {
- if (ls_l) {
- total += s.st_blocks;
- lsentry = ls_file(c, 0, 1, ls_F);
- if (lsentry != NULL) {
- /* This can actually happen even though the lstat() worked -
- if someone deletes the file between the lstat() and ls_file()
- calls. Unlikely, but better safe than sorry... */
- int flag = snprintf(dirlist + dl_used, dl_size - dl_used, "%s", lsentry);
- dl_used += (flag == -1 ? dl_size - dl_used : flag);
- free(lsentry);
- }
- }
- else {
- int flag;
- lsentry = ls_file(c, 1, 1, ls_F);
- flag = snprintf(dirlist + dl_used, dl_size - dl_used, "%s", lsentry);
- dl_used += (flag == -1 ? dl_size - dl_used : flag);
- }
- if ((ls_R != 0) && (S_ISDIR(s.st_mode))
- && (strcmp(c, "..") != 0) && (strcmp(c, ".") != 0)
- && !(strlen(c) > 3 && strcmp(c + strlen(c) - 3, "/..") == 0)
- && !(strlen(c) > 2 && strcmp(c + strlen(c) - 2, "/.") == 0)) {
- subdirs[numSubdirs++] = strdup(c);
- if ((numSubdirs % 200) == 0)
- subdirs = (char **) realloc(subdirs, (numSubdirs + 200) * sizeof(char *));
- }
- }
- if (dl_used + 512 >= dl_size) {
- dl_size += 65536;
- dirlist = (char *) realloc(dirlist, dl_size);
- }
- }
- globfree(&g);
- if (ls_l && isDir == 2 && omit_total == 0) {
- if (draconian_FILE != NULL) {
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- fprintf(out, "total %urn", total);
- }
- }
- if (draconian_FILE != NULL) {
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- fputs(dirlist, out);
- }
- free(dirlist);
- if (ls_R) {
- for (i = 0; i < numSubdirs; i++) {
- if (draconian_FILE != NULL) {
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- fprintf(out, "rn%s:rn", subdirs[i]);
- ls_dir(subdirs[i], ls_a, ls_F, ls_l, ls_R, 0, out);
- }
- free(subdirs[i]);
- }
- free(subdirs);
- }
- }
- }
- void ls(char *file, char nlst)
- {
- FILE *out;
- char free_file = 0;
- char ls_l = 0, ls_a = 0, ls_R = 0, ls_F = 0;
- if (nlst == 0)
- ls_l = 1; /* LIST defaults to ls -l */
- if (file == NULL) {
- file = strdup(".");
- free_file = 1;
- }
- if (strcmp(file, "*") == 0)
- file[0] = '.';
- if (file[0] == '-') { /* options... */
- if (strchr(file, ' ') == 0) {
- if (strchr(file, 'l'))
- ls_l = 1;
- if (strchr(file, 'a'))
- ls_a = 1;
- if (strchr(file, 'R'))
- ls_R = 1;
- if (strchr(file, 'F'))
- ls_F = 1;
- file = strdup(".");
- free_file = 1;
- }
- else {
- if (strchr(file, 'l') != NULL && strchr(file, 'l') < strchr(file, ' '))
- ls_l = 1;
- if (strchr(file, 'a') != NULL && strchr(file, 'a') < strchr(file, ' '))
- ls_a = 1;
- if (strchr(file, 'R') != NULL && strchr(file, 'R') < strchr(file, ' '))
- ls_R = 1;
- if (strchr(file, 'F') != NULL && strchr(file, 'F') < strchr(file, ' '))
- ls_F = 1;
- file = strchr(file, ' ');
- }
- }
- while (file[0] == ' ') /* ignore additional whitespaces between parameters */
- file++;
- if (strlen(file) == 0) {
- file = strdup(".");
- free_file = 1;
- }
- out = dataconn("directory listing", -1, "w");
- draconian_FILE = out;
- transflag++;
- fixpath(file);
- if (file[0] == ' ') {
- if (free_file != 0)
- free(file);
- file = strdup(".");
- free_file = 1;
- }
- ls_dir(file, ls_a, ls_F, ls_l, ls_R, 0, out);
- data = -1;
- pdata = -1;
- if (draconian_FILE != NULL) {
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- fflush(out);
- }
- if (draconian_FILE != NULL) {
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- socket_flush_wait(out);
- }
- if (draconian_FILE != NULL) {
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- fclose(out);
- draconian_FILE = NULL;
- }
- alarm(0);
- transflag = 0;
- reply(226, "Transfer complete.");
- if (free_file != 0)
- free(file);
- }
- #endif /* INTERNAL_LS */
- void retrieve(char *cmd, char *name)
- {
- FILE *fin = NULL, *dout;
- struct stat st, junk;
- int (*closefunc) () = NULL;
- int options = 0;
- int ThisRetrieveIsData = retrieve_is_data;
- time_t start_time = time(NULL);
- char *logname;
- char namebuf[MAXPATHLEN];
- char fnbuf[MAXPATHLEN];
- int TransferComplete = 0;
- struct convert *cptr;
- char realname[MAXPATHLEN];
- int stat_ret = -1;
- extern int checknoretrieve(char *);
- wu_realpath(name, realname, chroot_path);
- #ifdef TRANSFER_COUNT
- #ifdef TRANSFER_LIMIT
- if (retrieve_is_data)
- if (((file_limit_data_out > 0) && (file_count_out >= file_limit_data_out))
- || ((file_limit_data_total > 0) && (file_count_total >= file_limit_data_total))
- || ((data_limit_data_out > 0) && (data_count_out >= data_limit_data_out))
- || ((data_limit_data_total > 0) && (data_count_total >= data_limit_data_total))) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to retrieve %s (Transfer limits exceeded)",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to retrieve %s (Transfer limits exceeded)",
- pw->pw_name, remoteident, realname);
- reply(553, "Permission denied on server. (Transfer limits exceeded)");
- return;
- }
- if (((file_limit_raw_out > 0) && (xfer_count_out >= file_limit_raw_out))
- || ((file_limit_raw_total > 0) && (xfer_count_total >= file_limit_raw_total))
- || ((data_limit_raw_out > 0) && (byte_count_out >= data_limit_raw_out))
- || ((data_limit_raw_total > 0) && (byte_count_total >= data_limit_raw_total))) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to retrieve %s (Transfer limits exceeded)",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to retrieve %s (Transfer limits exceeded)",
- pw->pw_name, remoteident, realname);
- reply(553, "Permission denied on server. (Transfer limits exceeded)");
- return;
- }
- #endif
- #endif
- if (cmd == NULL && (stat_ret = stat(name, &st)) == 0)
- /* there isn't a command and the file exists */
- if (use_accessfile && checknoretrieve(name)) { /* see above. _H */
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to download %s (noretrieve)",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to download %s (noretrieve)",
- pw->pw_name, remoteident, realname);
- return;
- }
- logname = (char *) NULL;
- if (cmd == NULL && stat_ret != 0) { /* file does not exist */
- char *ptr;
- for (cptr = cvtptr; cptr != NULL; cptr = cptr->next) {
- if (!(mangleopts & O_COMPRESS) && (cptr->options & O_COMPRESS))
- continue;
- if (!(mangleopts & O_UNCOMPRESS) && (cptr->options & O_UNCOMPRESS))
- continue;
- if (!(mangleopts & O_TAR) && (cptr->options & O_TAR))
- continue;
- if ((cptr->stripfix) && (cptr->postfix)) {
- int pfxlen = strlen(cptr->postfix);
- int sfxlen = strlen(cptr->stripfix);
- int namelen = strlen(name);
- (void) strcpy(fnbuf, name);
- if (namelen <= pfxlen)
- continue;
- if ((namelen - pfxlen + sfxlen) >= sizeof(fnbuf))
- continue;
- if (strcmp(fnbuf + namelen - pfxlen, cptr->postfix))
- continue;
- *(fnbuf + namelen - pfxlen) = ' ';
- (void) strcat(fnbuf, cptr->stripfix);
- if (stat(fnbuf, &st) != 0)
- continue;
- }
- else if (cptr->postfix) {
- int pfxlen = strlen(cptr->postfix);
- int namelen = strlen(name);
- if (namelen <= pfxlen)
- continue;
- (void) strcpy(fnbuf, name);
- if (strcmp(fnbuf + namelen - pfxlen, cptr->postfix))
- continue;
- *(fnbuf + namelen - pfxlen) = (char) NULL;
- if (stat(fnbuf, &st) != 0)
- continue;
- }
- else if (cptr->stripfix) {
- (void) strcpy(fnbuf, name);
- (void) strcat(fnbuf, cptr->stripfix);
- if (stat(fnbuf, &st) != 0)
- continue;
- }
- else {
- continue;
- }
- if (S_ISDIR(st.st_mode)) {
- if (!cptr->types || !(cptr->types & T_DIR)) {
- reply(550, "Cannot %s directories.", cptr->name);
- return;
- }
- if ((cptr->options & O_TAR)) {
- strcpy(namebuf, fnbuf);
- strcat(namebuf, "/.notar");
- if (stat(namebuf, &junk) == 0) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to tar %s (.notar)",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to tar %s (.notar)",
- pw->pw_name, remoteident, realname);
- reply(550, "Sorry, you may not TAR that directory.");
- return;
- }
- }
- }
- /* XXX: checknoretrieve() test is weak in that if I can't get /etc/passwd
- but I can tar /etc or /, I still win. Be careful out there... _H*
- but you could put .notar in / and /etc and stop that ! */
- if (use_accessfile && checknoretrieve(fnbuf)) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to download %s (noretrieve)",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to download %s (noretrieve)",
- pw->pw_name, remoteident, realname);
- return;
- }
- if (S_ISREG(st.st_mode) && (!cptr->types || (cptr->types & T_REG) == 0)) {
- reply(550, "Cannot %s plain files.", cptr->name);
- return;
- }
- if (S_ISREG(st.st_mode) != 0 && S_ISDIR(st.st_mode) != 0) {
- reply(550, "Cannot %s special files.", cptr->name);
- return;
- }
- if ((!cptr->types || !(cptr->types & T_ASCII)) && deny_badasciixfer(550, ""))
- return;
- logname = &fnbuf[0];
- options |= cptr->options;
- strcpy(namebuf, cptr->external_cmd);
- if ((ptr = strchr(namebuf, ' ')) != NULL)
- *ptr = ' ';
- if (stat(namebuf, &st) != 0) {
- syslog(LOG_ERR, "external command %s not found",
- namebuf);
- reply(550,
- "Local error: conversion program not found. Cannot %s file.",
- cptr->name);
- return;
- }
- (void) retrieve(cptr->external_cmd, logname);
- goto logresults; /* transfer of converted file completed */
- }
- }
- if (cmd == NULL) { /* no command */
- fin = fopen(name, "r"), closefunc = fclose;
- st.st_size = 0;
- }
- else { /* run command */
- static char line[BUFSIZ];
- (void) snprintf(line, sizeof line, cmd, name), name = line;
- fin = ftpd_popen(line, "r", 1), closefunc = ftpd_pclose;
- st.st_size = -1;
- #ifdef HAVE_ST_BLKSIZE
- st.st_blksize = BUFSIZ;
- #endif
- }
- if (fin == NULL) {
- if (errno != 0)
- perror_reply(550, name);
- if ((errno == EACCES) || (errno == EPERM))
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to download %s (file permissions)",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to download %s (file permissions)",
- pw->pw_name, remoteident, realname);
- return;
- }
- if (cmd == NULL &&
- (fstat(fileno(fin), &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)) {
- reply(550, "%s: not a plain file.", name);
- goto done;
- }
- if (restart_point) {
- if (type == TYPE_A) {
- register int i, n, c;
- n = restart_point;
- i = 0;
- while (i++ < n) {
- if ((c = getc(fin)) == EOF) {
- perror_reply(550, name);
- goto done;
- }
- if (c == 'n')
- i++;
- }
- }
- else if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) {
- perror_reply(550, name);
- goto done;
- }
- }
- dout = dataconn(name, st.st_size, "w");
- if (dout == NULL)
- goto done;
- #ifdef BUFFER_SIZE
- #ifdef THROUGHPUT
- TransferComplete = send_data(name, fin, dout, BUFFER_SIZE);
- #else
- TransferComplete = send_data(fin, dout, BUFFER_SIZE);
- #endif
- #else
- #ifdef HAVE_ST_BLKSIZE
- #ifdef THROUGHPUT
- TransferComplete = send_data(name, fin, dout, st.st_blksize * 2);
- #else
- TransferComplete = send_data(fin, dout, st.st_blksize * 2);
- #endif
- #else
- #ifdef THROUGHPUT
- TransferComplete = send_data(name, fin, dout, BUFSIZ);
- #else
- TransferComplete = send_data(fin, dout, BUFSIZ);
- #endif
- #endif
- #endif
- (void) fclose(dout);
- logresults:
- if (ThisRetrieveIsData)
- fb_realpath((logname != NULL) ? logname : name, LastFileTransferred);
- if (log_outbound_xfers && (xferlog || syslogmsg) && (cmd == 0)) {
- char msg[MAXPATHLEN + 2 * MAXHOSTNAMELEN + 100 + 128]; /* AUTHNAMESIZE is 100 */
- size_t msglen; /* for stupid_sprintf */
- int xfertime = time(NULL) - start_time;
- time_t curtime = time(NULL);
- int loop;
- if (!xfertime)
- xfertime++;
- #ifdef XFERLOG_REALPATH
- wu_realpath((logname != NULL) ? logname : name, &namebuf[0], chroot_path);
- #else
- fb_realpath((logname != NULL) ? logname : name, &namebuf[0]);
- #endif
- for (loop = 0; namebuf[loop]; loop++)
- if (isspace(namebuf[loop]) || iscntrl(namebuf[loop]))
- namebuf[loop] = '_';
- /* Some systems use one format, some another. This takes care of the garbage */
- #ifndef L_FORMAT /* Autoconf detects this... */
- #if (defined(BSD) && (BSD >= 199103)) && !defined(LONGOFF_T)
- #define L_FORMAT "qd"
- #else
- #ifdef _AIX42
- #define L_FORMAT "lld"
- #else
- #ifdef SOLARIS_2
- #define L_FORMAT "ld"
- #else
- #define L_FORMAT "d"
- #endif
- #endif
- #endif
- #endif
- /* Some sprintfs can't deal with a lot of arguments, so we split this */
- /* Note it also needs fixing for C9X, so we ALWAYS split it. */
- sprintf(msg, "%.24s %d %s %" L_FORMAT " ",
- ctime(&curtime),
- xfertime,
- remotehost,
- byte_count
- );
- msglen = strlen(msg); /* sigh */
- snprintf(msg + msglen, sizeof(msg) - msglen, "%s %c %s %c %c %s ftp %d %s %cn",
- namebuf,
- (type == TYPE_A) ? 'a' : 'b',
- opt_string(options),
- 'o',
- anonymous ? 'a' : (guest ? 'g' : 'r'),
- anonymous ? guestpw : pw->pw_name,
- authenticated,
- authenticated ? authuser : "*",
- TransferComplete ? 'c' : 'i'
- );
- /* Ensure msg always ends with 'n' */
- if (strlen(msg) == sizeof(msg) - 1)
- msg[sizeof(msg) - 2] = 'n';
- if (syslogmsg != 1)
- write(xferlog, msg, strlen(msg));
- if (syslogmsg != 0)
- syslog(LOG_INFO, "xferlog (send): %s", msg + 25);
- }
- data = -1;
- pdata = -1;
- done:
- if (closefunc)
- (*closefunc) (fin);
- }
- void store(char *name, char *mode, int unique)
- {
- FILE *fout, *din;
- struct stat st;
- int TransferIncomplete = 1;
- char *gunique(char *local);
- time_t start_time = time(NULL);
- struct aclmember *entry = NULL;
- int fdout;
- char realname[MAXPATHLEN];
- #ifdef OVERWRITE
- int overwrite = 1;
- int exists = 0;
- #endif /* OVERWRITE */
- int open_flags = 0;
- #ifdef UPLOAD
- mode_t oldmask;
- uid_t uid;
- gid_t gid;
- uid_t oldid;
- int f_mode = -1, match_value = -1;
- int valid = 0;
- open_flags = (O_RDWR | O_CREAT |
- ((mode != NULL && *mode == 'a') ? O_APPEND : O_TRUNC));
- #endif /* UPLOAD */
- wu_realpath(name, realname, chroot_path);
- #ifdef TRANSFER_COUNT
- #ifdef TRANSFER_LIMIT
- if (((file_limit_data_in > 0) && (file_count_in >= file_limit_data_in))
- || ((file_limit_data_total > 0) && (file_count_total >= file_limit_data_total))
- || ((data_limit_data_in > 0) && (data_count_in >= data_limit_data_in))
- || ((data_limit_data_total > 0) && (data_count_total >= data_limit_data_total))) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to upload %s (Transfer limits exceeded)",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to upload %s (Transfer limits exceeded)",
- pw->pw_name, remoteident, realname);
- reply(553, "Permission denied on server. (Transfer limits exceeded)");
- return;
- }
- if (((file_limit_raw_in > 0) && (xfer_count_in >= file_limit_raw_in))
- || ((file_limit_raw_total > 0) && (xfer_count_total >= file_limit_raw_total))
- || ((data_limit_raw_in > 0) && (byte_count_in >= data_limit_raw_in))
- || ((data_limit_raw_total > 0) && (byte_count_total >= data_limit_raw_total))) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to upload %s (Transfer limits exceeded)",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to upload %s (Transfer limits exceeded)",
- pw->pw_name, remoteident, realname);
- reply(553, "Permission denied on server. (Transfer limits exceeded)");
- return;
- }
- #endif
- #endif
- if (unique && stat(name, &st) == 0 &&
- (name = gunique(name)) == NULL)
- return;
- /*
- * check the filename, is it legal?
- */
- if ((fn_check(name)) <= 0) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to upload "%s" (path-filter)",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to upload "%s" (path-filter)",
- pw->pw_name, remoteident, realname);
- return;
- }
- #ifdef OVERWRITE
- /* if overwrite permission denied and file exists... then deny the user
- * permission to write the file. */
- while (getaclentry("overwrite", &entry) && ARG0 && ARG1 != NULL) {
- if (type_match(ARG1))
- if (strcasecmp(ARG0, "yes") != 0) {
- overwrite = 0;
- open_flags |= O_EXCL;
- }
- }
- #ifdef PARANOID
- overwrite = 0;
- #endif
- if (!stat(name, &st))
- exists = 1;
- if (!overwrite && exists) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to overwrite %s",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to overwrite %s",
- pw->pw_name, remoteident, realname);
- reply(553, "%s: Permission denied on server. (Overwrite)", name);
- return;
- }
- #endif /* OVERWRITE */
- #ifdef UPLOAD
- if ((match_value = upl_check(name, &uid, &gid, &f_mode, &valid)) < 0) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to upload %s (upload denied)",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to upload %s (upload denied)",
- pw->pw_name, remoteident, realname);
- return;
- }
- /* do not truncate the file if we are restarting */
- if (restart_point)
- open_flags &= ~O_TRUNC;
- /* if the user has an explicit new file mode, than open the file using
- * that mode. We must take care to not let the umask affect the file
- * mode.
- *
- * else open the file and let the default umask determine the file mode. */
- if (f_mode >= 0) {
- oldmask = umask(0000);
- fdout = open(name, open_flags, f_mode);
- umask(oldmask);
- }
- else
- fdout = open(name, open_flags, 0666);
- if (fdout < 0) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to upload %s (permissions)",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to upload %s (permissions)",
- pw->pw_name, remoteident, realname);
- perror_reply(553, name);
- return;
- }
- /* if we have a uid and gid, then use them. */
- #ifdef OVERWRITE
- if (!exists)
- #endif
- if (valid > 0) {
- oldid = geteuid();
- if (uid != 0)
- (void) seteuid((uid_t) uid);
- if ((uid == 0) || ((fchown(fdout, uid, gid)) < 0)) {
- delay_signaling(); /* we can't allow any signals while euid==0: kinch */
- (void) seteuid((uid_t) 0);
- if ((fchown(fdout, uid, gid)) < 0) {
- (void) seteuid(oldid);
- enable_signaling(); /* we can allow signals once again: kinch */
- perror_reply(550, "fchown");
- return;
- }
- (void) seteuid(oldid);
- enable_signaling(); /* we can allow signals once again: kinch */
- }
- else
- (void) seteuid(oldid);
- }
- #endif /* UPLOAD */
- if (restart_point && (open_flags & O_APPEND) == 0)
- mode = "r+";
- #ifdef UPLOAD
- fout = fdopen(fdout, mode);
- #else
- fout = fopen(name, mode);
- #endif /* UPLOAD */
- if (fout == NULL) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to upload %s (permissions)",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to upload %s (permissions)",
- pw->pw_name, remoteident, realname);
- perror_reply(553, name);
- return;
- }
- if (restart_point) {
- if (type == TYPE_A) {
- register int i, n, c;
- n = restart_point;
- i = 0;
- while (i++ < n) {
- if ((c = getc(fout)) == EOF) {
- perror_reply(550, name);
- goto done;
- }
- if (c == 'n')
- i++;
- }
- /* We must do this seek to "current" position because we are
- * changing from reading to writing. */
- if (fseek(fout, 0L, SEEK_CUR) < 0) {
- perror_reply(550, name);
- goto done;
- }
- }
- else if (lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
- perror_reply(550, name);
- goto done;
- }
- }
- din = dataconn(name, (off_t) - 1, "r");
- if (din == NULL)
- goto done;
- TransferIncomplete = receive_data(din, fout);
- (void) fclose(din);
- if (TransferIncomplete == 0) {
- if (unique)
- reply(226, "Transfer complete (unique file name:%s).", name);
- else
- reply(226, "Transfer complete.");
- }
- fb_realpath(name, LastFileTransferred);
- #ifdef MAIL_ADMIN
- if (anonymous && incmails > 0) {
- FILE *sck = NULL;
- unsigned char temp = 0, temp2 = 0;
- char pathname[MAXPATHLEN];
- while ((temp < mailservers) && (sck == NULL))
- sck = SockOpen(mailserver[temp++], 25);
- if (sck == NULL) {
- syslog(LOG_ERR, "Can't connect to a mailserver.");
- goto mailfail;
- }
- if (Reply(sck) != 220) {
- syslog(LOG_ERR, "Mailserver failed to initiate contact.");
- goto mailfail;
- }
- if (Send(sck, "HELO localhostrn") != 250) {
- syslog(LOG_ERR, "Mailserver doesn't understand HELO.");
- goto mailfail;
- }
- if (Send(sck, "MAIL FROM: <%s>rn", email(mailfrom)) != 250) {
- syslog(LOG_ERR, "Mailserver didn't accept MAIL FROM.");
- goto mailfail;
- }
- for (temp = 0; temp < incmails; temp++) {
- if (Send(sck, "RCPT TO: <%s>rn", email(incmail[temp])) == 250)
- temp2++;
- }
- if (temp2 == 0) {
- syslog(LOG_ERR, "Mailserver didn't accept any RCPT TO.");
- goto mailfail;
- }
- if (Send(sck, "DATArn") != 354) {
- syslog(LOG_ERR, "Mailserver didn't accept DATA.");
- goto mailfail;
- }
- SockPrintf(sck, "From: wu-ftpd <%s>rn", mailfrom);
- SockPrintf(sck, "Subject: New file uploaded: %srnrn", name);
- fb_realpath(name, pathname);
- SockPrintf(sck, "%s uploaded %s from %s.rnFile size is %d.rnPlease move the file where it belongs.rn", guestpw, pathname, remotehost, byte_count);
- if (Send(sck, ".rn") != 250)
- syslog(LOG_ERR, "Message rejected by mailserver.");
- if (Send(sck, "QUITrn") != 221)
- syslog(LOG_ERR, "Mailserver didn't accept QUIT.");
- goto mailok;
- mailfail:
- if (sck != NULL)
- fclose(sck);
- mailok:
- sck = NULL; /* We don't need this, but some (stupid) compilers need an
- instruction after a label. This one can't hurt. */
- }
- #endif /* MAIL_ADMIN */
- if (log_incoming_xfers && (xferlog || syslogmsg)) {
- char namebuf[MAXPATHLEN], msg[MAXPATHLEN + 2 * MAXHOSTNAMELEN + 100 + 128]; /* AUTHNAMESIZE is 100 */
- size_t msglen; /* for stupid_sprintf */
- int xfertime = time(NULL) - start_time;
- time_t curtime = time(NULL);
- int loop;
- if (!xfertime)
- xfertime++;
- #ifdef XFERLOG_REALPATH
- wu_realpath(name, namebuf, chroot_path);
- #else
- fb_realpath(name, namebuf);
- #endif
- for (loop = 0; namebuf[loop]; loop++)
- if (isspace(namebuf[loop]) || iscntrl(namebuf[loop]))
- namebuf[loop] = '_';
- /* see above */
- sprintf(msg, "%.24s %d %s %" L_FORMAT " ",
- ctime(&curtime),
- xfertime,
- remotehost,
- byte_count
- );
- msglen = strlen(msg); /* sigh */
- snprintf(msg + msglen, sizeof(msg) - msglen, "%s %c %s %c %c %s ftp %d %s %cn",
- namebuf,
- (type == TYPE_A) ? 'a' : 'b',
- opt_string(0),
- 'i',
- anonymous ? 'a' : (guest ? 'g' : 'r'),
- anonymous ? guestpw : pw->pw_name,
- authenticated,
- authenticated ? authuser : "*",
- TransferIncomplete ? 'i' : 'c'
- );
- /* Ensure msg always ends with 'n' */
- if (strlen(msg) == sizeof(msg) - 1)
- msg[sizeof(msg) - 2] = 'n';
- if (syslogmsg != 1)
- write(xferlog, msg, strlen(msg));
- if (syslogmsg != 0)
- syslog(LOG_INFO, "xferlog (recv): %s", msg + 25);
- }
- data = -1;
- pdata = -1;
- done:
- (void) fclose(fout);
- }
- FILE *getdatasock(char *mode)
- {
- int s, on = 1, tries;
- if (data >= 0)
- return (fdopen(data, mode));
- delay_signaling(); /* we can't allow any signals while euid==0: kinch */
- (void) seteuid((uid_t) 0);
- s = socket(AF_INET, SOCK_STREAM, 0);
- if (s < 0)
- goto bad;
- if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
- (char *) &on, sizeof(on)) < 0)
- goto bad;
- if (keepalive)
- (void) setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on));
- if (TCPwindowsize)
- (void) setsockopt(s, SOL_SOCKET, (*mode == 'w' ? SO_SNDBUF : SO_RCVBUF),
- (char *) &TCPwindowsize, sizeof(TCPwindowsize));
- /* anchor socket to avoid multi-homing problems */
- data_source.sin_family = AF_INET;
- data_source.sin_addr = ctrl_addr.sin_addr;
- #if defined(VIRTUAL) && defined(CANT_BIND) /* can't bind to virtual address */
- data_source.sin_addr.s_addr = htonl(INADDR_ANY);
- #endif
- for (tries = 1;; tries++) {
- if (bind(s, (struct sockaddr *) &data_source,
- sizeof(data_source)) >= 0)
- break;
- if (errno != EADDRINUSE || tries > 10)
- goto bad;
- sleep(tries);
- }
- #if defined(M_UNIX) && !defined(_M_UNIX) /* bug in old TCP/IP release */
- {
- struct linger li;
- li.l_onoff = 1;
- li.l_linger = 900;
- if (setsockopt(s, SOL_SOCKET, SO_LINGER,
- (char *) &li, sizeof(struct linger)) < 0) {
- syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
- goto bad;
- }
- }
- #endif
- (void) seteuid((uid_t) pw->pw_uid);
- enable_signaling(); /* we can allow signals once again: kinch */
- #ifdef IPTOS_THROUGHPUT
- on = IPTOS_THROUGHPUT;
- if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &on, sizeof(int)) < 0)
- syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
- #endif
- #ifdef TCP_NOPUSH
- /*
- * Turn off push flag to keep sender TCP from sending short packets
- * at the boundaries of each write(). Should probably do a SO_SNDBUF
- * to set the send buffer size as well, but that may not be desirable
- * in heavy-load situations.
- */
- on = 1;
- if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, (char *) &on, sizeof on) < 0)
- syslog(LOG_WARNING, "setsockopt (TCP_NOPUSH): %m");
- #endif
- return (fdopen(s, mode));
- bad:
- on = errno; /* hold errno for return */
- (void) seteuid((uid_t) pw->pw_uid);
- enable_signaling(); /* we can allow signals once again: kinch */
- if (s != -1)
- (void) close(s);
- errno = on;
- return (NULL);
- }
- FILE *dataconn(char *name, off_t size, char *mode)
- {
- char sizebuf[32];
- FILE *file;
- int retry = 0;
- int on = 1;
- #ifdef IPTOS_LOWDELAY
- int tos;
- #endif
- #ifdef THROUGHPUT
- int bps;
- double bpsmult;
- #endif
- file_size = size;
- byte_count = 0;
- if (size != (off_t) - 1)
- (void) sprintf(sizebuf, " (%" L_FORMAT " bytes)", size);
- else
- (void) strcpy(sizebuf, "");
- if (pdata >= 0) {
- struct sockaddr_in from;
- char dataaddr[MAXHOSTNAMELEN];
- #if defined(UNIXWARE) || defined(AIX)
- size_t fromlen = sizeof(from);
- #else
- int fromlen = sizeof(from);
- #endif
- int s;
- #ifdef FD_ZERO
- int rv;
- #endif
- if (keepalive)
- (void) setsockopt(pdata, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on));
- if (TCPwindowsize)
- (void) setsockopt(pdata, SOL_SOCKET, (*mode == 'w' ? SO_SNDBUF : SO_RCVBUF),
- (char *) &TCPwindowsize, sizeof(TCPwindowsize));
- #ifdef FD_ZERO
- do {
- struct timeval timeout;
- fd_set set;
- FD_ZERO(&set);
- FD_SET(pdata, &set);
- timeout.tv_usec = 0;
- timeout.tv_sec = timeout_accept;
- #ifdef HPUX_SELECT
- rv = select(pdata + 1, (int *) &set, NULL, NULL, &timeout);
- #else
- rv = select(pdata + 1, &set, (fd_set *) 0, (fd_set *) 0,
- (struct timeval *) &timeout);
- #endif
- } while ((rv == -1) && (errno == EINTR));
- if ((rv != -1) && (rv != 0))
- s = accept(pdata, (struct sockaddr *) &from, &fromlen);
- else
- s = -1;
- #else /* FD_ZERO */
- (void) signal(SIGALRM, alarm_signal);
- alarm(timeout_accept);
- s = accept(pdata, (struct sockaddr *) &from, &fromlen);
- alarm(0);
- #endif
- if (s == -1) {
- reply(425, "Can't open data connection.");
- (void) close(pdata);
- pdata = -1;
- return (NULL);
- }
- (void) close(pdata);
- pdata = s;
- #ifdef IPTOS_LOWDELAY
- tos = IPTOS_LOWDELAY;
- (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &tos,
- sizeof(int));
- #endif
- (void) strncpy(dataaddr, inet_ntoa(from.sin_addr), sizeof(dataaddr));
- if (!pasv_allowed(dataaddr))
- if (strcasecmp(dataaddr, remoteaddr) != 0) {
- /*
- * This will log when data connection comes from an address different
- * than the control connection.
- */
- #ifdef FIGHT_PASV_PORT_RACE
- syslog(LOG_ERR, "%s of %s: data connect from %s for %s%s",
- anonymous ? guestpw : pw->pw_name, remoteident,
- dataaddr, name, sizebuf);
- reply(425, "Possible PASV port theft, cannot open data connection.");
- (void) close(pdata);
- pdata = -1;
- return (NULL);
- #else
- syslog(LOG_NOTICE, "%s of %s: data connect from %s for %s%s",
- anonymous ? guestpw : pw->pw_name, remoteident,
- dataaddr, name, sizebuf);
- #endif
- }
- #ifdef THROUGHPUT
- throughput_calc(name, &bps, &bpsmult);
- if (bps != -1) {
- lreply(150, "Opening %s mode data connection for %s%s.",
- type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
- reply(150, "Restricting network throughput to %d bytes/s.", bps);
- }
- else
- #endif
- reply(150, "Opening %s mode data connection for %s%s.",
- type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
- return (fdopen(pdata, mode));
- }
- if (data >= 0) {
- reply(125, "Using existing data connection for %s%s.",
- name, sizebuf);
- usedefault = 1;
- return (fdopen(data, mode));
- }
- if (usedefault)
- data_dest = his_addr;
- if (data_dest.sin_port == 0) {
- reply(500, "Can't build data connection: no PORT specified");
- return (NULL);
- }
- usedefault = 1;
- file = getdatasock(mode);
- if (file == NULL) {
- reply(425, "Can't create data socket (%s,%d): %s.",
- inet_ntoa(data_source.sin_addr),
- ntohs(data_source.sin_port), strerror(errno));
- return (NULL);
- }
- data = fileno(file);
- (void) signal(SIGALRM, alarm_signal);
- alarm(timeout_connect);
- while (connect(data, (struct sockaddr *) &data_dest,
- sizeof(data_dest)) < 0) {
- alarm(0);
- if ((errno == EADDRINUSE || errno == EINTR) && retry < swaitmax) {
- sleep((unsigned) swaitint);
- retry += swaitint;
- (void) signal(SIGALRM, alarm_signal);
- alarm(timeout_connect);
- continue;
- }
- perror_reply(425, "Can't build data connection");
- (void) fclose(file);
- data = -1;
- return (NULL);
- }
- alarm(0);
- if (keepalive)
- (void) setsockopt(pdata, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on));
- if (TCPwindowsize)
- (void) setsockopt(data, SOL_SOCKET, (*mode == 'w' ? SO_SNDBUF : SO_RCVBUF),
- (char *) &TCPwindowsize, sizeof(TCPwindowsize));
- #ifdef THROUGHPUT
- throughput_calc(name, &bps, &bpsmult);
- if (bps != -1) {
- lreply(150, "Opening %s mode data connection for %s%s.",
- type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
- reply(150, "Restricting network throughput to %d bytes/s.", bps);
- }
- else
- #endif
- reply(150, "Opening %s mode data connection for %s%s.",
- type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
- return (file);
- }
- /* Tranfer the contents of "instr" to "outstr" peer using the appropriate
- * encapsulation of the data subject to Mode, Structure, and Type.
- *
- * NB: Form isn't handled. */
- int
- #ifdef THROUGHPUT
- send_data(char *name, FILE *instr, FILE *outstr, off_t blksize)
- #else
- send_data(FILE *instr, FILE *outstr, off_t blksize)
- #endif
- {
- register int c, cnt = 0;
- static char *buf;
- int netfd, filefd;
- #ifdef THROUGHPUT
- int bps;
- double bpsmult;
- time_t t1, t2;
- #endif
- #ifdef THROUGHPUT
- throughput_calc(name, &bps, &bpsmult);
- #endif
- buf = NULL;
- if (wu_setjmp(urgcatch)) {
- draconian_FILE = NULL;
- alarm(0);
- transflag = 0;
- if (buf)
- (void) free(buf);
- retrieve_is_data = 1;
- return (0);
- }
- transflag++;
- switch (type) {
- case TYPE_A:
- draconian_FILE = outstr;
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- while ((draconian_FILE != NULL) && ((c = getc(instr)) != EOF)) {
- if (++byte_count % 4096 == 0) {
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- }
- if (c == 'n') {
- if (ferror(outstr))
- goto data_err;
- (void) putc('r', outstr);
- #ifdef TRANSFER_COUNT
- if (retrieve_is_data) {
- data_count_total++;
- data_count_out++;
- }
- byte_count_total++;
- byte_count_out++;
- #endif
- }
- (void) putc(c, outstr);
- #ifdef TRANSFER_COUNT
- if (retrieve_is_data) {
- data_count_total++;
- data_count_out++;
- }
- byte_count_total++;
- byte_count_out++;
- #endif
- }
- if (draconian_FILE != NULL) {
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- fflush(outstr);
- }
- if (draconian_FILE != NULL) {
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- socket_flush_wait(outstr);
- }
- transflag = 0;
- if (ferror(instr))
- goto file_err;
- if ((draconian_FILE == NULL) || ferror(outstr))
- goto data_err;
- draconian_FILE = NULL;
- alarm(0);
- reply(226, "Transfer complete.");
- #ifdef TRANSFER_COUNT
- if (retrieve_is_data) {
- file_count_total++;
- file_count_out++;
- }
- xfer_count_total++;
- xfer_count_out++;
- #endif
- retrieve_is_data = 1;
- return (1);
- case TYPE_I:
- case TYPE_L:
- #ifdef THROUGHPUT
- if (bps != -1)
- blksize = bps;
- #endif
- if ((buf = (char *) malloc(blksize)) == NULL) {
- transflag = 0;
- perror_reply(451, "Local resource failure: malloc");
- retrieve_is_data = 1;
- return (0);
- }
- netfd = fileno(outstr);
- filefd = fileno(instr);
- draconian_FILE = outstr;
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- #ifdef THROUGHPUT
- if (bps != -1)
- t1 = time(NULL);
- #endif
- while ((draconian_FILE != NULL) && ((cnt = read(filefd, buf, blksize)) > 0 && write(netfd, buf, cnt) == cnt)) {
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- byte_count += cnt;
- #ifdef TRANSFER_COUNT
- if (retrieve_is_data) {
- data_count_total += cnt;
- data_count_out += cnt;
- }
- byte_count_total += cnt;
- byte_count_out += cnt;
- #endif
- #ifdef THROUGHPUT
- if (bps != -1) {
- t2 = time(NULL);
- if (t2 == t1)
- sleep(1);
- t1 = time(NULL);
- }
- #endif
- }
- #ifdef THROUGHPUT
- if (bps != -1)
- throughput_adjust(name);
- #endif
- transflag = 0;
- (void) free(buf);
- if (draconian_FILE != NULL) {
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- socket_flush_wait(outstr);
- }
- if (cnt != 0) {
- if (cnt < 0)
- goto file_err;
- goto data_err;
- }
- if (draconian_FILE == NULL)
- goto data_err;
- draconian_FILE = NULL;
- alarm(0);
- reply(226, "Transfer complete.");
- #ifdef TRANSFER_COUNT
- if (retrieve_is_data) {
- file_count_total++;
- file_count_out++;
- }
- xfer_count_total++;
- xfer_count_out++;
- #endif
- retrieve_is_data = 1;
- return (1);
- default:
- transflag = 0;
- reply(550, "Unimplemented TYPE %d in send_data", type);
- retrieve_is_data = 1;
- return (0);
- }
- data_err:
- draconian_FILE = NULL;
- alarm(0);
- transflag = 0;
- perror_reply(426, "Data connection");
- retrieve_is_data = 1;
- return (0);
- file_err:
- draconian_FILE = NULL;
- alarm(0);
- transflag = 0;
- perror_reply(551, "Error on input file");
- retrieve_is_data = 1;
- return (0);
- }
- /* Transfer data from peer to "outstr" using the appropriate encapulation of
- * the data subject to Mode, Structure, and Type.
- *
- * N.B.: Form isn't handled. */
- int receive_data(FILE *instr, FILE *outstr)
- {
- register int c;
- int cnt = 0, bare_lfs = 0;
- static char *buf;
- int netfd, filefd;
- #ifdef BUFFER_SIZE
- size_t buffer_size = BUFFER_SIZE;
- #else
- size_t buffer_size = BUFSIZ;
- #endif
- buf = NULL;
- if (wu_setjmp(urgcatch)) {
- alarm(0);
- transflag = 0;
- if (buf)
- (void) free(buf);
- return (-1);
- }
- transflag++;
- switch (type) {
- case TYPE_I:
- case TYPE_L:
- if ((buf = (char *) malloc(buffer_size)) == NULL) {
- transflag = 0;
- perror_reply(451, "Local resource failure: malloc");
- return (-1);
- }
- netfd = fileno(instr);
- filefd = fileno(outstr);
- draconian_FILE = instr;
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- while ((draconian_FILE != NULL) && ((cnt = read(netfd, buf, buffer_size)) > 0 && write(filefd, buf, cnt) == cnt)) {
- byte_count += cnt;
- #ifdef TRANSFER_COUNT
- data_count_total += cnt;
- data_count_in += cnt;
- byte_count_total += cnt;
- byte_count_in += cnt;
- #endif
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- }
- transflag = 0;
- (void) free(buf);
- if (cnt != 0) {
- if (cnt < 0)
- goto data_err;
- goto file_err;
- }
- if (draconian_FILE == NULL)
- goto data_err;
- draconian_FILE = NULL;
- alarm(0);
- #ifdef TRANSFER_COUNT
- file_count_total++;
- file_count_in++;
- xfer_count_total++;
- xfer_count_in++;
- #endif
- return (0);
- case TYPE_E:
- reply(553, "TYPE E not implemented.");
- transflag = 0;
- return (-1);
- case TYPE_A:
- draconian_FILE = instr;
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- while ((draconian_FILE != NULL) && ((c = getc(instr)) != EOF)) {
- if (++byte_count % 4096 == 0) {
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- }
- if (c == 'n')
- bare_lfs++;
- while (c == 'r') {
- if (ferror(outstr))
- goto file_err;
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- if ((draconian_FILE != NULL) && ((c = getc(instr)) != 'n')) {
- (void) putc('r', outstr);
- #ifdef TRANSFER_COUNT
- data_count_total++;
- data_count_in++;
- byte_count_total++;
- byte_count_in++;
- #endif
- if (c == EOF) /* null byte fix, noid@cyborg.larc.nasa.gov */
- goto contin2;
- if (++byte_count % 4096 == 0) {
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- }
- }
- }
- (void) putc(c, outstr);
- #ifdef TRANSFER_COUNT
- data_count_total++;
- data_count_in++;
- byte_count_total++;
- byte_count_in++;
- #endif
- contin2:;
- }
- fflush(outstr);
- if ((draconian_FILE == NULL) || ferror(instr))
- goto data_err;
- if (ferror(outstr))
- goto file_err;
- transflag = 0;
- draconian_FILE = NULL;
- alarm(0);
- if (bare_lfs) {
- lreply(226, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
- lreply(0, " File may not have transferred correctly.");
- }
- #ifdef TRANSFER_COUNT
- file_count_total++;
- file_count_in++;
- xfer_count_total++;
- xfer_count_in++;
- #endif
- return (0);
- default:
- reply(550, "Unimplemented TYPE %d in receive_data", type);
- transflag = 0;
- return (-1);
- }
- data_err:
- draconian_FILE = NULL;
- alarm(0);
- transflag = 0;
- perror_reply(426, "Data Connection");
- return (-1);
- file_err:
- draconian_FILE = NULL;
- alarm(0);
- transflag = 0;
- perror_reply(452, "Error writing file");
- return (-1);
- }
- void statfilecmd(char *filename)
- {
- #ifndef INTERNAL_LS
- char line[BUFSIZ], *ptr;
- FILE *fin;
- int c;
- #endif /* ! INTERNAL_LS */
- fixpath(filename);
- if (filename[0] == ' ')
- filename = ".";
- #ifndef INTERNAL_LS
- if (anonymous && dolreplies)
- (void) snprintf(line, sizeof(line), ls_long, filename);
- else
- (void) snprintf(line, sizeof(line), ls_short, filename);
- fin = ftpd_popen(line, "r", 0);
- #endif /* ! INTERNAL_LS */
- lreply(213, "status of %s:", filename);
- #ifndef INTERNAL_LS
- /*
- while ((c = getc(fin)) != EOF) {
- if (c == 'n') {
- if (ferror(stdout)) {
- perror_reply(421, "control connection");
- (void) ftpd_pclose(fin);
- dologout(1);
- / * NOTREACHED * /
- }
- if (ferror(fin)) {
- perror_reply(551, filename);
- (void) ftpd_pclose(fin);
- return;
- }
- (void) putc('r', stdout);
- }
- (void) putc(c, stdout);
- }
- */
- while (fgets(line, sizeof(line), fin) != NULL) {
- if ((ptr = strchr(line, 'n'))) /* clip out unnecessary newline */
- *ptr = ' ';
- lreply(0, "%s", line);
- }
- (void) ftpd_pclose(fin);
- #else /* INTERNAL_LS */
- ls_dir(filename, 1, 0, 1, 0, 1, stdout);
- #endif /* INTERNAL_LS */
- reply(213, "End of Status");
- }
- void statcmd(void)
- {
- struct sockaddr_in *sin;
- u_char *a, *p;
- lreply(211, "%s FTP server status:", hostname);
- lreply(0, " %s", version);
- if (!isdigit(remotehost[0]))
- lreply(0, " Connected to %s (%s)", remotehost,
- inet_ntoa(his_addr.sin_addr));
- else
- lreply(0, " Connected to %s", remotehost);
- if (logged_in) {
- if (anonymous)
- lreply(0, " Logged in anonymously");
- else
- lreply(0, " Logged in as %s", pw->pw_name);
- }
- else if (askpasswd)
- lreply(0, " Waiting for password");
- else
- lreply(0, " Waiting for user name");
- if (type == TYPE_L)
- #ifdef NBBY
- lreply(0, " TYPE: %s %d; STRUcture: %s; transfer MODE: %s",
- typenames[type], NBBY, strunames[stru], modenames[mode]);
- #else
- lreply(0, " TYPE: %s %d; STRUcture: %s; transfer MODE: %s",
- typenames[type], bytesize, strunames[stru], modenames[mode]);
- #endif /* NBBY */
- else
- lreply(0, " TYPE: %s%s%s; STRUcture: %s; transfer MODE: %s",
- typenames[type], (type == TYPE_A || type == TYPE_E) ?
- ", FORM: " : "", (type == TYPE_A || type == TYPE_E) ?
- formnames[form] : "", strunames[stru], modenames[mode]);
- if (data != -1)
- lreply(0, " Data connection open");
- else if (pdata != -1 || usedefault == 0) {
- if (usedefault == 0)
- sin = &data_dest;
- else if (route_vectored)
- sin = &vect_addr;
- else
- sin = &pasv_addr;
- a = (u_char *) & sin->sin_addr;
- p = (u_char *) & sin->sin_port;
- #define UC(b) (((int) b) & 0xff)
- lreply(0, " %s (%d,%d,%d,%d,%d,%d)",
- usedefault == 0 ? "PORT" : "in Passive mode",
- UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
- #undef UC
- }
- else
- lreply(0, " No data connection");
- #ifdef TRANSFER_COUNT
- lreply(0, " %d data bytes received in %d files", data_count_in, file_count_in);
- lreply(0, " %d data bytes transmitted in %d files", data_count_out, file_count_out);
- lreply(0, " %d data bytes total in %d files", data_count_total, file_count_total);
- lreply(0, " %d traffic bytes received in %d transfers", byte_count_in, xfer_count_in);
- lreply(0, " %d traffic bytes transmitted in %d transfers", byte_count_out, xfer_count_out);
- lreply(0, " %d traffic bytes total in %d transfers", byte_count_total, xfer_count_total);
- #endif
- reply(211, "End of status");
- }
- void fatal(char *s)
- {
- reply(451, "Error in server: %sn", s);
- reply(221, "Closing connection due to server error.");
- dologout(0);
- /* NOTREACHED */
- }
- #define USE_REPLY_NOTFMT (1<<1) /* fmt is not a printf fmt (KLUDGE) */
- #define USE_REPLY_LONG (1<<2) /* this is a long reply; use a - */
- void vreply(long flags, int n, char *fmt, va_list ap)
- {
- char buf[BUFSIZ];
- flags &= USE_REPLY_NOTFMT | USE_REPLY_LONG;
- if (n) /* if numeric is 0, don't output one; use n==0 in place of printf's */
- sprintf(buf, "%03d%c", n, flags & USE_REPLY_LONG ? '-' : ' ');
- /* This is somewhat of a kludge for autospout. I personally think that
- * autospout should be done differently, but that's not my department. -Kev
- */
- if (flags & USE_REPLY_NOTFMT)
- snprintf(buf + (n ? 4 : 0), n ? sizeof(buf) - 4 : sizeof(buf), "%s", fmt);
- else
- vsnprintf(buf + (n ? 4 : 0), n ? sizeof(buf) - 4 : sizeof(buf), fmt, ap);
- if (debug) /* debugging output :) */
- syslog(LOG_DEBUG, "<--- %s", buf);
- /* Yes, you want the debugging output before the client output; wrapping
- * stuff goes here, you see, and you want to log the cleartext and send
- * the wrapped text to the client.
- */
- printf("%srn", buf); /* and send it to the client */
- #ifdef TRANSFER_COUNT
- byte_count_total += strlen(buf);
- byte_count_out += strlen(buf);
- #endif
- fflush(stdout);
- }
- void reply(int n, char *fmt,...)
- {
- VA_LOCAL_DECL
- if (autospout != NULL) { /* deal with the autospout stuff... */
- char *p, *ptr = autospout;
- while (*ptr) {
- if ((p = strchr(ptr, 'n')) != NULL) /* step through line by line */
- *p = ' ';
- /* send a line...(note that this overrides dolreplies!) */
- vreply(USE_REPLY_LONG | USE_REPLY_NOTFMT, n, ptr, ap);
- if (p)
- ptr = p + 1; /* set to the next line... ( is handled in the while) */
- else
- break; /* oh, we're done; drop out of the loop */
- }
- if (autospout_free) { /* free autospout if necessary */
- (void) free(autospout);
- autospout_free = 0;
- }
- autospout = 0; /* clear the autospout */
- }
- VA_START(fmt);
- /* send the reply */
- vreply(0L, n, fmt, ap);
- VA_END;
- }
- void lreply(int n, char *fmt,...)
- {
- VA_LOCAL_DECL
- if (!dolreplies) /* prohibited from doing long replies? */
- return;
- VA_START(fmt);
- /* send the reply */
- vreply(USE_REPLY_LONG, n, fmt, ap);
- VA_END;
- }
- void ack(char *s)
- {
- reply(250, "%s command successful.", s);
- }
- void nack(char *s)
- {
- reply(502, "%s command not implemented.", s);
- }
- void yyerror(char *s)
- {
- char *cp;
- if (s == NULL || yyerrorcalled != 0)
- return;
- if ((cp = strchr(cbuf, 'n')) != NULL)
- *cp = ' ';
- reply(500, "'%s': command not understood.", cbuf);
- yyerrorcalled = 1;
- return;
- }
- void delete(char *name)
- {
- struct stat st;
- char realname[MAXPATHLEN];
- /*
- * delete permission?
- */
- wu_realpath(name, realname, chroot_path);
- if ((del_check(name)) == 0) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to delete %s",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to delete %s",
- pw->pw_name, remoteident, realname);
- return;
- }
- if (lstat(name, &st) < 0) {
- perror_reply(550, name);
- return;
- }
- if ((st.st_mode & S_IFMT) == S_IFDIR) {
- uid_t uid;
- gid_t gid;
- int d_mode;
- int valid;
- /*
- * check the directory, can we rmdir here?
- */
- if ((dir_check(name, &uid, &gid, &d_mode, &valid)) <= 0) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to delete directory %s",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to delete directory %s",
- pw->pw_name, remoteident, realname);
- return;
- }
- if (rmdir(name) < 0) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to delete directory %s (permissions)",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to delete directory %s (permissions)",
- pw->pw_name, remoteident, realname);
- perror_reply(550, name);
- return;
- }
- goto done;
- }
- if (unlink(name) < 0) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to delete %s (permissions)",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to delete %s (permissions)",
- pw->pw_name, remoteident, realname);
- perror_reply(550, name);
- return;
- }
- done:
- {
- char path[MAXPATHLEN];
- wu_realpath(name, path, chroot_path);
- if (log_security)
- if ((st.st_mode & S_IFMT) == S_IFDIR)
- if (anonymous) {
- syslog(LOG_NOTICE, "%s of %s deleted directory %s", guestpw, remoteident, path);
- }
- else {
- syslog(LOG_NOTICE, "%s of %s deleted directory %s", pw->pw_name,
- remoteident, path);
- }
- else if (anonymous) {
- syslog(LOG_NOTICE, "%s of %s deleted %s", guestpw,
- remoteident, path);
- }
- else {
- syslog(LOG_NOTICE, "%s of %s deleted %s", pw->pw_name,
- remoteident, path);
- }
- }
- ack("DELE");
- }
- void cwd(char *path)
- {
- struct aclmember *entry = NULL;
- char cdpath[MAXPATHLEN + 1];
- if (chdir(path) < 0) {
- /* alias checking */
- while (getaclentry("alias", &entry) && ARG0 && ARG1 != NULL) {
- if (!strcasecmp(ARG0, path)) {
- if (chdir(ARG1) < 0)
- perror_reply(550, path);
- else {
- show_message(250, C_WD);
- show_readme(250, C_WD);
- ack("CWD");
- }
- return;
- }
- }
- /* check for "cdpath" directories. */
- entry = (struct aclmember *) NULL;
- while (getaclentry("cdpath", &entry) && ARG0 != NULL) {
- snprintf(cdpath, sizeof cdpath, "%s/%s", ARG0, path);
- if (chdir(cdpath) >= 0) {
- show_message(250, C_WD);
- show_readme(250, C_WD);
- ack("CWD");
- return;
- }
- }
- perror_reply(550, path);
- }
- else {
- show_message(250, C_WD);
- show_readme(250, C_WD);
- ack("CWD");
- }
- }
- void makedir(char *name)
- {
- uid_t uid;
- gid_t gid;
- int d_mode;
- mode_t oldumask;
- int valid;
- uid_t oldid;
- char path[MAXPATHLEN + 1]; /* for realpath() later - cky */
- char realname[MAXPATHLEN];
- wu_realpath(name, realname, chroot_path);
- /*
- * check the directory, can we mkdir here?
- */
- if ((dir_check(name, &uid, &gid, &d_mode, &valid)) <= 0) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to create directory %s",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to create directory %s",
- pw->pw_name, remoteident, realname);
- return;
- }
- /*
- * check the filename, is it legal?
- */
- if ((fn_check(name)) <= 0) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to create directory %s (path-filter)",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to create directory %s (path-filter)",
- pw->pw_name, remoteident, realname);
- return;
- }
- oldumask = umask(0000);
- if (valid <= 0) {
- d_mode = 0777;
- umask(oldumask);
- }
- if (mkdir(name, d_mode) < 0) {
- if (errno == EEXIST) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to create directory %s (exists)",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to create directory %s (exists)",
- pw->pw_name, remoteident, realname);
- fb_realpath(name, path);
- reply(521, ""%s" directory exists", path);
- }
- else {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to create directory %s (permissions)",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to create directory %s (permissions)",
- pw->pw_name, remoteident, realname);
- perror_reply(550, name);
- }
- umask(oldumask);
- return;
- }
- umask(oldumask);
- if (valid > 0) {
- oldid = geteuid();
- if (uid != 0)
- (void) seteuid((uid_t) uid);
- if ((uid == 0) || ((chown(name, uid, gid)) < 0)) {
- delay_signaling(); /* we can't allow any signals while euid==0: kinch */
- (void) seteuid((uid_t) 0);
- if ((chown(name, uid, gid)) < 0) {
- (void) seteuid(oldid);
- enable_signaling(); /* we can allow signals once again: kinch */
- perror_reply(550, "chown");
- return;
- }
- (void) seteuid(oldid);
- enable_signaling(); /* we can allow signals once again: kinch */
- }
- else
- (void) seteuid(oldid);
- }
- wu_realpath(name, path, chroot_path);
- if (log_security)
- if (anonymous) {
- syslog(LOG_NOTICE, "%s of %s created directory %s", guestpw, remoteident, path);
- }
- else {
- syslog(LOG_NOTICE, "%s of %s created directory %s", pw->pw_name,
- remoteident, path);
- }
- fb_realpath(name, path);
- /* According to RFC 959:
- * The 257 reply to the MKD command must always contain the
- * absolute pathname of the created directory.
- * This is implemented here using similar code to the PWD command.
- * XXX - still need to do `quote-doubling'.
- */
- reply(257, ""%s" new directory created.", path);
- }
- void removedir(char *name)
- {
- uid_t uid;
- gid_t gid;
- int d_mode;
- int valid;
- char realname[MAXPATHLEN];
- wu_realpath(name, realname, chroot_path);
- /*
- * delete permission?
- */
- if ((del_check(name)) == 0)
- return;
- /*
- * check the directory, can we rmdir here?
- */
- if ((dir_check(name, &uid, &gid, &d_mode, &valid)) <= 0) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to remove directory %s",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to remove directory %s",
- pw->pw_name, remoteident, realname);
- return;
- }
- if (rmdir(name) < 0) {
- if (errno == EBUSY)
- perror_reply(450, name);
- else {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to remove directory %s (permissions)",
- guestpw, remoteident, realname);
- else
- syslog(LOG_NOTICE, "%s of %s tried to remove directory %s (permissions)",
- pw->pw_name, remoteident, realname);
- perror_reply(550, name);
- }
- }
- else {
- char path[MAXPATHLEN];
- wu_realpath(name, path, chroot_path);
- if (log_security)
- if (anonymous) {
- syslog(LOG_NOTICE, "%s of %s deleted directory %s", guestpw, remoteident, path);
- }
- else {
- syslog(LOG_NOTICE, "%s of %s deleted directory %s", pw->pw_name,
- remoteident, path);
- }
- ack("RMD");
- }
- }
- void pwd(void)
- {
- char path[MAXPATHLEN + 1];
- char rhome[MAXPATHLEN + 1];
- char *rpath = path; /* Path to return to client */
- int pathlen;
- #ifndef MAPPING_CHDIR
- #ifdef HAVE_GETCWD
- extern char *getcwd();
- #else
- extern char *getwd(char *);
- #endif
- #endif /* MAPPING_CHDIR */
- #ifdef HAVE_GETCWD
- if (getcwd(path, MAXPATHLEN) == (char *) NULL)
- #else
- if (getwd(path) == (char *) NULL)
- #endif
- /* Dink! If you couldn't get the path and the buffer is now likely to
- be undefined, why are you trying to PRINT it?! _H*
- reply(550, "%s.", path); */
- {
- fb_realpath(".", path); /* realpath_on_steroids can deal */
- }
- /* relative to home directory if restricted_user */
- if (restricted_user) {
- fb_realpath(home, rhome);
- pathlen = strlen(rhome);
- if (pathlen && rhome[pathlen - 1] == '/')
- pathlen--;
- rpath = rpath + pathlen;
- if (!*rpath)
- strcpy(rpath, "/");
- }
- reply(257, ""%s" is current directory.", rpath);
- }
- char *renamefrom(char *name)
- {
- struct stat st;
- if (lstat(name, &st) < 0) {
- perror_reply(550, name);
- return ((char *) 0);
- }
- reply(350, "File exists, ready for destination name");
- return (name);
- }
- void renamecmd(char *from, char *to)
- {
- int allowed = (anonymous ? 0 : 1);
- char realfrom[MAXPATHLEN];
- char realto[MAXPATHLEN];
- struct aclmember *entry = NULL;
- #ifdef PARANOID
- struct stat st;
- #endif
- wu_realpath(from, realfrom, chroot_path);
- wu_realpath(to, realto, chroot_path);
- /*
- * check the filename, is it legal?
- */
- if ((fn_check(to)) == 0) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to rename %s to "%s" (path-filter)",
- guestpw, remoteident, realfrom, realto);
- else
- syslog(LOG_NOTICE, "%s of %s tried to rename %s to "%s" (path-filter)",
- pw->pw_name, remoteident, realfrom, realto);
- return;
- }
- /*
- * if rename permission denied and file exists... then deny the user
- * permission to rename the file.
- */
- while (getaclentry("rename", &entry) && ARG0 && ARG1 != NULL) {
- if (type_match(ARG1))
- if (anonymous) {
- if (*ARG0 == 'y')
- allowed = 1;
- }
- else if (*ARG0 == 'n')
- allowed = 0;
- }
- if (!allowed) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to rename %s to %s",
- guestpw, remoteident, realfrom, realto);
- else
- syslog(LOG_NOTICE, "%s of %s tried to rename %s to %s",
- pw->pw_name, remoteident, realfrom, realto);
- reply(553, "%s: Permission denied on server. (rename)", from);
- return;
- }
- #ifdef PARANOID
- /* Almost forgot about this. Don't allow renaming TO existing files --
- otherwise someone can rename "trivial" to "warez", and "warez" is gone!
- XXX: This part really should do the same "overwrite" check as store(). */
- if (!stat(to, &st)) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to rename %s to %s",
- guestpw, remoteident, realfrom, realto);
- else
- syslog(LOG_NOTICE, "%s of %s tried to rename %s to %s",
- pw->pw_name, remoteident, realfrom, realto);
- reply(550, "%s: Permission denied on server. (rename)", to);
- return;
- }
- #endif
- if (rename(from, to) < 0) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to rename %s to %s",
- guestpw, remoteident, realfrom, realto);
- else
- syslog(LOG_NOTICE, "%s of %s tried to rename %s to %s",
- pw->pw_name, remoteident, realfrom, realto);
- perror_reply(550, "rename");
- }
- else {
- char frompath[MAXPATHLEN];
- char topath[MAXPATHLEN];
- wu_realpath(from, frompath, chroot_path);
- wu_realpath(to, topath, chroot_path);
- if (log_security)
- if (anonymous) {
- syslog(LOG_NOTICE, "%s of %s renamed %s to %s", guestpw, remoteident, frompath, topath);
- }
- else {
- syslog(LOG_NOTICE, "%s of %s renamed %s to %s", pw->pw_name,
- remoteident, frompath, topath);
- }
- ack("RNTO");
- }
- }
- void dolog(struct sockaddr_in *sin)
- {
- #ifndef NO_DNS
- struct hostent *hp;
- char *blah;
- #ifdef DNS_TRYAGAIN
- int num_dns_tries = 0;
- /*
- * 27-Apr-93 EHK/BM
- * far away connections might take some time to get their IP address
- * resolved. That's why we try again -- maybe our DNS cache has the
- * PTR-RR now. This code is sloppy. Far better is to check what the
- * resolver returned so that in case of error, there's no need to
- * try again.
- */
- dns_again:
- hp = gethostbyaddr((char *) &sin->sin_addr,
- sizeof(struct in_addr), AF_INET);
- if (!hp && ++num_dns_tries <= 1) {
- sleep(3);
- goto dns_again; /* try DNS lookup once more */
- }
- #else
- hp = gethostbyaddr((char *) &sin->sin_addr, sizeof(struct in_addr), AF_INET);
- #endif
- blah = inet_ntoa(sin->sin_addr);
- (void) strncpy(remoteaddr, blah, sizeof(remoteaddr));
- if (!strcasecmp(remoteaddr, "0.0.0.0")) {
- nameserved = 1;
- strncpy(remotehost, "localhost", sizeof(remotehost));
- }
- else {
- if (hp) {
- nameserved = 1;
- (void) strncpy(remotehost, hp->h_name, sizeof(remotehost));
- }
- else {
- nameserved = 0;
- (void) strncpy(remotehost, remoteaddr, sizeof(remotehost));
- }
- }
- #else
- char *blah;
- blah = inet_ntoa(sin->sin_addr);
- (void) strncpy(remoteaddr, blah, sizeof(remoteaddr));
- nameserved = 0;
- (void) strncpy(remotehost, remoteaddr, sizeof(remotehost));
- #endif
- remotehost[sizeof(remotehost) - 1] = ' ';
- sprintf(proctitle, "%s: connected", remotehost);
- setproctitle(proctitle);
- wu_authenticate();
- /* Create a composite source identification string, to improve the logging
- * when RFC 931 is being used. */
- {
- int n = 20 + strlen(remotehost) + strlen(remoteaddr) +
- (authenticated ? strlen(authuser + 5) : 0);
- if ((remoteident = malloc(n)) == NULL) {
- syslog(LOG_ERR, "malloc: %m");
- #ifndef DEBUG
- exit(1);
- #endif
- }
- else if (authenticated)
- sprintf(remoteident, "%s @ %s [%s]",
- authuser, remotehost, remoteaddr);
- else
- sprintf(remoteident, "%s [%s]", remotehost, remoteaddr);
- }
- #ifdef DAEMON
- if (be_daemon && logging)
- syslog(LOG_INFO, "connection from %s", remoteident);
- #else
- #if 0 /* this is redundant unless the caller doesn't do *anything*, and
- tcpd will pick it up and deal with it better anyways. _H */
- if (logging)
- syslog(LOG_INFO, "connection from %s", remoteident);
- #endif
- #endif
- }
- /* Record logout in wtmp file and exit with supplied status. */
- void dologout(int status)
- {
- /*
- * Prevent reception of SIGURG from resulting in a resumption
- * back to the main program loop.
- */
- transflag = 0;
- /*
- * Cancel any pending alarm request, reception of SIGALRM would cause
- * dologout() to be called again from the SIGALRM handler toolong().
- */
- (void) alarm(0);
- if (logged_in) {
- delay_signaling(); /* we can't allow any signals while euid==0: kinch */
- (void) seteuid((uid_t) 0);
- if (wtmp_logging)
- wu_logwtmp(ttyline, pw->pw_name, remotehost, 0);
- }
- if (logging)
- syslog(LOG_INFO, "FTP session closed");
- if (xferlog)
- close(xferlog);
- acl_remove();
- close(data); /* H* fix: clean up a little better */
- close(pdata);
- #ifdef AFS_AUTH
- ktc_ForgetAllTokens();
- #endif
- /* beware of flushing buffers after a SIGPIPE */
- _exit(status);
- }
- SIGNAL_TYPE myoob(int sig)
- {
- char *cp;
- /* only process if transfer occurring */
- if (!transflag) {
- #ifdef SIGURG
- (void) signal(SIGURG, myoob);
- #endif
- return;
- }
- cp = tmpline;
- if (wu_getline(cp, sizeof(tmpline) - 1, stdin) == NULL) {
- reply(221, "You could at least say goodbye.");
- dologout(0);
- }
- upper(cp);
- if (strcasecmp(cp, "ABORrn") == 0) {
- tmpline[0] = ' ';
- reply(426, "Transfer aborted. Data connection closed.");
- reply(226, "Abort successful");
- #ifdef SIGURG
- (void) signal(SIGURG, myoob);
- #endif
- if (ftwflag > 0) {
- ftwflag++;
- return;
- }
- wu_longjmp(urgcatch, 1);
- }
- if (strcasecmp(cp, "STATrn") == 0) {
- tmpline[0] = ' ';
- if (file_size != (off_t) - 1)
- reply(213, "Status: %" L_FORMAT " of %" L_FORMAT " bytes transferred",
- byte_count, file_size);
- else
- reply(213, "Status: %" L_FORMAT " bytes transferred", byte_count);
- }
- #ifdef SIGURG
- (void) signal(SIGURG, myoob);
- #endif
- }
- /* Note: a response of 425 is not mentioned as a possible response to the
- * PASV command in RFC959. However, it has been blessed as a legitimate
- * response by Jon Postel in a telephone conversation with Rick Adams on 25
- * Jan 89. */
- void passive(void)
- {
- #if defined(UNIXWARE) || defined(AIX)
- size_t len;
- #else
- int len;
- #endif
- int bind_error;
- int on = 1;
- register char *p, *a;
- /* H* fix: if we already *have* a passive socket, close it first. Prevents
- a whole variety of entertaining clogging attacks. */
- if (pdata > 0) {
- close(pdata);
- pdata = -1;
- }
- if (!logged_in) {
- reply(530, "Login with USER first.");
- return;
- }
- pdata = socket(AF_INET, SOCK_STREAM, 0);
- if (pdata < 0) {
- perror_reply(425, "Can't open passive connection");
- return;
- }
- if (keepalive)
- (void) setsockopt(pdata, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on));
- if (TCPwindowsize) {
- (void) setsockopt(pdata, SOL_SOCKET, SO_SNDBUF, (char *) &TCPwindowsize, sizeof(TCPwindowsize));
- (void) setsockopt(pdata, SOL_SOCKET, SO_RCVBUF, (char *) &TCPwindowsize, sizeof(TCPwindowsize));
- }
- pasv_addr = ctrl_addr;
- pasv_addr.sin_port = 0;
- delay_signaling(); /* we can't allow any signals while euid==0: kinch */
- (void) seteuid((uid_t) 0); /* XXX: not needed if > 1024 */
- checkports();
- if (passive_port_min == -1) {
- passive_port_max = 65534;
- passive_port_min = 1024;
- }
- {
- int *port_array;
- int *pasv_port_array;
- int i;
- int j;
- int k;
- if (passive_port_max < passive_port_min) {
- (void) seteuid((uid_t) pw->pw_uid);
- enable_signaling(); /* we can allow signals once again: kinch */
- goto pasv_error;
- }
- i = passive_port_max - passive_port_min + 1;
- port_array = calloc(i, sizeof(int));
- if (port_array == NULL) {
- (void) seteuid((uid_t) pw->pw_uid);
- enable_signaling(); /* we can allow signals once again: kinch */
- goto pasv_error;
- }
- pasv_port_array = calloc(i, sizeof(int));
- if (pasv_port_array == NULL) {
- free(port_array);
- (void) seteuid((uid_t) pw->pw_uid);
- enable_signaling(); /* we can allow signals once again: kinch */
- goto pasv_error;
- }
- do {
- --i;
- port_array[i] = passive_port_min + i;
- } while (i > 0);
- srand(time(NULL));
- /*
- i = passive_port_max - passive_port_min + 1;
- do {
- --i;
- j = (int) (((1.0 + i) * rand()) / (RAND_MAX + 1.0));
- pasv_port_array [i] = port_array [j];
- while (++j <= i)
- port_array [j-1] = port_array [j];
- } while (i > 0);
- */
- bind_error = -1;
- errno = EADDRINUSE;
- for (i = 3; (bind_error < 0) && (errno == EADDRINUSE) && (i > 0); i--) {
- for (j = passive_port_max - passive_port_min + 1; (bind_error < 0) && (errno == EADDRINUSE) && (j > 0); j--) {
- if (i == 3) {
- k = (int) ((1.0 * j * rand()) / (RAND_MAX + 1.0));
- pasv_port_array[j - 1] = port_array[k];
- while (++k < j)
- port_array[k - 1] = port_array[k];
- }
- pasv_addr.sin_port = htons(pasv_port_array[j - 1]);
- bind_error = bind(pdata, (struct sockaddr *) &pasv_addr, sizeof(pasv_addr));
- }
- }
- free(pasv_port_array);
- free(port_array);
- if (bind_error < 0) {
- (void) seteuid((uid_t) pw->pw_uid);
- enable_signaling(); /* we can allow signals once again: kinch */
- goto pasv_error;
- }
- }
- (void) seteuid((uid_t) pw->pw_uid);
- enable_signaling(); /* we can allow signals once again: kinch */
- len = sizeof(pasv_addr);
- if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
- goto pasv_error;
- if (listen(pdata, 1) < 0)
- goto pasv_error;
- usedefault = 1;
- if (route_vectored)
- a = (char *) &vect_addr.sin_addr;
- else
- a = (char *) &pasv_addr.sin_addr;
- p = (char *) &pasv_addr.sin_port;
- #define UC(b) (((int) b) & 0xff)
- if (debug) {
- char *s = calloc(128 + strlen(remoteident), sizeof(char));
- if (s) {
- int i = ntohs(pasv_addr.sin_port);
- sprintf(s, "PASV port %i assigned to %s", i, remoteident);
- syslog(LOG_DEBUG, s);
- free(s);
- }
- }
- reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
- UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
- return;
- pasv_error:
- (void) close(pdata);
- pdata = -1;
- if (debug) {
- char *s = calloc(128 + strlen(remoteident), sizeof(char));
- if (s) {
- sprintf(s, "PASV port assignment assigned for %s", remoteident);
- syslog(LOG_DEBUG, s);
- free(s);
- }
- }
- perror_reply(425, "Can't open passive connection");
- return;
- }
- /*
- * Generate unique name for file with basename "local". The file named
- * "local" is already known to exist. Generates failure reply on error.
- */
- char *gunique(char *local)
- {
- static char new[MAXPATHLEN];
- struct stat st;
- char *cp = strrchr(local, '/');
- int count = 0;
- if (cp)
- *cp = ' ';
- if (stat(cp ? local : ".", &st) < 0) {
- perror_reply(553, cp ? local : ".");
- return ((char *) 0);
- }
- if (cp)
- *cp = '/';
- (void) strncpy(new, local, (sizeof new) - 3);
- new[sizeof(new) - 3] = ' ';
- cp = new + strlen(new);
- *cp++ = '.';
- for (count = 1; count < 100; count++) {
- if (count == 10) {
- cp -= 2;
- *cp++ = '.';
- }
- (void) sprintf(cp, "%d", count);
- if (stat(new, &st) < 0)
- return (new);
- }
- reply(452, "Unique file name cannot be created.");
- return ((char *) 0);
- }
- /* Format and send reply containing system error number. */
- void perror_reply(int code, char *string)
- {
- /*
- * If restricted user and string starts with home dir path, strip it off
- * and return only the relative path.
- */
- if (restricted_user && (home != NULL) && (home[0] != ' ')) {
- size_t len = strlen (home);
- if (strncmp (home, string, len) == 0) {
- if (string[len - 1] == '/')
- string += len - 1;
- else if (string[len] == '/')
- string += len;
- else if (string[len] == ' ')
- string = "/";
- }
- }
- reply(code, "%s: %s.", string, strerror(errno));
- }
- static char *onefile[] =
- {"", 0};
- extern char **ftpglob(register char *v);
- extern char *globerr;
- void send_file_list(char *whichfiles)
- {
- /* static so not clobbered by longjmp(), volatile would also work */
- static FILE *dout;
- static DIR *dirp;
- static char **sdirlist;
- static char *wildcard = NULL;
- struct stat st;
- register char **dirlist, *dirname;
- int simple = 0;
- /* This is ANSI/ISO C .. strpbrk should be in <string.h> which we've
- ** already included so we don't need the following line. 'sides, it
- ** breaks the GNU EGCS C compiler
- ** extern char *strpbrk(const char *, const char *);
- */
- #ifdef TRANSFER_COUNT
- #ifdef TRANSFER_LIMIT
- if (((file_limit_raw_out > 0) && (xfer_count_out >= file_limit_raw_out))
- || ((file_limit_raw_total > 0) && (xfer_count_total >= file_limit_raw_total))
- || ((data_limit_raw_out > 0) && (byte_count_out >= data_limit_raw_out))
- || ((data_limit_raw_total > 0) && (byte_count_total >= data_limit_raw_total))) {
- if (log_security)
- if (anonymous)
- syslog(LOG_NOTICE, "anonymous(%s) of %s tried to list files (Transfer limits exceeded)",
- guestpw, remoteident);
- else
- syslog(LOG_NOTICE, "%s of %s tried to list files (Transfer limits exceeded)",
- pw->pw_name, remoteident);
- reply(553, "Permission denied on server. (Transfer limits exceeded)");
- return;
- }
- #endif
- #endif
- draconian_FILE = NULL;
- dout = NULL;
- dirp = NULL;
- sdirlist = NULL;
- wildcard = NULL;
- if (strpbrk(whichfiles, "~{[*?") == NULL) {
- if (whichfiles[0] == ' ') {
- wildcard = strdup("*");
- if (wildcard == NULL) {
- reply(550, "Memory allocation error");
- goto globfree;
- }
- whichfiles = wildcard;
- }
- else if (stat(whichfiles, &st) >= 0) {
- if ((st.st_mode & S_IFMT) == S_IFDIR) {
- wildcard = malloc(strlen(whichfiles) + 3);
- if (wildcard == NULL) {
- reply(550, "Memory allocation error");
- goto globfree;
- }
- strcpy(wildcard, whichfiles);
- strcat(wildcard, "/*");
- whichfiles = wildcard;
- }
- }
- }
- if (strpbrk(whichfiles, "~{[*?") != NULL) {
- globerr = NULL;
- dirlist = ftpglob(whichfiles);
- sdirlist = dirlist; /* save to free later */
- if (globerr != NULL) {
- reply(550, globerr);
- goto globfree;
- }
- else if (dirlist == NULL) {
- errno = ENOENT;
- perror_reply(550, whichfiles);
- goto globfree;
- }
- }
- else {
- onefile[0] = whichfiles;
- dirlist = onefile;
- simple = 1;
- }
- if (wu_setjmp(urgcatch)) {
- transflag = 0;
- if (dout != NULL)
- (void) fclose(dout);
- if (dirp != NULL)
- (void) closedir(dirp);
- data = -1;
- pdata = -1;
- goto globfree;
- }
- while ((dirname = *dirlist++) != NULL) {
- if (stat(dirname, &st) < 0) {
- /* If user typed "ls -l", etc, and the client used NLST, do what
- * the user meant. */
- if (dirname[0] == '-' && *dirlist == NULL && transflag == 0) {
- retrieve_is_data = 0;
- #ifndef INTERNAL_LS
- retrieve(ls_plain, dirname);
- #else
- ls(dirname, 1);
- #endif
- retrieve_is_data = 1;
- goto globfree;
- }
- perror_reply(550, dirname);
- if (dout != NULL) {
- (void) fclose(dout);
- transflag = 0;
- data = -1;
- pdata = -1;
- }
- goto globfree;
- }
- if ((st.st_mode & S_IFMT) != S_IFDIR) {
- if (dout == NULL) {
- dout = dataconn("file list", (off_t) - 1, "w");
- if (dout == NULL)
- goto globfree;
- transflag++;
- draconian_FILE = dout;
- }
- if (draconian_FILE != NULL) {
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- fprintf(dout, "%s%sn", dirname,
- type == TYPE_A ? "r" : "");
- }
- byte_count += strlen(dirname) + 1;
- #ifdef TRANSFER_COUNT
- byte_count_total += strlen(dirname) + 1;
- byte_count_out += strlen(dirname) + 1;
- if (type == TYPE_A) {
- byte_count_total++;
- byte_count_out++;
- }
- #endif
- }
- }
- if (dout != NULL) {
- if (draconian_FILE != NULL) {
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- fflush(dout);
- }
- if (draconian_FILE != NULL) {
- (void) signal(SIGALRM, draconian_alarm_signal);
- alarm(timeout_data);
- socket_flush_wait(dout);
- }
- }
- if (dout == NULL)
- reply(550, "No files found.");
- else if ((draconian_FILE == NULL) || ferror(dout) != 0) {
- alarm(0);
- perror_reply(550, "Data connection");
- }
- else {
- #ifdef TRANSFER_COUNT
- xfer_count_total++;
- xfer_count_out++;
- #endif
- alarm(0);
- reply(226, "Transfer complete.");
- }
- transflag = 0;
- if ((dout != NULL) && (draconian_FILE != NULL))
- (void) fclose(dout);
- data = -1;
- pdata = -1;
- globfree:
- if (wildcard != NULL) {
- free(wildcard);
- wildcard = NULL;
- }
- if (sdirlist) {
- blkfree(sdirlist);
- free((char *) sdirlist);
- }
- }
- /*
- ** SETPROCTITLE -- set process title for ps
- **
- ** Parameters:
- ** fmt -- a printf style format string.
- ** a, b, c -- possible parameters to fmt.
- **
- ** Returns:
- ** none.
- **
- ** Side Effects:
- ** Clobbers argv of our main procedure so ps(1) will
- ** display the title.
- */
- #define SPT_NONE 0 /* don't use it at all */
- #define SPT_REUSEARGV 1 /* cover argv with title information */
- #define SPT_BUILTIN 2 /* use libc builtin */
- #define SPT_PSTAT 3 /* use pstat(PSTAT_SETCMD, ...) */
- #define SPT_PSSTRINGS 4 /* use PS_STRINGS->... */
- #define SPT_SYSMIPS 5 /* use sysmips() supported by NEWS-OS 6 */
- #define SPT_SCO 6 /* write kernel u. area */
- #define SPT_CHANGEARGV 7 /* write our own strings into argv[] */
- #define MAXLINE 2048 /* max line length for setproctitle */
- #define SPACELEFT(buf, ptr) (sizeof buf - ((ptr) - buf))
- #ifndef SPT_TYPE
- #define SPT_TYPE SPT_REUSEARGV
- #endif
- #if SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN
- #if SPT_TYPE == SPT_PSTAT
- #include <sys/pstat.h>
- #endif
- #if SPT_TYPE == SPT_PSSTRINGS
- #include <machine/vmparam.h>
- #include <sys/exec.h>
- #ifndef PS_STRINGS /* hmmmm.... apparently not available after all */
- #undef SPT_TYPE
- #define SPT_TYPE SPT_REUSEARGV
- #else
- #ifndef NKPDE /* FreeBSD 2.0 */
- #define NKPDE 63
- typedef unsigned int *pt_entry_t;
- #endif
- #endif
- #endif
- #if SPT_TYPE == SPT_PSSTRINGS || SPT_TYPE == SPT_CHANGEARGV
- #define SETPROC_STATIC static
- #else
- #define SETPROC_STATIC
- #endif
- #if SPT_TYPE == SPT_SYSMIPS
- #include <sys/sysmips.h>
- #include <sys/sysnews.h>
- #endif
- #if SPT_TYPE == SPT_SCO
- #ifdef UNIXWARE
- #include <sys/exec.h>
- #include <sys/ksym.h>
- #include <sys/proc.h>
- #include <sys/user.h>
- #else /* UNIXWARE */
- #include <sys/immu.h>
- #include <sys/dir.h>
- #include <sys/user.h>
- #include <sys/fs/s5param.h>
- #endif /* UNIXWARE */
- #if PSARGSZ > MAXLINE
- #define SPT_BUFSIZE PSARGSZ
- #endif
- #ifndef _PATH_KMEM
- #define _PATH_KMEM "/dev/kmem"
- #endif /* _PATH_KMEM */
- #endif /* SPT_SCO */
- #ifndef SPT_PADCHAR
- #define SPT_PADCHAR ' '
- #endif
- #ifndef SPT_BUFSIZE
- #define SPT_BUFSIZE MAXLINE
- #endif
- #endif /* SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN */
- #if SPT_TYPE == SPT_REUSEARGV || SPT_TYPE == SPT_CHANGEARGV
- char **Argv = NULL; /* pointer to argument vector */
- #endif
- #if SPT_TYPE == SPT_REUSEARGV
- char *LastArgv = NULL; /* end of argv */
- #endif
- /*
- ** Pointers for setproctitle.
- ** This allows "ps" listings to give more useful information.
- */
- void initsetproctitle(argc, argv, envp)
- int argc;
- char **argv;
- char **envp;
- {
- #if SPT_TYPE == SPT_REUSEARGV
- register int i, envpsize = 0;
- char **newenviron;
- extern char **environ;
- /*
- ** Save start and extent of argv for setproctitle.
- */
- LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
- if (envp != NULL) {
- /*
- ** Move the environment so setproctitle can use the space at
- ** the top of memory.
- */
- for (i = 0; envp[i] != NULL; i++)
- envpsize += strlen(envp[i]) + 1;
- newenviron = (char **) malloc(sizeof(char *) * (i + 1));
- if (newenviron) {
- int err = 0;
- for (i = 0; envp[i] != NULL; i++) {
- if ((newenviron[i] = strdup(envp[i])) == NULL) {
- err = 1;
- break;
- }
- }
- if (err) {
- for (i = 0; newenviron[i] != NULL; i++)
- free(newenviron[i]);
- free(newenviron);
- i = 0;
- }
- else {
- newenviron[i] = NULL;
- environ = newenviron;
- }
- }
- else {
- i = 0;
- }
- /*
- ** Find the last environment variable within wu-ftpd's
- ** process memory area.
- */
- while (i > 0 && (envp[i - 1] < argv[0] ||
- envp[i - 1] > (argv[argc - 1] + strlen(argv[argc - 1]) +
- 1 + envpsize)))
- i--;
- if (i > 0)
- LastArgv = envp[i - 1] + strlen(envp[i - 1]);
- }
- #endif /* SPT_TYPE == SPT_REUSEARGV */
- #if SPT_TYPE == SPT_REUSEARGV || SPT_TYPE == SPT_CHANGEARGV
- Argv = argv;
- #endif
- }
- #if SPT_TYPE != SPT_BUILTIN
- /*VARARGS1 */
- void setproctitle(const char *fmt,...)
- {
- #if SPT_TYPE != SPT_NONE
- register char *p;
- register int i;
- SETPROC_STATIC char buf[SPT_BUFSIZE];
- VA_LOCAL_DECL
- #if SPT_TYPE == SPT_PSTAT
- union pstun pst;
- #endif
- #if SPT_TYPE == SPT_SCO
- static off_t seek_off;
- static int kmemfd = -1;
- static int kmempid = -1;
- #ifdef UNIXWARE
- off_t offset;
- void *ptr;
- struct mioc_rksym rks;
- #endif /* UNIXWARE */
- #endif /* SPT_SCO */
- p = buf;
- /* print ftpd: heading for grep */
- (void) strcpy(p, "ftpd: ");
- p += strlen(p);
- /* print the argument string */
- VA_START(fmt);
- (void) vsnprintf(p, SPACELEFT(buf, p), fmt, ap);
- VA_END;
- i = strlen(buf);
- #if SPT_TYPE == SPT_PSTAT
- pst.pst_command = buf;
- pstat(PSTAT_SETCMD, pst, i, 0, 0);
- #endif
- #if SPT_TYPE == SPT_PSSTRINGS
- PS_STRINGS->ps_nargvstr = 1;
- PS_STRINGS->ps_argvstr = buf;
- #endif
- #if SPT_TYPE == SPT_SYSMIPS
- sysmips(SONY_SYSNEWS, NEWS_SETPSARGS, buf);
- #endif
- #if SPT_TYPE == SPT_SCO
- if (kmemfd < 0 || kmempid != getpid()) {
- if (kmemfd >= 0)
- close(kmemfd);
- if ((kmemfd = open(_PATH_KMEM, O_RDWR, 0)) < 0)
- return;
- (void) fcntl(kmemfd, F_SETFD, 1);
- kmempid = getpid();
- #ifdef UNIXWARE
- seek_off = 0;
- rks.mirk_symname = "upointer";
- rks.mirk_buf = &ptr;
- rks.mirk_buflen = sizeof(ptr);
- if (ioctl(kmemfd, MIOC_READKSYM, &rks) < 0)
- return;
- offset = (off_t) ptr + (off_t) & ((struct user *) 0)->u_procp;
- if (lseek(kmemfd, offset, SEEK_SET) != offset)
- return;
- if (read(kmemfd, &ptr, sizeof(ptr)) != sizeof(ptr))
- return;
- offset = (off_t) ptr + (off_t) & ((struct proc *) 0)->p_execinfo;
- if (lseek(kmemfd, offset, SEEK_SET) != offset)
- return;
- if (read(kmemfd, &ptr, sizeof(ptr)) != sizeof(ptr))
- return;
- seek_off = (off_t) ptr + (off_t) ((struct execinfo *) 0)->ei_psargs;
- #else /* UNIXWARE */
- seek_off = UVUBLK + (off_t) & ((struct user *) 0)->u_psargs;
- #endif /* UNIXWARE */
- }
- #ifdef UNIXWARE
- if (seek_off == 0)
- return;
- #endif /* UNIXWARE */
- buf[PSARGSZ - 1] = ' ';
- if (lseek(kmemfd, (off_t) seek_off, SEEK_SET) == seek_off)
- (void) write(kmemfd, buf, PSARGSZ);
- #endif /* SPT_SCO */
- #if SPT_TYPE == SPT_REUSEARGV
- if (i > LastArgv - Argv[0] - 2) {
- i = LastArgv - Argv[0] - 2;
- buf[i] = ' ';
- }
- (void) strcpy(Argv[0], buf);
- p = &Argv[0][i];
- while (p < LastArgv)
- *p++ = SPT_PADCHAR;
- Argv[1] = NULL;
- #endif
- #if SPT_TYPE == SPT_CHANGEARGV
- Argv[0] = buf;
- Argv[1] = 0;
- #endif
- #endif /* SPT_TYPE != SPT_NONE */
- }
- #endif /* SPT_TYPE != SPT_BUILTIN */
- #ifdef KERBEROS
- /* thanks to gshapiro@wpi.wpi.edu for the following kerberosities */
- void init_krb()
- {
- char hostname[100];
- #ifdef HAVE_SYSINFO
- if (sysinfo(SI_HOSTNAME, hostname, sizeof(hostname)) < 0) {
- perror("sysinfo");
- #else
- if (gethostname(hostname, sizeof(hostname)) < 0) {
- perror("gethostname");
- #endif
- exit(1);
- }
- if (strchr(hostname, '.'))
- *(strchr(hostname, '.')) = 0;
- sprintf(krb_ticket_name, "/var/dss/kerberos/tkt/tkt.%d", getpid());
- krb_set_tkt_string(krb_ticket_name);
- config_auth();
- if (krb_svc_init("hesiod", hostname, (char *) NULL, 0, (char *) NULL,
- (char *) NULL) != KSUCCESS) {
- fprintf(stderr, "Couldn't initialize Kerberosn");
- exit(1);
- }
- }
- void end_krb()
- {
- unlink(krb_ticket_name);
- }
- #endif /* KERBEROS */
- #ifdef ULTRIX_AUTH
- static int ultrix_check_pass(char *passwd, char *xpasswd)
- {
- struct svcinfo *svp;
- int auth_status;
- if ((svp = getsvc()) == (struct svcinfo *) NULL) {
- syslog(LOG_WARNING, "getsvc() failed in ultrix_check_pass");
- return -1;
- }
- if (pw == (struct passwd *) NULL) {
- return -1;
- }
- if (((svp->svcauth.seclevel == SEC_UPGRADE) &&
- (!strcmp(pw->pw_passwd, "*")))
- || (svp->svcauth.seclevel == SEC_ENHANCED)) {
- if ((auth_status = authenticate_user(pw, passwd, "/dev/ttypXX")) >= 0) {
- /* Indicate successful validation */
- return auth_status;
- }
- if (auth_status < 0 && errno == EPERM) {
- /* Log some information about the failed login attempt. */
- switch (abs(auth_status)) {
- case A_EBADPASS:
- break;
- case A_ESOFTEXP:
- syslog(LOG_NOTICE, "password will expire soon for user %s",
- pw->pw_name);
- break;
- case A_EHARDEXP:
- syslog(LOG_NOTICE, "password has expired for user %s",
- pw->pw_name);
- break;
- case A_ENOLOGIN:
- syslog(LOG_NOTICE, "user %s attempted login to disabled acct",
- pw->pw_name);
- break;
- }
- }
- }
- else {
- if ((*pw->pw_passwd != ' ') && (!strcmp(xpasswd, pw->pw_passwd))) {
- /* passwd in /etc/passwd isn't empty && encrypted passwd matches */
- return 0;
- }
- }
- return -1;
- }
- #endif /* ULTRIX_AUTH */
- #ifdef USE_PAM
- /* This is rather an abuse of PAM, but the FTP protocol doesn't allow much
- * flexibility here. :-(
- */
- #include <security/pam_appl.h>
- /* Static variables used to communicate between the conversation function
- * and the server_login function
- */
- static char *PAM_password;
- /* PAM conversation function
- * Here we assume (for now, at least) that echo on means login name, and
- * echo off means password.
- */
- static int PAM_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
- {
- int replies = 0;
- struct pam_response *reply = NULL;
- #define COPY_STRING(s) (s) ? strdup(s) : NULL
- reply = malloc(sizeof(struct pam_response) * num_msg);
- if (!reply)
- return PAM_CONV_ERR;
- for (replies = 0; replies < num_msg; replies++) {
- switch (msg[replies]->msg_style) {
- case PAM_PROMPT_ECHO_ON:
- return PAM_CONV_ERR;
- break;
- case PAM_PROMPT_ECHO_OFF:
- reply[replies].resp_retcode = PAM_SUCCESS;
- reply[replies].resp = COPY_STRING(PAM_password);
- /* PAM frees resp */
- break;
- case PAM_TEXT_INFO:
- /* ignore it... */
- reply[replies].resp_retcode = PAM_SUCCESS;
- reply[replies].resp = NULL;
- break;
- case PAM_ERROR_MSG:
- /* ignore it... */
- reply[replies].resp_retcode = PAM_SUCCESS;
- reply[replies].resp = NULL;
- break;
- default:
- /* Must be an error of some sort... */
- return PAM_CONV_ERR;
- }
- }
- *resp = reply;
- return PAM_SUCCESS;
- }
- static struct pam_conv PAM_conversation =
- {
- &PAM_conv,
- NULL
- };
- static int pam_check_pass(char *user, char *passwd)
- {
- pam_handle_t *pamh;
- int pam_error;
- /* Now use PAM to do authentication. For now, we won't worry about
- * session logging, only authentication. Bail out if there are any
- * errors. Since this is a limited protocol, and an even more limited
- * function within a server speaking this protocol, we can't be as
- * verbose as would otherwise make sense.
- */
- #define PAM_BAIL if (pam_error != PAM_SUCCESS) { pam_end(pamh, 0); return 0; }
- PAM_password = passwd;
- pam_error = pam_start("ftp", user, &PAM_conversation, &pamh);
- pam_set_item(pamh, PAM_RHOST, remotehost);
- PAM_BAIL;
- pam_error = pam_authenticate(pamh, 0);
- PAM_BAIL;
- pam_error = pam_acct_mgmt(pamh, 0);
- PAM_BAIL;
- #ifdef PAM_ESTABLISH_CRED
- pam_error = pam_setcred(pamh, PAM_ESTABLISH_CRED);
- #else
- pam_error = pam_setcred(pamh, PAM_CRED_ESTABLISH);
- #endif
- PAM_BAIL;
- pam_end(pamh, PAM_SUCCESS);
- /* If this point is reached, the user has been authenticated. */
- return 1;
- }
- #endif
- #ifdef DAEMON
- static unsigned long int acl_DaemonAddress(void)
- {
- unsigned long int rv = INADDR_ANY;
- struct aclmember *entry = NULL;
- if (getaclentry("daemonaddress", &entry) && ARG0) {
- rv = inet_addr(ARG0);
- if (rv == -1)
- rv = INADDR_ANY;
- }
- return rv;
- }
- /* I am running as a standalone daemon (not under inetd) */
- void do_daemon(int argc, char **argv, char **envp)
- {
- struct sockaddr_in server;
- struct servent *serv;
- int pgrp;
- int lsock;
- int one = 1;
- FILE *pidfile;
- int i;
- /* Some of this is "borrowed" from inn - lots of it isn't */
- if (be_daemon == 2) {
- /* Fork - so I'm not the owner of the process group any more */
- i = fork();
- if (i < 0) {
- syslog(LOG_ERR, "cant fork %m");
- exit(1);
- }
- /* No need for the parent any more */
- if (i > 0)
- exit(0);
- #ifdef NO_SETSID
- pgrp = setpgrp(0, getpid());
- #else
- pgrp = setsid();
- #endif
- if (pgrp < 0) {
- syslog(LOG_ERR, "cannot daemonise: %m");
- exit(1);
- }
- }
- if (!Bypass_PID_Files)
- if ((pidfile = fopen(_PATH_FTPD_PID, "w"))) {
- fprintf(pidfile, "%ldn", (long) getpid());
- fclose(pidfile);
- }
- else {
- syslog(LOG_ERR, "Cannot write pidfile: %m");
- }
- /* Close off all file descriptors and reopen syslog */
- if (be_daemon == 2) {
- int i, fds;
- #ifdef HAVE_GETRLIMIT
- struct rlimit rlp;
- rlp.rlim_cur = rlp.rlim_max = RLIM_INFINITY;
- if (getrlimit(RLIMIT_NOFILE, &rlp))
- return;
- fds = rlp.rlim_cur;
- #else
- #ifdef HAVE_GETDTABLESIZE
- if ((fds = getdtablesize()) <= 0)
- return;
- #else
- #ifdef OPEN_MAX
- fds = OPEN_MAX; /* need to include limits.h somehow */
- #else
- fds = sizeof(long); /* XXX -- magic */
- #endif
- #endif
- #endif
- closelog();
- for (i = 0; i <= fds; i++) {
- close(i);
- }
- #ifdef FACILITY
- openlog("ftpd", LOG_PID | LOG_NDELAY, FACILITY);
- #else
- openlog("ftpd", LOG_PID);
- #endif
- /* junk stderr */
- (void) freopen(_PATH_DEVNULL, "w", stderr);
- }
- if (RootDirectory != NULL) {
- if ((chroot(RootDirectory) < 0)
- || (chdir("/") < 0)) {
- syslog(LOG_ERR, "Cannot chroot to initial directory, aborting.");
- exit(1);
- }
- free(RootDirectory);
- RootDirectory = NULL;
- }
- if (!use_accessfile)
- syslog(LOG_WARNING, "FTP server started without ftpaccess file");
- syslog(LOG_INFO, "FTP server (%s) ready.", version);
- /* Create a socket to listen on */
- lsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (lsock < 0) {
- syslog(LOG_ERR, "Cannot create socket to listen on: %m");
- exit(1);
- }
- if (setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)) < 0) {
- syslog(LOG_ERR, "Cannot set SO_REUSEADDR option: %m");
- exit(1);
- }
- if (keepalive)
- (void) setsockopt(lsock, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(one));
- server.sin_family = AF_INET;
- server.sin_addr.s_addr = acl_DaemonAddress();
- if (daemon_port == 0) {
- if (!(serv = getservbyname("ftp", "tcp"))) {
- syslog(LOG_ERR, "Cannot find service ftp: %m");
- exit(1);
- }
- server.sin_port = serv->s_port;
- daemon_port = ntohs(serv->s_port);
- }
- else
- server.sin_port = htons(daemon_port);
- if (bind(lsock, (struct sockaddr *) &server, sizeof(server)) < 0) {
- syslog(LOG_ERR, "Cannot bind socket: %m");
- exit(1);
- }
- listen(lsock, MAX_BACKLOG);
- sprintf(proctitle, "accepting connections on port %i", daemon_port);
- setproctitle("%s", proctitle);
- while (1) {
- int pid;
- int msgsock;
- msgsock = accept(lsock, 0, 0);
- if (msgsock < 0) {
- syslog(LOG_ERR, "Accept failed: %m");
- sleep(1);
- continue;
- }
- if (keepalive)
- (void) setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(one));
- /* Fork off a handler */
- pid = fork();
- if (pid < 0) {
- syslog(LOG_ERR, "failed to fork: %m");
- sleep(1);
- continue;
- }
- if (pid == 0) {
- /* I am that forked off child */
- closelog();
- /* Make sure that stdin/stdout are the new socket */
- dup2(msgsock, 0);
- dup2(msgsock, 1);
- /* Only parent needs lsock */
- if (lsock != 0 && lsock != 1)
- close(lsock);
- #ifdef FACILITY
- openlog("ftpd", LOG_PID | LOG_NDELAY, FACILITY);
- #else
- openlog("ftpd", LOG_PID);
- #endif
- return;
- }
- /* I am the parent */
- close(msgsock);
- /* Quick check to see if any of the forked off children have
- * terminated. */
- while ((pid = waitpid((pid_t) -1, (int *) 0, WNOHANG)) > 0) {
- /* A child has finished */
- }
- }
- }
- #endif /* DAEMON */
- int pasv_allowed(char *remoteaddr)
- {
- char class[MAXPATHLEN];
- int which;
- struct aclmember *entry = NULL;
- (void) acl_getclass(class);
- while (getaclentry("pasv-allow", &entry)) {
- if ((ARG0 != NULL) && (strcasecmp(class, ARG0) == 0))
- for (which = 1; (which < MAXARGS) && (ARG[which] != NULL); which++) {
- if (hostmatch(ARG[which], remoteaddr, NULL))
- return 1;
- }
- }
- return 0;
- }
- int port_allowed(char *remoteaddr)
- {
- char class[MAXPATHLEN];
- int which;
- struct aclmember *entry = NULL;
- (void) acl_getclass(class);
- while (getaclentry("pasv-allow", &entry)) {
- if ((ARG0 != NULL) && (strcasecmp(class, ARG0) == 0))
- for (which = 1; (which < MAXARGS) && (ARG[which] != NULL); which++) {
- if (hostmatch(ARG[which], remoteaddr, NULL))
- return 1;
- }
- }
- return 0;
- }
- #ifdef MAIL_ADMIN
- char *email(char *full_address)
- {
- /* Get the plain address part from an e-mail address
- (i.e. remove realname) */
- char *addr;
- addr = (char *) malloc(strlen(full_address) + 1);
- memset(addr, 0, strlen(full_address) + 1);
- strcpy(addr, full_address);
- /* Realname <user@host> type address */
- if (((char *) strchr(addr, '<')) != NULL) {
- addr = (char *) strchr(addr, '<') + 1;
- addr[strchr(addr, '>') - addr] = ' ';
- }
- /* user@host (Realname) type address */
- if (((char *) strchr(addr, ' ')) != NULL)
- addr[strchr(addr, ' ') - addr] = ' ';
- return addr;
- }
- FILE *SockOpen(char *host, int clientPort)
- {
- int sock;
- unsigned long inaddr;
- struct sockaddr_in ad;
- struct hostent *hp;
- FILE *fp;
- memset(&ad, 0, sizeof(ad));
- ad.sin_family = AF_INET;
- inaddr = inet_addr(host);
- if (inaddr != (unsigned long) -1)
- memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
- else {
- hp = gethostbyname(host);
- if (hp == NULL)
- return (FILE *) NULL;
- memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
- }
- ad.sin_port = htons(clientPort);
- sock = socket(AF_INET, SOCK_STREAM, 0);
- if (sock < 0)
- return (FILE *) NULL;
- if (connect(sock, (struct sockaddr *) &ad, sizeof(ad)) < 0) {
- close(sock);
- return (FILE *) NULL;
- }
- fp = fdopen(sock, "r+");
- setvbuf(fp, NULL, _IOLBF, 2048);
- return (fp);
- }
- int SockPrintf(FILE *sockfp, char *format,...)
- {
- va_list ap;
- char buf[32768];
- va_start(ap, format);
- vsprintf(buf, format, ap);
- va_end(ap);
- return SockWrite(buf, 1, strlen(buf), sockfp);
- }
- int SockWrite(char *buf, int size, int len, FILE *sockfp)
- {
- return (fwrite(buf, size, len, sockfp));
- }
- char *SockGets(FILE *sockfp, char *buf, int len)
- {
- return (fgets(buf, len, sockfp));
- }
- int SockPuts(FILE *sockfp, char *buf)
- {
- int rc;
- if ((rc = SockWrite(buf, 1, strlen(buf), sockfp)))
- return rc;
- return SockWrite("rn", 1, 2, sockfp);
- }
- int Reply(FILE *sockfp)
- {
- char *reply, *rec, *separator;
- int ret = 0;
- reply = (char *) malloc(1024);
- memset(reply, 0, 1024);
- do {
- rec = SockGets(sockfp, reply, 1024);
- if (rec != NULL) {
- ret = strtol(reply, &separator, 10);
- }
- else
- ret = 250;
- } while ((rec != NULL) && (separator[0] != ' '));
- return ret;
- }
- int Send(FILE *sockfp, char *format,...)
- {
- va_list ap;
- char buf[32728];
- va_start(ap, format);
- vsprintf(buf, format, ap);
- va_end(ap);
- SockWrite(buf, 1, strlen(buf), sockfp);
- return Reply(sockfp);
- }
- #endif /* MAIL_ADMIN */
- /*
- * fixpath
- *
- * In principal, this is similar to realpath() or the mapping chdir function.
- * It removes unnecessary path components. We do this to put a stop to
- * attempts to cause a memory starvation DoS.
- *
- */
- void fixpath(char *path)
- {
- int abs = 0;
- char *in;
- char *out;
- if (*path == '/') {
- abs = 1;
- path++;
- }
- else if (*path == '~') {
- do
- path++;
- while ((*path != ' ') && (*path != '/'));
- if (*path == '/')
- path++;
- }
- in = path;
- out = path;
- while (*in != ' ') {
- if (*in == '/')
- in++;
- else if ((in[0] == '.') && ((in[1] == '/') || (in[1] == ' '))) {
- in++;
- if (*in == '/')
- in++;
- }
- else if ((in[0] == '.') && (in[1] == '.') && ((in[2] == '/') || (in[2] == ' '))) {
- if (out == path) {
- if (abs) {
- in++;
- in++;
- if (*in == '/')
- in++;
- }
- else {
- *out++ = *in++;
- *out++ = *in++;
- if (*in == '/')
- *out++ = *in++;
- path = out;
- }
- }
- else {
- out--;
- while ((out != path) && (*--out != '/'));
- in++;
- in++;
- if (*in == '/')
- in++;
- }
- }
- else {
- do
- *out++ = *in++;
- while ((*in != ' ') && (*in != '/'));
- if (*in == '/')
- *out++ = *in++;
- }
- }
- *out = ' ';
- }