ftpd: add optional support for authentication
No binary (build failed)
—
(log yok)
| @@ -1316,6 +1316,7 @@ int sd_listen_fds(void); | |
| #define SETUP_ENV_NO_CHDIR (1 << 4) | |
| void setup_environment(const char *shell, int flags, const struct passwd *pw) FAST_FUNC; | |
| void nuke_str(char *str) FAST_FUNC; | |
| + | int check_password(const struct passwd *pw, const char *plaintext) FAST_FUNC; |
| int ask_and_check_password_extended(const struct passwd *pw, int timeout, const char *prompt) FAST_FUNC; | |
| int ask_and_check_password(const struct passwd *pw) FAST_FUNC; | |
| /* Returns a malloced string */ | |
| @@ -150,6 +150,7 @@ lib-$(CONFIG_VLOCK) += pw_encrypt.o correct_password.o | |
| lib-$(CONFIG_SU) += pw_encrypt.o correct_password.o | |
| lib-$(CONFIG_LOGIN) += pw_encrypt.o correct_password.o | |
| lib-$(CONFIG_FEATURE_HTTPD_AUTH_MD5) += pw_encrypt.o | |
| + | lib-$(CONFIG_FEATURE_FTP_AUTHENTICATION) += pw_encrypt.o |
| lib-$(CONFIG_DF) += find_mount_point.o | |
| lib-$(CONFIG_MKFS_MINIX) += find_mount_point.o | |
| @@ -30,6 +30,63 @@ | |
| #include "libbb.h" | |
| + | #define SHADOW_BUFSIZE 256 |
| + | |
| + | /* Retrieve encrypted password string for pw. |
| + | * If pw == NULL, return a string which fails password check against any |
| + | * password. |
| + | */ |
| + | #if !ENABLE_FEATURE_SHADOWPASSWDS |
| + | #define get_passwd(pw, buffer) get_passwd(pw) |
| + | #endif |
| + | static const char *get_passwd(const struct passwd *pw, char buffer[SHADOW_BUFSIZE]) |
| + | { |
| + | const char *pass; |
| + | |
| + | if (!pw) |
| + | return "aa"; /* "aa" will never match */ |
| + | |
| + | pass = pw->pw_passwd; |
| + | #if ENABLE_FEATURE_SHADOWPASSWDS |
| + | /* Using _r function to avoid pulling in static buffers */ |
| + | if ((pass[0] == 'x' || pass[0] == '*') && !pass[1]) { |
| + | struct spwd spw; |
| + | int r; |
| + | /* getspnam_r may return 0 yet set result to NULL. |
| + | * At least glibc 2.4 does this. Be extra paranoid here. */ |
| + | struct spwd *result = NULL; |
| + | r = getspnam_r(pw->pw_name, &spw, buffer, SHADOW_BUFSIZE, &result); |
| + | pass = (r || !result) ? "aa" : result->sp_pwdp; |
| + | } |
| + | #endif |
| + | return pass; |
| + | } |
| + | |
| + | /* |
| + | * Return 1 if PW has an empty password. |
| + | * Return 1 if the user gives the correct password for entry PW, |
| + | * 0 if not. |
| + | * NULL pw means "just fake it for login with bad username" |
| + | */ |
| + | int FAST_FUNC check_password(const struct passwd *pw, const char *plaintext) |
| + | { |
| + | IF_FEATURE_SHADOWPASSWDS(char buffer[SHADOW_BUFSIZE];) |
| + | char *encrypted; |
| + | const char *pw_pass; |
| + | int r; |
| + | |
| + | pw_pass = get_passwd(pw, buffer); |
| + | if (!pw_pass[0]) { /* empty password field? */ |
| + | return 1; |
| + | } |
| + | |
| + | encrypted = pw_encrypt(plaintext, /*salt:*/ pw_pass, 1); |
| + | r = (strcmp(encrypted, pw_pass) == 0); |
| + | free(encrypted); |
| + | return r; |
| + | } |
| + | |
| + | |
| /* Ask the user for a password. | |
| * Return 1 without asking if PW has an empty password. | |
| * Return -1 on EOF, error while reading input, or timeout. | |
| @@ -41,42 +98,23 @@ | |
| int FAST_FUNC ask_and_check_password_extended(const struct passwd *pw, | |
| int timeout, const char *prompt) | |
| { | |
| - | char *unencrypted, *encrypted; |
| - | const char *correct; |
| + | IF_FEATURE_SHADOWPASSWDS(char buffer[SHADOW_BUFSIZE];) |
| + | char *plaintext; |
| + | const char *pw_pass; |
| int r; | |
| - | /* fake salt. crypt() can choke otherwise. */ |
| - | correct = "aa"; |
| - | if (!pw) { |
| - | /* "aa" will never match */ |
| - | goto fake_it; |
| - | } |
| - | correct = pw->pw_passwd; |
| - | #if ENABLE_FEATURE_SHADOWPASSWDS |
| - | /* Using _r function to avoid pulling in static buffers */ |
| - | if ((correct[0] == 'x' || correct[0] == '*') && !correct[1]) { |
| - | struct spwd spw; |
| - | char buffer[256]; |
| - | /* getspnam_r may return 0 yet set result to NULL. |
| - | * At least glibc 2.4 does this. Be extra paranoid here. */ |
| - | struct spwd *result = NULL; |
| - | r = getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result); |
| - | correct = (r || !result) ? "aa" : result->sp_pwdp; |
| - | } |
| - | #endif |
| - | if (!correct[0]) /* empty password field? */ |
| + | pw_pass = get_passwd(pw, buffer); |
| + | if (!pw_pass[0]) /* empty password field? */ |
| return 1; | |
| - | fake_it: |
| - | unencrypted = bb_ask(STDIN_FILENO, timeout, prompt); |
| - | if (!unencrypted) { |
| + | plaintext = bb_ask(STDIN_FILENO, timeout, prompt); |
| + | if (!plaintext) { |
| /* EOF (such as ^D) or error (such as ^C) or timeout */ | |
| return -1; | |
| } | |
| - | encrypted = pw_encrypt(unencrypted, correct, 1); |
| - | r = (strcmp(encrypted, correct) == 0); |
| - | free(encrypted); |
| - | nuke_str(unencrypted); |
| + | |
| + | r = check_password(pw, plaintext); |
| + | nuke_str(plaintext); |
| return r; | |
| } | |
| @@ -134,6 +134,13 @@ config FEATURE_FTPD_ACCEPT_BROKEN_LIST | |
| it increases the code size by ~40 bytes. | |
| Most other ftp servers seem to behave similar to this. | |
| + | config FEATURE_FTP_AUTHENTICATION |
| + | bool "Enable authentication" |
| + | default y |
| + | depends on FTPD |
| + | help |
| + | Enable basic system login as seen in telnet etc. |
| + | |
| config FTPGET | |
| bool "ftpget" | |
| default y | |
| @@ -1172,18 +1172,6 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) | |
| if (logmode) | |
| applet_name = xasprintf("%s[%u]", applet_name, (int)getpid()); | |
| - | #if !BB_MMU |
| - | G.root_fd = -1; |
| - | #endif |
| - | argv += optind; |
| - | if (argv[0]) { |
| - | #if !BB_MMU |
| - | G.root_fd = xopen("/", O_RDONLY | O_DIRECTORY); |
| - | close_on_exec_on(G.root_fd); |
| - | #endif |
| - | xchroot(argv[0]); |
| - | } |
| - | |
| //umask(077); - admin can set umask before starting us | |
| /* Signals. We'll always take -EPIPE rather than a rude signal, thanks */ | |
| @@ -1199,23 +1187,22 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) | |
| WRITE_OK(FTP_GREET); | |
| signal(SIGALRM, timeout_handler); | |
| - | #ifdef IF_WE_WANT_TO_REQUIRE_LOGIN |
| + | #if ENABLE_FEATURE_FTP_AUTHENTICATION |
| { | |
| - | smallint user_was_specified = 0; |
| + | struct passwd *pw = NULL; |
| + | |
| while (1) { | |
| uint32_t cmdval = cmdio_get_cmd_and_arg(); | |
| if (cmdval == const_USER) { | |
| - | if (G.ftp_arg == NULL || strcasecmp(G.ftp_arg, "anonymous") != 0) |
| - | cmdio_write_raw(STR(FTP_LOGINERR)" Server is anonymous only\r\n"); |
| - | else { |
| - | user_was_specified = 1; |
| - | cmdio_write_raw(STR(FTP_GIVEPWORD)" Please specify the password\r\n"); |
| - | } |
| + | pw = getpwnam(G.ftp_arg); |
| + | cmdio_write_raw(STR(FTP_GIVEPWORD)" Please specify password\r\n"); |
| } else if (cmdval == const_PASS) { | |
| - | if (user_was_specified) |
| - | break; |
| - | cmdio_write_raw(STR(FTP_NEEDUSER)" Login with USER\r\n"); |
| + | if (check_password(pw, G.ftp_arg) > 0) { |
| + | break; /* login success */ |
| + | } |
| + | cmdio_write_raw(STR(FTP_LOGINERR)" Login failed\r\n"); |
| + | pw = NULL; |
| } else if (cmdval == const_QUIT) { | |
| WRITE_OK(FTP_GOODBYE); | |
| return 0; | |
| @@ -1223,10 +1210,24 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) | |
| cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER and PASS\r\n"); | |
| } | |
| } | |
| + | change_identity(pw); |
| } | |
| WRITE_OK(FTP_LOGINOK); | |
| #endif | |
| + | /* Do this after auth, else /etc/passwd is not accessible */ |
| + | #if !BB_MMU |
| + | G.root_fd = -1; |
| + | #endif |
| + | argv += optind; |
| + | if (argv[0]) { |
| + | #if !BB_MMU |
| + | G.root_fd = xopen("/", O_RDONLY | O_DIRECTORY); |
| + | close_on_exec_on(G.root_fd); |
| + | #endif |
| + | xchroot(argv[0]); |
| + | } |
| + | |
| /* RFC-959 Section 5.1 | |
| * The following commands and options MUST be supported by every | |
| * server-FTP and user-FTP, except in cases where the underlying | |