From b9a72bbfbc5e1f6480028bb3f3ffb375b1c7a57f Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Sun, 26 Feb 2023 14:16:08 +0100 Subject: [PATCH] Add ssh port. --- build-aux/iso-grub-cfg.sh | 20 + build-aux/ports.conf | 2 +- libc/include/sys/cdefs.h | 28 +- ports/ssh/ssh.patch | 1781 +++++++++++++++++++++ ports/ssh/ssh.port | 12 + share/man/man7/installation.7 | 28 +- share/man/man7/release-iso-bootconfig.7 | 5 + share/man/man7/release-iso-modification.7 | 107 +- share/man/man7/upgrade.7 | 3 +- sysinstall/sysinstall.c | 225 ++- tix/tix-iso-bootconfig | 4 + tix/tix-iso-bootconfig.8 | 26 + tix/tix-iso-liveconfig | 94 ++ tix/tix-iso-liveconfig.8 | 198 +++ 14 files changed, 2524 insertions(+), 9 deletions(-) create mode 100644 ports/ssh/ssh.patch create mode 100644 ports/ssh/ssh.port diff --git a/build-aux/iso-grub-cfg.sh b/build-aux/iso-grub-cfg.sh index fd274223..fc7a8ddd 100755 --- a/build-aux/iso-grub-cfg.sh +++ b/build-aux/iso-grub-cfg.sh @@ -125,6 +125,7 @@ mkdir -p boot/grub/init echo "furthermore" > boot/grub/init/furthermore # TODO: Possibly use a 'try' feature to not warn in case it was already unset. echo "unset require dhclient" > boot/grub/init/network-no-dhclient +echo "require sshd optional" > boot/grub/init/local-sshd exec > boot/grub/grub.cfg @@ -193,6 +194,7 @@ fi set enable_src=true set enable_network_drivers= set enable_dhclient=true +set enable_sshd=false export version export machine @@ -204,6 +206,7 @@ export no_random_seed export enable_src export enable_network_drivers export enable_dhclient +export enable_sshd EOF if [ -n "$ports" ]; then @@ -299,6 +302,11 @@ cat << EOF module /boot/grub/init/network-no-dhclient --append-to /etc/init/network echo done fi + if \$enable_sshd; then + echo -n "Enabling sshd ... " + module /boot/grub/init/local-sshd --append-to /etc/init/local + echo done + fi if [ \$no_random_seed != --no-random-seed ]; then echo -n "Loading /boot/random.seed (256) ... " module /boot/random.seed --random-seed @@ -458,6 +466,18 @@ else } fi +if \$enable_sshd; then + menuentry "Disable SSH server" { + enable_sshd=false + configfile /boot/grub/advanced.cfg + } +else + menuentry "Enable SSH server" { + enable_sshd=true + configfile /boot/grub/advanced.cfg + } +fi + menuentry "Select binary packages..." { configfile /boot/grub/tix.cfg } diff --git a/build-aux/ports.conf b/build-aux/ports.conf index 97a5e0c9..cf9a0ea8 100644 --- a/build-aux/ports.conf +++ b/build-aux/ports.conf @@ -1,3 +1,3 @@ set_minimal="cut dash e2fsprogs grep grub mdocml sed xargs" -set_basic="$set_minimal binutils bison bzip2 diffutils ed flex gawk gcc git gzip libcurl libcurses libssl libstdc++ nano make patch pkg-config python tar vim wget xz xorriso" +set_basic="$set_minimal binutils bison bzip2 diffutils ed flex gawk gcc git gzip libcurl libcurses libssl libstdc++ nano make patch pkg-config python ssh tar vim wget xz xorriso" sets="basic minimal" diff --git a/libc/include/sys/cdefs.h b/libc/include/sys/cdefs.h index 9b4f614f..62b23a5c 100644 --- a/libc/include/sys/cdefs.h +++ b/libc/include/sys/cdefs.h @@ -99,13 +99,33 @@ is updated to not rely on this macro. */ #undef __SORTIX_HAS_RESTARTABLE_SYSCALLS__ - -/* TODO: Define when initgroups(2) is implemented. Remove when libdbus is +/* TODO: Define when general user security is implemented. Remove when ssh is updated to not rely on this macro. */ +#undef __SORTIX_HAS_UID_SECURITY__ + +/* TODO: Define when mmap MAP_SHARED works properly. Remove when ssh is updated + to not rely on this macro. */ +#undef __SORTIX_HAS_WORKING_MAP_SHARED__ + +/* TODO: Define when shared memory, file descriptor passing, and general user + security are implemented. Remove when ssh is updated to not rely on + this macro. */ +#undef __SORTIX_HAS_WORKING_PRIVSEP__ + +/* TODO: Define when initgroups(2) is implemented. Remove when libdbus and ssh + are updated not rely on this macro. */ #undef __SORTIX_HAS_INITGROUPS__ -/* TODO: Define when setgroups(2) is implemented. Remove when libdbus is updated - to not rely on this macro. */ +/* TODO: Define when setgroups(2) is implemented. Remove when libdbus and ssh + are updated not rely on this macro. */ #undef __SORTIX_HAS_SETGROUPS__ +/* TODO: Define when getgroups(2) is implemented. Remove when ssh is updated to + not rely on this macro. */ +#undef __SORTIX_HAS_GETGROUPS__ + +/* TODO: Define when getservbyname(3) is implemented and works. Remove when ssh + is updated to not rely on this macro. */ +#undef __SORTIX_HAS_GETSERVBYNAME__ + #endif diff --git a/ports/ssh/ssh.patch b/ports/ssh/ssh.patch new file mode 100644 index 00000000..859b1839 --- /dev/null +++ b/ports/ssh/ssh.patch @@ -0,0 +1,1781 @@ +diff -Paur --no-dereference -- ssh.upstream/auth.c ssh/auth.c +--- ssh.upstream/auth.c ++++ ssh/auth.c +@@ -100,7 +100,9 @@ + int + allowed_user(struct ssh *ssh, struct passwd * pw) + { ++#if !defined(__sortix__) + struct stat st; ++#endif + const char *hostname = NULL, *ipaddr = NULL; + u_int i; + int r; +@@ -121,6 +123,8 @@ + */ + if (options.chroot_directory == NULL || + strcasecmp(options.chroot_directory, "none") == 0) { ++/* PATCH: Sortix searches PATH for the shell which is not implemented here. */ ++#if !defined(__sortix__) + char *shell = xstrdup((pw->pw_shell[0] == '\0') ? + _PATH_BSHELL : pw->pw_shell); /* empty = /bin/sh */ + +@@ -138,6 +142,7 @@ + return 0; + } + free(shell); ++#endif + } + + if (options.num_deny_users > 0 || options.num_allow_users > 0 || +diff -Paur --no-dereference -- ssh.upstream/auth-passwd.c ssh/auth-passwd.c +--- ssh.upstream/auth-passwd.c ++++ ssh/auth-passwd.c +@@ -44,6 +44,7 @@ + #include + #include + #include ++#include + + #include "packet.h" + #include "sshbuf.h" +@@ -187,6 +188,21 @@ + return (auth_close(as)); + } + } ++#elif defined(__sortix__) ++/* PATCH: Sortix authentication support. */ ++int ++sys_auth_passwd(struct ssh *ssh, const char *password) ++{ ++ Authctxt *authctxt = ssh->authctxt; ++ struct passwd *pw = authctxt->pw; ++ ++ /* Just use the supplied fake password if authctxt is invalid */ ++ /* TODO: Use the default number of rounds according to login.conf once ++ this is implemented. */ ++ char *pw_password = authctxt->valid ? pw->pw_passwd : NULL; ++ ++ return crypt_checkpass(password, pw_password) == 0; ++} + #elif !defined(CUSTOM_SYS_AUTH_PASSWD) + int + sys_auth_passwd(struct ssh *ssh, const char *password) +diff -Paur --no-dereference -- ssh.upstream/channels.c ssh/channels.c +--- ssh.upstream/channels.c ++++ ssh/channels.c +@@ -1390,7 +1390,6 @@ + channel_decode_socks4(Channel *c, struct sshbuf *input, struct sshbuf *output) + { + const u_char *p; +- char *host; + u_int len, have, i, found, need; + char username[256]; + struct { +@@ -1454,7 +1453,9 @@ + free(c->path); + c->path = NULL; + if (need == 1) { /* SOCKS4: one string */ +- host = inet_ntoa(s4_req.dest_addr); ++ /* PATCH: Prefer the better designed inet_ntop over inet_ntoa. */ ++ char host[INET_ADDRSTRLEN + 1]; ++ inet_ntop(AF_INET, &s4_req.dest_addr, host, sizeof(host)); + c->path = xstrdup(host); + } else { /* SOCKS4A: two strings */ + have = sshbuf_len(input); +@@ -2450,8 +2451,8 @@ + return; + } + if ((euid != 0) && (getuid() != euid)) { +- error("multiplex uid mismatch: peer euid %u != uid %u", +- (u_int)euid, (u_int)getuid()); ++ error("multiplex uid mismatch: peer euid %ju != uid %ju", ++ (uintmax_t)euid, (uintmax_t)getuid()); + close(newsock); + return; + } +diff -Paur --no-dereference -- ssh.upstream/configure ssh/configure +--- ssh.upstream/configure ++++ ssh/configure +@@ -8688,6 +8688,7 @@ + # the --with-solaris-privs option and --with-sandbox=solaris). + SOLARIS_PRIVS="no" + ++default_user_path="/usr/bin:/bin:/usr/sbin:/sbin" + # Check for some target-specific stuff + case "$host" in + *-*-aix*) +@@ -8701,6 +8702,9 @@ + cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + ++/* PATCH: Fix implicit declaration of exit(3). */ ++#include ++ + #define testmacro foo + #define testmacro bar + int +@@ -10399,6 +10403,10 @@ + printf "%s\n" "#define BROKEN_SETVBUF 1" >>confdefs.h + + ;; ++*-*-sortix*) ++ default_user_path="/bin:/sbin" ++ MANTYPE=doc ++ ;; + esac + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking compiler and flags for sanity" >&5 +@@ -19135,7 +19143,12 @@ + long long num = 0x7fffffffffffffffll; + #endif + strcpy(expected_out, "9223372036854775807"); +- snprintf(buf, mazsize, "%lld", num); ++/* PATCH: Fix format type issue. */ ++#if (SIZEOF_LONG_INT == 8) ++ snprintf(buf, mazsize, "%ld", num); ++#else ++ snprintf(buf, mazsize, "%lld", num); ++#endif + if(strcmp(buf, expected_out) != 0) + exit(1); + exit(0); +@@ -22069,7 +22082,8 @@ + elif ${NROFF} -man ${srcdir}/ssh.1 >/dev/null 2>&1; then + MANTYPE=man + else +- MANTYPE=cat ++ # PATCH: Sortix uses mandoc (mdocml) even when cross-compiling. ++ MANTYPE=doc + fi + fi + +@@ -22258,7 +22272,7 @@ + fi + if test "$cross_compiling" = yes + then : +- user_path="/usr/bin:/bin:/usr/sbin:/sbin" ++ user_path="$default_user_path" + + else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +@@ -22274,7 +22288,7 @@ + # ifdef _PATH_USERPATH /* Irix */ + # define _PATH_STDPATH _PATH_USERPATH + # else +-# define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin" ++# define _PATH_STDPATH "$default_user_path" + # endif + #endif + #include +@@ -22306,7 +22320,7 @@ + then : + user_path=`cat conftest.stdpath` + else $as_nop +- user_path="/usr/bin:/bin:/usr/sbin:/sbin" ++ user_path="$default_user_path" + fi + rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +diff -Paur --no-dereference -- ssh.upstream/defines.h ssh/defines.h +--- ssh.upstream/defines.h ++++ ssh/defines.h +@@ -56,7 +56,9 @@ + * Definitions for IP type of service (ip_tos) + */ + #include ++#if __has_include() + #include ++#endif + #ifndef IPTOS_LOWDELAY + # define IPTOS_LOWDELAY 0x10 + # define IPTOS_THROUGHPUT 0x08 +@@ -121,10 +123,14 @@ + # ifdef PATH_MAX + # define MAXPATHLEN PATH_MAX + # else /* PATH_MAX */ +-# define MAXPATHLEN 64 ++# define MAXPATHLEN 4096 + # endif /* PATH_MAX */ + #endif /* MAXPATHLEN */ + ++#ifndef PATH_MAX ++# define PATH_MAX 4096 ++#endif ++ + #ifndef HOST_NAME_MAX + # include "netdb.h" /* for MAXHOSTNAMELEN */ + # if defined(_POSIX_HOST_NAME_MAX) +@@ -216,7 +222,9 @@ + /* (or die trying) */ + + #ifndef HAVE_U_INT ++typedef unsigned short u_short; + typedef unsigned int u_int; ++typedef unsigned long u_long; + #endif + + #ifndef HAVE_INTXX_T +diff -Paur --no-dereference -- ssh.upstream/dh.c ssh/dh.c +--- ssh.upstream/dh.c ++++ ssh/dh.c +@@ -54,7 +54,10 @@ + + static const char * get_moduli_filename(void) + { +- return moduli_filename ? moduli_filename : _PATH_DH_MODULI; ++ /* PATCH: Use /etc/default for the default configuration. */ ++ return moduli_filename ? moduli_filename : ++ !access(_PATH_DH_MODULI, F_OK) ? _PATH_DH_MODULI : ++ _PATH_DH_MODULI_DEFAULT; + } + + static int +diff -Paur --no-dereference -- ssh.upstream/includes.h ssh/includes.h +--- ssh.upstream/includes.h ++++ ssh/includes.h +@@ -109,7 +109,9 @@ + #endif + + #include ++#if __has_include() + #include /* For typedefs */ ++#endif + #ifdef HAVE_RPC_TYPES_H + # include /* For INADDR_LOOPBACK */ + #endif +@@ -152,6 +154,9 @@ + #endif + + #include ++#ifndef EPFNOSUPPORT ++#define EPFNOSUPPORT EAFNOSUPPORT ++#endif + + /* + * On HP-UX 11.11, shadow.h and prot.h provide conflicting declarations +diff -Paur --no-dereference -- ssh.upstream/init/sshd ssh/init/sshd +--- ssh.upstream/init/sshd ++++ ssh/init/sshd +@@ -0,0 +1,3 @@ ++require network ++require sshd-keygen ++exec /sbin/sshd -De +diff -Paur --no-dereference -- ssh.upstream/init/sshd-keygen ssh/init/sshd-keygen +--- ssh.upstream/init/sshd-keygen ++++ ssh/init/sshd-keygen +@@ -0,0 +1 @@ ++exec ssh-keygen -A +diff -Paur --no-dereference -- ssh.upstream/kex.h ssh/kex.h +--- ssh.upstream/kex.h ++++ ssh/kex.h +@@ -85,6 +85,9 @@ + PROPOSAL_MAX + }; + ++#ifdef MODE_MAX ++#undef MODE_MAX /* PATCH: Collides with Sortix constant. */ ++#endif + enum kex_modes { + MODE_IN, + MODE_OUT, +diff -Paur --no-dereference -- ssh.upstream/loginrec.c ssh/loginrec.c +--- ssh.upstream/loginrec.c ++++ ssh/loginrec.c +@@ -147,6 +147,7 @@ + + #include "includes.h" + ++#include + #include + #include + #include +@@ -187,6 +188,20 @@ + # include + #endif + ++#if defined(__sortix__) && !__has_include() ++struct utmpx ++{ ++ short ut_type; ++ pid_t ut_pid; ++ char ut_line[32]; ++ char ut_id[4]; ++ char ut_user[32]; ++ struct timeval ut_tv; ++}; ++#define USER_PROCESS 7 ++#define DEAD_PROCESS 8 ++#endif ++ + /** + ** prototypes for helper functions in this file + **/ +@@ -439,6 +454,10 @@ + int + login_write(struct logininfo *li) + { ++/* TODO: Sortix doesn't have anything like utmp yet. */ ++#if defined(__sortix__) && !__has_include() ++ return 0; ++#endif + #ifndef HAVE_CYGWIN + if (geteuid() != 0) { + logit("Attempt to write login records by non-root user (aborting)"); +@@ -1033,7 +1052,7 @@ + return (0); + } + # else +- if (!utmpx_write_direct(li, &ut)) { ++ if (!utmpx_write_direct(li, &utx)) { + logit("%s: utmp_write_direct() failed", __func__); + return (0); + } +diff -Paur --no-dereference -- ssh.upstream/Makefile.in ssh/Makefile.in +--- ssh.upstream/Makefile.in ++++ ssh/Makefile.in +@@ -18,7 +18,7 @@ + abs_top_srcdir=@abs_top_srcdir@ + abs_top_builddir=@abs_top_builddir@ + +-DESTDIR= ++DESTDIR?= + VPATH=@srcdir@ + SSH_PROGRAM=@bindir@/ssh + ASKPASS_PROGRAM=$(libexecdir)/ssh-askpass +@@ -28,11 +28,12 @@ + SSH_SK_HELPER=$(libexecdir)/ssh-sk-helper + PRIVSEP_PATH=@PRIVSEP_PATH@ + SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@ +-STRIP_OPT=@STRIP_OPT@ ++STRIP_OPT= + TEST_SHELL=@TEST_SHELL@ + BUILDDIR=@abs_top_builddir@ + + PATHS= -DSSHDIR=\"$(sysconfdir)\" \ ++ -DSSHDIRDEFAULT=\"$(sysconfdir)/default\" \ + -D_PATH_SSH_PROGRAM=\"$(SSH_PROGRAM)\" \ + -D_PATH_SSH_ASKPASS_DEFAULT=\"$(ASKPASS_PROGRAM)\" \ + -D_PATH_SFTP_SERVER=\"$(SFTP_SERVER)\" \ +@@ -204,44 +205,44 @@ + $(RANLIB) $@ + + ssh$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHOBJS) +- $(LD) -o $@ $(SSHOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(GSSLIBS) $(CHANNELLIBS) ++ $(CC) -o $@ $(SSHOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(GSSLIBS) $(CHANNELLIBS) + + sshd$(EXEEXT): libssh.a $(LIBCOMPAT) $(SSHDOBJS) +- $(LD) -o $@ $(SSHDOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHDLIBS) $(LIBS) $(GSSLIBS) $(K5LIBS) $(CHANNELLIBS) ++ $(CC) -o $@ $(SSHDOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHDLIBS) $(LIBS) $(GSSLIBS) $(K5LIBS) $(CHANNELLIBS) + + scp$(EXEEXT): $(LIBCOMPAT) libssh.a $(SCP_OBJS) +- $(LD) -o $@ $(SCP_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) ++ $(CC) -o $@ $(SCP_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) + + ssh-add$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHADD_OBJS) +- $(LD) -o $@ $(SSHADD_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(CHANNELLIBS) ++ $(CC) -o $@ $(SSHADD_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(CHANNELLIBS) + + ssh-agent$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHAGENT_OBJS) +- $(LD) -o $@ $(SSHAGENT_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(CHANNELLIBS) ++ $(CC) -o $@ $(SSHAGENT_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(CHANNELLIBS) + + ssh-keygen$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHKEYGEN_OBJS) +- $(LD) -o $@ $(SSHKEYGEN_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(CHANNELLIBS) ++ $(CC) -o $@ $(SSHKEYGEN_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(CHANNELLIBS) + + ssh-keysign$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHKEYSIGN_OBJS) +- $(LD) -o $@ $(SSHKEYSIGN_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(CHANNELLIBS) ++ $(CC) -o $@ $(SSHKEYSIGN_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(CHANNELLIBS) + + ssh-pkcs11-helper$(EXEEXT): $(LIBCOMPAT) libssh.a $(P11HELPER_OBJS) +- $(LD) -o $@ $(P11HELPER_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) $(CHANNELLIBS) ++ $(CC) -o $@ $(P11HELPER_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) $(CHANNELLIBS) + + ssh-sk-helper$(EXEEXT): $(LIBCOMPAT) libssh.a $(SKHELPER_OBJS) +- $(LD) -o $@ $(SKHELPER_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) $(LIBFIDO2) $(CHANNELLIBS) ++ $(CC) -o $@ $(SKHELPER_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) $(LIBFIDO2) $(CHANNELLIBS) + + ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHKEYSCAN_OBJS) +- $(LD) -o $@ $(SSHKEYSCAN_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) $(CHANNELLIBS) ++ $(CC) -o $@ $(SSHKEYSCAN_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) $(CHANNELLIBS) + + sftp-server$(EXEEXT): $(LIBCOMPAT) libssh.a $(SFTPSERVER_OBJS) +- $(LD) -o $@ $(SFTPSERVER_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) ++ $(CC) -o $@ $(SFTPSERVER_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) + + sftp$(EXEEXT): $(LIBCOMPAT) libssh.a $(SFTP_OBJS) +- $(LD) -o $@ $(SFTP_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(LIBEDIT) ++ $(CC) -o $@ $(SFTP_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(LIBEDIT) + + # test driver for the loginrec code - not built by default + logintest: logintest.o $(LIBCOMPAT) libssh.a loginrec.o +- $(LD) -o $@ logintest.o $(LDFLAGS) loginrec.o -lopenbsd-compat -lssh $(LIBS) ++ $(CC) -o $@ logintest.o $(LDFLAGS) loginrec.o -lopenbsd-compat -lssh $(LIBS) + + $(MANPAGES): $(MANPAGES_IN) + if test "$(MANTYPE)" = "cat"; then \ +@@ -377,7 +378,8 @@ + $(AUTORECONF) + -rm -rf autom4te.cache .depend.bak + +-install: $(CONFIGFILES) $(MANPAGES) $(TARGETS) install-files install-sysconf host-key check-config ++# PATCH: Depending on check-config isn't safe for cross-compilation. ++install: $(CONFIGFILES) $(MANPAGES) $(TARGETS) install-files install-sysconf host-key + install-nokeys: $(CONFIGFILES) $(MANPAGES) $(TARGETS) install-files install-sysconf + install-nosysconf: $(CONFIGFILES) $(MANPAGES) $(TARGETS) install-files + +@@ -391,6 +393,8 @@ + $(MKDIR_P) $(DESTDIR)$(mandir)/$(mansubdir)5 + $(MKDIR_P) $(DESTDIR)$(mandir)/$(mansubdir)8 + $(MKDIR_P) $(DESTDIR)$(libexecdir) ++ $(MKDIR_P) $(DESTDIR)$(datadir) ++ $(MKDIR_P) $(DESTDIR)$(datadir)/init + $(MKDIR_P) -m 0755 $(DESTDIR)$(PRIVSEP_PATH) + $(INSTALL) -m 0755 $(STRIP_OPT) ssh$(EXEEXT) $(DESTDIR)$(bindir)/ssh$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) scp$(EXEEXT) $(DESTDIR)$(bindir)/scp$(EXEEXT) +@@ -419,29 +423,16 @@ + $(INSTALL) -m 644 ssh-keysign.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-keysign.8 + $(INSTALL) -m 644 ssh-pkcs11-helper.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-pkcs11-helper.8 + $(INSTALL) -m 644 ssh-sk-helper.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-sk-helper.8 ++ $(INSTALL) -m 644 init/sshd $(DESTDIR)$(datadir)/init/sshd ++ $(INSTALL) -m 644 init/sshd-keygen $(DESTDIR)$(datadir)/init/sshd-keygen + + install-sysconf: + $(MKDIR_P) $(DESTDIR)$(sysconfdir) +- @if [ ! -f $(DESTDIR)$(sysconfdir)/ssh_config ]; then \ +- $(INSTALL) -m 644 ssh_config.out $(DESTDIR)$(sysconfdir)/ssh_config; \ +- else \ +- echo "$(DESTDIR)$(sysconfdir)/ssh_config already exists, install will not overwrite"; \ +- fi +- @if [ ! -f $(DESTDIR)$(sysconfdir)/sshd_config ]; then \ +- $(INSTALL) -m 644 sshd_config.out $(DESTDIR)$(sysconfdir)/sshd_config; \ +- else \ +- echo "$(DESTDIR)$(sysconfdir)/sshd_config already exists, install will not overwrite"; \ +- fi +- @if [ ! -f $(DESTDIR)$(sysconfdir)/moduli ]; then \ +- if [ -f $(DESTDIR)$(sysconfdir)/primes ]; then \ +- echo "moving $(DESTDIR)$(sysconfdir)/primes to $(DESTDIR)$(sysconfdir)/moduli"; \ +- mv "$(DESTDIR)$(sysconfdir)/primes" "$(DESTDIR)$(sysconfdir)/moduli"; \ +- else \ +- $(INSTALL) -m 644 moduli.out $(DESTDIR)$(sysconfdir)/moduli; \ +- fi ; \ +- else \ +- echo "$(DESTDIR)$(sysconfdir)/moduli already exists, install will not overwrite"; \ +- fi ++ # PATCH: Use /etc/default for the default configuration. ++ $(MKDIR_P) $(DESTDIR)$(sysconfdir)/default ++ $(INSTALL) -m 644 ssh_config.out $(DESTDIR)$(sysconfdir)/default/ssh_config ++ $(INSTALL) -m 644 sshd_config.out $(DESTDIR)$(sysconfdir)/default/sshd_config ++ $(INSTALL) -m 644 moduli.out $(DESTDIR)$(sysconfdir)/default/moduli + + host-key: ssh-keygen$(EXEEXT) + @if [ -z "$(DESTDIR)" ] ; then \ +@@ -457,8 +448,8 @@ + fi + + uninstallall: uninstall +- -rm -f $(DESTDIR)$(sysconfdir)/ssh_config +- -rm -f $(DESTDIR)$(sysconfdir)/sshd_config ++ -rm -f $(DESTDIR)$(sysconfdir)/default/ssh_config ++ -rm -f $(DESTDIR)$(sysconfdir)/default/sshd_config + -rmdir $(DESTDIR)$(sysconfdir) + -rmdir $(DESTDIR)$(bindir) + -rmdir $(DESTDIR)$(sbindir) +@@ -553,7 +544,7 @@ + + regress/unittests/sshbuf/test_sshbuf$(EXEEXT): ${UNITTESTS_TEST_SSHBUF_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a +- $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_SSHBUF_OBJS) \ ++ $(CC) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_SSHBUF_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(TESTLIBS) + +@@ -567,7 +558,7 @@ + + regress/unittests/sshkey/test_sshkey$(EXEEXT): ${UNITTESTS_TEST_SSHKEY_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a +- $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_SSHKEY_OBJS) \ ++ $(CC) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_SSHKEY_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(TESTLIBS) + +@@ -578,7 +569,7 @@ + + regress/unittests/sshsig/test_sshsig$(EXEEXT): ${UNITTESTS_TEST_SSHSIG_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a +- $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_SSHSIG_OBJS) \ ++ $(CC) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_SSHSIG_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(TESTLIBS) + +@@ -587,7 +578,7 @@ + + regress/unittests/bitmap/test_bitmap$(EXEEXT): ${UNITTESTS_TEST_BITMAP_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a +- $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_BITMAP_OBJS) \ ++ $(CC) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_BITMAP_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(TESTLIBS) + +@@ -599,7 +590,7 @@ + regress/unittests/authopt/test_authopt$(EXEEXT): \ + ${UNITTESTS_TEST_AUTHOPT_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a +- $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_AUTHOPT_OBJS) \ ++ $(CC) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_AUTHOPT_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(TESTLIBS) + +@@ -609,7 +600,7 @@ + regress/unittests/conversion/test_conversion$(EXEEXT): \ + ${UNITTESTS_TEST_CONVERSION_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a +- $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_CONVERSION_OBJS) \ ++ $(CC) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_CONVERSION_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(TESTLIBS) + +@@ -621,7 +612,7 @@ + + regress/unittests/kex/test_kex$(EXEEXT): ${UNITTESTS_TEST_KEX_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a +- $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_KEX_OBJS) \ ++ $(CC) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_KEX_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(TESTLIBS) + +@@ -633,7 +624,7 @@ + regress/unittests/hostkeys/test_hostkeys$(EXEEXT): \ + ${UNITTESTS_TEST_HOSTKEYS_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a +- $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_HOSTKEYS_OBJS) \ ++ $(CC) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_HOSTKEYS_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(TESTLIBS) + +@@ -643,7 +634,7 @@ + regress/unittests/match/test_match$(EXEEXT): \ + ${UNITTESTS_TEST_MATCH_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a +- $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_MATCH_OBJS) \ ++ $(CC) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_MATCH_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(TESTLIBS) + +@@ -660,7 +651,7 @@ + regress/unittests/misc/test_misc$(EXEEXT): \ + ${UNITTESTS_TEST_MISC_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a +- $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_MISC_OBJS) \ ++ $(CC) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_MISC_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(TESTLIBS) + +@@ -670,7 +661,7 @@ + regress/unittests/utf8/test_utf8$(EXEEXT): \ + ${UNITTESTS_TEST_UTF8_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a +- $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_UTF8_OBJS) \ ++ $(CC) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_UTF8_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(TESTLIBS) + +diff -Paur --no-dereference -- ssh.upstream/misc.c ssh/misc.c +--- ssh.upstream/misc.c ++++ ssh/misc.c +@@ -70,6 +70,33 @@ + #include "ssherr.h" + #include "platform.h" + ++#if defined(__sortix__) && !defined(timerclear) ++static inline void timerclear(struct timeval *tvp) ++{ ++ tvp->tv_sec = 0; ++ tvp->tv_usec = 0; ++} ++#endif ++#if defined(__sortix__) && !defined(timerisset) ++static inline int timerisset(const struct timeval *tvp) ++{ ++ return tvp->tv_sec || tvp->tv_usec; ++} ++#endif ++#if defined(__sortix__) && !defined(timercmp) ++#define timercmp(s,t,op) ((s)->tv_sec == (t)->tv_sec ? \ ++ (s)->tv_usec op (t)->tv_usec : (s)->tv_sec op (t)->tv_sec) ++#endif ++ ++#if defined(__sortix__) && !defined(__SORTIX_HAS_INITGROUPS__) ++static inline int initgroups(const char *user, gid_t group) ++{ ++ (void)user; ++ (void)group; ++ return 0; ++} ++#endif ++ + /* remove newline at end of string */ + char * + chop(char *s) +@@ -2745,17 +2772,17 @@ + + if (geteuid() == 0 && + initgroups(pw->pw_name, pw->pw_gid) == -1) { +- error("%s: initgroups(%s, %u): %s", tag, +- pw->pw_name, (u_int)pw->pw_gid, strerror(errno)); ++ error("%s: initgroups(%s, %ju): %s", tag, ++ pw->pw_name, (uintmax_t)pw->pw_gid, strerror(errno)); + _exit(1); + } + if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1) { +- error("%s: setresgid %u: %s", tag, (u_int)pw->pw_gid, ++ error("%s: setresgid %ju: %s", tag, (uintmax_t)pw->pw_gid, + strerror(errno)); + _exit(1); + } + if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) { +- error("%s: setresuid %u: %s", tag, (u_int)pw->pw_uid, ++ error("%s: setresuid %ju: %s", tag, (uintmax_t)pw->pw_uid, + strerror(errno)); + _exit(1); + } +diff -Paur --no-dereference -- ssh.upstream/monitor_fdpass.c ssh/monitor_fdpass.c +--- ssh.upstream/monitor_fdpass.c ++++ ssh/monitor_fdpass.c +@@ -67,11 +67,11 @@ + + memset(&msg, 0, sizeof(msg)); + #ifdef HAVE_ACCRIGHTS_IN_MSGHDR +- msg.msg_accrights = (caddr_t)&fd; ++ msg.msg_accrights = (char *)&fd; + msg.msg_accrightslen = sizeof(fd); + #else + memset(&cmsgbuf, 0, sizeof(cmsgbuf)); +- msg.msg_control = (caddr_t)&cmsgbuf.buf; ++ msg.msg_control = (char *)&cmsgbuf.buf; + msg.msg_controllen = sizeof(cmsgbuf.buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); +@@ -132,7 +132,7 @@ + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + #ifdef HAVE_ACCRIGHTS_IN_MSGHDR +- msg.msg_accrights = (caddr_t)&fd; ++ msg.msg_accrights = (char *)&fd; + msg.msg_accrightslen = sizeof(fd); + #else + memset(&cmsgbuf, 0, sizeof(cmsgbuf)); +diff -Paur --no-dereference -- ssh.upstream/mux.c ssh/mux.c +--- ssh.upstream/mux.c ++++ ssh/mux.c +@@ -497,7 +497,7 @@ + /* prepare reply */ + if ((r = sshbuf_put_u32(reply, MUX_S_ALIVE)) != 0 || + (r = sshbuf_put_u32(reply, rid)) != 0 || +- (r = sshbuf_put_u32(reply, (u_int)getpid())) != 0) ++ (r = sshbuf_put_u32(reply, (uintmax_t)getpid())) != 0) + fatal_fr(r, "reply"); + + return 0; +diff -Paur --no-dereference -- ssh.upstream/openbsd-compat/bsd-misc.c ssh/openbsd-compat/bsd-misc.c +--- ssh.upstream/openbsd-compat/bsd-misc.c ++++ ssh/openbsd-compat/bsd-misc.c +@@ -109,12 +109,18 @@ + #ifndef HAVE_UTIMES + int utimes(const char *filename, struct timeval *tvp) + { +- struct utimbuf ub; +- +- ub.actime = tvp[0].tv_sec; +- ub.modtime = tvp[1].tv_sec; +- +- return (utime(filename, &ub)); ++ /* PATCH: Sortix only has the nanosecond utimens. */ ++ struct timespec times[2]; ++ ++ if (!tvp) ++ return (utimens(filename, NULL)); ++ ++ times[0].tv_sec = tvp[0].tv_sec; ++ times[0].tv_nsec = tvp[0].tv_usec * 1000L; ++ times[1].tv_sec = tvp[0].tv_sec; ++ times[1].tv_nsec = tvp[0].tv_usec * 1000L; ++ ++ return (utimens(filename, times)); + } + #endif + +diff -Paur --no-dereference -- ssh.upstream/openbsd-compat/bsd-misc.h ssh/openbsd-compat/bsd-misc.h +--- ssh.upstream/openbsd-compat/bsd-misc.h ++++ ssh/openbsd-compat/bsd-misc.h +@@ -23,7 +23,7 @@ + int seed_from_prngd(unsigned char *, size_t); + + #ifndef HAVE_SETSID +-#define setsid() setpgrp(0, getpid()) ++#define setsid() setpgid(0, getpid()) + #endif /* !HAVE_SETSID */ + + #ifndef HAVE_SETENV +@@ -190,7 +190,10 @@ + struct tm *localtime_r(const time_t *, struct tm *); + #endif + +-#ifndef HAVE_REALPATH ++/* TODO: Quick hack to avoid rewriting all the realpath invocations */ ++#if !defined(HAVE_REALPATH) || defined(__sortix__) ++#include ++char* sftp_realpath(const char *path, char *resolved); + #define realpath(x, y) (sftp_realpath((x), (y))) + #endif + +diff -Paur --no-dereference -- ssh.upstream/openbsd-compat/bsd-pselect.c ssh/openbsd-compat/bsd-pselect.c +--- ssh.upstream/openbsd-compat/bsd-pselect.c ++++ ssh/openbsd-compat/bsd-pselect.c +@@ -102,8 +102,8 @@ + set_nonblock(notify_pipe[0]); + set_nonblock(notify_pipe[1]); + notify_pid = getpid(); +- debug3_f("pid %d saved %d pipe0 %d pipe1 %d", getpid(), +- notify_pid, notify_pipe[0], notify_pipe[1]); ++ debug3_f("pid %jd saved %jd pipe0 %d pipe1 %d", (intmax_t)getpid(), ++ (intmax_t)notify_pid, notify_pipe[0], notify_pipe[1]); + initialized = 1; + return; + } +diff -Paur --no-dereference -- ssh.upstream/openbsd-compat/bsd-setres_id.c ssh/openbsd-compat/bsd-setres_id.c +--- ssh.upstream/openbsd-compat/bsd-setres_id.c ++++ ssh/openbsd-compat/bsd-setres_id.c +@@ -37,20 +37,20 @@ + #if defined(HAVE_SETREGID) && !defined(BROKEN_SETREGID) + if (setregid(rgid, egid) < 0) { + saved_errno = errno; +- error("setregid %lu: %.100s", (u_long)rgid, strerror(errno)); ++ error("setregid %ju: %.100s", (uintmax_t)rgid, strerror(errno)); + errno = saved_errno; + ret = -1; + } + #else + if (setegid(egid) < 0) { + saved_errno = errno; +- error("setegid %lu: %.100s", (u_long)egid, strerror(errno)); ++ error("setegid %ju: %.100s", (uintmax_t)egid, strerror(errno)); + errno = saved_errno; + ret = -1; + } + if (setgid(rgid) < 0) { + saved_errno = errno; +- error("setgid %lu: %.100s", (u_long)rgid, strerror(errno)); ++ error("setgid %ju: %.100s", (uintmax_t)rgid, strerror(errno)); + errno = saved_errno; + ret = -1; + } +@@ -72,7 +72,7 @@ + #if defined(HAVE_SETREUID) && !defined(BROKEN_SETREUID) + if (setreuid(ruid, euid) < 0) { + saved_errno = errno; +- error("setreuid %lu: %.100s", (u_long)ruid, strerror(errno)); ++ error("setreuid %ju: %.100s", (uintmax_t)ruid, strerror(errno)); + errno = saved_errno; + ret = -1; + } +@@ -81,14 +81,14 @@ + # ifndef SETEUID_BREAKS_SETUID + if (seteuid(euid) < 0) { + saved_errno = errno; +- error("seteuid %lu: %.100s", (u_long)euid, strerror(errno)); ++ error("seteuid %ju: %.100s", (uintmax_t)euid, strerror(errno)); + errno = saved_errno; + ret = -1; + } + # endif + if (setuid(ruid) < 0) { + saved_errno = errno; +- error("setuid %lu: %.100s", (u_long)ruid, strerror(errno)); ++ error("setuid %ju: %.100s", (uintmax_t)ruid, strerror(errno)); + errno = saved_errno; + ret = -1; + } +diff -Paur --no-dereference -- ssh.upstream/openbsd-compat/getrrsetbyname.c ssh/openbsd-compat/getrrsetbyname.c +--- ssh.upstream/openbsd-compat/getrrsetbyname.c ++++ ssh/openbsd-compat/getrrsetbyname.c +@@ -57,6 +57,28 @@ + + #include "getrrsetbyname.h" + ++#if !__has_include() ++ ++int ++getrrsetbyname(const char *hostname, unsigned int rdclass, ++ unsigned int rdtype, unsigned int flags, ++ struct rrsetinfo **res) ++{ ++ (void)hostname; ++ (void)rdclass; ++ (void)rdtype; ++ (void)flags; ++ return ERRSET_NONAME; ++} ++ ++void ++freerrset(struct rrsetinfo *rrset) ++{ ++ (void)rrset; ++} ++ ++#else ++ + #if defined(HAVE_DECL_H_ERRNO) && !HAVE_DECL_H_ERRNO + extern int h_errno; + #endif +@@ -612,4 +634,6 @@ + return (n); + } + ++#endif ++ + #endif /* !defined (HAVE_GETRRSETBYNAME) && !defined (HAVE_LDNS) */ +diff -Paur --no-dereference -- ssh.upstream/openbsd-compat/getrrsetbyname.h ssh/openbsd-compat/getrrsetbyname.h +--- ssh.upstream/openbsd-compat/getrrsetbyname.h ++++ ssh/openbsd-compat/getrrsetbyname.h +@@ -54,9 +54,13 @@ + + #include + #include ++#if __has_include() + #include ++#endif + #include ++#if __has_include() + #include ++#endif + + #ifndef HFIXEDSZ + #define HFIXEDSZ 12 +diff -Paur --no-dereference -- ssh.upstream/openbsd-compat/pwcache.c ssh/openbsd-compat/pwcache.c +--- ssh.upstream/openbsd-compat/pwcache.c ++++ ssh/openbsd-compat/pwcache.c +@@ -67,7 +67,7 @@ + if ((pw = getpwuid(uid)) == NULL) { + if (nouser) + return (NULL); +- (void)snprintf(nbuf, sizeof(nbuf), "%lu", (u_long)uid); ++ (void)snprintf(nbuf, sizeof(nbuf), "%ju", (uintmax_t)uid); + } + cp->uid = uid; + if (cp->name != NULL) +@@ -102,7 +102,7 @@ + if ((gr = getgrgid(gid)) == NULL) { + if (nogroup) + return (NULL); +- (void)snprintf(nbuf, sizeof(nbuf), "%lu", (u_long)gid); ++ (void)snprintf(nbuf, sizeof(nbuf), "%ju", (uintmax_t)gid); + } + cp->gid = gid; + if (cp->name != NULL) +diff -Paur --no-dereference -- ssh.upstream/openbsd-compat/strptime.c ssh/openbsd-compat/strptime.c +--- ssh.upstream/openbsd-compat/strptime.c ++++ ssh/openbsd-compat/strptime.c +@@ -68,7 +68,9 @@ + { + unsigned char c; + const unsigned char *bp; ++#if 0 + size_t len; ++#endif + int alt_format, i; + static int century, relyear; + +diff -Paur --no-dereference -- ssh.upstream/pathnames.h ssh/pathnames.h +--- ssh.upstream/pathnames.h ++++ ssh/pathnames.h +@@ -13,9 +13,11 @@ + */ + + #define ETCDIR "/etc" ++#define ETCDIRDEFAULT "/etc/default" + + #ifndef SSHDIR + #define SSHDIR ETCDIR "/ssh" ++#define SSHDIRDEFAULT ETCDIRDEFAULT "/ssh" + #endif + + #ifndef _PATH_SSH_PIDDIR +@@ -35,13 +37,16 @@ + * should be world-readable. + */ + #define _PATH_SERVER_CONFIG_FILE SSHDIR "/sshd_config" ++#define _PATH_SERVER_CONFIG_DEFAULT SSHDIRDEFAULT "/sshd_config" + #define _PATH_HOST_CONFIG_FILE SSHDIR "/ssh_config" ++#define _PATH_HOST_CONFIG_DEFAULT SSHDIRDEFAULT "/ssh_config" + #define _PATH_HOST_DSA_KEY_FILE SSHDIR "/ssh_host_dsa_key" + #define _PATH_HOST_ECDSA_KEY_FILE SSHDIR "/ssh_host_ecdsa_key" + #define _PATH_HOST_ED25519_KEY_FILE SSHDIR "/ssh_host_ed25519_key" + #define _PATH_HOST_XMSS_KEY_FILE SSHDIR "/ssh_host_xmss_key" + #define _PATH_HOST_RSA_KEY_FILE SSHDIR "/ssh_host_rsa_key" + #define _PATH_DH_MODULI SSHDIR "/moduli" ++#define _PATH_DH_MODULI_DEFAULT SSHDIRDEFAULT "/moduli" + + #ifndef _PATH_SSH_PROGRAM + #define _PATH_SSH_PROGRAM "/usr/bin/ssh" +diff -Paur --no-dereference -- ssh.upstream/progressmeter.c ssh/progressmeter.c +--- ssh.upstream/progressmeter.c ++++ ssh/progressmeter.c +@@ -81,7 +81,8 @@ + static int + can_output(void) + { +- return (getpgrp() == tcgetpgrp(STDOUT_FILENO)); ++ /* PATCH: Prefer the agreed upon and standard getpgid over getpgrp. */ ++ return (getpgid(0) == tcgetpgrp(STDOUT_FILENO)); + } + + static void +diff -Paur --no-dereference -- ssh.upstream/readconf.c ssh/readconf.c +--- ssh.upstream/readconf.c ++++ ssh/readconf.c +@@ -510,6 +510,10 @@ + int + default_ssh_port(void) + { ++// TODO: Sortix doesn't have getservbyname or a replacement at this time. ++#if defined(__sortix__) && !defined(__SORTIX_HAS_GETSERVBYNAME__) ++ return SSH_DEFAULT_PORT; ++#else + static int port; + struct servent *sp; + +@@ -518,6 +522,7 @@ + port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT; + } + return port; ++#endif + } + + /* +diff -Paur --no-dereference -- ssh.upstream/regress/netcat.c ssh/regress/netcat.c +--- ssh.upstream/regress/netcat.c ++++ ssh/regress/netcat.c +@@ -1020,11 +1020,11 @@ + + memset(&msg, 0, sizeof(msg)); + #ifdef HAVE_ACCRIGHTS_IN_MSGHDR +- msg.msg_accrights = (caddr_t)&nfd; ++ msg.msg_accrights = (char *)&nfd; + msg.msg_accrightslen = sizeof(nfd); + #else + memset(&cmsgbuf, 0, sizeof(cmsgbuf)); +- msg.msg_control = (caddr_t)&cmsgbuf.buf; ++ msg.msg_control = (char *)&cmsgbuf.buf; + msg.msg_controllen = sizeof(cmsgbuf.buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); +diff -Paur --no-dereference -- ssh.upstream/scp.c ssh/scp.c +--- ssh.upstream/scp.c ++++ ssh/scp.c +@@ -645,7 +645,7 @@ + mode = MODE_SCP; + + if ((pwd = getpwuid(userid = getuid())) == NULL) +- fatal("unknown user %u", (u_int) userid); ++ fatal("unknown user %ju", (uintmax_t) userid); + + if (!isatty(STDOUT_FILENO)) + showprogress = 0; +@@ -1007,7 +1007,7 @@ + + static struct sftp_conn * + do_sftp_connect(char *host, char *user, int port, char *sftp_direct, +- int *reminp, int *remoutp, int *pidp) ++ int *reminp, int *remoutp, pid_t *pidp) + { + if (sftp_direct == NULL) { + if (do_cmd(ssh_program, host, user, port, 1, "sftp", +diff -Paur --no-dereference -- ssh.upstream/servconf.c ssh/servconf.c +--- ssh.upstream/servconf.c ++++ ssh/servconf.c +@@ -309,7 +309,10 @@ + if (options->pid_file == NULL) + options->pid_file = xstrdup(_PATH_SSH_DAEMON_PID_FILE); + if (options->moduli_file == NULL) +- options->moduli_file = xstrdup(_PATH_DH_MODULI); ++ /* PATCH: Use /etc/default for the default configuration. */ ++ options->moduli_file = xstrdup(!access(_PATH_DH_MODULI, F_OK) ? ++ _PATH_DH_MODULI : ++ _PATH_DH_MODULI_DEFAULT); + if (options->login_grace_time == -1) + options->login_grace_time = 120; + if (options->permit_root_login == PERMIT_NOT_SET) +@@ -454,7 +457,12 @@ + + /* Turn privilege separation and sandboxing on by default */ + if (use_privsep == -1) ++/* PATCH: Until Sortix MAP_SHARED works. */ ++#if defined(__sortix__) && !defined(__SORTIX_HAS_WORKING_MAP_SHARED__) ++ use_privsep = PRIVSEP_OFF; ++#else + use_privsep = PRIVSEP_ON; ++#endif + + #define CLEAR_ON_NONE(v) \ + do { \ +diff -Paur --no-dereference -- ssh.upstream/session.c ssh/session.c +--- ssh.upstream/session.c ++++ ssh/session.c +@@ -104,6 +104,15 @@ + #include + #endif + ++#if defined(__sortix__) && !defined(__SORTIX_HAS_INITGROUPS__) ++static inline int initgroups(const char *user, gid_t group) ++{ ++ (void)user; ++ (void)group; ++ return 0; ++} ++#endif ++ + #define IS_INTERNAL_SFTP(c) \ + (!strncmp(c, INTERNAL_SFTP_NAME, sizeof(INTERNAL_SFTP_NAME) - 1) && \ + (c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\0' || \ +@@ -1052,9 +1061,11 @@ + #endif /* HAVE_LOGIN_CAP */ + + if (!options.use_pam) { ++#ifdef _PATH_MAILDIR + snprintf(buf, sizeof buf, "%.200s/%.50s", + _PATH_MAILDIR, pw->pw_name); + child_set_env(&env, &envsize, "MAIL", buf); ++#endif + } + + /* Normal systems set SHELL by default. */ +@@ -1434,7 +1445,7 @@ + } + + if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) +- fatal("Failed to set uids to %u.", (u_int) pw->pw_uid); ++ fatal("Failed to set uids to %ju.", (uintmax_t)pw->pw_uid); + } + + static void +@@ -1696,7 +1707,8 @@ + /* Execute the shell. */ + argv[0] = argv0; + argv[1] = NULL; +- execve(shell, argv, env); ++ /* PATCH: Sortix passwd(5) shell paths search the PATH. */ ++ execvpe(shell, argv, env); + + /* Executing the shell failed. */ + perror(shell); +@@ -1710,7 +1722,7 @@ + argv[1] = "-c"; + argv[2] = (char *) command; + argv[3] = NULL; +- execve(shell, argv, env); ++ execvpe(shell, argv, env); + perror(shell); + exit(1); + } +diff -Paur --no-dereference -- ssh.upstream/sftp-common.c ssh/sftp-common.c +--- ssh.upstream/sftp-common.c ++++ ssh/sftp-common.c +@@ -217,18 +217,18 @@ + { + int ulen, glen, sz = 0; + struct tm *ltime = localtime(&st->st_mtime); +- char buf[1024], lc[8], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1]; ++ char buf[1024], lc[8], mode[11+1], tbuf[12+1], ubuf[3*sizeof(uid_t)], gbuf[3*sizeof(gid_t)]; + char sbuf[FMT_SCALED_STRSIZE]; + time_t now; + + strmode(st->st_mode, mode); + if (remote) { + if (user == NULL) { +- snprintf(ubuf, sizeof ubuf, "%u", (u_int)st->st_uid); ++ snprintf(ubuf, sizeof ubuf, "%ju", (uintmax_t)st->st_uid); + user = ubuf; + } + if (group == NULL) { +- snprintf(gbuf, sizeof gbuf, "%u", (u_int)st->st_gid); ++ snprintf(gbuf, sizeof gbuf, "%ju", (uintmax_t)st->st_gid); + group = gbuf; + } + strlcpy(lc, "?", sizeof(lc)); +diff -Paur --no-dereference -- ssh.upstream/sftp-server.c ssh/sftp-server.c +--- ssh.upstream/sftp-server.c ++++ ssh/sftp-server.c +@@ -1020,8 +1020,8 @@ + status = errno_to_portable(errno); + } + if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { +- logit("set \"%s\" owner %lu group %lu", name, +- (u_long)a.uid, (u_long)a.gid); ++ logit("set \"%s\" owner %ju group %ju", name, ++ (uintmax_t)a.uid, (uintmax_t)a.gid); + r = chown(name, a.uid, a.gid); + if (r == -1) + status = errno_to_portable(errno); +@@ -1081,8 +1081,8 @@ + status = errno_to_portable(errno); + } + if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { +- logit("set \"%s\" owner %lu group %lu", name, +- (u_long)a.uid, (u_long)a.gid); ++ logit("set \"%s\" owner %ju group %ju", name, ++ (uintmax_t)a.uid, (uintmax_t)a.gid); + #ifdef HAVE_FCHOWN + r = fchown(fd, a.uid, a.gid); + #else +@@ -1501,8 +1501,8 @@ + status = errno_to_portable(errno); + } + if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { +- logit("set \"%s\" owner %lu group %lu", name, +- (u_long)a.uid, (u_long)a.gid); ++ logit("set \"%s\" owner %ju group %ju", name, ++ (uintmax_t)a.uid, (uintmax_t)a.gid); + r = fchownat(AT_FDCWD, name, a.uid, a.gid, + AT_SYMLINK_NOFOLLOW); + if (r == -1) +diff -Paur --no-dereference -- ssh.upstream/sftp-server-main.c ssh/sftp-server-main.c +--- ssh.upstream/sftp-server-main.c ++++ ssh/sftp-server-main.c +@@ -43,8 +43,8 @@ + sanitise_stdfd(); + + if ((user_pw = getpwuid(getuid())) == NULL) { +- fprintf(stderr, "No user found for uid %lu\n", +- (u_long)getuid()); ++ fprintf(stderr, "No user found for uid %ju\n", ++ (uintmax_t)getuid()); + return 1; + } + +diff -Paur --no-dereference -- ssh.upstream/ssh-add.c ssh/ssh-add.c +--- ssh.upstream/ssh-add.c ++++ ssh/ssh-add.c +@@ -979,8 +979,8 @@ + int count = 0; + + if ((pw = getpwuid(getuid())) == NULL) { +- fprintf(stderr, "No user found with uid %u\n", +- (u_int)getuid()); ++ fprintf(stderr, "No user found with uid %ju\n", ++ (uintmax_t)getuid()); + ret = 1; + goto done; + } +diff -Paur --no-dereference -- ssh.upstream/ssh-agent.c ssh/ssh-agent.c +--- ssh.upstream/ssh-agent.c ++++ ssh/ssh-agent.c +@@ -1749,8 +1749,8 @@ + return -1; + } + if ((euid != 0) && (getuid() != euid)) { +- error("uid mismatch: peer euid %u != uid %u", +- (u_int) euid, (u_int) getuid()); ++ error("uid mismatch: peer euid %ju != uid %ju", ++ (uintmax_t)euid, (uintmax_t)getuid()); + close(fd); + return -1; + } +diff -Paur --no-dereference -- ssh.upstream/ssh.c ssh/ssh.c +--- ssh.upstream/ssh.c ++++ ssh/ssh.c +@@ -561,6 +561,7 @@ + { + char buf[PATH_MAX]; + int r; ++ const char* host_config_file = _PATH_HOST_CONFIG_FILE; + + if (config != NULL) { + if (strcasecmp(config, "none") != 0 && +@@ -577,8 +578,11 @@ + &options, SSHCONF_CHECKPERM | SSHCONF_USERCONF | + (final_pass ? SSHCONF_FINAL : 0), want_final_pass); + ++ /* PATCH: Use /etc/default for the default configuration. */ ++ if (access(host_config_file, F_OK) < 0) ++ host_config_file = _PATH_HOST_CONFIG_DEFAULT; + /* Read systemwide configuration file after user config. */ +- (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, ++ (void)read_config_file(host_config_file, pw, + host, host_name, &options, + final_pass ? SSHCONF_FINAL : 0, want_final_pass); + } +@@ -671,7 +675,7 @@ + /* Get user data. */ + pw = getpwuid(getuid()); + if (!pw) { +- logit("No user exists for uid %lu", (u_long)getuid()); ++ logit("No user exists for uid %ju", (uintmax_t)getuid()); + exit(255); + } + /* Take a copy of the returned structure. */ +diff -Paur --no-dereference -- ssh.upstream/sshconnect.c ssh/sshconnect.c +--- ssh.upstream/sshconnect.c ++++ ssh/sshconnect.c +@@ -164,7 +164,8 @@ + * Execute the proxy command. + * Note that we gave up any extra privileges above. + */ +- execv(argv[0], argv); ++ /* PATCH: Search the PATH for the shell. */ ++ execvp(argv[0], argv); + perror(argv[0]); + exit(1); + } +@@ -248,7 +249,7 @@ + * extra privileges above. + */ + ssh_signal(SIGPIPE, SIG_DFL); +- execv(argv[0], argv); ++ execvp(argv[0], argv); + perror(argv[0]); + exit(1); + } +@@ -306,7 +307,9 @@ + for (allow_local = 0; allow_local < 2; allow_local++) { + for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL || ++#ifdef IFF_UP + (ifa->ifa_flags & IFF_UP) == 0 || ++#endif + ifa->ifa_addr->sa_family != af || + strcmp(ifa->ifa_name, options.bind_interface) != 0) + continue; +@@ -1671,7 +1674,7 @@ + if (pid == 0) { + ssh_signal(SIGPIPE, SIG_DFL); + debug3("Executing %s -c \"%s\"", shell, args); +- execl(shell, shell, "-c", args, (char *)NULL); ++ execlp(shell, shell, "-c", args, (char *)NULL); + error("Couldn't execute %s -c \"%s\": %s", + shell, args, strerror(errno)); + _exit(1); +diff -Paur --no-dereference -- ssh.upstream/sshd.8 ssh/sshd.8 +--- ssh.upstream/sshd.8 ++++ ssh/sshd.8 +@@ -63,8 +63,8 @@ + .Pp + .Nm + listens for connections from clients. +-It is normally started at boot from +-.Pa /etc/rc . ++It is normally started at boot by ++.Xr init 8 . + It forks a new + daemon for each incoming connection. + The forked daemons handle +@@ -81,7 +81,7 @@ + rereads its configuration file when it receives a hangup signal, + .Dv SIGHUP , + by executing itself with the name and options it was started with, e.g.\& +-.Pa /usr/sbin/sshd . ++.Pa /sbin/sshd . + .Pp + The options are as follows: + .Bl -tag -width Ds +@@ -994,14 +994,6 @@ + during privilege separation in the pre-authentication phase. + The directory should not contain any files and must be owned by root + and not group or world-writable. +-.Pp +-.It Pa /var/run/sshd.pid +-Contains the process ID of the +-.Nm +-listening for connections (if there are several daemons running +-concurrently for different ports, this contains the process ID of the one +-started last). +-The content of this file is not sensitive; it can be world-readable. + .El + .Sh SEE ALSO + .Xr scp 1 , +diff -Paur --no-dereference -- ssh.upstream/sshd.c ssh/sshd.c +--- ssh.upstream/sshd.c ++++ ssh/sshd.c +@@ -129,6 +129,15 @@ + #include "srclimit.h" + #include "dh.h" + ++#if defined(__sortix__) && !defined(__SORTIX_HAS_SETGROUPS__) ++static inline int setgroups(size_t size, const gid_t *list) ++{ ++ (void)size; ++ (void)list; ++ return 0; ++} ++#endif ++ + /* Re-exec fds */ + #define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) + #define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2) +@@ -234,7 +243,11 @@ + static int startup_pipe = -1; /* in child */ + + /* variables used for privilege separation */ ++#if defined(__sortix__) && !defined(__SORTIX_HAS_WORKING_PRIVSEP__) ++int use_privsep = 0; /* Needs shared memory and fd passing and security. */ ++#else + int use_privsep = -1; ++#endif + struct monitor *pmonitor = NULL; + int privsep_is_preauth = 1; + static int privsep_chroot = 1; +@@ -460,8 +473,8 @@ + fatal("chdir(\"/\"): %s", strerror(errno)); + + /* Drop our privileges */ +- debug3("privsep user:group %u:%u", (u_int)privsep_pw->pw_uid, +- (u_int)privsep_pw->pw_gid); ++ debug3("privsep user:group %ju:%ju", (uintmax_t)privsep_pw->pw_uid, ++ (uintmax_t)privsep_pw->pw_gid); + gidset[0] = privsep_pw->pw_gid; + if (setgroups(1, gidset) == -1) + fatal("setgroups: %.100s", strerror(errno)); +@@ -1579,6 +1592,10 @@ + /* Initialize configuration options to their default values. */ + initialize_server_options(&options); + ++ /* PATCH: Use /etc/default for the default configuration. */ ++ if (access(config_file_name, F_OK) < 0) ++ config_file_name = _PATH_SERVER_CONFIG_DEFAULT; ++ + /* Parse command-line arguments. */ + while ((opt = getopt(ac, av, + "C:E:b:c:f:g:h:k:o:p:u:46DQRTdeiqrtV")) != -1) { +@@ -1695,10 +1712,32 @@ + rexec_flag = 0; + if (!test_flag && rexec_flag && !path_absolute(av[0])) + fatal("sshd re-exec requires execution with an absolute path"); +- if (rexeced_flag) +- closefrom(REEXEC_MIN_FREE_FD); +- else +- closefrom(REEXEC_DEVCRYPTO_RESERVED_FD); ++ /* ++ * Close unexpected file descriptors, taking care to preserve the ++ * readiness file descriptor if any. ++ */ ++ if (getenv("READYFD")) { ++ int readyfd = atoi(getenv("READYFD")); ++ int lastfd = (rexeced_flag ? ++ REEXEC_MIN_FREE_FD : ++ REEXEC_DEVCRYPTO_RESERVED_FD); ++ if (readyfd != lastfd) { ++ char str[sizeof(int) * 3]; ++ if (dup2(readyfd, lastfd) < 0) ++ fatal("dup2: %.200s", strerror(errno)); ++ close(readyfd); ++ readyfd = lastfd; ++ snprintf(str, sizeof(str), "%i", readyfd); ++ if (setenv("READYFD", str, 1) < 0) ++ fatal("setenv: %.200s", strerror(errno)); ++ } ++ closefrom(lastfd + 1); ++ } else { ++ if (rexeced_flag) ++ closefrom(REEXEC_MIN_FREE_FD); ++ else ++ closefrom(REEXEC_DEVCRYPTO_RESERVED_FD); ++ } + + seed_rng(); + +@@ -2074,7 +2113,8 @@ + * Write out the pid file after the sigterm handler + * is setup and the listen sockets are bound + */ +- if (options.pid_file != NULL && !debug_flag) { ++ /* PATCH: Sortix daemons don't write out pid files. */ ++ if (options.pid_file != NULL && !debug_flag && !no_daemon_flag) { + FILE *f = fopen(options.pid_file, "w"); + + if (f == NULL) { +@@ -2086,6 +2126,15 @@ + } + } + ++ /* Notify the parent process that we're up and running. */ ++ if (getenv("READYFD")) { ++ int readyfd = atoi(getenv("READYFD")); ++ char c = '\n'; ++ write(readyfd, &c, 1); ++ close(readyfd); ++ unsetenv("READYFD"); ++ } ++ + /* Accept a connection and return in a forked child */ + server_accept_loop(&sock_in, &sock_out, + &newsock, config_s); +@@ -2445,10 +2494,10 @@ + do_cleanup(the_active_state, the_authctxt); + if (use_privsep && privsep_is_preauth && + pmonitor != NULL && pmonitor->m_pid > 1) { +- debug("Killing privsep child %d", pmonitor->m_pid); ++ debug("Killing privsep child %jd", (intmax_t)pmonitor->m_pid); + if (kill(pmonitor->m_pid, SIGKILL) != 0 && + errno != ESRCH) { +- error_f("kill(%d): %s", pmonitor->m_pid, ++ error_f("kill(%jd): %s", (intmax_t)pmonitor->m_pid, + strerror(errno)); + } + } +diff -Paur --no-dereference -- ssh.upstream/sshd_config ssh/sshd_config +--- ssh.upstream/sshd_config ++++ ssh/sshd_config +@@ -10,6 +10,13 @@ + # possible, but leave them commented. Uncommented options override the + # default value. + ++# This operating system owned /etc/default/sshd_config sets these new defaults: ++# ++# * "PasswordAuthentication no" - Prevent remote guessing of insecure local ++# passwords, public key authentication should be used instead. ++# ++# Create /etc/sshd_config instead if you want to configure sshd. ++ + #Port 22 + #AddressFamily any + #ListenAddress 0.0.0.0 +@@ -54,7 +61,7 @@ + #IgnoreRhosts yes + + # To disable tunneled clear text passwords, change to no here! +-#PasswordAuthentication yes ++PasswordAuthentication no # upstream openssh: yes + #PermitEmptyPasswords no + + # Change to no to disable s/key passwords +diff -Paur --no-dereference -- ssh.upstream/ssh-keygen.1 ssh/ssh-keygen.1 +--- ssh.upstream/ssh-keygen.1 ++++ ssh/ssh-keygen.1 +@@ -214,7 +214,7 @@ + .Pa ~/.ssh/id_rsa . + Additionally, the system administrator may use this to generate host keys, + as seen in +-.Pa /etc/rc . ++.Xr init 8 . + .Pp + Normally this program generates the key and asks for a file in which + to store the private key. +@@ -280,7 +280,7 @@ + has also been specified, its argument is used as a prefix to the + default path for the resulting host key files. + This is used by +-.Pa /etc/rc ++.Xr init 8 + to generate new host keys. + .It Fl a Ar rounds + When saving a private key, this option specifies the number of KDF +diff -Paur --no-dereference -- ssh.upstream/ssh-keygen.c ssh/ssh-keygen.c +--- ssh.upstream/ssh-keygen.c ++++ ssh/ssh-keygen.c +@@ -829,7 +829,7 @@ + } + sshkey_free(prv); + free(comment); +- exit(0); ++ return; + } + + static void +@@ -987,7 +987,7 @@ + free(line); + fclose(f); + fingerprint_private(path); +- exit(0); ++ return; + } + + /* +@@ -1035,7 +1035,7 @@ + + if (invalid) + fatal("%s is not a public key file.", path); +- exit(0); ++ return; + } + + static void +@@ -1075,14 +1075,32 @@ + + /* Check whether private key exists and is not zero-length */ + if (stat(prv_file, &st) == 0) { +- if (st.st_size != 0) ++ /* PATCH: ssh-keygen -Al lists the generated host keys. */ ++ if (st.st_size != 0) { ++ if (print_fingerprint || print_bubblebabble) { ++ char saved[sizeof(identity_file)]; ++ memcpy(saved, identity_file, sizeof(identity_file)); ++ if (strlcpy(identity_file, key_types[i].path, ++ sizeof(identity_file)) >= sizeof(identity_file)) ++ fatal("Identity filename too long"); ++ if (strlcat(identity_file, ".pub", ++ sizeof(identity_file)) >= sizeof(identity_file)) ++ fatal("Identity filename too long"); ++ have_identity = 1; ++ do_fingerprint(pw); ++ memcpy(identity_file, saved, sizeof(identity_file)); ++ } + goto next; ++ } + } else if (errno != ENOENT) { + error("Could not stat %s: %s", key_types[i].path, + strerror(errno)); + goto failnext; + } + ++ if (print_fingerprint || print_bubblebabble) ++ goto next; ++ + /* + * Private key doesn't exist or is invalid; proceed with + * key generation. +@@ -3347,7 +3365,7 @@ + /* we need this for the home * directory. */ + pw = getpwuid(getuid()); + if (!pw) +- fatal("No user exists for uid %lu", (u_long)getuid()); ++ fatal("No user exists for uid %ju", (uintmax_t)getuid()); + pw = pwcopy(pw); + if (gethostname(hostname, sizeof(hostname)) == -1) + fatal("gethostname: %s", strerror(errno)); +@@ -3703,8 +3721,10 @@ + } + return do_download_sk(sk_provider, sk_device); + } +- if (print_fingerprint || print_bubblebabble) ++ if ((print_fingerprint || print_bubblebabble) && !gen_all_hostkeys) { + do_fingerprint(pw); ++ return (0); ++ } + if (change_passphrase) + do_change_passphrase(pw); + if (change_comment) +diff -Paur --no-dereference -- ssh.upstream/ssh-keyscan.c ssh/ssh-keyscan.c +--- ssh.upstream/ssh-keyscan.c ++++ ssh/ssh-keyscan.c +@@ -35,6 +35,11 @@ + #include + #include + ++#if defined(__sortix__) && !defined(timercmp) ++#define timercmp(s,t,op) ((s)->tv_sec == (t)->tv_sec ? \ ++ (s)->tv_usec op (t)->tv_usec : (s)->tv_sec op (t)->tv_sec) ++#endif ++ + #include "xmalloc.h" + #include "ssh.h" + #include "sshbuf.h" +@@ -54,6 +59,14 @@ + #include "dns.h" + #include "addr.h" + ++#if defined(__sortix__) && !defined(timerclear) ++static inline void timerclear(struct timeval *tvp) ++{ ++ tvp->tv_sec = 0; ++ tvp->tv_usec = 0; ++} ++#endif ++ + /* Flag indicating whether IPv4 or IPv6. This can be set on the command line. + Default value is AF_UNSPEC means both IPv4 and IPv6. */ + int IPv4or6 = AF_UNSPEC; +diff -Paur --no-dereference -- ssh.upstream/ssh-keysign.c ssh/ssh-keysign.c +--- ssh.upstream/ssh-keysign.c ++++ ssh/ssh-keysign.c +@@ -184,6 +184,7 @@ + u_char *signature, *data, rver; + char *host, *fp, *pkalg; + size_t slen, dlen; ++ const char* host_config_file = _PATH_HOST_CONFIG_FILE; + + if (pledge("stdio rpath getpw dns id", NULL) != 0) + fatal("%s: pledge: %s", __progname, strerror(errno)); +@@ -217,12 +218,15 @@ + + /* verify that ssh-keysign is enabled by the admin */ + initialize_options(&options); +- (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, "", "", ++ /* PATCH: Use /etc/default for the default configuration. */ ++ if (access(host_config_file, F_OK) < 0) ++ host_config_file = _PATH_HOST_CONFIG_DEFAULT; ++ (void)read_config_file(host_config_file, pw, "", "", + &options, 0, NULL); + (void)fill_default_options(&options); + if (options.enable_ssh_keysign != 1) + fatal("ssh-keysign not enabled in %s", +- _PATH_HOST_CONFIG_FILE); ++ host_config_file); + + if (pledge("stdio dns", NULL) != 0) + fatal("%s: pledge: %s", __progname, strerror(errno)); +diff -Paur --no-dereference -- ssh.upstream/sshpty.c ssh/sshpty.c +--- ssh.upstream/sshpty.c ++++ ssh/sshpty.c +@@ -194,12 +194,12 @@ + if (chown(tty, pw->pw_uid, gid) == -1) { + if (errno == EROFS && + (st.st_uid == pw->pw_uid || st.st_uid == 0)) +- debug("chown(%.100s, %u, %u) failed: %.100s", +- tty, (u_int)pw->pw_uid, (u_int)gid, ++ debug("chown(%.100s, %ju, %ju) failed: %.100s", ++ tty, (uintmax_t)pw->pw_uid, (uintmax_t)gid, + strerror(errno)); + else +- fatal("chown(%.100s, %u, %u) failed: %.100s", +- tty, (u_int)pw->pw_uid, (u_int)gid, ++ fatal("chown(%.100s, %ju, %ju) failed: %.100s", ++ tty, (uintmax_t)pw->pw_uid, (uintmax_t)gid, + strerror(errno)); + } + } +diff -Paur --no-dereference -- ssh.upstream/uidswap.c ssh/uidswap.c +--- ssh.upstream/uidswap.c ++++ ssh/uidswap.c +@@ -28,6 +28,35 @@ + #include "uidswap.h" + #include "xmalloc.h" + ++#if defined(__sortix__) && !defined(__SORTIX_HAS_UID_SECURITY__) ++#define NO_UID_RESTORATION_TEST ++#endif ++ ++#if defined(__sortix__) && !defined(__SORTIX_HAS_INITGROUPS__) ++static inline int initgroups(const char *user, gid_t group) ++{ ++ (void)user; ++ (void)group; ++ return 0; ++} ++#endif ++#if defined(__sortix__) && !defined(__SORTIX_HAS_GETGROUPS__) ++static inline int getgroups(int size, gid_t list[]) ++{ ++ (void)size; ++ (void)list; ++ return 0; ++} ++#endif ++#if defined(__sortix__) && !defined(__SORTIX_HAS_SETGROUPS__) ++static inline int setgroups(size_t size, const gid_t *list) ++{ ++ (void)size; ++ (void)list; ++ return 0; ++} ++#endif ++ + /* + * Note: all these functions must work in all of the following cases: + * 1. euid=0, ruid=0 +@@ -64,9 +93,9 @@ + #ifdef SAVED_IDS_WORK_WITH_SETEUID + saved_euid = geteuid(); + saved_egid = getegid(); +- debug("temporarily_use_uid: %u/%u (e=%u/%u)", +- (u_int)pw->pw_uid, (u_int)pw->pw_gid, +- (u_int)saved_euid, (u_int)saved_egid); ++ debug("temporarily_use_uid: %ju/%ju (e=%ju/%ju)", ++ (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid, ++ (uintmax_t)saved_euid, (uintmax_t)saved_egid); + #ifndef HAVE_CYGWIN + if (saved_euid != 0) { + privileged = 0; +@@ -122,16 +151,16 @@ + #ifndef SAVED_IDS_WORK_WITH_SETEUID + /* Propagate the privileged gid to all of our gids. */ + if (setgid(getegid()) == -1) +- debug("setgid %u: %.100s", (u_int) getegid(), strerror(errno)); ++ debug("setgid %ju: %.100s", (uintmax_t) getegid(), strerror(errno)); + /* Propagate the privileged uid to all of our uids. */ + if (setuid(geteuid()) == -1) +- debug("setuid %u: %.100s", (u_int) geteuid(), strerror(errno)); ++ debug("setuid %ju: %.100s", (uintmax_t) geteuid(), strerror(errno)); + #endif /* SAVED_IDS_WORK_WITH_SETEUID */ + if (setegid(pw->pw_gid) == -1) +- fatal("setegid %u: %.100s", (u_int)pw->pw_gid, ++ fatal("setegid %ju: %.100s", (uintmax_t)pw->pw_gid, + strerror(errno)); + if (seteuid(pw->pw_uid) == -1) +- fatal("seteuid %u: %.100s", (u_int)pw->pw_uid, ++ fatal("seteuid %ju: %.100s", (uintmax_t)pw->pw_uid, + strerror(errno)); + } + +@@ -150,12 +179,12 @@ + fatal("restore_uid: temporarily_use_uid not effective"); + + #ifdef SAVED_IDS_WORK_WITH_SETEUID +- debug("restore_uid: %u/%u", (u_int)saved_euid, (u_int)saved_egid); ++ debug("restore_uid: %ju/%ju", (uintmax_t)saved_euid, (uintmax_t)saved_egid); + /* Set the effective uid back to the saved privileged uid. */ + if (seteuid(saved_euid) == -1) +- fatal("seteuid %u: %.100s", (u_int)saved_euid, strerror(errno)); ++ fatal("seteuid %ju: %.100s", (uintmax_t)saved_euid, strerror(errno)); + if (setegid(saved_egid) == -1) +- fatal("setegid %u: %.100s", (u_int)saved_egid, strerror(errno)); ++ fatal("setegid %ju: %.100s", (uintmax_t)saved_egid, strerror(errno)); + #else /* SAVED_IDS_WORK_WITH_SETEUID */ + /* + * We are unable to restore the real uid to its unprivileged value. +@@ -189,11 +218,11 @@ + fatal("permanently_set_uid: no user given"); + if (temporarily_use_uid_effective) + fatal("permanently_set_uid: temporarily_use_uid effective"); +- debug("permanently_set_uid: %u/%u", (u_int)pw->pw_uid, +- (u_int)pw->pw_gid); ++ debug("permanently_set_uid: %ju/%ju", (uintmax_t)pw->pw_uid, ++ (uintmax_t)pw->pw_gid); + + if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1) +- fatal("setresgid %u: %.100s", (u_int)pw->pw_gid, strerror(errno)); ++ fatal("setresgid %ju: %.100s", (uintmax_t)pw->pw_gid, strerror(errno)); + + #ifdef __APPLE__ + /* +@@ -201,12 +230,12 @@ + * memberd support for >16 supplemental groups. + */ + if (initgroups(pw->pw_name, pw->pw_gid) == -1) +- fatal("initgroups %.100s %u: %.100s", +- pw->pw_name, (u_int)pw->pw_gid, strerror(errno)); ++ fatal("initgroups %.100s %ju: %.100s", ++ pw->pw_name, (uintmax_t)pw->pw_gid, strerror(errno)); + #endif + + if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) +- fatal("setresuid %u: %.100s", (u_int)pw->pw_uid, strerror(errno)); ++ fatal("setresuid %ju: %.100s", (uintmax_t)pw->pw_uid, strerror(errno)); + + #ifndef NO_UID_RESTORATION_TEST + /* Try restoration of GID if changed (test clearing of saved gid) */ +@@ -217,9 +246,9 @@ + + /* Verify GID drop was successful */ + if (getgid() != pw->pw_gid || getegid() != pw->pw_gid) { +- fatal("%s: egid incorrect gid:%u egid:%u (should be %u)", +- __func__, (u_int)getgid(), (u_int)getegid(), +- (u_int)pw->pw_gid); ++ fatal("%s: egid incorrect gid:%ju egid:%ju (should be %ju)", ++ __func__, (uintmax_t)getgid(), (uintmax_t)getegid(), ++ (uintmax_t)pw->pw_gid); + } + + #ifndef NO_UID_RESTORATION_TEST +@@ -231,8 +260,8 @@ + + /* Verify UID drop was successful */ + if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) { +- fatal("%s: euid incorrect uid:%u euid:%u (should be %u)", +- __func__, (u_int)getuid(), (u_int)geteuid(), +- (u_int)pw->pw_uid); ++ fatal("%s: euid incorrect uid:%ju euid:%ju (should be %ju)", ++ __func__, (uintmax_t)getuid(), (uintmax_t)geteuid(), ++ (uintmax_t)pw->pw_uid); + } + } diff --git a/ports/ssh/ssh.port b/ports/ssh/ssh.port new file mode 100644 index 00000000..ce79376a --- /dev/null +++ b/ports/ssh/ssh.port @@ -0,0 +1,12 @@ +NAME=ssh +BUILD_LIBRARIES='libz libssl' +VERSION=9.2p1 +DISTNAME=openssh-$VERSION +COMPRESSION=tar.gz +ARCHIVE=$DISTNAME.$COMPRESSION +SHA256SUM=3f66dbf1655fb45f50e1c56da62ab01218c228807b21338d634ebcdf9d71cf46 +UPSTREAM_SITE=https://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable +UPSTREAM_ARCHIVE=$ARCHIVE +LICENSE='SSH-OpenSSH AND BSD-2-Clause AND BSD-3-Clause AND ISC AND MIT' +BUILD_SYSTEM=configure +VERSION_REGEX='([0-9]+\.[0-9]+p[0-9]+)' diff --git a/share/man/man7/installation.7 b/share/man/man7/installation.7 index 4f9b82a6..43800b00 100644 --- a/share/man/man7/installation.7 +++ b/share/man/man7/installation.7 @@ -79,11 +79,16 @@ Optionally, you might want to modify a release .iso to meet your custom needs per the instructions in .Xr release-iso-modification 7 . .Pp +If you want to ssh into your installation, it's recommended to amend the +installation .iso with your public key, pregenerated the server keys and obtain +fingerprints, and seed randomness using this procedure. +.Pp The release modification procedure lets you customize aspects such as the default bootloader menu option and timeout, the default hostname, the default keyboard layout, the default graphics resolution, adding files of your choice to the live environment, control which drivers are loaded by default, control which -live environment daemons are started by default, and so on. +live environment daemons are started by default, deploy ssh keys so secure shell +connections are trusted on the first connection, and so on. .Pp Warning: The live environment does not come with any random entropy and entropy gathering is not yet implemented. @@ -358,6 +363,8 @@ A root account is made in and .Xr group 5 . This question is skipped if the root account already exists. +If the live environment's root user has ssh keys and configuration, you will be +asked whether you'd like to copy the files to the new installation. .Ss Users You will be asked in a loop if you wish to make another user. Answer @@ -378,6 +385,25 @@ and .Pp Please note that Sortix is not currently secure as a multi-user system and filesystem permissions are not enforced. +.Ss SSH Server +You will be asked if you want to enable a +.Xr sshd 8 +server for remotely logging into this machine over a secure cryptographic +channel. +Answer no if in doubt as anyone who obtains your credentials will be able to +connect to your computer and log in as you. +Password authentication is disabled by default as public key cryptography should +be used for security, but if you have a very strong password, you could enable +it when asked. +It's recommended to securely bootstrap ssh authentication using the +.Xr release-iso-modification 7 +procedure to amend the installation medium with your public key, pregenerated +server private keys, and a random seed. +You are using a bad workflow if you encounter a ssh server fingerprint check. +If the installer environment contains a +.Xr sshd_config +or private sshd keys, then you will be asked if you want to copy the files into +the new installation. .Ss Completion This will complete the operating system installation. Upon reboot, the new system will start normally. diff --git a/share/man/man7/release-iso-bootconfig.7 b/share/man/man7/release-iso-bootconfig.7 index a44078f5..ce1db588 100644 --- a/share/man/man7/release-iso-bootconfig.7 +++ b/share/man/man7/release-iso-bootconfig.7 @@ -279,6 +279,11 @@ Whether to load the source code initrd containing .Pa /src . (Default: .Sy true ) +.It Sy enable_sshd +Whether to start the +.Xr sshd 8 +daemon. +(Default: false) .It Sy machine The machine type this release was built for. .It Sy menu_title diff --git a/share/man/man7/release-iso-modification.7 b/share/man/man7/release-iso-modification.7 index 06ed566b..b6ac0d1b 100644 --- a/share/man/man7/release-iso-modification.7 +++ b/share/man/man7/release-iso-modification.7 @@ -17,7 +17,8 @@ The release modification procedure lets you customize aspects such as the default bootloader menu option and timeout, the default hostname, the default keyboard layout, the default graphics resolution, adding files of your choice to the live environment, control which drivers are loaded by default, control which -live environment daemons are started by default, and so on. +live environment daemons are started by default, deploy ssh keys so secure shell +connections are trusted on the first connection, and so on. .Ss Prerequisites .Bl -bullet -compact .It @@ -408,6 +409,110 @@ wants to manually configure network interfaces with tix-iso-bootconfig --disable-dhclient bootconfig tix-iso-add sortix.iso bootconfig .Ed +.Ss Enable SSH Server By Default +To customize a release so it starts the SSH server +.Xr sshd 8 +automatically using the SSH configuration found in the liveconfig directory: +.Bd -literal +tix-iso-bootconfig --liveconfig=liveconfig --enable-sshd bootconfig +tix-iso-add sortix.iso bootconfig +.Ed +.Ss SSH Into Live Environment +To customize the live environment of a release so you can ssh into its root +user, to have the hostname +.Sy example.com , +to start a ssh server with the keys generated now, authorize the local user to +ssh into the live environment's root user, and register the sshd server's keys +by their hostnames and network addresses so the connection is trusted on the +first attempt (you can omit the network addresses if you don't know yet): +.Bd -literal +tix-iso-liveconfig \\ + --hostname=example.com \\ + --root-ssh-authorized-keys="$HOME/.ssh/id_rsa.pub" \\ + --sshd-keygen \\ + --sshd-key-known-hosts-file="$HOME/.ssh/known_hosts" \\ + --sshd-key-known-hosts-hosts="example.com example.com,192.0.2.1 192.0.2.1" \\ + liveconfig +tix-iso-bootconfig --liveconfig=liveconfig --enable-sshd bootconfig +tix-iso-add sortix.iso bootconfig +rm -f liveconfig/etc/ssh_host_*_key # When no longer useful. +rm -f bootconfig/boot/liveconfig.xz # When no longer useful. +rm -f sortix.iso # When no longer useful. +# And erase any media made from sortix.iso when no longer useful. +ssh root@example.org # When the system is running. +.Ed +.Pp +This example generates sshd private keys (remember to delete them when no longer +needed, see the warnings in +.Xr tix-iso-liveconfig 8 ) +and shows them by running: +.Bd -literal +mkdir -p liveconfig/etc +for keytype in rsa ecdsa ed25519; do + ssh-keygen -t $keytype -f liveconfig/etc/ssh_host_${keytype}_key" -N "" \\ + -C "root@$hostname" +done +for keytype in rsa ecdsa ed25519; do + ssh-keygen -l -f liveconfig/etc/ssh_host_${keytype}_key +done +.Ed +.Pp +It then constructs a +.Pa known_hosts +file for each network address and hashes it. +.Bd -literal +(for host in $network_addresses; do + for keytype in rsa ecdsa ed25519; do + printf '%s ' "$host" && + sed -E 's/^([^ ]* [^ ]*).*/\1/' \\ + liveconfig/etc/ssh_host_${keytype}_key.pub + done +done) > known_hosts +ssh-keygen -H -f known_hosts +rm -f known_hosts.old +.Ed +.Pp +.Xr ssh 1 +will trust the server by the network addresses on the first connection if you +append the contents of +.Pa known_hosts +to your +.Pa ~/.ssh/known_hosts . +.Pa liveconfig/root/.ssh/authorized_keys +file is made by appending the appropriate public keys previously made with +.Xr ssh-keygen 1 . +.Ss SSH Back From Live Environment +To customize the live environment of a release so its root user can ssh back to +your user, where the local hostname is +.Sy example.com +(the address to which the new installation will be connecting), by +generating a private key for the root user +(remember to delete it when no longer +needed, see the warnings in +.Xr tix-iso-liveconfig 8 ) +and adding its public key to your local +.Pa ~/.ssh/authorized_keys : +.Bd -literal +tix-iso-liveconfig --root-ssh-keygen liveconfig +ssh-keyscan -H example.com > liveconfig/root/.ssh/known_hosts +cat liveconfig/root/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys +tix-iso-bootconfig --liveconfig=liveconfig --enable-sshd bootconfig +tix-iso-add sortix.iso bootconfig +rm -f output-directory/root/.ssh/id_rsa # When no longer useful. +rm -f bootconfig/boot/liveconfig.xz # When no longer useful. +rm -f sortix.iso # When no longer useful. +# And erase any media made from sortix.iso when no longer useful. +.Ed +.Pp +This example will generate a ssh key for the root user by running: +.Bd -literal +mkdir -p -m 700 liveconfig/root/.ssh +ssh-keygen -t rsa -f liveconfig/root/.ssh/id_rsa -N "" -C "root@$hostname" +.Ed +.Pp +Consider omitting the +.Fl N +option and password protect the private key to protect it in the case of a leak. .Sh SEE ALSO .Xr xorriso 1 , .Xr development 7 , diff --git a/share/man/man7/upgrade.7 b/share/man/man7/upgrade.7 index f9181efd..43781ab7 100644 --- a/share/man/man7/upgrade.7 +++ b/share/man/man7/upgrade.7 @@ -30,7 +30,8 @@ The release modification procedure lets you customize aspects such as the default bootloader menu option and timeout, the default hostname, the default keyboard layout, the default graphics resolution, adding files of your choice to the live environment, control which drivers are loaded by default, control which -live environment daemons are started by default, and so on. +live environment daemons are started by default, deploy ssh keys so secure shell +connections are trusted on the first connection, and so on. .Pp Warning: The live environment does not come with any random entropy and entropy gathering is not yet implemented. diff --git a/sysinstall/sysinstall.c b/sysinstall/sysinstall.c index f35589fb..cffc42be 100644 --- a/sysinstall/sysinstall.c +++ b/sysinstall/sysinstall.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, 2020, 2021 Jonas 'Sortie' Termansen. + * Copyright (c) 2015, 2016, 2020, 2021, 2022, 2023 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -439,6 +439,14 @@ int main(void) "impatient. Take the time to read the documentation and be patient " "while you learn the new system. This is a very good time to start an " "external music player that plays soothing classical music on loop.\n\n"); + + if ( !access_or_die("/tix/tixinfo/ssh", F_OK) && + access_or_die("/root/.ssh/authorized_keys", F_OK) < 0 ) + text("If you wish to ssh into your new installation, it's recommended " + "to first add your public keys to the .iso and obtain " + "fingerprints per release-iso-modification(7) before installing." + "\n\n"); + const char* readies[] = { "Ready", @@ -1026,6 +1034,56 @@ int main(void) textf("Group '%s' added to /etc/group.\n", "root"); break; } + + struct ssh_file + { + const char* key; + const char* path; + const char* pub; + }; + const struct ssh_file ssh_files[] = + { + {"copy_ssh_authorized_keys_root", "/root/.ssh/authorized_keys", NULL}, + {"copy_ssh_config_root", "/root/.ssh/config", NULL}, + {"copy_ssh_id_rsa_root", "/root/.ssh/id_rsa", "/root/.ssh/id_rsa.pub"}, + {"copy_ssh_known_hosts_root", "/root/.ssh/known_hosts", NULL}, + }; + size_t ssh_files_count = sizeof(ssh_files) / sizeof(ssh_files[0]); + bool any_ssh_keys = false; + for ( size_t i = 0; i < ssh_files_count; i++ ) + { + const struct ssh_file* file = &ssh_files[i]; + if ( access_or_die(file->path, F_OK) < 0 ) + continue; + text("\n"); + textf("Found %s\n", file->path); + if ( file->pub && !access_or_die(file->pub, F_OK) ) + textf("Found %s\n", file->pub); + while ( true ) + { + char question[256]; + snprintf(question, sizeof(question), + "Copy %s from installer environment? (yes/no)", + file->path); + prompt(input, sizeof(input), file->key, question, "yes"); + if ( strcasecmp(input, "no") == 0 ) + break; + if ( strcasecmp(input, "yes") != 0 ) + continue; + mkdir_or_chmod_or_die("root/.ssh", 0700); + textf("Copying %s -> %s\n", file->path, file->path + 1); + execute((const char*[]) + {"cp", file->path, file->path+ 1, NULL }, "f"); + if ( file->pub ) + { + textf("Copying %s -> %s\n", file->pub, file->pub + 1); + execute((const char*[]) + {"cp", file->pub, file->pub + 1, NULL }, "f"); + } + any_ssh_keys = true; + break; + } + } text("\n"); if ( mkdir("etc/init", 0755) < 0 ) @@ -1131,6 +1189,171 @@ int main(void) // TODO: Ask if networking should be disabled / enabled. + struct sshd_key_file + { + const char* pri; + const char* pub; + }; + const struct sshd_key_file sshd_key_files[] = + { + {"/etc/ssh_host_ecdsa_key", "/etc/ssh_host_ecdsa_key.pub"}, + {"/etc/ssh_host_ed25519_key", "/etc/ssh_host_ed25519_key.pub"}, + {"/etc/ssh_host_rsa_key", "/etc/ssh_host_rsa_key.pub"}, + }; + size_t sshd_key_files_count + = sizeof(sshd_key_files) / sizeof(sshd_key_files[0]); + bool any_sshd_keys = false; + for ( size_t i = 0; i < sshd_key_files_count; i++ ) + { + if ( !access_or_die(sshd_key_files[i].pri, F_OK) ) + { + textf("Found %s\n", sshd_key_files[i].pri); + any_sshd_keys = true; + } + } + + bool enabled_sshd = false; + if ( !access_or_die("/tix/tixinfo/ssh", F_OK) ) + { + text("A ssh server has been installed. You have the option of starting " + "it on boot to allow remote login over a cryptographically secure " + "channel. Answer no if you don't know what ssh is.\n\n"); + if ( !any_sshd_keys ) + text("Warning: " BRAND_DISTRIBUTION_NAME " does not yet collect " + "entropy for secure random numbers. Unless you type '!' and " + "escape to a shell and put 256 bytes of actual randomness " + "into boot/random.seed, the first boot will use the " + "randomness of this installer environment to generate ssh " + "keys. This initial randomness may be as weak as the wall " + "time when you booted the installer, which is easily guessed " + "by an attacker. The same warning applies to outgoing secure " + "connections as well.\n\n"); + bool might_want_sshd = + any_ssh_keys || + any_sshd_keys || + !access_or_die("/etc/sshd_config", F_OK); + while ( true ) + { + prompt(input, sizeof(input), "enable_ssh", + "Enable ssh server? (yes/no)", + might_want_sshd ? "yes" : "no"); + if ( strcasecmp(input, "no") == 0 ) + break; + if ( strcasecmp(input, "yes") != 0 ) + continue; + if ( !install_configurationf("etc/init/local", "a", + "require sshd optional\n") ) + { + warn("etc/init/local"); + continue; + } + enabled_sshd = true; + text("Added 'require sshd optional' to /etc/init/local\n"); + text("The ssh server will be started when the system boots.\n"); + break; + } + text("\n"); + } + + bool has_sshd_config = false; + if ( !access_or_die("/etc/sshd_config", F_OK) ) + { + while ( true ) + { + prompt(input, sizeof(input), "copy_sshd_config", + "Copy /etc/sshd_config from installer environment? (yes/no)", + "yes"); + if ( strcasecmp(input, "no") == 0 ) + break; + if ( strcasecmp(input, "yes") != 0 ) + continue; + const char* file = "/etc/sshd_config"; + textf("Copying %s -> %s\n", file, file + 1); + execute((const char*[]) {"cp", file, file + 1}, "f"); + has_sshd_config = true; + break; + } + text("\n"); + } + + if ( enabled_sshd && !has_sshd_config ) + { + text("Password authentication has been disabled by default in sshd to " + "prevent remotely guessing insecure passwords. The recommended " + "approach is to put your public key in the installation .iso and " + "generate the sshd credentials ahead of time as documented in " + "release-iso-modification(7). However, you could enable password " + "authentication if you picked a very strong password.\n\n"); + bool enable_sshd_password = false; + while ( true ) + { + prompt(input, sizeof(input), "enable_ssh_password", + "Enable sshd password authentication? (yes/no)", "no"); + if ( strcasecmp(input, "no") == 0 ) + break; + if ( strcasecmp(input, "yes") != 0 ) + continue; + if ( !install_configurationf("etc/sshd_config", "a", + "PasswordAuthentication yes\n") ) + { + warn("etc/sshd_config"); + continue; + } + enable_sshd_password = true; + text("Added 'PasswordAuthentication yes' to /etc/sshd_config\n"); + break; + } + while ( enable_sshd_password ) + { + prompt(input, sizeof(input), "enable_ssh_root_password", + "Enable sshd password authentication for root? (yes/no)", + "no"); + if ( strcasecmp(input, "no") == 0 ) + break; + if ( strcasecmp(input, "yes") != 0 ) + continue; + if ( !install_configurationf("etc/sshd_config", "a", + "PermitRootLogin yes\n") ) + { + warn("etc/sshd_config"); + continue; + } + text("Added 'PermitRootLogin yes' to /etc/sshd_config\n"); + break; + } + text("\n"); + } + + if ( any_sshd_keys ) + { + while ( true ) + { + const char* question = + "Copy sshd private keys from installer environment? (yes/no)"; + prompt(input, sizeof(input), "copy_sshd_private_keys", + question, + "yes"); + if ( strcasecmp(input, "no") == 0 ) + break; + if ( strcasecmp(input, "yes") != 0 ) + continue; + for ( size_t i = 0; i < sshd_key_files_count; i++ ) + { + const struct sshd_key_file* file = &sshd_key_files[i]; + if ( access_or_die(file->pri, F_OK) < 0 ) + continue; + textf("Copying %s -> %s\n", file->pri, file->pri + 1); + execute((const char*[]) + {"cp", file->pri, file->pri + 1, NULL }, "f"); + textf("Copying %s -> %s\n", file->pub, file->pub + 1); + execute((const char*[]) + {"cp", file->pub, file->pub + 1, NULL }, "f"); + } + break; + } + text("\n"); + } + text("It's time to boot into the newly installed system.\n\n"); if ( strcasecmp(accept_grub, "no") == 0 ) diff --git a/tix/tix-iso-bootconfig b/tix/tix-iso-bootconfig index 71f0be9b..9303317a 100755 --- a/tix/tix-iso-bootconfig +++ b/tix/tix-iso-bootconfig @@ -25,6 +25,7 @@ enable_append_title=true enable_dhclient= enable_network_drivers= enable_src= +enable_sshd= init_target= liveconfig= operand=1 @@ -56,10 +57,12 @@ for argument do --disable-dhclient) enable_dhclient=false ;; --disable-network-drivers) enable_network_drivers=false ;; --disable-src) enable_src=false ;; + --disable-sshd) enable_sshd=false ;; --enable-append-title) enable_append_title=true ;; --enable-dhclient) enable_dhclient=true ;; --enable-network-drivers) enable_network_drivers=true ;; --enable-src) enable_src=true ;; + --enable-sshd) enable_sshd=true ;; --init-target=*) init_target=$parameter ;; --init-target) previous_option=init_target ;; --liveconfig=*) liveconfig=$parameter ;; @@ -144,6 +147,7 @@ mkdir -p -- "$directory/boot/grub" print_enable_default_bool "$enable_dhclient" dhclient dhclient print_enable_default "$enable_network_drivers" network_drivers network-drivers print_enable_default_bool "$enable_src" src src + print_enable_default_bool "$enable_sshd" sshd sshd if $enable_append_title; then printf "base_menu_title=\"\$base_menu_title - \"'%s'\n" \ "$(printf '%s\n' "$append_title" | sed "s/'/'\\\\''/g")" diff --git a/tix/tix-iso-bootconfig.8 b/tix/tix-iso-bootconfig.8 index b40184d6..882e37ba 100644 --- a/tix/tix-iso-bootconfig.8 +++ b/tix/tix-iso-bootconfig.8 @@ -12,10 +12,12 @@ .Op Fl \-disable-dhclient .Op Fl \-disable-network-drivers .Op Fl \-disable-src +.Op Fl \-disable-sshd .Op Fl \-enable-append-title .Op Fl \-enable-dhclient .Op Fl \-enable-network-drivers .Op Fl \-enable-src +.Op Fl \-enable-sshd .Op Fl \-init-target Ns = Ns Ar target .Op Fl \-liveconfig Ns = Ns Ar liveconfig-directory .Op Fl \-random-seed @@ -116,6 +118,14 @@ by setting .Sy enable_src GRUB variable to .Sy false . +.It Fl \-disable-sshd +Disable automatically starting the ssh server by setting the +.Sy enable_sshd +GRUB variable to +.Sy false , +selecting the default behavior of not starting the +.Xr sshd 8 +daemon. .It Fl \-enable-append-title Enable appending " - " followed by the value set with .Fl \-append-title @@ -147,6 +157,14 @@ by setting .Sy enable_src GRUB variable to .Sy true . +.It Fl \-enable-sshd +Enable automatically starting the ssh server by setting the +.Sy enable_sshd +GRUB variable to +.Sy true , +causing the bootloader to load additional configuration that turns on the +.Xr sshd 8 +daemon on boot. .It Fl \-init-target Ns = Ns Ar target Add a new first menu entry that boots the .Ar target @@ -299,6 +317,14 @@ wants to manually configure network interfaces with tix-iso-bootconfig --disable-dhclient bootconfig tix-iso-add sortix.iso bootconfig .Ed +.Ss Enable SSH Server By Default +To customize a release so it starts the SSH server +.Xr sshd 8 +automatically using the SSH configuration found in the liveconfig directory: +.Bd -literal +tix-iso-bootconfig --liveconfig=liveconfig --enable-sshd bootconfig +tix-iso-add sortix.iso bootconfig +.Ed .Sh SEE ALSO .Xr xorriso 1 , .Xr kernel 7 , diff --git a/tix/tix-iso-liveconfig b/tix/tix-iso-liveconfig index 46305647..5b8d82a1 100755 --- a/tix/tix-iso-liveconfig +++ b/tix/tix-iso-liveconfig @@ -23,6 +23,15 @@ directory= hostname= kblayout= operand=1 +root_ssh_authorized_keys= +root_ssh_config= +root_ssh_keygen=false +root_ssh_known_hosts= +ssh_config= +sshd_config= +sshd_keygen=false +sshd_key_known_hosts_file= +sshd_key_known_hosts_hosts= videomode= dashdash= @@ -48,6 +57,22 @@ for argument do --hostname) previous_option=hostname ;; --kblayout=*) kblayout=$parameter ;; --kblayout) previous_option=kblayout ;; + --root-ssh-authorized-keys=*) root_ssh_authorized_keys=$parameter ;; + --root-ssh-authorized-keys) previous_option=root_ssh_authorized_keys ;; + --root-ssh-config=*) root_ssh_config=$parameter ;; + --root-ssh-config) previous_option=root_ssh_config ;; + --root-ssh-keygen) root_ssh_keygen=true ;; + --root-ssh-known-hosts=*) root_ssh_known_hosts=$parameter ;; + --root-ssh-known-hosts) previous_option=root_ssh_known_hosts ;; + --ssh-config=*) ssh_config=$parameter ;; + --ssh-config) previous_option=ssh_config ;; + --sshd-config=*) sshd_config=$parameter ;; + --sshd-config) previous_option=sshd_config ;; + --sshd-keygen) sshd_keygen=true ;; + --sshd-key-known-hosts-file=*) sshd_key_known_hosts_file=$parameter ;; + --sshd-key-known-hosts-file) previous_option=sshd_key_known_hosts_file ;; + --sshd-key-known-hosts-hosts=*) sshd_key_known_hosts_hosts=$parameter ;; + --sshd-key-known-hosts-hosts) previous_option=sshd_key_known_hosts_hosts ;; --videomode=*) videomode=$parameter ;; --videomode) previous_option=videomode ;; -*) echo "$0: unrecognized option $argument" >&2 @@ -101,3 +126,72 @@ if [ -n "$videomode" ]; then mkdir -p -- "$directory/etc" printf "%s\n" "$videomode" > "$directory/etc/videomode" fi + +if [ -n "$ssh_config" ]; then + mkdir -p -- "$directory/etc" + cp -- "$ssh_config" "$directory/etc/ssh_config" +fi + +if [ -n "$sshd_config" ]; then + mkdir -p -- "$directory/etc" + cp -- "$sshd_config" "$directory/etc/sshd_config" +fi + +if $sshd_keygen; then + mkdir -p -- "$directory/etc" + for keytype in rsa ecdsa ed25519; do + if [ ! -e "$directory/etc/ssh_host_${keytype}_key" ]; then + ssh-keygen -t $keytype -f "$directory/etc/ssh_host_${keytype}_key" -N "" \ + -C "root@$hostname" + fi + done + for keytype in rsa ecdsa ed25519; do + ssh-keygen -l -f "$directory/etc/ssh_host_${keytype}_key" + done +fi + +if [ -n "$sshd_key_known_hosts_file" ]; then + known_hosts_tmp=$(mktemp) + for host in $sshd_key_known_hosts_hosts; do + for keytype in rsa ecdsa ed25519; do + if [ ! -e "$directory/etc/ssh_host_${keytype}_key.pub" ]; then + continue + fi + (printf '%s ' "$host" && + sed -E 's/^([^ ]* [^ ]*).*/\1/' \ + "$directory/etc/ssh_host_${keytype}_key.pub") \ + >> "$known_hosts_tmp" + done + done + # TODO: ssh-keygen needs a standalone way to make such a hash. + ssh-keygen -H -f "$known_hosts_tmp" 1>/dev/null 2>/dev/null + cat -- "$known_hosts_tmp" >> "$sshd_key_known_hosts_file" + rm -f "$known_hosts_tmp" + rm -f "$known_hosts_tmp.old" +fi + +if [ -n "$root_ssh_authorized_keys" ]; then + mkdir -p -- "$directory/root" + mkdir -p -m 700 -- "$directory/root/.ssh" + cp -- "$root_ssh_authorized_keys" "$directory/root/.ssh/authorized_keys" +fi + +if [ -n "$root_ssh_config" ]; then + mkdir -p -- "$directory/root" + mkdir -p -m 700 -- "$directory/root/.ssh" + cp -- "$root_ssh_config" "$directory/root/.ssh/config" +fi + +if [ -n "$root_ssh_known_hosts" ]; then + mkdir -p -- "$directory/root" + mkdir -p -m 700 -- "$directory/root/.ssh" + cp -- "$root_ssh_known_hosts" "$directory/root/.ssh/known_hosts" +fi + +if $root_ssh_keygen; then + mkdir -p -- "$directory/root" + mkdir -p -m 700 -- "$directory/root/.ssh" + if [ ! -e "$directory/root/.ssh/id_rsa"]; then + ssh-keygen -t rsa -f "$directory/root/.ssh/id_rsa" -N "" -C "root@$hostname" + fi +fi diff --git a/tix/tix-iso-liveconfig.8 b/tix/tix-iso-liveconfig.8 index 05d2035f..3ff73c89 100644 --- a/tix/tix-iso-liveconfig.8 +++ b/tix/tix-iso-liveconfig.8 @@ -9,6 +9,15 @@ .Op Fl \-daemons Ns = Ns Ar daemons .Op Fl \-hostname Ns = Ns Ar hostname .Op Fl \-kblayout Ns = Ns Ar kblayout +.Op Fl \-root-ssh-authorized-keys Ns = Ns Ar file +.Op Fl \-root-ssh-config Ns = Ns Ar file +.Op Fl \-root-ssh-keygen +.Op Fl \-root-ssh-known-hosts Ns = Ns Ar file +.Op Fl \-ssh-config Ns = Ns Ar file +.Op Fl \-sshd-config Ns = Ns Ar file +.Op Fl \-sshd-keygen +.Op Fl \-sshd-key-known-hosts-file Ns = Ns Ar file +.Op Fl \-sshd-key-known-hosts-hosts Ns = Ns Ar host-list .Op Fl \-videomode Ns = Ns Ar videomode .Ar output-directory .Sh DESCRIPTION @@ -67,6 +76,151 @@ to .Pa output-directory/etc/kblayout . (See .Xr kblayout 5 ) +.It Fl \-root-ssh-authorized-keys Ns = Ns Ar file +Copy +.Ar file +to +.Pa output-directory/root/.ssh/authorized_keys +so it becomes root's list of authorized ssh keys. +.It Fl \-root-ssh-config Ns = Ns Ar file +Copy +.Ar file +to +.Pa output-directory/root/.ssh/config +so it becomes root's +.Xr ssh_config 5 . +.It Fl \-root-ssh-keygen +Generate a ssh private and public key pair for rsa (see the warnings below) at +.Pa output-directory/root/.ssh/id_rsa +and +.Pa output-directory/root/.ssh/id_rsa.pub . +These keys are not regenerated if they already exist. +The comment in the key uses the +.Fl \-hostname +option if set, otherwise it defaults to +.Sy sortix . +The key is not password protected. +.Pp +The key is generated by running: +.Bd -literal +ssh-keygen \\ + -t rsa \\ + -f "$output_directory/root/.ssh/id_rsa" \\ + -N "" \\ + -C "root@$hostname" +.Ed +.Pp +Warning: The information in the generated +.Pa output-directory/root/.ssh/id_rsa +private key must be kept confidential and should be securely erased whereever it +goes whenever it is no longer useful in a particular place, otherwise +unauthorized may be able to impersonate this user. +These keys should be reissued whenever a root user of a new installation should +be considered distinct from other installations using the same keys. +The installer will offer to copy the keys to the newly installed system. +Once the +.Ar output-directory +is no longer useful, the +.Pa output-directory/root/.ssh/id_rsa +file inside it should be securely erased. +If a bootconfig has been made whose liveconfig contains thes private key, +.Pa bootconfig/boot/liveconfig.xz +should be securely erased when no longer useful. +If a release .iso has been made from +.Ar output-directory , +it should be securely erased when no longer useful. +If a release .iso has been burned to a physical media, it should be securely +erased when no longer useful. +.It Fl \-root-ssh-known-hosts Ns = Ns Ar file +Copy +.Ar file +to +.Pa output-directory/root/.ssh/known_hosts +so it becomes root's list of known ssh hosts and their public keys. +.It Fl \-ssh-config Ns = Ns Ar file +Copy +.Ar file +to +.Pa output-directory/etc/ssh_config +so it becomes the +.Xr ssh_config 5 +of the live environment. +.It Fl \-sshd-config Ns = Ns Ar file +Copy +.Ar file +to +.Pa output-directory/etc/sshd_config +so it becomes the +.Xr sshd_config 5 +of the live environment. +.It Fl \-sshd-keygen +Generate sshd private keys for rsa, ecdsa, and ed25519 (see the below +warnings), but don't overwrite any existing keys in the +.Ar output-directory +directory. +The comment in the key uses the +.Fl \-hostname +option if set, otherwise it defaults to +.Sy sortix . +Each key is generated by running: +.Bd -literal +ssh-keygen \\ + -t $keytype \\ + -f "$output_directory/etc/ssh_host_${keytype}_key" \\ + -N "" \\ + -C "root@$hostname" +.Ed +.Pp +The fingerprints of each key is printed afterwards by running: +.Bd -literal +.Li ssh-keygen -l -f "$output_directory/etc/ssh_host_${keytype}_key" +.Ed +.Pp +Warning: The information in the generated +.Pa output_directory/etc/ssh_host_*_key +files must be kept confidential and should be securely erased whereever it goes +whenever it is no longer useful in a particular place, otherwise unauthorized +people may be able to impersonate the ssh server. +These keys should not be recycled to image more than a single system. +The installer will offer to copy the keys to the newly installed system. +Once the +.Ar output-directory +is no longer useful, the +.Pa output_directory/etc/ssh_host_*_key +files inside it should be securely erased. +If a bootconfig has been made whose liveconfig contains these keys, +.Pa bootconfig/boot/liveconfig.xz +should be securely erased when no longer useful. +If a release .iso has been made from +.Ar output-directory , +it should be securely erased when no longer useful. +If a release .iso has been burned to a physical media, it should be securely +erased when no longer useful. +.It Fl \-sshd-key-known-hosts-file Ns = Ns Ar file +Append the ssh known_hosts entries to +.Ar file +for the +.Pa output_directory/etc/ssh_host_*_key.pub +.Xr sshd 8 +keys for each hostname provided in the +.Fl \-sshd-key-known-hosts-hosts +option. +For each hostname, for each public key, a line is written to the +.Ar file +consisting of the hostname followed by a space and then followed by the public +key. +The written entries are then hashed so an attacker can't discover the hosts from +the known_hosts file, which is done by running +.Xr ssh-keygen 1 +with the +.Fl H +option on the produced file. +.It Fl \-sshd-key-known-hosts-hosts Ns = Ns Ar host-list +A space delimited list of hostnames, network addresses, and hostnames followed +by a comma and then the network address, which the sshd server will be +connectible by, used to generate the known_hosts entries in the +.Fl \-sshd-key-known-hosts-file +option. .It Fl \-videomode Ns = Ns Ar videomode Set the live environment's graphics resolution by writing .Ar videomode @@ -92,11 +246,55 @@ tix-iso-liveconfig \\ tix-iso-bootconfig --liveconfig=liveconfig bootconfig tix-iso-add sortix.iso bootconfig .Ed +.Ss SSH Into Live Environment +To customize the live environment of a release so you can ssh into its root +user, to have the hostname +.Sy example.com , +to start a ssh server with the keys generated now, authorize the local user to +ssh into the live environment's root user, and register the sshd server's keys +by their hostnames and network addresses so the connection is trusted on the +first attempt (you can omit the network addresses if you don't know yet): +.Bd -literal +tix-iso-liveconfig \\ + --hostname=example.com \\ + --root-ssh-authorized-keys="$HOME/.ssh/id_rsa.pub" \\ + --sshd-keygen \\ + --sshd-key-known-hosts-file="$HOME/.ssh/known_hosts" \\ + --sshd-key-known-hosts-hosts="example.com example.com,192.0.2.1 192.0.2.1" \\ + liveconfig +tix-iso-bootconfig --liveconfig=liveconfig --enable-sshd bootconfig +tix-iso-add sortix.iso bootconfig +rm -f liveconfig/etc/ssh_host_*_key # When no longer useful. +rm -f bootconfig/boot/liveconfig.xz # When no longer useful. +rm -f sortix.iso # When no longer useful. +# And erase any media made from sortix.iso when no longer useful. +ssh root@example.org # When the system is running. +.Ed +.Ss SSH Back From Live Environment +To customize the live environment of a release so its root user can ssh back to +your user, where the local hostname is +.Sy example.com +(the address to which the new installation will be connecting): +.Bd -literal +tix-iso-liveconfig --root-ssh-keygen liveconfig +ssh-keyscan -H example.com > liveconfig/root/.ssh/known_hosts +cat liveconfig/root/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys +tix-iso-bootconfig --liveconfig=liveconfig --enable-sshd bootconfig +tix-iso-add sortix.iso bootconfig +rm -f output-directory/root/.ssh/id_rsa # When no longer useful. +rm -f bootconfig/boot/liveconfig.xz # When no longer useful. +rm -f sortix.iso # When no longer useful. +# And erase any media made from sortix.iso when no longer useful. +.Ed .Sh SEE ALSO +.Xr ssh-keygen 1 , .Xr xorriso 1 , .Xr hostname 5 , .Xr kblayout 5 , +.Xr ssh_config 5 , +.Xr sshd_config 5 , .Xr videomode 5 , .Xr release-iso-modification 7 , +.Xr sshd 8 , .Xr tix-iso-add 8 , .Xr tix-iso-bootconfig 8