📄 ftpd.c
字号:
return (new);}/* * Save the result of a getpwnam. Used for USER command, since * the data returned must not be clobbered by any other command * (e.g., globbing). */static struct passwd *sgetpwnam(name) const char *name;{ static struct passwd save; struct passwd *p; if ((p = getpwnam(name)) == NULL) return (p);#ifdef USE_SHADOW if ((spw = getspnam(name)) != NULL) p->pw_passwd = spw->sp_pwdp; endspent(); /* ? */#endif if (save.pw_name) { free(save.pw_name); memset(save.pw_passwd, 0, strlen(save.pw_passwd)); free(save.pw_passwd); free(save.pw_gecos); free(save.pw_dir); free(save.pw_shell); } save = *p; save.pw_name = sgetsave(p->pw_name); save.pw_passwd = sgetsave(p->pw_passwd); save.pw_gecos = sgetsave(p->pw_gecos); save.pw_dir = sgetsave(p->pw_dir); save.pw_shell = sgetsave(p->pw_shell); return (&save);}static int login_attempts; /* number of failed login attempts */static int askpasswd; /* had user command, ask for passwd */static char curname[16]; /* current USER name *//* * USER command. * Sets global passwd pointer pw if named account exists and is acceptable; * sets askpasswd if a PASS command is expected. If logged in previously, * need to reset state. If name is "ftp" or "anonymous", the name is not in * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. * If account doesn't exist, ask for passwd anyway. Otherwise, check user * requesting login privileges. Disallow anyone who does not have a standard * shell as returned by getusershell(). Disallow anyone mentioned in the file * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. */voiduser(name) char *name;{ const char *cp, *shell; if (logged_in) { if (guest) { reply(530, "Can't change user from guest login."); return; } else if (dochroot) { reply(530, "Can't change user from chroot user."); return; } end_login(); } guest = 0; if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { if (checkuser(_PATH_FTPUSERS, "ftp") || checkuser(_PATH_FTPUSERS, "anonymous")) reply(530, "User %s access denied.", name); else if ((pw = sgetpwnam("ftp")) != NULL) { guest = 1; askpasswd = 1; reply(331, "Guest login ok, type your name as password."); } else reply(530, "User %s unknown.", name); if (!askpasswd && logging) syslog(LOG_NOTICE, "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); return; } if (anon_only && !checkuser(_PATH_FTPCHROOT, name)) { reply(530, "Sorry, only anonymous ftp allowed."); return; } if ((pw = sgetpwnam(name))!=NULL) { if ((shell = pw->pw_shell) == NULL || *shell == 0) shell = _PATH_BSHELL; while ((cp = getusershell()) != NULL) if (strcmp(cp, shell) == 0) break; endusershell(); if (cp == NULL || checkuser(_PATH_FTPUSERS, name)) { reply(530, "User %s access denied.", name); if (logging) syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s", remotehost, name); pw = (struct passwd *) NULL; return; } } if (logging) { strncpy(curname, name, sizeof(curname)-1); curname[sizeof(curname)-1] = '\0'; }#ifdef SKEY if (!skey_haskey(name)) { char *myskey, *skey_keyinfo __P((char *name)); myskey = skey_keyinfo(name); reply(331, "Password [ %s ] for %s required.", myskey ? myskey : "error getting challenge", name); } else#endif reply(331, "Password required for %s.", name); askpasswd = 1; /* * Delay before reading passwd after first failed * attempt to slow down passwd-guessing programs. */ if (login_attempts) sleep((unsigned) login_attempts);}/* * Check if a user is in the file "fname" */static intcheckuser(fname, name) const char *fname; const char *name;{ FILE *fd; int found = 0; char *p, line[BUFSIZ]; if ((fd = fopen(fname, "r")) != NULL) { while (fgets(line, sizeof(line), fd) != NULL) if ((p = strchr(line, '\n')) != NULL) { *p = '\0'; if (line[0] == '#') continue; if (strcmp(line, name) == 0) { found = 1; break; } } (void) fclose(fd); } return (found);}/* * Terminate login as previous user, if any, resetting state; * used when USER command is given or login fails. */static voidend_login(){ sigset_t allsigs; sigfillset (&allsigs); sigprocmask (SIG_BLOCK, &allsigs, NULL); (void) seteuid((uid_t)0); if (logged_in) { logwtmp(ttyline, "", ""); if (doutmp) logout(utmp.ut_line); } pw = NULL; logged_in = 0; guest = 0; dochroot = 0;}voidpass(passwd) char *passwd;{ int rval; FILE *fd; static char homedir[MAXPATHLEN]; char rootdir[MAXPATHLEN]; sigset_t allsigs; if (logged_in || askpasswd == 0) { reply(503, "Login with USER first."); return; } askpasswd = 0; if (!guest) { /* "ftp" is only account allowed no password */ if (pw == NULL) { rval = 1; /* failure below */ goto skip; }#if defined(KERBEROS) rval = klogin(pw, "", hostname, passwd); if (rval == 0) goto skip;#endif#ifdef SKEY if (skey_haskey(pw->pw_name) == 0 && (skey_passcheck(pw->pw_name, passwd) != -1)) { rval = 0; goto skip; }#endif /* the strcmp does not catch null passwords! */ if (pw == NULL || *pw->pw_passwd == '\0' || strcmp(crypt(passwd, (pw ? pw->pw_passwd : "xx")), pw->pw_passwd)) { rval = 1; /* failure */ goto skip; } rval = 0;skip: /* * If rval == 1, the user failed the authentication check * above. If rval == 0, either Kerberos or local authentication * succeeded. */ if (rval) { reply(530, "Login incorrect."); if (logging) syslog(LOG_NOTICE, "FTP LOGIN FAILED FROM %s, %s", remotehost, curname); pw = NULL; if (login_attempts++ >= 5) { syslog(LOG_NOTICE, "repeated login failures from %s", remotehost); exit(0); } return; } } else { /* Save anonymous' password. */ guestpw = strdup(passwd); if (guestpw == (char *)NULL) fatal("Out of memory"); } login_attempts = 0; /* this time successful */#ifdef USE_SHADOW switch (isexpired(spw)) { case 0: /* success */ break; case 1: syslog(LOG_NOTICE, "expired password from %s, %s", remotehost, pw->pw_name); reply(530, "Please change your password and try again."); return; case 2: syslog(LOG_NOTICE, "inactive login from %s, %s", remotehost, pw->pw_name); reply(530, "Login inactive -- contact administrator."); return; case 3: syslog(LOG_NOTICE, "expired login from %s, %s", remotehost, pw->pw_name); reply(530, "Account expired -- contact administrator."); return; }#endif if (setegid((gid_t)pw->pw_gid) < 0) { reply(550, "Can't set gid."); return; } (void) initgroups(pw->pw_name, pw->pw_gid); /* open wtmp before chroot */ logwtmp(ttyline, pw->pw_name, remotehost); /* open utmp before chroot */ if (doutmp) { memset((void *)&utmp, 0, sizeof(utmp)); (void)time(&utmp.ut_time); (void)strncpy(utmp.ut_name, pw->pw_name, sizeof(utmp.ut_name)); (void)strncpy(utmp.ut_host, remotehost, sizeof(utmp.ut_host)); (void)strncpy(utmp.ut_line, ttyline, sizeof(utmp.ut_line)); login(&utmp); } /* open stats file before chroot */ if (guest && (stats == 1) && (statfd < 0)) if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0) stats = 0; logged_in = 1; dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name); if (guest || dochroot) { if (multihome) { struct stat ts; /* Compute root directory. */ snprintf (rootdir, sizeof(rootdir), "%s/%s", pw->pw_dir, dhostname); if (stat(rootdir, &ts) < 0) { snprintf (rootdir, sizeof(rootdir), "%s/%s", pw->pw_dir, hostname); } } else strcpy (rootdir, pw->pw_dir); } if (guest) { /* * We MUST do a chdir() after the chroot. Otherwise * the old current directory will be accessible as "." * outside the new root! */ if (chroot(rootdir) < 0 || chdir("/") < 0) { reply(550, "Can't set guest privileges."); goto bad; } strcpy(pw->pw_dir, "/"); setenv("HOME", "/", 1); } else if (dochroot) { if (chroot(rootdir) < 0 || chdir("/") < 0) { reply(550, "Can't change root."); goto bad; } strcpy(pw->pw_dir, "/"); setenv("HOME", "/", 1); } else if (chdir(pw->pw_dir) < 0) { if (chdir("/") < 0) { reply(530, "User %s: can't change directory to %s.", pw->pw_name, pw->pw_dir); goto bad; } else lreply(230, "No directory! Logging in with home=/"); } if (seteuid((uid_t)pw->pw_uid) < 0) { reply(550, "Can't set uid."); goto bad; } sigfillset(&allsigs); sigprocmask(SIG_UNBLOCK,&allsigs,NULL); /* * Set home directory so that use of ~ (tilde) works correctly. */ if (getcwd(homedir, MAXPATHLEN) != NULL) setenv("HOME", homedir, 1); /* * Display a login message, if it exists. * N.B. reply(230,) must follow the message. */ if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { char *cp, line[LINE_MAX]; while (fgets(line, sizeof(line), fd) != NULL) { if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; lreply(230, "%s", line); } (void) fflush(stdout); (void) fclose(fd); } if (guest) { if (ident != NULL) free(ident); ident = strdup(passwd); if (ident == (char *)NULL) fatal("Ran out of memory."); reply(230, "Guest login ok, access restrictions apply.");#ifdef HASSETPROCTITLE snprintf(proctitle, sizeof(proctitle), "%s: anonymous/%.*s", remotehost, sizeof(proctitle) - sizeof(remotehost) - sizeof(": anonymous/"), passwd); setproctitle(proctitle);#endif /* HASSETPROCTITLE */ if (logging) syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", remotehost, passwd); } else { reply(230, "User %s logged in.", pw->pw_name);#ifdef HASSETPROCTITLE snprintf(proctitle, sizeof(proctitle), "%s: %s", remotehost, pw->pw_name); setproctitle(proctitle);#endif /* HASSETPROCTITLE */ if (logging) syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", remotehost, pw->pw_name); } (void) umask(defumask); return;bad: /* Forget all about it... */ end_login();}voidretrieve(cmd, name) const char *cmd, *name;{ FILE *fin, *dout; struct stat st; int (*closefunc) __P((FILE *)); time_t start; if (cmd == 0) { fin = fopen(name, "r"), closefunc = fclose; st.st_size = 0; } else { char line[BUFSIZ]; (void) snprintf(line, sizeof(line), cmd, name); name = line; fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; st.st_size = -1; st.st_blksize = BUFSIZ; } if (fin == NULL) { if (errno != 0) { perror_reply(550, name); if (cmd == 0) { LOGCMD("get", name); } } return; } byte_count = -1; if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) { reply(550, "%s: not a plain file.", name); goto done; } if (restart_point) { if (type == TYPE_A) { off_t i, n; int 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, L_SET) < 0) { perror_reply(550, name); goto done; } } dout = dataconn(name, st.st_size, "w"); if (dout == NULL) goto done; time(&start); send_data(fin, dout, st.st_blksize, st.st_size, (restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode))); if ((cmd == 0) && stats) logxfer(name, st.st_size, start); (void) fclose(dout); data = -1; pdata = -1;done: if (cmd == 0) LOGBYTES("get", name, byte_count); (*closefunc)(fin);}voidstore(name, mode, unique) const char *name, *mode; int unique;{ FILE *fout, *din; int (*closefunc) __P((FILE *)); struct stat st; int fd; if (unique && stat(name, &st) == 0) { char *nam; fd = guniquefd(name, &nam); if (fd == -1) { LOGCMD(*mode == 'w' ? "put" : "append", name); return; } name = nam; if (restart_point) mode = "r+"; fout = fdopen(fd, mode); } else fout = fopen(name, mode); closefunc = fclose; if (fout == NULL) { perror_reply(553, name); LOGCMD(*mode == 'w' ? "put" : "append", name); return; } byte_count = -1; if (restart_point) { if (type == TYPE_A) { off_t i, n; int 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, L_INCR) < 0) { perror_reply(550, name); goto done; } } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { perror_reply(550, name); goto done; } } din = dataconn(name, (off_t)-1, "r"); if (din == NULL) goto done; if (receive_data(din, fout) == 0) { if (unique) reply(226, "Transfer complete (unique file name:%s).", name); else reply(226, "Transfer complete.");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -