📄 ftpd.c
字号:
}#endif#ifdef SO_KEEPALIVE /* Set keepalives on the socket to detect dropped connections. */ { int keepalive = 1; if (setsockopt (STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, (char *)&keepalive, sizeof (keepalive)) < 0) syslog (LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); }#endif#ifdef F_SETOWN if (fcntl (STDIN_FILENO, F_SETOWN, getpid ()) == -1) syslog (LOG_ERR, "fcntl F_SETOWN: %m");#endif dolog (&his_addr, &cred); /* Deal with login disable. */ if (display_file (PATH_NOLOGIN, 530) == 0) { reply (530, "System not available."); exit (0); } /* Display a Welcome message if exists, N.B. reply(220,) must follow. */ display_file (PATH_FTPWELCOME, 220); hostname = localhost (); if (! hostname) perror_reply (550, "Local resource failure: malloc"); /* Tell them we're ready to roll. */ if (!no_version) reply (220, "%s FTP server (%s %s) ready.", hostname, PACKAGE_NAME, PACKAGE_VERSION); else reply (220, "%s FTP server ready.", hostname); /* Set the jump, if we have an error parsing, come here and start fresh. */ setjmp (errcatch); /* Roll. */ for (;;) yyparse (); /* NOTREACHED */}static char *curdir (void){ static char *path = 0; extern char *xgetcwd (void); if (path) free (path); path = xgetcwd (); if (! path) return (char *)""; if (path[1] != '\0') /* special case for root dir. */ { char *tmp = realloc (path, strlen (path) + 2); /* '/' + '\0' */ if (! tmp) { free(path); return (char *)""; } strcat(tmp, "/"); path = tmp; } /* For guest account, skip / since it's chrooted */ return (cred.guest ? path+1 : path);}static RETSIGTYPEsigquit (int signo){ syslog (LOG_ERR, "got signal %s", strsignal (signo)); dologout (-1);}static RETSIGTYPElostconn (int signo ARG_UNUSED){ signo; if (debug) syslog (LOG_DEBUG, "lost connection"); dologout (-1);}/* Helper function. */char *sgetsave (const char *s){ char *string; size_t len; if (s == NULL) s = ""; len = strlen (s) + 1; string = malloc (len); if (string == NULL) { perror_reply (421, "Local resource failure: malloc"); dologout (1); /* NOTREACHED */ } /* (void) strcpy (string, s); */ memcpy (string, s, len); return string;}static voidcomplete_login (struct credentials *pcred){ if (setegid ((gid_t)pcred->gid) < 0) { reply (550, "Can't set gid."); return; }#ifdef HAVE_INITGROUPS initgroups (pcred->name, pcred->gid);#endif /* open wtmp before chroot */ snprintf (ttyline, sizeof (ttyline), "ftp%d", getpid ()); logwtmp_keep_open (ttyline, pcred->name, pcred->remotehost); if (pcred->guest) { /* We MUST do a chdir () after the chroot. Otherwise the old current directory will be accessible as "." outside the new root! */ if (chroot (pcred->rootdir) < 0 || chdir (pcred->homedir) < 0) { reply (550, "Can't set guest privileges."); goto bad; } } else if (pcred->dochroot) { if (chroot (pcred->rootdir) < 0 || chdir(pcred->homedir) < 0) { reply (550, "Can't change root."); goto bad; } setenv ("HOME", pcred->homedir, 1); } else if (chdir (pcred->rootdir) < 0) { if (chdir ("/") < 0) { reply (530, "User %s: can't change directory to %s.", pcred->name, pcred->homedir); goto bad; } else lreply (230, "No directory! Logging in with home=/"); } if (seteuid ((uid_t)pcred->uid) < 0) { reply (550, "Can't set uid."); goto bad; } /* Display a login message, if it exists. N.B. reply(230,) must follow the message. */ display_file (PATH_FTPLOGINMESG, 230); if (pcred->guest) { reply (230, "Guest login ok, access restrictions apply.");#ifdef HAVE_SETPROCTITLE snprintf (proctitle, sizeof (proctitle), "%s: anonymous", pcred->remotehost); setproctitle ("%s",proctitle);#endif /* HAVE_SETPROCTITLE */ if (logging) syslog (LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s", pcred->remotehost); } else { reply (230, "User %s logged in.", pcred->name);#ifdef HAVE_SETPROCTITLE snprintf (proctitle, sizeof (proctitle), "%s: %s", pcred->remotehost, pcred->name); setproctitle ("%s",proctitle);#endif /* HAVE_SETPROCTITLE */ if (logging) syslog (LOG_INFO, "FTP LOGIN FROM %s as %s", pcred->remotehost, pcred->name); } umask(defumask); return;bad: /* Forget all about it... */ end_login (pcred);}/* 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. */voiduser (const char *name){ if (cred.logged_in) { if (cred.guest || cred.dochroot) { reply (530, "Can't change user from guest login."); return; } end_login (&cred); } /* Non zero means failed. */ if (auth_user (name, &cred) != 0) { /* If they gave us a reason. */ if (cred.message) { reply (530, "%s", cred.message); free (cred.message); cred.message = NULL; } else reply (530, "User %s access denied.", name); if (logging) syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s", cred.remotehost, name); return; } /* If the server is set to serve anonymous service only the request have to come from a guest or a chrooted. */ if (anon_only && !cred.guest && !cred.dochroot) { reply (530, "Sorry, only anonymous ftp allowed"); return; } if (logging) { strncpy (curname, name, sizeof (curname) - 1); curname [sizeof (curname) - 1] = '\0'; /* Make sure null terminated. */ } if (cred.message) { reply (331, "%s", cred.message); free (cred.message); cred.message = NULL; } else 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);}/* Terminate login as previous user, if any, resetting state; used when USER command is given or login fails. */static voidend_login (struct credentials *pcred){ char *remotehost = pcred->remotehost; int atype = pcred->auth_type; seteuid ((uid_t)0); if (pcred->logged_in) logwtmp_keep_open (ttyline, "", ""); if (pcred->name) free (pcred->name); if (pcred->passwd) { memset (pcred->passwd, 0, strlen (pcred->passwd)); free (pcred->passwd); } if (pcred->homedir) free (pcred->homedir); if (pcred->rootdir) free (pcred->rootdir); if (pcred->shell) free (pcred->shell); if (pcred->pass) /* ??? */ { memset (pcred->pass, 0, strlen (pcred->pass)); free (pcred->pass); } if (pcred->message) free (pcred->message); memset (pcred, 0, sizeof (*pcred)); pcred->remotehost = remotehost; pcred->auth_type = atype;}voidpass (const char *passwd){ if (cred.logged_in || askpasswd == 0) { reply(503, "Login with USER first."); return; } askpasswd = 0; if (!cred.guest) /* "ftp" is the only account allowed no password. */ { /* Try to authenticate the user. Failed if != 0. */ if (auth_pass (passwd, &cred) != 0) { /* Any particular reasons. */ if (cred.message) { reply (530, "%s", cred.message); free (cred.message); cred.message = NULL; } else reply (530, "Login incorrect."); if (logging) syslog (LOG_NOTICE, "FTP LOGIN FAILED FROM %s, %s", cred.remotehost, curname); if (login_attempts++ >= 5) { syslog(LOG_NOTICE, "repeated login failures from %s", cred.remotehost); exit(0); } return; } } cred.logged_in = 1; /* Everything seems to be allright. */ complete_login (&cred); login_attempts = 0; /* This time successful. */}voidretrieve (const char *cmd, const char *name){ FILE *fin, *dout; struct stat st; int (*closefunc) (FILE *); size_t buffer_size = 0; if (cmd == 0) { fin = fopen (name, "r"), closefunc = fclose; st.st_size = 0; } else { char line[BUFSIZ]; snprintf (line, sizeof line, cmd, name); name = line; fin = ftpd_popen (line, "r"), closefunc = ftpd_pclose; st.st_size = -1; buffer_size = 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) { c = getc (fin); if (c == 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; send_data (fin, dout, buffer_size); fclose (dout); data = -1; pdata = -1;done: if (cmd == 0) LOGBYTES ("get", name, byte_count); (*closefunc) (fin);}voidstore (const char *name, const char *mode, int unique){ FILE *fout, *din; struct stat st; int (*closefunc) (FILE *); if (unique && stat (name, &st) == 0 && (name = gunique (name)) == NULL) { LOGCMD (*mode == 'w' ? "put" : "append", name); return; } if (restart_point) mode = "r+"; 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) { c = getc (fout); if (c == 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; if (receive_data (din, fout) == 0) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -