📄 ftpd.c
字号:
void dopasv(int psvtype){ struct sockaddr_storage dataconn; /* my data connection endpoint */ unsigned long a = 0U; unsigned int p; int on; unsigned int firstporttried; if (loggedin == 0) { addreply_noformat(530, MSG_NOT_LOGGED_IN); return; } if (datafd != -1) { /* for buggy clients */ (void) close(datafd); datafd = -1; } fourinsix(&ctrlconn); if (STORAGE_FAMILY(ctrlconn) == AF_INET6 && psvtype == 0) { addreply_noformat(425, MSG_CANT_PASV); return; } firstporttried = firstport + zrand() % (lastport - firstport + 1); p = firstporttried; datafd = socket(STORAGE_FAMILY(ctrlconn), SOCK_STREAM, IPPROTO_TCP); if (datafd == -1) { error(425, MSG_CANT_PASSIVE); return; } on = 1; if (setsockopt(datafd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof on) < 0) { error(421, "setsockopt"); return; } dataconn = ctrlconn; for (;;) { if (STORAGE_FAMILY(dataconn) == AF_INET6) { STORAGE_PORT6(dataconn) = htons(p); } else { STORAGE_PORT(dataconn) = htons(p); } if (bind(datafd, (struct sockaddr *) &dataconn, STORAGE_LEN(dataconn)) == 0) { break; } p--; if (p < firstport) { p = lastport; } if (p == firstporttried) { (void) close(datafd); datafd = -1; addreply_noformat(425, MSG_PORTS_BUSY); return; } } alarm(idletime); if (listen(datafd, DEFAULT_BACKLOG_DATA) < 0) { (void) close(datafd); datafd = -1; error(425, MSG_GETSOCKNAME_DATA); return; } switch (psvtype) { case 0: if (STORAGE_FAMILY(force_passive_ip) == 0) { a = ntohl(STORAGE_SIN_ADDR(dataconn)); } else if (STORAGE_FAMILY(force_passive_ip) == AF_INET6) { (void) close(datafd); datafd = -1; addreply_noformat(425, MSG_NO_EPSV); return; } else if (STORAGE_FAMILY(force_passive_ip) == AF_INET) { a = ntohl(STORAGE_SIN_ADDR(force_passive_ip)); } else { _EXIT(EXIT_FAILURE); }/* According to RFC, any message can follow 227. But broken NAT gateways * and connection tracking code rely on this. So don't translate the following * messages */ addreply(227, "Entering Passive Mode (%lu,%lu,%lu,%lu,%u,%u)", (a >> 24) & 255, (a >> 16) & 255, (a >> 8) & 255, a & 255, (p >> 8) & 255, p & 255); break; case 1: addreply(229, "Extended Passive mode OK (|||%u|)", p); break; case 2: addreply(227, "%u", p); break; default: _EXIT(EXIT_FAILURE); } passive = 1;}void doport(const char *arg){ unsigned int a1, a2, a3, a4, p1, p2; struct sockaddr_storage a; if (6 != sscanf(arg, "%u,%u,%u,%u,%u,%u", &a1, &a2, &a3, &a4, &p1, &p2) || a1 > 255 || a2 > 255 || a3 > 255 || a4 > 255 || p1 > 255 || p2 > 255 || (a1|a2|a3|a4) == 0 || (p1|p2) == 0) { addreply_noformat(501, MSG_SYNTAX_ERROR_IP); return; } memset(&a, 0, sizeof a); STORAGE_FAMILY(a) = AF_INET; STORAGE_SIN_ADDR(a) = htonl((a1 << 24) | (a2 << 16) | (a3 << 8) | a4); SET_STORAGE_LEN(a, sizeof(struct sockaddr_in)); doport2(a, (p1 << 8) | p2);}#ifndef WITH_PRIVSEPstatic int doport3(const int protocol){ struct sockaddr_storage dataconn; /* his endpoint */ # ifndef NON_ROOT_FTP static const unsigned short portlist[] = FTP_ACTIVE_SOURCE_PORTS; const unsigned short *portlistpnt = portlist;# else static const unsigned short portlist[] = { 0U }; const unsigned short *portlistpnt = portlist;# endif int on; # ifndef NON_ROOT_FTP# ifndef HAVE_SYS_FSUID_H disablesignals(); seteuid((uid_t) 0);# endif# endif if ((datafd = socket(protocol, SOCK_STREAM, IPPROTO_TCP)) == -1) { data_socket_error:# ifndef NON_ROOT_FTP# ifndef HAVE_SYS_FSUID_H if (seteuid(authresult.uid) != 0) { _EXIT(EXIT_FAILURE); } enablesignals();# endif# endif (void) close(datafd); datafd = -1; error(425, MSG_CANT_CREATE_DATA_SOCKET); return -1; } on = 1;# ifdef SO_REUSEPORT (void) setsockopt(datafd, SOL_SOCKET, SO_REUSEPORT, (char *) &on, sizeof on); # else (void) setsockopt(datafd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof on);# endif memcpy(&dataconn, &ctrlconn, sizeof dataconn); for (;;) { if (STORAGE_FAMILY(dataconn) == AF_INET6) { STORAGE_PORT6(dataconn) = htons(*portlistpnt); } else { STORAGE_PORT(dataconn) = htons(*portlistpnt); } if (bind(datafd, (struct sockaddr *) &dataconn, STORAGE_LEN(dataconn)) == 0) { break; }# ifdef USE_ONLY_FIXED_DATA_PORT (void) sleep(1U);# else if (*portlistpnt == (unsigned short) 0U) { goto data_socket_error; } portlistpnt++;# endif }# ifndef NON_ROOT_FTP# ifndef HAVE_SYS_FSUID_H if (seteuid(authresult.uid) != 0) { _EXIT(EXIT_FAILURE); } enablesignals();# endif# endif return 0;}#else/* Privilege-separated version of doport3() */static int doport3(const int protocol){ if ((datafd = privsep_bindresport(protocol, ctrlconn)) == -1) { error(425, MSG_CANT_CREATE_DATA_SOCKET); return -1; } return 0;}#endifvoid doport2(struct sockaddr_storage a, unsigned int p){ if (loggedin == 0) { addreply_noformat(530, MSG_NOT_LOGGED_IN); return; } if (epsv_all != 0) { addreply_noformat(501, MSG_ACTIVE_DISABLED); return; } if (datafd != -1) { /* for buggy clients saying PORT over and over */ (void) close(datafd); datafd = -1; } if (p < 1024U) { addreply_noformat(501, MSG_BAD_PORT); return; } if (doport3(STORAGE_FAMILY(a) == AF_INET6 ? PF_INET6 : PF_INET) != 0) { return; } peerdataport = (unsigned short) p; if (addrcmp(&a, &peer) != 0) { char hbuf[NI_MAXHOST]; char peerbuf[NI_MAXHOST]; if (getnameinfo((struct sockaddr *) &a, STORAGE_LEN(a), hbuf, sizeof hbuf, NULL, (size_t) 0U, NI_NUMERICHOST) != 0 || getnameinfo((struct sockaddr *) &peer, STORAGE_LEN(peer), peerbuf, sizeof peerbuf, NULL, (size_t) 0U, NI_NUMERICHOST) != 0) { goto hu; } if (allowfxp == 0 || (allowfxp == 1 && guest != 0)) { hu: (void) close(datafd); datafd = -1; addreply(500, MSG_NO_FXP, hbuf, peerbuf); return; } else { addreply(0, MSG_FXP, peerbuf, hbuf); memcpy(&peer, &a, sizeof a); } } passive = 0; addreply_noformat(200, MSG_PORT_SUCCESSFUL); return;}void closedata(void){ volatile int tmp_xferfd = xferfd; /* do not simplify this... */ xferfd = -1; /* ...it avoids a race */ (void) close(tmp_xferfd);}void opendata(void){ struct sockaddr_storage dataconn; /* his data connection endpoint */ int fd; socklen_t socksize; if (xferfd != -1) { closedata(); } if (datafd == -1) { addreply_noformat(425, MSG_NO_DATA_CONN); return; } if (passive != 0) { fd_set rs; struct timeval tv; alarm(idletime); for (;;) { FD_ZERO(&rs); FD_SET(datafd, &rs); tv.tv_sec = idletime; tv.tv_usec = 0; /* I suppose it would be better to listen for ABRT too... */ if (select(datafd + 1, &rs, NULL, NULL, &tv) <= 0) { die(421, LOG_INFO, MSG_TIMEOUT_DATA , (unsigned long) idletime); } socksize = (socklen_t) sizeof(dataconn); memset(&dataconn, 0, sizeof dataconn); if ((fd = accept(datafd, (struct sockaddr *) &dataconn, &socksize)) == -1) { nope: (void) close(datafd); datafd = -1; error(421, MSG_ACCEPT_FAILED); return; } if (STORAGE_FAMILY(dataconn) != AF_INET && STORAGE_FAMILY(dataconn) != AF_INET6) { (void) close(fd); goto nope; } fourinsix(&dataconn); if (addrcmp(&peer, &dataconn) == 0) { break; } if (allowfxp == 0 || (allowfxp == 1 && guest != 0)) { shutdown(fd, 2); (void) close(fd); } else { break; } } addreply_noformat(150, MSG_ACCEPT_SUCCESS); } else { struct sockaddr_storage peer2; unsigned long tries = 1UL + idletime / 2UL; peer2 = peer; if (STORAGE_FAMILY(peer) == AF_INET6) { STORAGE_PORT6(peer2) = htons(peerdataport); } else { STORAGE_PORT(peer2) = htons(peerdataport); } again: if (connect(datafd, (struct sockaddr *) &peer2, STORAGE_LEN(peer2)) != 0) { if ((errno == EAGAIN || errno == EINTR#ifdef EADDRINUSE || errno == EADDRINUSE#endif ) && tries > 0UL) { tries--; usleep2(1000000UL); goto again; } addreply(425, MSG_CNX_PORT_FAILED ": %s", peerdataport, strerror(errno)); (void) close(datafd); datafd = -1; return; } fd = datafd; datafd = -1; addreply(150, MSG_CNX_PORT, peerdataport); } { int fodder;#ifdef IPTOS_THROUGHPUT fodder = IPTOS_THROUGHPUT; setsockopt(fd, SOL_IP, IP_TOS, (char *) &fodder, sizeof fodder);#endif#ifndef NO_TCP_NOPUSH# ifdef TCP_NOPUSH fodder = 1; setsockopt(fd, SOL_TCP, TCP_NOPUSH, (char *) &fodder, sizeof fodder);# endif#endif#ifndef NO_TCP_LARGE_WINDOW#if defined(SO_SNDBUF) || defined(SO_RCVBUF) fodder = 65536;#endif#ifdef SO_SNDBUF setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &fodder, sizeof fodder);#endif #ifdef SO_RCVBUF setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &fodder, sizeof fodder);#endif #endif#ifndef NO_KEEPALIVE keepalive(fd, 1);#endif } xferfd = fd;}#ifndef MINIMALvoid dochmod(char *name, mode_t mode){ static dev_t root_st_dev; static ino_t root_st_ino; struct stat st2; int fd = -1; if (nochmod != 0 && authresult.uid != (uid_t) 0) { addreply(550, MSG_CHMOD_FAILED, name); return; }# ifndef ANON_CAN_CHANGE_PERMS if (guest != 0) { addreply_noformat(550, MSG_ANON_CANT_CHANGE_PERMS); return; }# endif if (name == NULL || *name == 0) { addreply_noformat(501, MSG_NO_FILE_NAME); return; } if (checknamesanity(name, dot_write_ok) != 0) { addreply(550, MSG_SANITY_FILE_FAILURE, name); return; } fd = open(name, O_RDONLY); if (fd == -1) { goto failure; } if ((root_st_dev | root_st_ino) == 0) { struct stat st; if (stat("/", &st) != 0) { goto failure; } root_st_dev = st.st_dev; root_st_ino = st.st_ino; } if (fstat(fd, &st2) != 0) { goto failure; }# ifdef QUOTAS if (hasquota() == 0 && S_ISDIR(st2.st_mode)) { mode |= 0500; }# endif if (st2.st_ino == root_st_ino && st2.st_dev == root_st_dev) { mode |= 0700; } else if (be_customer_proof != 0) { mode |= (S_ISDIR(st2.st_mode) ? 0700 : 0600); } if (fchmod(fd, mode) < 0 && chmod(name, mode) < 0) { (void) close(fd); failure: if (fd != -1) { (void) close(fd); } addreply(550, MSG_CHMOD_FAILED ": %s", name, strerror(errno)); return; } (void) close(fd); addreply(200, MSG_CHMOD_SUCCESS, name);}#endifvoid dodele(char *name){#ifndef ANON_CAN_DELETE if (guest != 0) { addreply_noformat(550, MSG_ANON_CANT_DELETE); return; }#endif if (name == NULL || *name == 0) { addreply_noformat(501, MSG_NO_FILE_NAME); return; } if (checknamesanity(name, dot_write_ok) != 0) { addreply(550, MSG_SANITY_FILE_FAILURE, name); return; } if (keepallfiles != 0) {#ifdef EPERM errno = EPERM;#else errno = 1;#endif goto denied; } /* * What we do here may look a bit strange. It's to defend against * change-after-stat attacks. If we simply do lstat(name), then unlink(name) * there's a race. An attacker can rename the file between these two * system calls, so that a big file is lstat()ed, but a dummy tiny file is * unlinked. That way, an attacker could easily get extra quota. * To defend against this attack, we rename the file to an unique dot-file * (an atomic operation) . People subject to quotas can't access dot-files. * So we can securely stat it and unlink it. Having the pid in the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -