From c8ee260ebc92473cb5705562c80b4b786ab10c18 Mon Sep 17 00:00:00 2001 From: Ubuntu Developers Date: Wed, 10 Apr 2024 14:39:11 +0800 Subject: [PATCH] sshd-socket-generator Gbp-Pq: Name sshd-socket-generator.patch --- Makefile.in | 19 ++- sshd-socket-generator.c | 292 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 310 insertions(+), 1 deletion(-) create mode 100644 sshd-socket-generator.c diff --git a/Makefile.in b/Makefile.in index e0e45c9..2c6da4f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -17,6 +17,7 @@ srcdir=@srcdir@ top_srcdir=@top_srcdir@ abs_top_srcdir=@abs_top_srcdir@ abs_top_builddir=@abs_top_builddir@ +systemd_system_generator_dir=$(shell pkg-config --variable=systemd_system_generator_dir systemd) DESTDIR= VPATH=@srcdir@ @@ -69,7 +70,7 @@ MKDIR_P=@MKDIR_P@ .SUFFIXES: .lo -TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-sk-helper$(EXEEXT) +TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-sk-helper$(EXEEXT) sshd-socket-generator$(EXEEXT) XMSS_OBJS=\ ssh-xmss.o \ @@ -134,6 +135,16 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o \ sandbox-seccomp-filter.o sandbox-capsicum.o sandbox-pledge.o \ sandbox-solaris.o uidswap.o $(SKOBJS) +SSHD_SOCKET_GEN_OBJS=sshd-socket-generator.o \ + platform.o servconf.o groupaccess.o audit.o audit-linux.o \ + auth2.o auth2-none.o auth2-gss.o \ + auth2-passwd.o auth2-kbdint.o auth2-hostbased.o \ + auth-options.o uidswap.o auth2-pubkey.o auth.o \ + auth2-pubkeyfile.o auth-rhosts.o auth-passwd.o \ + gss-serv.o auth2-chall.o auth-pam.o gss-serv-krb5.o \ + loginrec.o auth-krb5.o auth-shadow.o \ + monitor.o monitor_wrap.o + SFTP_CLIENT_OBJS=sftp-common.o sftp-client.o sftp-glob.o SCP_OBJS= scp.o progressmeter.o $(SFTP_CLIENT_OBJS) @@ -210,6 +221,9 @@ ssh$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHOBJS) sshd$(EXEEXT): libssh.a $(LIBCOMPAT) $(SSHDOBJS) $(LD) -o $@ $(SSHDOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHDLIBS) $(LIBS) $(GSSLIBS) $(K5LIBS) $(CHANNELLIBS) +sshd-socket-generator$(EXEEXT): libssh.a $(LIBCOMPAT) $(SSHD_SOCKET_GEN_OBJS) + $(LD) -o $@ $(SSHD_SOCKET_GEN_OBJS) $(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) @@ -392,6 +406,7 @@ install-files: $(MKDIR_P) $(DESTDIR)$(mandir)/$(mansubdir)5 $(MKDIR_P) $(DESTDIR)$(mandir)/$(mansubdir)8 $(MKDIR_P) $(DESTDIR)$(libexecdir) + $(MKDIR_P) $(DESTDIR)$(systemd_system_generator_dir) $(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) @@ -421,6 +436,7 @@ install-files: $(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 0755 $(STRIP_OPT) sshd-socket-generator$(EXEEXT) $(DESTDIR)$(systemd_system_generator_dir)/sshd-socket-generator$(EXEEXT) install-sysconf: $(MKDIR_P) $(DESTDIR)$(sysconfdir) @@ -478,6 +494,7 @@ uninstall: -rm -f $(DESTDIR)$(bindir)/ssh-keyscan$(EXEEXT) -rm -f $(DESTDIR)$(bindir)/sftp$(EXEEXT) -rm -f $(DESTDIR)$(sbindir)/sshd$(EXEEXT) + -rm -f $(DESTDIR)$(systemd_system_generator_dir)/sshd-socket-generator$(EXEEXT) -rm -r $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) -rm -f $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT) -rm -f $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT) diff --git a/sshd-socket-generator.c b/sshd-socket-generator.c new file mode 100644 index 0000000..773ec31 --- /dev/null +++ b/sshd-socket-generator.c @@ -0,0 +1,292 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "includes.h" + +#include "hostfile.h" /* Needs to be included before auth.h */ +#include "auth.h" +#include "kex.h" +#include "log.h" +#include "misc.h" +#include "monitor.h" +#include "ssh-gss.h" /* Needs to be included before monitor_wrap.h */ +#include "monitor_wrap.h" +#include "pathnames.h" +#include "servconf.h" +#include "sshbuf.h" + +#define MAX_LISTEN_STREAMS (16) +#define MAX_LISTEN_STREAM_LEN (NI_MAXHOST + NI_MAXSERV + sizeof("ListenAddress=[:]") + 1) +typedef char listen_stream_set[MAX_LISTEN_STREAMS][MAX_LISTEN_STREAM_LEN]; + +/* Global variables required for sshd config parsing. */ +ServerOptions options = {}; +struct sshbuf *cfg = NULL; +struct include_list includes = TAILQ_HEAD_INITIALIZER(includes); + +/* Other global variables that are required for this to build, because of their + * use throughout the codebase. We do NOT use these variables for the + * generator. */ +Authctxt *the_authctxt = NULL; +int privsep_is_preauth = 1; +int use_privsep = -1; +struct monitor *pmonitor = NULL; +struct ssh *the_active_state = NULL; +struct sshauthopt *auth_opts = NULL; +struct sshbuf *loginmsg = NULL; + +static int listen_stream_set_append(listen_stream_set set, const char *listen_stream) { + size_t n; + + if (!set) + return -EINVAL; + + n = strnlen(listen_stream, MAX_LISTEN_STREAM_LEN); + if (n == MAX_LISTEN_STREAM_LEN) + return -EINVAL; + + for (int i = 0; i < MAX_LISTEN_STREAMS; i++) { + if (strcmp(set[i], listen_stream) == 0) + return 0; + + if (strnlen(set[i], MAX_LISTEN_STREAM_LEN) > 0) + continue; + + memcpy(set[i], listen_stream, n); + + return 0; + } + + return -E2BIG; +} + +static int listen_stream_set_len(listen_stream_set set) { + int r = 0; + + if (!set) + return 0; + + for (int i = 0; i < MAX_LISTEN_STREAMS; i++) { + if (strnlen(set[i], MAX_LISTEN_STREAM_LEN) > 0) + r++; + else + break; + } + + return r; +} + +static char *path_append(const char *base, const char *append) { + bool add_slash; + size_t n = 0, len_base, len_append; + char *path = NULL; + + len_base = strnlen(base, PATH_MAX); + len_append = strnlen(append, PATH_MAX); + add_slash = base[len_base - 1] != '/'; + + path = calloc(len_base + len_append + (add_slash ? 2 : 1), sizeof(char)); + if (!path) + return NULL; + + memcpy(path, base, len_base); + n += len_base; + + if (add_slash) + path[n++] = '/'; + + memcpy(path + n, append, len_append); + n += len_append; + path[n] = '\0'; + + return path; +} + +static int fflush_and_check(FILE *f) { + errno = 0; + fflush(f); + + if (ferror(f)) + return errno > 0 ? -errno : -EIO; + + return 0; +} + +static int write_systemd_socket_file(const char *destdir) { + listen_stream_set listen_streams = {}; + int num_listen_streams; + char *conf = NULL, *overridedir = NULL; + FILE *f = NULL; + int r; + + overridedir = path_append(destdir, "ssh.socket.d"); + if (!overridedir) { + r = -ENOMEM; + goto out; + } + + if (mkdir(overridedir, 0755) < 0 && errno != EEXIST) { + r = -errno; + goto out; + } + + conf = path_append(overridedir, "addresses.conf"); + if (!conf) { + r = -ENOMEM; + goto out; + } + + f = fopen(conf, "we"); + if (!f) { + r = -errno; + goto out; + } + + fprintf(f, + "# Automatically generated by sshd-socket-generator\n" + "\n[Socket]\n" + "ListenStream=\n"); + + for (u_int i = 0; i < options.num_listen_addrs; i++) { + for (struct addrinfo *ai = options.listen_addrs[i].addrs; ai; ai = ai->ai_next) { + char addr[NI_MAXHOST] = {}, port[NI_MAXSERV] = {}, + listen_stream[MAX_LISTEN_STREAM_LEN] = {}; + + r = getnameinfo(ai->ai_addr, ai->ai_addrlen, + addr, sizeof(addr), + port, sizeof(port), + NI_NUMERICHOST|NI_NUMERICSERV); + if (r != 0) { + fprintf(stderr, "%s\n", gai_strerror(r)); + r = r == EAI_SYSTEM ? -errno : -EINVAL; + goto out; + } + + if (strcmp(addr, "0.0.0.0") == 0 || strcmp(addr, "::") == 0) { + /* If ListenAddress is 0.0.0.0 or ::, only + * write the port in ListenStream=. */ + snprintf(listen_stream, + MAX_LISTEN_STREAM_LEN, + "ListenStream=%s", + port); + } else + snprintf(listen_stream, + MAX_LISTEN_STREAM_LEN, + "ListenStream=%s%s%s:%s", + ai->ai_family == AF_INET6 ? "[" : "", + addr, + ai->ai_family == AF_INET6 ? "]" : "", + port); + + r = listen_stream_set_append(listen_streams, listen_stream); + if (r < 0) + goto out; + } + } + + num_listen_streams = listen_stream_set_len(listen_streams); + + if (num_listen_streams <= 0) { + /* We didn't generate anything useful, so clean up and leave + * ssh.socket as-is. */ + r = -ENODATA; + goto out; + } + + if (num_listen_streams == 1 && strcmp(listen_streams[0], "ListenStream=22") == 0) { + /* This is the default already specified in ssh.socket. No need + * to write the override. */ + r = -ENODATA; + goto out; + } + + for (int i = 0; i < num_listen_streams; i++) + fprintf(f, "%s\n", listen_streams[i]); + + r = fflush_and_check(f); + if (r < 0) + goto out; + +out: + if (f) + fclose(f); + + if (r < 0) { + (void) remove(conf); + (void) remove(overridedir); + } + + free(overridedir); + free(conf); + + return r; +} + +static int parse_sshd_config_options() { + struct connection_info *connection_info; + + cfg = sshbuf_new(); + if (!cfg) + return -ENOMEM; + + initialize_server_options(&options); + load_server_config(_PATH_SERVER_CONFIG_FILE, cfg); + parse_server_config(&options, _PATH_SERVER_CONFIG_FILE, cfg, &includes, NULL, 0); + fill_default_server_options(&options); + + connection_info = get_connection_info(NULL, 0, 0); + connection_info->test = 1; + + parse_server_match_config(&options, &includes, connection_info); + + return 0; +} + +int main(int argc, char **argv) { + const char *destdir = NULL; + int r; + + if (argc < 2) { + fprintf(stderr, "Expected at least one argument.\n"); + + return EXIT_FAILURE; + } + + destdir = argv[1]; + + r = parse_sshd_config_options(); + if (r < 0) { + fprintf(stderr, "Faild to parse sshd config: %s\n", strerror(-r)); + + return EXIT_FAILURE; + } + + if (options.num_listen_addrs <= 0) { + /* No listen addresses configured? Don't generate anything. */ + fprintf(stderr, "No listen addresses configured. Will not generate anything.\n"); + + return EXIT_SUCCESS; + } + + r = write_systemd_socket_file(destdir); + if (r == -ENODATA) { + fprintf(stderr, "No custom listen addresses configured. Will not generated anything.\n"); + + return EXIT_SUCCESS; + } + if (r < 0) { + fprintf(stderr, "Failed to generate ssh.socket: %s\n", strerror(-r)); + + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +}