ftpd: add optional support for authentication
authorMorten Kvistgaard <MK@pch-engineering.dk>
Tue, 5 Aug 2014 19:57:18 +0000 (21:57 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Tue, 5 Aug 2014 19:57:18 +0000 (21:57 +0200)
function                                             old     new   delta
cmdio_get_cmd_and_arg                                  -     237    +237
get_passwd                                             -      97     +97
check_password                                         -      82     +82
ftpd_main                                           2297    2178    -119
ask_and_check_password_extended                      206      84    -122
------------------------------------------------------------------------------
(add/remove: 3/0 grow/shrink: 0/2 up/down: 416/-241)          Total: 175 bytes

Signed-off-by: Morten Kvistgaard <MK@pch-engineering.dk>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
include/libbb.h
libbb/Kbuild.src
libbb/correct_password.c
networking/Config.src
networking/ftpd.c

index 858084bc5b7dcb83823694f788cdb825dade1bf5..ff223dd0f520ff6ade20d1b880a8f026fad14b5a 100644 (file)
@@ -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 */
index 62680bd52b0d0421e31e950019648ed6399132ce..0a9e803d7718e50a9c134e5e7a453236ead005fc 100644 (file)
@@ -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
index acadf391434c4b06b52e32f24057e50bba2512e4..513c930286feb89565a2895d40c7e2e9eaa18827 100644 (file)
 
 #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.
 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;
 }
 
index fbad7ecb202cefc3f4ad185387746eb1b5a1327e..e56646917b3714c9cd27b8ad5774f3d6cc0f8057 100644 (file)
@@ -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
index 2d2a3a44cfe2dc3732ffd7e8860b5630981e4aaf..9fcc3e9637c71165418f2ee80a5970376e6e7d02 100644 (file)
@@ -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