You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
simplesshd/NOTES

245 lines
9.3 KiB

BUILD INSTRUCTIONS:
To build this, you'll need to remove my key.* settings in ant.properties.
You'll have to build a "debug" apk instead of a "release" one. Android
will not let you install a debug apk on top of a release one, so you have
to remove stock SimpleSSHD first before installing the debug build.
Then follow these steps (roughly the "doit" script):
ndk-build -j8 &&
mv libs/armeabi/scp libs/armeabi/libscp.so &&
mv libs/armeabi/sftp-server libs/armeabi/libsftp-server.so &&
mv libs/armeabi/rsync libs/armeabi/librsync.so &&
mv libs/armeabi/buffersu libs/armeabi/libbuffersu.so &&
ant debug
The mv steps are very important, because ant will only package the
necessary binaries if they have a .so extension (even though they are
stand alone executables).
DEVELOPMENT JOURNAL:
December 6, 2014.
The idea is to make a proper ssh implementation for Android. Important
features:
* it should run happily without root (on a non-root port)
* it should be a regular android app requiring no special permissions,
and not requiring any 'magic' executable files
* should not rely on busybox
* preferably support sftp
* open source
The existing apps are either expensive, don't work, need root, or too
complicated, or a mix of all of the above. And none of them are open
source.
I figure I'll start with dropbear, which I will run through JNI instead
of putting it in its own binary (because making such a binary executable
is a bit of a hack).
So that's the plan........
December 14, 2014.
I got dropbear to compile under the Android NDK, so now it's time to work
on the Android side of it.
I need:
* a Service that can be started, stopped, and queried for whether it's
running or not
* a Thread to implement the Service's work (by calling into dropbear's
main()), which can also be stopped.
* a config UI with at least these choices:
- bool: start on boot (def: false)
- number: port number (def: 2222)
- string: path to authorized_keys file (def: /sdcard/ssh)
- string: name of default shell (def: /system/bin/sh -l)
- string: default path for HOME (def: /sdcard/ssh)
- button: start or (if it's running) stop
December 15, 2014.
Getting to the fun part. Process management...
To start sshd, it seems like I can startService(). Then in the Service's
onStartCommand(), call startForeground() so it won't be killed (return
START_STICKY too?).
The question is if dropbear's main() should run under a separate Thread,
or a separate Process. The trouble with a Thread is that it might be
hard to kill. The trouble with a process is that there is no way to
report back status (such as a failure to start sshd).
Connectbot starts a new process for its shell -- it really doesn't have a
choice because the shell binary isn't linked with Connectbot, and exec()
in a thread stinks. To stop it, it just closes stdin/stdout!!! So
zombies can (and do) linger.
I suppose dropbear could be in its own process if it had something like
stdin/stdout to communicate failure? Or it could just write error
messages to (i.e.) /sdcard/ssh/log. To stop the service, it would just
use kill().
I am curious how the main waiting-for-connections loop looks, but even if
it uses select(), I'm not sure how I would honor Thread.interrupt() or
whatever. It's not guaranteed to interrupt select(), and I'm not keen on
adding an arbitrary timeout/polling feature to it.
December 20, 2014.
So, I added a builtin scp endpoint. It was pretty straight forward,
except dropbear defaults to vfork(), which blocks the parent until the
child runs execve()!!
Anyways, I noticed that scp doesn't quote its arguments to the remote
scp. That means you can't conveniently copy a remote file with a space in
its name (it becomes two files). But the upside is that this is where
wildcards are handled -- by the shell!
So I need to either run it as a separate executable launched through the
shell, or make my own implementation of wildcards.
It is easy, using a $(BUILD_EXECUTABLE) script, to get ndk to build an
executable. But it is only packaged up if it is named "gdbserver" (and
debug apk), or "libfoo.so". The good news is that libfoo.so can be
executed in /data/data/org.galexander.sshd/lib/libfoo.so, so that is a
viable option.
Doing the expansion myself is not necessarily hard either, though. I
need a library function called glob(), which is apparently not part of
bionic. But I have the idea some cut and paste would resolve that with
very little extra work on my part.
December 21, 2014.
Well, bionic libc *does* provide fnmatch(), and even scandir() (a
shortcut for readdir). In the best case, though, that still leaves me
with a bit of a path parsing conundrum (I have to tell scandir which
directory to operate on). And also a bit of an escape character
conundrum -- \* and "*" should not act like wildcards.
Those are not insurmountable but I think I've talked myself out of it.
So then the question is, do I figure out how to ship an executable, or
do I do some hack like open a pipe to "/system/bin/sh echo filespec" and
use the shell solely for expansion?
I'm developing the idea that it's actually pretty easy to ship an
executable, I just need to find some -pre-package step where I can do
"mv scp libscp.so" and then it will ship. ndk-build will not let me make
a target with a "." in it directly.
...
Now scp, sftp-server, and rsync work as separate executables... rsync
does fail at -z because it needs it's own custom zlib...The stock
"external" one seems to lack the "old-style compress" method. There is
another commandline option for the new (deflate?) technique, but I think
the normal -z ought to work too. But that is literally the last feature!
Then just release details.
December 29, 2014.
First problem report from a user. Lollipop (Android 5.0) requires "PIE"
executables -- position independent code. I think that is a modern
equivalent to -fpic that Android is now requiring so that it can
randomize addresses to try to obscure stack smashing attacks that rely on
fixed addresses. It is epicly lame.
Anyways, the big fuck-you from Google is that Ice Cream Sandwich (Android
4.1) and earlier require fixed-position code. So one binary will not
generally work on both.
Here is a good summary:
https://code.google.com/p/android-developer-preview/issues/detail?id=888
There is something called "run_pie" which you can wrap your executables
in that lets older Android run PIE executables. It would require a
relatively small change to the exec() call to prepend it with "run_pie".
That seems like a hack.
The suggested remedy is to build two different apks! Yuck!
Anyways, it is only executables (not libraries -- they are position
independent already) that are affected. And apparently static
executables don't care one way or the other.
So that is my remedy -- static executables for the moment. I tested them
and it is only a little bit bigger -- 904kB of binaries instead of 668kB.
January 18, 2015.
Markus Ethen suggested it display the current IP address so you know
where to ssh to, in case it isn't convenient to use static dhcp or to
remember the address. That seems to be easier said than done. You can
use WifiManager, but that won't give your IP address unless you're on
wifi. That is probably "good enough", but it is certainly not ideal.
There is also java.net.NetworkInterface, which seems to return a random
ipv6 address.
Ah-hah! It is fe80::macaddr, which is a bogus "local-connection only"
ipv6 address, like 192.168, but automatically-generated without dhcp.
So if I skip that, it finds the proper ipv4 address!
Pfew! I was thinking I'd have to directly use /proc/net/dev and
SIOCGIFCONF, just like ifconfig does, but it works fine with
java.net.NetworkInterface.
June 20, 2015.
At some points, rsync is only write()ing, and assumes that the other end
will receive it all. The other end does a little bit of write()ing of
its own, and then is happy to read() all it wants. So this written stuff
may sit in a buffer somewhere indefinitely. If that happens, an
infelicity in the design of SuperSU causes everything to wedge.
Of course, this is only if you set the shell to /system/xbin/su as a way
of having root access for rsync.
Anyways, I made a new program, "buffersu", which is just a
stdin/stdout-buffering wrapper for rsync that is guaranteed to always
perform any read() that is possible at any time, no matter how many
write()s are outstanding. That seems to do the trick.
June 21, 2016.
Chris Moore reports that rsync and sftp do not like files larger than 2GB.
rsync was easy - it just needed an additional #define in rsync/config.h
to enable its builtin support for using stat64/lseek64/off64_t/etc.
Now that I'm investigating sftp, I find this surprising fact about bionic
(though the glibc man page for stat(2) tried to tell me this) - stat64
and stat are the same thing! But off64_t and lseek64 are significant.
That should make converting sftp pretty convenient. Especially since
sftp already uses "u_int64_t" instead of off_t.
p.s. Chris Moore gave me this command to test sftp, which turned out to
be useful:
curl -v --pubkey .ssh/id_rsa.pub -r 2147482624-2147484672 -k sftp://mushroom:2222/sdcard/ssh/buh -o buh-new
XXX - what about scp?
XXX - test on large files
XXX - if you remove it from the recent apps list, does it stop the service??
XXX - support password-based logins?