Last time I already tried to prove PHP can do anything when it comes to network protocols by implementing a DNS server. This time I’m doing it again with a server-side implementation of the SSH2 protocol.
You probably know SSH at least by its name. It’s a of secure telnet replacement which also allows many other things such as port forwarding, remote file management (with sftp) and more.
With PHP I could write a fully working SSH server in only 3 days. Of course I didn’t implement every single extension there is to SSH, but I’ve implemented:
- SSH2 protocol only (no SSH1, anyway who uses that anymore?)
- Encryption protocols: aes128-cbc,blowfish-cbc,serpent256-cbc,cast128-cbc,3des-cbc (via mcrypt)
- Message digests: hmac-sha1,hmac-sha1-96,hmac-md5,hmac-md5-96 (via hash)
- No compression as I cannot easily keep a compression context active (the gzip extension in php is missing a way to create a compression context)
- Password and public key (ssh-dss and ssh-rsa) identification
- Ability to program an interactive shell in PHP (there are send and recv functions in a separate class, anyone can have some fun and write something out of that. Should be possible to make a wrapper to communicate with a shell launched with proc_open)
- Support for multiple channels
- SFTP subsystem
- Can be easily extended to add support for custom channels or re-use the ssh protocol for something else
My goal when writing this was to provide a replacement for the FTP protocol for the customers of my hosting service. FTP has many drawbacks, including no encryption (you can with ftps or ftpes) and the way ftp transmits data (another connection has to be opened on a different port, leading most of the time to some problems for people behind a NAT and/or firewalled servers).
With this ssh server supporting sftp, I finally got the replacement I was looking for. Of course it uses more CPU than a C ssh server (about 3 times more) but the difference isn’t that big. Next steps will include fork()’ing to open channels (will allow the SFTP server to chroot) and maybe support for some SSH extensions.
To implement the SSH protocol the following PHP extensions were used:
- OpenSSL: used to generate secure bits when negociating the key, and used to generate the host signature on connection. I was hoping to use openssl_verify() to verify the key used when logging in, but I couldn’t manage to convert the ssh-rsa key to something openssl would understand, so I re-implemented signature verification with gmp.
- MCrypt: The ssh protocol is encrypted (usually with something like AES128). mcrypt has the required functions to handle encryption in block mode
- Hash: each packet transmitted over SSH is optionally signed with a HMAC signature. In order to generate and verify those signatures I used hash_hmac()
- And finally the most important: GMP. As I was missing some functions to properly handle the initial Diffie-Hellman key exchange (and later to implement publickey authentication) I had to re-implement those in PHP. Of course working with 1024 bits integers is not something we can use the native int type for. GMP (and bc) allows such calculations (and I used them). I was missing the ability in gmp to read from/convert to binary values, so I had to add the use of bin2hex() and pack(‘H*’, …) to be able to work with binary values easily. GMP computations are only used when negociating keys (the ssh rfc recommands doing this once an hour, or every gigabyte of data transmitted) or when using the publickey authentification.
What did I create a ssh server for? The same thing I created a DNS server for fun and for KalyHost. In order to provide services updated in realtime I wrote a database-backed dns server a while ago, and now a ssh server (which can be database-backed too by extending the “Base” class).
The sourcecode can be downloaded from the SVN: http://ookoo.org/svn/pinetd2/trunk/code/classes/Daemon/SSHd/ this depends on pinetd2, a framework I wrote which allows to easily create daemons in PHP, and which I already used to create various things (FTP, Mail server, etc).
People willing to help on pinetd2 (code and/or documentation) are welcome. If you do not mind being called crazy because you make something else than webpages in PHP, you can contact me by mail or on IRC (or by leaving a comment on this post too if you wish to).