Building OpenSSH for OS X?

How do I build OpenSSH using the updated OpenSSL on OS X? Where do I find the instructions?

Grawity was correct - I downloaded the wrong OpenSSH. I needed a portable one, and not the one provided at the FTP link. The portable ones are listed at the bottom of Portable OpenSSH.

Once you have the portable one, unpack, and then perform the following. The following assumes you have an updated OpenSSL libcrypto installed at /usr/local/ssl/darwin and an updated Zlib libz at /usr/local. Adjust them to suite your taste.

Replace -lz with absolute path to archive

This is needed to ensure static linking because there is no apparent option for it in configure. When the binary is built, otool -L will show there are no external Zlib dependencies.

$ grep "\-lz" *
configure:{ echo "$as_me:$LINENO: checking for deflate in -lz" >&5
configure:echo $ECHO_N "checking for deflate in -lz... $ECHO_C" >&6; }
configure:LIBS="-lz  $LIBS"
configure:  LIBS="-lz $LIBS"
configure:      LIBS="$LIBS -lz"
configure.ac:       LIBS="$LIBS -lz"

Then:

$ sed -i "" 's|-lz|/usr/local/lib/libz.a|g' config.h.in configure configure.ac
$

Replace -lcrypto with absolute path to archive

This is needed to ensure static linking because there is no apparent option for it in configure. When the binary is built, otool -L will show there are no external OpenSSL dependencies.

$ grep "\-lcrypto" *
configure:  LIBS="-lcrypto $LIBS"
configure.ac:   LIBS="-lcrypto $LIBS"

Then:

$ sed -i "" 's|-lcrypto|/usr/local/ssl/darwin/lib/libcrypto.a|g' configure configure.ac
$

In the above, I use /usr/local/ssl/darwin as --openssldir when building the OpenSSL library. Its usually just /usr/local/ssl. Adjust it as necessary.

Configure OpenSSH

Configure OpenSSH with the needed paths. You might need to include a OS X library, too.

If find /usr/lib/ -name libsandbox* returns libsandbox.dylib, then you need to include --with-libs="-lsandbox. Its missing on OS X 10.5, but its present on OS X 10.8. If the library is present but --with-libs is omitted, then it will result in an error similar to riemann.local sshd[15748]: fatal: ssh_sandbox_child: sandbox_init: dlopen(/usr/lib/libsandbox.1.dylib, 261): image not found [preauth].

$ ./configure --without-ssh1 --with-ssl-dir=/usr/local/ssl/darwin --with-zlib=/usr/local \
  --with-libs="-lsandbox" --prefix=/usr/local
checking for gcc... gcc
checking for C compiler default output file name... a.out
...

OpenSSH has been configured with the following options:
                     User binaries: /usr/local/bin
                   System binaries: /usr/local/sbin
               Configuration files: /usr/local/etc
...

Host: x86_64-apple-darwin12.6.0
Compiler: gcc
Compiler flags: -g -O2 -Qunused-arguments -Wunknown-warning-option -Wall -Wpointer-arith 
    -Wuninitialized -Wsign-compare -Wformat-security -Wsizeof-pointer-memaccess
    -Wno-pointer-sign -Wno-unused-result -fno-strict-aliasing -D_FORTIFY_SOURCE=2
    -ftrapv -fno-builtin-memset -fstack-protector-all -fPIE 
Preprocessor flags: -I/usr/local/ssl/darwin/include -I/usr/local/include 
Linker flags: -L/usr/local/ssl/darwin/lib -L/usr/local/lib  -fstack-protector-all -pie
Libraries: /usr/local/ssl/darwin/lib/libcrypto.a /usr/local/lib/libz.a -lsandbox -lresolv

You might need --with-pam to get password authentication working. I was not concerned with the feature, so I did not add the configuration option.

Build and Install OpenSSH

The -fwrapv is somewhat troubling when seen in high integrity software. That usually means there's an illegal C program with undefined behavior afoot. The developers have not fixed it, so they use -fwrapv as a band-aide.

$ make
conffile=`echo sshd_config.out | sed 's/.out$//'`; \
...

gcc -g -O2 -Qunused-arguments -Wunknown-warning-option -Wall -Wpointer-arith -Wuninitialized
  -Wsign-compare -Wformat-security -Wsizeof-pointer-memaccess -Wno-pointer-sign
  -Wno-unused-result -fno-strict-aliasing -D_FORTIFY_SOURCE=2 -ftrapv -fno-builtin-memset
  -fstack-protector-all -fPIE  -I. -I.. -I. -I./.. -I/usr/local/ssl/darwin/include
