Android, IT

openssh-5.8p2 patch for Android (getpwuid replacement / placeholder)

I wanted to use rsync / ssh combination to do a backup of a phone that I don’t want to root. There were few problems:

a) the rsync4android failed with an error that said: ‘ssh: exiting: string is too long’:

2015-05-03 14.22.38

b) other solutions meant being root.

I’ve decided to cross-compile openssh-5.8p2 (which came with my busybox) so that I can use it in a combination with rsync (which I’ve also cross compiled) and all controlled by GScript (this will be a topic of a separate post).

The troubles encountered with getting openssh to run are that there’s no luck for the getpw* methods in Android (as /etc/passwd doesn’t exist and thus they return null. Just executing ssh on the phone produced the error: “You don’t exist, go away!”

I’ve tracked this down into ssh.c file:

        pw = getpwuid(original_real_uid);
        if (!pw) {
                logit("You don't exist, go away!");
                exit(255);
        }

Just patching this line doesn’t work, since there will be segmentation faults and other calls to getpwuid

I’ve created a small patch that addresses this issue:

diff -rupN openssh-5.8p2-orig/misc.c openssh-5.8p2/misc.c
--- openssh-5.8p2-orig/misc.c   2011-01-13 02:21:36.000000000 +0100
+++ openssh-5.8p2/misc.c        2015-05-03 13:21:07.488601303 +0200
@@ -533,7 +533,7 @@ tilde_expand_filename(const char *filena
                user[slash] = '\0';
                if ((pw = getpwnam(user)) == NULL)
                        fatal("tilde_expand_filename: No such user %s", user);
-       } else if ((pw = getpwuid(uid)) == NULL)        /* ~/path */
+       } else if ((pw = vi_getpwuid(uid)) == NULL)     /* ~/path */
                fatal("tilde_expand_filename: No such uid %ld", (long)uid);

        if (strlcpy(ret, pw->pw_dir, sizeof(ret)) >= sizeof(ret))
@@ -996,3 +996,27 @@ sock_set_v6only(int s)
                error("setsockopt IPV6_V6ONLY: %s", strerror(errno));
 #endif
 }
+
+/*
+ * viulian: since getpwuid fails on nonrooted phones (or roote
+ *  but without /etc/passwd, let's replace it
+ */
+struct passwd* vi_getpwuid(uid_t uid) {
+
+        struct passwd *pw = (struct passwd *)calloc(1, sizeof(struct passwd));
+        if (!pw) {
+                logit("Not enough memory.");
+                exit(255);
+        }
+
+        pw->pw_name = strdup("android");
+       pw->pw_passwd = strdup("********");
+        pw->pw_uid = getuid();
+        pw->pw_gid = getgid();
+        pw->pw_gecos = strdup("android");
+        pw->pw_dir = strdup("/sdcard");
+        pw->pw_shell = strdup("/system/bin/sh");
+
+        return pw;
+}
+
diff -rupN openssh-5.8p2-orig/misc.h openssh-5.8p2/misc.h
--- openssh-5.8p2-orig/misc.h   2010-12-01 01:50:35.000000000 +0100
+++ openssh-5.8p2/misc.h        2015-05-03 12:45:31.284430274 +0200
@@ -102,4 +102,6 @@ char        *read_passphrase(const char *, int)
 int     ask_permission(const char *, ...) __attribute__((format(printf, 1, 2)));
 int     read_keyfile_line(FILE *, const char *, char *, size_t, u_long *);

+struct passwd* vi_getpwuid(uid_t);
+
 #endif /* _MISC_H */
diff -rupN openssh-5.8p2-orig/ssh.c openssh-5.8p2/ssh.c
--- openssh-5.8p2-orig/ssh.c    2011-02-04 01:42:15.000000000 +0100
+++ openssh-5.8p2/ssh.c 2015-05-03 12:42:42.134518990 +0200
@@ -268,7 +268,7 @@ main(int ac, char **av)
        }
 #endif
        /* Get user data. */
-       pw = getpwuid(original_real_uid);
+       pw = vi_getpwuid(original_real_uid);
        if (!pw) {
                logit("You don't exist, go away!");
                exit(255);
@@ -1481,7 +1481,7 @@ load_public_identity_files(void)
                xfree(keys);
        }
 #endif /* ENABLE_PKCS11 */
-       if ((pw = getpwuid(original_real_uid)) == NULL)
+       if ((pw = vi_getpwuid(original_real_uid)) == NULL)
                fatal("load_public_identity_files: getpwuid failed");
        pwname = xstrdup(pw->pw_name);
        pwdir = xstrdup(pw->pw_dir);

What it does is that it introduces a fake getpwuid method (called vi_getpwuid) which returns a valid passwd struct but filled with fake data (I haven’t investigated the security risks linked to this – I just wanted to get the code running). It assumes the username is ‘android’ (the username is in fact the application id, but I haven’t studied how to retrieve that from the NDK layer).
It also assumes the user home dir to be /sdcard (where ssh will create the .ssh/known_hosts file.

By trial and error, I found 3 places where the changes had to be done to get ssh to work via rsync: there were two checks in ssh.c file and one more in misc.c itself inside the tilde_expand_filename method. This too had to be fixed, otherwise ssh would complain with:

tilde_expand_filename: No such uid 11041

Hope this helps!

1 Comment

  1. THANK YOU MAN! I SPENT ALMOST FUCKING 2 DAYS ON THIS SHIT! THANK YOU SO MUCH. SO WIERD Android SSH 8.0 works and 9.0 fails due to this problem.

Leave a Reply