-I/usr/local/include  -DHAVE_CONFIG_H -c arc4random.c
gcc -g -O2 -Qunused-arguments -Wunknown-warning-option -Wall -Wpointer-arith -Wuninitialized
  -Wsign-compare -Wformat-security -Wsizeof-pointer-memaccess -Wno-pointer-sign
  -Wno-unused-result -fno-strict-aliasing -D_FORTIFY_SOURCE=2 -ftrapv -fno-builtin-memset
  -fstack-protector-all -fPIE  -I. -I.. -I. -I./.. -I/usr/local/ssl/darwin/include
  -I/usr/local/include  -DHAVE_CONFIG_H -c bsd-asprintf.c
...

External dependencies (or lack thereof) can be verified with:

$ otool -L ./sshd 
./sshd:
    /usr/lib/libsandbox.1.dylib (compatibility version 1.0.0, current version 1.0.0)
    /usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)

Unfortunately, there is no make check or make test targets, so there's no apparent way to test the software before installing it.

$ sudo make install
...

Generating public/private ed25519 key pair.
Your identification has been saved in /usr/local/etc/ssh_host_ed25519_key.
Your public key has been saved in /usr/local/etc/ssh_host_ed25519_key.pub.
The key fingerprint is:
SHA256:u/xQ6haFqxjG0fBgSMt0W58N8zmhu+NmyQxNMkMcwPc [email protected]
The key's randomart image is:
+--[ED25519 256]--+
| .ooooo.o .      |
| o.o=o+. B o     |
|  o..B .+.=      |
|    . * E...     |
|   . . *So.      |
|    + . +=       |
|   . o =*o       |
|    . .+B+       |
|       +=..      |
+----[SHA256]-----+
...

Create a Plist for the new SSH binary

The following uses the existing ssh.plist, removes the Apple wrapper script, and changes the port number. Copy it to /System/Library/LaunchDaemons/ssh-7.1.plist.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Disabled</key>
    <true/>
    <key>Label</key>
    <string>com.openssh.sshd.7-1</string>
    <key>Program</key>
    <string>/usr/local/sbin/sshd</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/sbin/sshd</string>
        <string>-i</string>
        <string>-e</string>
        <string>-f</string>
        <string>/usr/local/etc/sshd_config</string>
        <string>-o</string>
        <string>PidFile=/var/run/sshd-7.1.pid</string>
    </array>
    <key>Sockets</key>
    <dict>
            <key>Listeners</key>
            <dict>
                    <key>SockServiceName</key>
                    <string>1522</string>
            </dict>
    </dict>
    <key>inetdCompatibility</key>
    <dict>
        <key>Wait</key>
        <false/>
    </dict>
    <key>StandardErrorPath</key>
    <string>/var/log/sshd-7.1.log</string>
    <key>SHAuthorizationRight</key>
    <string>system.preferences</string>
</dict>
</plist>

There's a few things going on in the plist:

  • the service label was changed to com.openssh.sshd.7-1 to avoid colliding with Apple's com.openssh.sshd
  • the PID file was changed to var/run/sshd-7.1.pid to avoid colliding with Apple's var/run/sshd.pid
  • the program's absolute path was included in ProgramArguments due to a sshd re-exec requires execution with an absolute path while servicing client connections
  • sshd_config is called out specifically to avoid open questions and confusion. But its used by default
  • the use of the -i option even though its an inetd(8) option. According to sshd and -i option on OS X, it should be used

Start the new SSH service

Use launchd to start the service. Notice the use of -w to avoid the "nothing found to load" error message because the service is disabled.

$ sudo launchctl load -w /System/Library/LaunchDaemons/ssh-7.1.plist

Finally, verify the new SSH server is operating:

$ netstat -an | grep 1522
tcp6       0      0  *.1522                 *.*                    LISTEN     
tcp4       0      0  *.1522                 *.*                    LISTEN  

I visited openssh.com and fetched the latest version (7.1) via FTP.

The link under "For OpenBSD: FTP / HTTP" is, as the name says, for OpenBSD only. It needs to be built using the OpenBSD 'Ports' tools.

Since you're running OS X, not OpenBSD, you'll have to download it from "For other OSs → Others…". (Scroll down to the "Download" section.) The official OpenSSH-portable 7.1p1 tarball definitely has both a configure script and an INSTALL document.

Static linking shouldn't be necessary; OS X binaries can depend on libraries at an absolute path. (I think that might even be the default?)

There is no download link on the homepage; you kinda have to rummage for it.

I agree that the website is sort of stuck in 1990's, but I wouldn't say two clicks instead of one count as rummaging... If it had a download page, it would just contain the same OS links which are already in the sidebar, wouldn't it?

Where do I find the instructions?

Take a look at Homebrew, especially its openssh formula. Also:

  • http://www.dctrwatson.com/2013/07/how-to-update-openssh-on-mac-os-x/
  • https://mochtu.de/2015/01/07/updating-openssh-on-mac-os-x-10-10-yosemite/