From dceabc7ad7ebc7769c8214a1647af64c9a1d92e5 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Thu, 5 Oct 2017 15:52:03 +0000 Subject: upstream commit replace statically-sized arrays in ServerOptions with dynamic ones managed by xrecallocarray, removing some arbitrary (though large) limits and saving a bit of memory; "much nicer" markus@ Upstream-ID: 1732720b2f478fe929d6687ac7b0a97ff2efe9d2 --- sshd.c | 54 ++++++++++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 30 deletions(-) (limited to 'sshd.c') diff --git a/sshd.c b/sshd.c index 51a1aaf6e..2ff385552 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.492 2017/09/12 06:32:07 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.493 2017/10/05 15:52:03 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -467,7 +467,7 @@ sshd_exchange_identification(struct ssh *ssh, int sock_in, int sock_out) void destroy_sensitive_data(void) { - int i; + u_int i; for (i = 0; i < options.num_host_key_files; i++) { if (sensitive_data.host_keys[i]) { @@ -486,7 +486,7 @@ void demote_sensitive_data(void) { struct sshkey *tmp; - int i; + u_int i; for (i = 0; i < options.num_host_key_files; i++) { if (sensitive_data.host_keys[i]) { @@ -685,7 +685,7 @@ list_hostkey_types(void) Buffer b; const char *p; char *ret; - int i; + u_int i; struct sshkey *key; buffer_init(&b); @@ -745,7 +745,7 @@ list_hostkey_types(void) static struct sshkey * get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh) { - int i; + u_int i; struct sshkey *key; for (i = 0; i < options.num_host_key_files; i++) { @@ -785,7 +785,7 @@ get_hostkey_private_by_type(int type, int nid, struct ssh *ssh) struct sshkey * get_hostkey_by_index(int ind) { - if (ind < 0 || ind >= options.num_host_key_files) + if (ind < 0 || (u_int)ind >= options.num_host_key_files) return (NULL); return (sensitive_data.host_keys[ind]); } @@ -793,7 +793,7 @@ get_hostkey_by_index(int ind) struct sshkey * get_hostkey_public_by_index(int ind, struct ssh *ssh) { - if (ind < 0 || ind >= options.num_host_key_files) + if (ind < 0 || (u_int)ind >= options.num_host_key_files) return (NULL); return (sensitive_data.host_pubkeys[ind]); } @@ -801,7 +801,7 @@ get_hostkey_public_by_index(int ind, struct ssh *ssh) int get_hostkey_index(struct sshkey *key, int compare, struct ssh *ssh) { - int i; + u_int i; for (i = 0; i < options.num_host_key_files; i++) { if (key_is_cert(key)) { @@ -830,7 +830,8 @@ notify_hostkeys(struct ssh *ssh) { struct sshbuf *buf; struct sshkey *key; - int i, nkeys, r; + u_int i, nkeys; + int r; char *fp; /* Some clients cannot cope with the hostkeys message, skip those. */ @@ -861,7 +862,7 @@ notify_hostkeys(struct ssh *ssh) packet_put_string(sshbuf_ptr(buf), sshbuf_len(buf)); nkeys++; } - debug3("%s: sent %d hostkeys", __func__, nkeys); + debug3("%s: sent %u hostkeys", __func__, nkeys); if (nkeys == 0) fatal("%s: no hostkeys", __func__); packet_send(); @@ -1357,13 +1358,12 @@ main(int ac, char **av) struct ssh *ssh = NULL; extern char *optarg; extern int optind; - int r, opt, i, j, on = 1, already_daemon; + int r, opt, on = 1, already_daemon, remote_port; int sock_in = -1, sock_out = -1, newsock = -1; const char *remote_ip; - int remote_port; char *fp, *line, *laddr, *logfile = NULL; int config_s[2] = { -1 , -1 }; - u_int n; + u_int i, j; u_int64_t ibytes, obytes; mode_t new_umask; struct sshkey *key; @@ -1416,12 +1416,8 @@ main(int ac, char **av) config_file_name = optarg; break; case 'c': - if (options.num_host_cert_files >= MAX_HOSTCERTS) { - fprintf(stderr, "too many host certificates.\n"); - exit(1); - } - options.host_cert_files[options.num_host_cert_files++] = - derelativise_path(optarg); + servconf_add_hostcert("[command-line]", 0, + &options, optarg); break; case 'd': if (debug_flag == 0) { @@ -1480,12 +1476,8 @@ main(int ac, char **av) /* protocol 1, ignored */ break; case 'h': - if (options.num_host_key_files >= MAX_HOSTKEYS) { - fprintf(stderr, "too many host keys.\n"); - exit(1); - } - options.host_key_files[options.num_host_key_files++] = - derelativise_path(optarg); + servconf_add_hostkey("[command-line]", 0, + &options, optarg); break; case 't': test_flag = 1; @@ -1611,12 +1603,12 @@ main(int ac, char **av) * and warns for trivial misconfigurations that could break login. */ if (options.num_auth_methods != 0) { - for (n = 0; n < options.num_auth_methods; n++) { - if (auth2_methods_valid(options.auth_methods[n], + for (i = 0; i < options.num_auth_methods; i++) { + if (auth2_methods_valid(options.auth_methods[i], 1) == 0) break; } - if (n >= options.num_auth_methods) + if (i >= options.num_auth_methods) fatal("AuthenticationMethods cannot be satisfied by " "enabled authentication methods"); } @@ -1752,7 +1744,7 @@ main(int ac, char **av) continue; } sensitive_data.host_certificates[j] = key; - debug("host certificate: #%d type %d %s", j, key->type, + debug("host certificate: #%u type %d %s", j, key->type, key_type(key)); } @@ -1796,8 +1788,10 @@ main(int ac, char **av) debug("setgroups() failed: %.200s", strerror(errno)); if (rexec_flag) { + if (rexec_argc < 0) + fatal("rexec_argc %d < 0", rexec_argc); rexec_argv = xcalloc(rexec_argc + 2, sizeof(char *)); - for (i = 0; i < rexec_argc; i++) { + for (i = 0; i < (u_int)rexec_argc; i++) { debug("rexec_argv[%d]='%s'", i, saved_argv[i]); rexec_argv[i] = saved_argv[i]; } -- cgit v1.2.3 From e3fa20e2e58fdc88a0e842358778f2de448b771b Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Mon, 23 Oct 2017 16:25:24 +1100 Subject: avoid -Wsign-compare warning in argv copying --- sshd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sshd.c') diff --git a/sshd.c b/sshd.c index 2ff385552..0e3ac26db 100644 --- a/sshd.c +++ b/sshd.c @@ -1383,7 +1383,7 @@ main(int ac, char **av) saved_argc = ac; rexec_argc = ac; saved_argv = xcalloc(ac + 1, sizeof(*saved_argv)); - for (i = 0; i < ac; i++) + for (i = 0; (int)i < ac; i++) saved_argv[i] = xstrdup(av[i]); saved_argv[i] = NULL; -- cgit v1.2.3 From acf559e1cffbd1d6167cc1742729fc381069f06b Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Wed, 25 Oct 2017 00:15:35 +0000 Subject: upstream commit Add optional rdomain qualifier to sshd_config's ListenAddress option to allow listening on a different rdomain(4), e.g. ListenAddress 0.0.0.0 rdomain 4 Upstream-ID: 24b6622c376feeed9e9be8b9605e593695ac9091 --- channels.c | 19 +---- misc.c | 40 +++++++++- misc.h | 4 +- servconf.c | 229 +++++++++++++++++++++++++++++++++++++++++----------------- servconf.h | 25 ++++++- sshd.c | 45 ++++++++---- sshd_config.5 | 29 ++++++-- 7 files changed, 283 insertions(+), 108 deletions(-) (limited to 'sshd.c') diff --git a/channels.c b/channels.c index 83442be06..07dc9577a 100644 --- a/channels.c +++ b/channels.c @@ -1,4 +1,4 @@ -/* $OpenBSD: channels.c,v 1.375 2017/09/24 13:45:34 djm Exp $ */ +/* $OpenBSD: channels.c,v 1.376 2017/10/25 00:15:35 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1668,19 +1668,6 @@ port_open_helper(struct ssh *ssh, Channel *c, char *rtype) free(local_ipaddr); } -static void -channel_set_reuseaddr(int fd) -{ - int on = 1; - - /* - * Set socket options. - * Allow local port reuse in TIME_WAIT. - */ - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) - error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno)); -} - void channel_set_x11_refuse_time(struct ssh *ssh, u_int refuse_time) { @@ -3368,7 +3355,7 @@ channel_setup_fwd_listener_tcpip(struct ssh *ssh, int type, continue; } - channel_set_reuseaddr(sock); + set_reuseaddr(sock); if (ai->ai_family == AF_INET6) sock_set_v6only(sock); @@ -4439,7 +4426,7 @@ x11_create_display_inet(struct ssh *ssh, int x11_display_offset, if (ai->ai_family == AF_INET6) sock_set_v6only(sock); if (x11_use_localhost) - channel_set_reuseaddr(sock); + set_reuseaddr(sock); if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { debug2("%s: bind port %d: %.100s", __func__, port, strerror(errno)); diff --git a/misc.c b/misc.c index cb8bf5c14..cc22fbef4 100644 --- a/misc.c +++ b/misc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.c,v 1.116 2017/10/24 19:41:45 millert Exp $ */ +/* $OpenBSD: misc.c,v 1.117 2017/10/25 00:15:35 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2005,2006 Damien Miller. All rights reserved. @@ -167,6 +167,44 @@ set_nodelay(int fd) error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); } +/* Allow local port reuse in TIME_WAIT */ +int +set_reuseaddr(int fd) +{ + int on = 1; + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { + error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno)); + return -1; + } + return 0; +} + +/* Set routing table */ +int +set_rdomain(int fd, const char *name) +{ + int rtable; + const char *errstr; + + if (name == NULL) + return 0; /* default table */ + + rtable = (int)strtonum(name, 0, 255, &errstr); + if (errstr != NULL) { + /* Shouldn't happen */ + error("Invalid routing domain \"%s\": %s", name, errstr); + return -1; + } + if (setsockopt(fd, SOL_SOCKET, SO_RTABLE, + &rtable, sizeof(rtable)) == -1) { + error("Failed to set routing domain %d on fd %d: %s", + rtable, fd, strerror(errno)); + return -1; + } + return 0; +} + /* Characters considered whitespace in strsep calls. */ #define WHITESPACE " \t\r\n" #define QUOTE "\"" diff --git a/misc.h b/misc.h index 19fdf5c84..f36081f5d 100644 --- a/misc.h +++ b/misc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.h,v 1.65 2017/10/23 05:08:00 djm Exp $ */ +/* $OpenBSD: misc.h,v 1.66 2017/10/25 00:15:35 djm Exp $ */ /* * Author: Tatu Ylonen @@ -48,6 +48,8 @@ char *strdelim(char **); int set_nonblock(int); int unset_nonblock(int); void set_nodelay(int); +int set_reuseaddr(int); +int set_rdomain(int, const char *); int a2port(const char *); int a2tun(const char *, int *); char *put_host_port(const char *, u_short); diff --git a/servconf.c b/servconf.c index a96df4f67..68db047f2 100644 --- a/servconf.c +++ b/servconf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: servconf.c,v 1.314 2017/10/05 15:52:03 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.315 2017/10/25 00:15:35 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -15,10 +15,16 @@ #include #include +#ifdef HAVE_SYS_SYSCTL_H +#include +#endif #include #include #include +#ifdef HAVE_NET_ROUTE_H +#include +#endif #include #include @@ -58,8 +64,10 @@ #include "myproposal.h" #include "digest.h" -static void add_listen_addr(ServerOptions *, char *, int); -static void add_one_listen_addr(ServerOptions *, char *, int); +static void add_listen_addr(ServerOptions *, const char *, + const char *, int); +static void add_one_listen_addr(ServerOptions *, const char *, + const char *, int); /* Use of privilege separation or not */ extern int use_privsep; @@ -81,6 +89,7 @@ initialize_server_options(ServerOptions *options) options->queued_listen_addrs = NULL; options->num_queued_listens = 0; options->listen_addrs = NULL; + options->num_listen_addrs = 0; options->address_family = -1; options->num_host_key_files = 0; options->num_host_cert_files = 0; @@ -252,7 +261,7 @@ fill_default_server_options(ServerOptions *options) if (options->address_family == -1) options->address_family = AF_UNSPEC; if (options->listen_addrs == NULL) - add_listen_addr(options, NULL, 0); + add_listen_addr(options, NULL, NULL, 0); if (options->pid_file == NULL) options->pid_file = xstrdup(_PATH_SSH_DAEMON_PID_FILE); if (options->login_grace_time == -1) @@ -658,23 +667,51 @@ derelativise_path(const char *path) } static void -add_listen_addr(ServerOptions *options, char *addr, int port) +add_listen_addr(ServerOptions *options, const char *addr, + const char *rdomain, int port) { u_int i; - if (port == 0) - for (i = 0; i < options->num_ports; i++) - add_one_listen_addr(options, addr, options->ports[i]); - else - add_one_listen_addr(options, addr, port); + if (port > 0) + add_one_listen_addr(options, addr, rdomain, port); + else { + for (i = 0; i < options->num_ports; i++) { + add_one_listen_addr(options, addr, rdomain, + options->ports[i]); + } + } } static void -add_one_listen_addr(ServerOptions *options, char *addr, int port) +add_one_listen_addr(ServerOptions *options, const char *addr, + const char *rdomain, int port) { struct addrinfo hints, *ai, *aitop; char strport[NI_MAXSERV]; int gaierr; + u_int i; + + /* Find listen_addrs entry for this rdomain */ + for (i = 0; i < options->num_listen_addrs; i++) { + if (rdomain == NULL && options->listen_addrs[i].rdomain == NULL) + break; + if (rdomain == NULL || options->listen_addrs[i].rdomain == NULL) + continue; + if (strcmp(rdomain, options->listen_addrs[i].rdomain) == 0) + break; + } + if (i >= options->num_listen_addrs) { + /* No entry for this rdomain; allocate one */ + if (i >= INT_MAX) + fatal("%s: too many listen addresses", __func__); + options->listen_addrs = xrecallocarray(options->listen_addrs, + options->num_listen_addrs, options->num_listen_addrs + 1, + sizeof(*options->listen_addrs)); + i = options->num_listen_addrs++; + if (rdomain != NULL) + options->listen_addrs[i].rdomain = xstrdup(rdomain); + } + /* options->listen_addrs[i] points to the addresses for this rdomain */ memset(&hints, 0, sizeof(hints)); hints.ai_family = options->address_family; @@ -687,8 +724,37 @@ add_one_listen_addr(ServerOptions *options, char *addr, int port) ssh_gai_strerror(gaierr)); for (ai = aitop; ai->ai_next; ai = ai->ai_next) ; - ai->ai_next = options->listen_addrs; - options->listen_addrs = aitop; + ai->ai_next = options->listen_addrs[i].addrs; + options->listen_addrs[i].addrs = aitop; +} + +/* Returns nonzero if the routing domain name is valid */ +static int +valid_rdomain(const char *name) +{ + const char *errstr; + long long num; + struct rt_tableinfo info; + int mib[6]; + size_t miblen = sizeof(mib); + + if (name == NULL) + return 1; + + num = strtonum(name, 0, 255, &errstr); + if (errstr != NULL) + return 0; + + /* Check whether the table actually exists */ + memset(mib, 0, sizeof(mib)); + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[4] = NET_RT_TABLE; + mib[5] = (int)num; + if (sysctl(mib, 6, &info, &miblen, NULL, 0) == -1) + return 0; + + return 1; } /* @@ -696,18 +762,19 @@ add_one_listen_addr(ServerOptions *options, char *addr, int port) * and AddressFamily options. */ static void -queue_listen_addr(ServerOptions *options, char *addr, int port) +queue_listen_addr(ServerOptions *options, const char *addr, + const char *rdomain, int port) { - options->queued_listen_addrs = xreallocarray( - options->queued_listen_addrs, options->num_queued_listens + 1, - sizeof(addr)); - options->queued_listen_ports = xreallocarray( - options->queued_listen_ports, options->num_queued_listens + 1, - sizeof(port)); - options->queued_listen_addrs[options->num_queued_listens] = - xstrdup(addr); - options->queued_listen_ports[options->num_queued_listens] = port; - options->num_queued_listens++; + struct queued_listenaddr *qla; + + options->queued_listen_addrs = xrecallocarray( + options->queued_listen_addrs, + options->num_queued_listens, options->num_queued_listens + 1, + sizeof(*options->queued_listen_addrs)); + qla = &options->queued_listen_addrs[options->num_queued_listens++]; + qla->addr = xstrdup(addr); + qla->port = port; + qla->rdomain = rdomain == NULL ? NULL : xstrdup(rdomain); } /* @@ -717,6 +784,7 @@ static void process_queued_listen_addrs(ServerOptions *options) { u_int i; + struct queued_listenaddr *qla; if (options->num_ports == 0) options->ports[options->num_ports++] = SSH_DEFAULT_PORT; @@ -724,15 +792,13 @@ process_queued_listen_addrs(ServerOptions *options) options->address_family = AF_UNSPEC; for (i = 0; i < options->num_queued_listens; i++) { - add_listen_addr(options, options->queued_listen_addrs[i], - options->queued_listen_ports[i]); - free(options->queued_listen_addrs[i]); - options->queued_listen_addrs[i] = NULL; + qla = &options->queued_listen_addrs[i]; + add_listen_addr(options, qla->addr, qla->rdomain, qla->port); + free(qla->addr); + free(qla->rdomain); } free(options->queued_listen_addrs); options->queued_listen_addrs = NULL; - free(options->queued_listen_ports); - options->queued_listen_ports = NULL; options->num_queued_listens = 0; } @@ -1127,20 +1193,33 @@ process_server_config_line(ServerOptions *options, char *line, /* check for bare IPv6 address: no "[]" and 2 or more ":" */ if (strchr(arg, '[') == NULL && (p = strchr(arg, ':')) != NULL && strchr(p+1, ':') != NULL) { - queue_listen_addr(options, arg, 0); - break; - } - p = hpdelim(&arg); - if (p == NULL) - fatal("%s line %d: bad address:port usage", - filename, linenum); - p = cleanhostname(p); - if (arg == NULL) port = 0; - else if ((port = a2port(arg)) <= 0) - fatal("%s line %d: bad port number", filename, linenum); + p = arg; + } else { + p = hpdelim(&arg); + if (p == NULL) + fatal("%s line %d: bad address:port usage", + filename, linenum); + p = cleanhostname(p); + if (arg == NULL) + port = 0; + else if ((port = a2port(arg)) <= 0) + fatal("%s line %d: bad port number", + filename, linenum); + } + /* Optional routing table */ + arg2 = NULL; + if ((arg = strdelim(&cp)) != NULL) { + if (strcmp(arg, "rdomain") != 0 || + (arg2 = strdelim(&cp)) == NULL) + fatal("%s line %d: bad ListenAddress syntax", + filename, linenum); + if (!valid_rdomain(arg2)) + fatal("%s line %d: bad routing domain", + filename, linenum); + } - queue_listen_addr(options, p, port); + queue_listen_addr(options, p, arg2, port); break; @@ -2251,45 +2330,61 @@ dump_cfg_strarray_oneline(ServerOpCodes code, u_int count, char **vals) printf("\n"); } -void -dump_config(ServerOptions *o) +static char * +format_listen_addrs(struct listenaddr *la) { - u_int i; - int ret; + int r; struct addrinfo *ai; - char addr[NI_MAXHOST], port[NI_MAXSERV], *s = NULL; + char addr[NI_MAXHOST], port[NI_MAXSERV]; char *laddr1 = xstrdup(""), *laddr2 = NULL; - /* these are usually at the top of the config */ - for (i = 0; i < o->num_ports; i++) - printf("port %d\n", o->ports[i]); - dump_cfg_fmtint(sAddressFamily, o->address_family); - /* * ListenAddress must be after Port. add_one_listen_addr pushes * addresses onto a stack, so to maintain ordering we need to * print these in reverse order. */ - for (ai = o->listen_addrs; ai; ai = ai->ai_next) { - if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, + for (ai = la->addrs; ai; ai = ai->ai_next) { + if ((r = getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, sizeof(addr), port, sizeof(port), NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { - error("getnameinfo failed: %.100s", - (ret != EAI_SYSTEM) ? gai_strerror(ret) : - strerror(errno)); + error("getnameinfo: %.100s", ssh_gai_strerror(r)); + continue; + } + laddr2 = laddr1; + if (ai->ai_family == AF_INET6) { + xasprintf(&laddr1, "listenaddress [%s]:%s%s%s\n%s", + addr, port, + la->rdomain == NULL ? "" : " rdomain ", + la->rdomain == NULL ? "" : la->rdomain, + laddr2); } else { - laddr2 = laddr1; - if (ai->ai_family == AF_INET6) - xasprintf(&laddr1, "listenaddress [%s]:%s\n%s", - addr, port, laddr2); - else - xasprintf(&laddr1, "listenaddress %s:%s\n%s", - addr, port, laddr2); - free(laddr2); + xasprintf(&laddr1, "listenaddress %s:%s%s%s\n%s", + addr, port, + la->rdomain == NULL ? "" : " rdomain ", + la->rdomain == NULL ? "" : la->rdomain, + laddr2); } + free(laddr2); + } + return laddr1; +} + +void +dump_config(ServerOptions *o) +{ + char *s; + u_int i; + + /* these are usually at the top of the config */ + for (i = 0; i < o->num_ports; i++) + printf("port %d\n", o->ports[i]); + dump_cfg_fmtint(sAddressFamily, o->address_family); + + for (i = 0; i < o->num_listen_addrs; i++) { + s = format_listen_addrs(&o->listen_addrs[i]); + printf("%s", s); + free(s); } - printf("%s", laddr1); - free(laddr1); /* integer arguments */ #ifdef USE_PAM diff --git a/servconf.h b/servconf.h index 1ff3bc5a1..3d0a0653f 100644 --- a/servconf.h +++ b/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.127 2017/10/05 15:52:03 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.128 2017/10/25 00:15:35 djm Exp $ */ /* * Author: Tatu Ylonen @@ -51,14 +51,31 @@ struct ssh; struct fwd_perm_list; +/* + * Used to store addresses from ListenAddr directives. These may be + * incomplete, as they may specify addresses that need to be merged + * with any ports requested by ListenPort. + */ +struct queued_listenaddr { + char *addr; + int port; /* <=0 if unspecified */ + char *rdomain; +}; + +/* Resolved listen addresses, grouped by optional routing domain */ +struct listenaddr { + char *rdomain; + struct addrinfo *addrs; +}; + typedef struct { u_int num_ports; u_int ports_from_cmdline; int ports[MAX_PORTS]; /* Port number to listen on. */ + struct queued_listenaddr *queued_listen_addrs; u_int num_queued_listens; - char **queued_listen_addrs; - int *queued_listen_ports; - struct addrinfo *listen_addrs; /* Addresses for server to listen. */ + struct listenaddr *listen_addrs; + u_int num_listen_addrs; int address_family; /* Address family used by the server. */ char **host_key_files; /* Files containing host keys. */ diff --git a/sshd.c b/sshd.c index 0e3ac26db..93b02b6c8 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.493 2017/10/05 15:52:03 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.494 2017/10/25 00:15:35 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1015,13 +1015,13 @@ server_accept_inetd(int *sock_in, int *sock_out) * Listen for TCP connections */ static void -server_listen(void) +listen_on_addrs(struct listenaddr *la) { - int ret, listen_sock, on = 1; + int ret, listen_sock; struct addrinfo *ai; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; - for (ai = options.listen_addrs; ai; ai = ai->ai_next) { + for (ai = la->addrs; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; if (num_listen_socks >= MAX_LISTEN_SOCKS) @@ -1051,13 +1051,13 @@ server_listen(void) close(listen_sock); continue; } - /* - * Set socket options. - * Allow local port reuse in TIME_WAIT. - */ - if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, - &on, sizeof(on)) == -1) - error("setsockopt SO_REUSEADDR: %s", strerror(errno)); + /* Socket options */ + set_reuseaddr(listen_sock); + if (la->rdomain != NULL && + set_rdomain(listen_sock, la->rdomain) == -1) { + close(listen_sock); + continue; + } /* Only communicate in IPv6 over AF_INET6 sockets. */ if (ai->ai_family == AF_INET6) @@ -1079,9 +1079,28 @@ server_listen(void) if (listen(listen_sock, SSH_LISTEN_BACKLOG) < 0) fatal("listen on [%s]:%s: %.100s", ntop, strport, strerror(errno)); - logit("Server listening on %s port %s.", ntop, strport); + logit("Server listening on %s port %s%s%s.", + ntop, strport, + la->rdomain == NULL ? "" : " rdomain ", + la->rdomain == NULL ? "" : la->rdomain); + } +} + +static void +server_listen(void) +{ + u_int i; + + for (i = 0; i < options.num_listen_addrs; i++) { + listen_on_addrs(&options.listen_addrs[i]); + freeaddrinfo(options.listen_addrs[i].addrs); + free(options.listen_addrs[i].rdomain); + memset(&options.listen_addrs[i], 0, + sizeof(options.listen_addrs[i])); } - freeaddrinfo(options.listen_addrs); + free(options.listen_addrs); + options.listen_addrs = NULL; + options.num_listen_addrs = 0; if (!num_listen_socks) fatal("Cannot bind any address."); diff --git a/sshd_config.5 b/sshd_config.5 index 77a8354ab..b63a022b7 100644 --- a/sshd_config.5 +++ b/sshd_config.5 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd_config.5,v 1.255 2017/10/13 16:50:45 jmc Exp $ -.Dd $Mdocdate: October 13 2017 $ +.\" $OpenBSD: sshd_config.5,v 1.256 2017/10/25 00:15:35 djm Exp $ +.Dd $Mdocdate: October 25 2017 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -909,31 +909,48 @@ The following forms may be used: .It .Cm ListenAddress .Sm off -.Ar host | Ar IPv4_addr | Ar IPv6_addr +.Ar hostname | Ar address .Sm on +.Oo rdomain Ar domain Oc .It .Cm ListenAddress .Sm off -.Ar host | Ar IPv4_addr : Ar port +.Ar hostname : Ar port .Sm on +.Oo rdomain Ar domain Oc +.It +.Cm ListenAddress +.Sm off +.Ar IPv4_address : Ar port +.Sm on +.Oo rdomain Ar domain Oc .It .Cm ListenAddress .Sm off .Oo -.Ar host | Ar IPv6_addr Oc : Ar port +.Ar hostname | address Oc : Ar port .Sm on +.Oo rdomain Ar domain Oc .El .Pp +The optional +.Cm rdomain +qualifier requests +.Xr sshd 8 +listen in an explicit routing domain. If .Ar port is not specified, sshd will listen on the address and all .Cm Port options specified. -The default is to listen on all local addresses. +The default is to listen on all local addresses on the current default +routing domain. Multiple .Cm ListenAddress options are permitted. +For more information on routing domains, see +.Xr rdomain 4. .It Cm LoginGraceTime The server disconnects after this time if the user has not successfully logged in. -- cgit v1.2.3 From 35eb33fb957979e3fcbe6ea0eaee8bf4a217421a Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Wed, 25 Oct 2017 00:17:08 +0000 Subject: upstream commit add sshd_config RDomain keyword to place sshd and the subsequent user session (including the shell and any TCP/IP forwardings) into the specified rdomain(4) ok markus@ Upstream-ID: be2358e86346b5cacf20d90f59f980b87d1af0f5 --- misc.c | 20 ++++++++++++++++++-- misc.h | 3 ++- packet.c | 14 +++++++++++++- packet.h | 4 +++- servconf.c | 21 +++++++++++++++++++-- servconf.h | 5 ++++- sshd.c | 30 +++++++++++++++++++++++++++++- sshd_config.5 | 17 ++++++++++++++++- 8 files changed, 104 insertions(+), 10 deletions(-) (limited to 'sshd.c') diff --git a/misc.c b/misc.c index cc22fbef4..9b1ea4fa6 100644 --- a/misc.c +++ b/misc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.c,v 1.117 2017/10/25 00:15:35 djm Exp $ */ +/* $OpenBSD: misc.c,v 1.118 2017/10/25 00:17:08 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2005,2006 Damien Miller. All rights reserved. @@ -180,7 +180,23 @@ set_reuseaddr(int fd) return 0; } -/* Set routing table */ +/* Get/set routing domain */ +char * +get_rdomain(int fd) +{ + int rtable; + char *ret; + socklen_t len = sizeof(rtable); + + if (getsockopt(fd, SOL_SOCKET, SO_RTABLE, &rtable, &len) == -1) { + error("Failed to get routing domain for fd %d: %s", + fd, strerror(errno)); + return NULL; + } + xasprintf(&ret, "%d", rtable); + return ret; +} + int set_rdomain(int fd, const char *name) { diff --git a/misc.h b/misc.h index f36081f5d..5ad30ce3b 100644 --- a/misc.h +++ b/misc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.h,v 1.66 2017/10/25 00:15:35 djm Exp $ */ +/* $OpenBSD: misc.h,v 1.67 2017/10/25 00:17:08 djm Exp $ */ /* * Author: Tatu Ylonen @@ -49,6 +49,7 @@ int set_nonblock(int); int unset_nonblock(int); void set_nodelay(int); int set_reuseaddr(int); +char *get_rdomain(int); int set_rdomain(int, const char *); int a2port(const char *); int a2tun(const char *, int *); diff --git a/packet.c b/packet.c index 85638cb2a..448da0964 100644 --- a/packet.c +++ b/packet.c @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.c,v 1.265 2017/10/13 21:13:54 djm Exp $ */ +/* $OpenBSD: packet.c,v 1.266 2017/10/25 00:17:08 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -557,6 +557,18 @@ ssh_local_port(struct ssh *ssh) return ssh->local_port; } +/* Returns the routing domain of the input socket, or NULL if unavailable */ +const char * +ssh_packet_rdomain_in(struct ssh *ssh) +{ + if (ssh->rdomain_in != NULL) + return ssh->rdomain_in; + if (!ssh_packet_connection_is_on_socket(ssh)) + return NULL; + ssh->rdomain_in = get_rdomain(ssh->state->connection_in); + return ssh->rdomain_in; +} + /* Closes the connection and clears and frees internal data structures. */ static void diff --git a/packet.h b/packet.h index 40837e9db..55f07fc90 100644 --- a/packet.h +++ b/packet.h @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.h,v 1.82 2017/09/12 06:32:07 djm Exp $ */ +/* $OpenBSD: packet.h,v 1.83 2017/10/25 00:17:08 djm Exp $ */ /* * Author: Tatu Ylonen @@ -61,6 +61,7 @@ struct ssh { int remote_port; char *local_ipaddr; int local_port; + char *rdomain_in; /* Optional preamble for log messages (e.g. username) */ char *log_preamble; @@ -162,6 +163,7 @@ const char *ssh_remote_ipaddr(struct ssh *); int ssh_remote_port(struct ssh *); const char *ssh_local_ipaddr(struct ssh *); int ssh_local_port(struct ssh *); +const char *ssh_packet_rdomain_in(struct ssh *); void ssh_packet_set_rekey_limits(struct ssh *, u_int64_t, u_int32_t); time_t ssh_packet_get_rekey_timeout(struct ssh *); diff --git a/servconf.c b/servconf.c index 68db047f2..51139c31c 100644 --- a/servconf.c +++ b/servconf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: servconf.c,v 1.315 2017/10/25 00:15:35 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.316 2017/10/25 00:17:08 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -91,6 +91,7 @@ initialize_server_options(ServerOptions *options) options->listen_addrs = NULL; options->num_listen_addrs = 0; options->address_family = -1; + options->routing_domain = NULL; options->num_host_key_files = 0; options->num_host_cert_files = 0; options->host_key_agent = NULL; @@ -406,6 +407,7 @@ fill_default_server_options(ServerOptions *options) CLEAR_ON_NONE(options->authorized_principals_file); CLEAR_ON_NONE(options->adm_forced_command); CLEAR_ON_NONE(options->chroot_directory); + CLEAR_ON_NONE(options->routing_domain); for (i = 0; i < options->num_host_key_files; i++) CLEAR_ON_NONE(options->host_key_files[i]); for (i = 0; i < options->num_host_cert_files; i++) @@ -469,7 +471,7 @@ typedef enum { sAuthenticationMethods, sHostKeyAgent, sPermitUserRC, sStreamLocalBindMask, sStreamLocalBindUnlink, sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding, - sExposeAuthInfo, + sExposeAuthInfo, sRDomain, sDeprecated, sIgnore, sUnsupported } ServerOpCodes; @@ -614,6 +616,7 @@ static struct { { "fingerprinthash", sFingerprintHash, SSHCFG_GLOBAL }, { "disableforwarding", sDisableForwarding, SSHCFG_ALL }, { "exposeauthinfo", sExposeAuthInfo, SSHCFG_ALL }, + { "rdomain", sRDomain, SSHCFG_ALL }, { NULL, sBadOption, 0 } }; @@ -1984,6 +1987,19 @@ process_server_config_line(ServerOptions *options, char *line, intptr = &options->expose_userauth_info; goto parse_flag; + case sRDomain: + charptr = &options->routing_domain; + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing argument.", + filename, linenum); + if (strcasecmp(arg, "none") != 0 && strcmp(arg, "%D") != 0 && + !valid_rdomain(arg)) + fatal("%s line %d: bad routing domain", + filename, linenum); + if (*activep && *charptr == NULL) + *charptr = xstrdup(arg); + case sDeprecated: case sIgnore: case sUnsupported: @@ -2473,6 +2489,7 @@ dump_config(ServerOptions *o) o->hostkeyalgorithms : KEX_DEFAULT_PK_ALG); dump_cfg_string(sPubkeyAcceptedKeyTypes, o->pubkey_key_types ? o->pubkey_key_types : KEX_DEFAULT_PK_ALG); + dump_cfg_string(sRDomain, o->routing_domain); /* string arguments requiring a lookup */ dump_cfg_string(sLogLevel, log_level_name(o->log_level)); diff --git a/servconf.h b/servconf.h index 3d0a0653f..1f042e872 100644 --- a/servconf.h +++ b/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.128 2017/10/25 00:15:35 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.129 2017/10/25 00:17:08 djm Exp $ */ /* * Author: Tatu Ylonen @@ -78,6 +78,8 @@ typedef struct { u_int num_listen_addrs; int address_family; /* Address family used by the server. */ + char *routing_domain; /* Bind session to routing domain */ + char **host_key_files; /* Files containing host keys. */ u_int num_host_key_files; /* Number of files for host keys. */ char **host_cert_files; /* Files containing host certs. */ @@ -239,6 +241,7 @@ struct connection_info { M_CP_STROPT(authorized_principals_command_user); \ M_CP_STROPT(hostbased_key_types); \ M_CP_STROPT(pubkey_key_types); \ + M_CP_STROPT(routing_domain); \ M_CP_STRARRAYOPT(authorized_keys_files, num_authkeys_files); \ M_CP_STRARRAYOPT(allow_users, num_allow_users); \ M_CP_STRARRAYOPT(deny_users, num_deny_users); \ diff --git a/sshd.c b/sshd.c index 93b02b6c8..3ad106f72 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.494 2017/10/25 00:15:35 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.495 2017/10/25 00:17:08 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1368,6 +1368,31 @@ check_ip_options(struct ssh *ssh) #endif /* IP_OPTIONS */ } +/* Set the routing domain for this process */ +static void +set_process_rdomain(struct ssh *ssh, const char *name) +{ + int rtable, ortable = getrtable(); + const char *errstr; + + if (name == NULL) + return; /* default */ + + if (strcmp(name, "%D") == 0) { + /* "expands" to routing domain of connection */ + if ((name = ssh_packet_rdomain_in(ssh)) == NULL) + return; + } + + rtable = (int)strtonum(name, 0, 255, &errstr); + if (errstr != NULL) /* Shouldn't happen */ + fatal("Invalid routing domain \"%s\": %s", name, errstr); + if (rtable != ortable && setrtable(rtable) != 0) + fatal("Unable to set routing domain %d: %s", + rtable, strerror(errno)); + debug("%s: set routing domain %d (was %d)", __func__, rtable, ortable); +} + /* * Main program for the daemon. */ @@ -1983,6 +2008,9 @@ main(int ac, char **av) cleanup_exit(255); } + if (options.routing_domain != NULL) + set_process_rdomain(ssh, options.routing_domain); + /* * The rest of the code depends on the fact that * ssh_remote_ipaddr() caches the remote ip, even if diff --git a/sshd_config.5 b/sshd_config.5 index b63a022b7..c216fb75b 100644 --- a/sshd_config.5 +++ b/sshd_config.5 @@ -33,7 +33,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd_config.5,v 1.256 2017/10/25 00:15:35 djm Exp $ +.\" $OpenBSD: sshd_config.5,v 1.257 2017/10/25 00:17:08 djm Exp $ .Dd $Mdocdate: October 25 2017 $ .Dt SSHD_CONFIG 5 .Os @@ -1118,6 +1118,7 @@ Available keywords are .Cm PubkeyAuthentication , .Cm RekeyLimit , .Cm RevokedKeys , +.Cm RDomain , .Cm StreamLocalBindMask , .Cm StreamLocalBindUnlink , .Cm TrustedUserCAKeys , @@ -1378,6 +1379,15 @@ an OpenSSH Key Revocation List (KRL) as generated by .Xr ssh-keygen 1 . For more information on KRLs, see the KEY REVOCATION LISTS section in .Xr ssh-keygen 1 . +.It Cm RDomain +Specifies an explicit routing domain that is applied after authentication +has completed. +The user session, as well and any forwarded or listening IP sockets will +be bound to this +.Xr rdomain 4 . +If the routing domain is set to +.Cm \&%D , +then the domain in which the incoming connection was recieved will be applied. .It Cm StreamLocalBindMask Sets the octal file creation mode mask .Pq umask @@ -1643,6 +1653,8 @@ which are expanded at runtime: .It %% A literal .Sq % . +.It \&%D +The routing domain in which the incoming connection was received. .It %F The fingerprint of the CA key. .It %f @@ -1679,6 +1691,9 @@ accepts the tokens %%, %h, and %u. .Pp .Cm ChrootDirectory accepts the tokens %%, %h, and %u. +.Pp +.Cm RoutingDomain +accepts the token %D. .Sh FILES .Bl -tag -width Ds .It Pa /etc/ssh/sshd_config -- cgit v1.2.3 From 68af80e6fdeaeb79432209db614386ff0f37e75f Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Wed, 25 Oct 2017 00:19:47 +0000 Subject: upstream commit add a "rdomain" criteria for the sshd_config Match keyword to allow conditional configuration that depends on which rdomain(4) a connection was recevied on. ok markus@ Upstream-ID: 27d8fd5a3f1bae18c9c6e533afdf99bff887a4fb --- servconf.c | 15 ++++++++++++++- servconf.h | 3 ++- sshd.8 | 5 +++-- sshd.c | 12 ++++++++---- sshd_config.5 | 11 +++++++++-- 5 files changed, 36 insertions(+), 10 deletions(-) (limited to 'sshd.c') diff --git a/servconf.c b/servconf.c index 51139c31c..64a865180 100644 --- a/servconf.c +++ b/servconf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: servconf.c,v 1.316 2017/10/25 00:17:08 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.317 2017/10/25 00:19:47 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -855,6 +855,7 @@ get_connection_info(int populate, int use_dns) ci.address = ssh_remote_ipaddr(ssh); ci.laddress = ssh_local_ipaddr(ssh); ci.lport = ssh_local_port(ssh); + ci.rdomain = ssh_packet_rdomain_in(ssh); return &ci; } @@ -1038,6 +1039,16 @@ match_cfg_line(char **condition, int line, struct connection_info *ci) ci->laddress, port, line); else result = 0; + } else if (strcasecmp(attrib, "rdomain") == 0) { + if (ci == NULL || ci->rdomain == NULL) { + result = 0; + continue; + } + if (match_pattern_list(ci->rdomain, arg, 0) != 1) + result = 0; + else + debug("user %.100s matched 'RDomain %.100s' at " + "line %d", ci->rdomain, arg, line); } else { error("Unsupported Match attribute %s", attrib); return -1; @@ -2080,6 +2091,8 @@ int parse_server_match_testspec(struct connection_info *ci, char *spec) ci->user = xstrdup(p + 5); } else if (strncmp(p, "laddr=", 6) == 0) { ci->laddress = xstrdup(p + 6); + } else if (strncmp(p, "rdomain=", 8) == 0) { + ci->rdomain = xstrdup(p + 8); } else if (strncmp(p, "lport=", 6) == 0) { ci->lport = a2port(p + 6); if (ci->lport == -1) { diff --git a/servconf.h b/servconf.h index 1f042e872..37a0fb1a3 100644 --- a/servconf.h +++ b/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.129 2017/10/25 00:17:08 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.130 2017/10/25 00:19:47 djm Exp $ */ /* * Author: Tatu Ylonen @@ -218,6 +218,7 @@ struct connection_info { const char *address; /* remote address */ const char *laddress; /* local address */ int lport; /* local port */ + const char *rdomain; /* routing domain if available */ }; diff --git a/sshd.8 b/sshd.8 index a4201146b..c16c433ef 100644 --- a/sshd.8 +++ b/sshd.8 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd.8,v 1.291 2017/06/24 06:28:50 jmc Exp $ -.Dd $Mdocdate: June 24 2017 $ +.\" $OpenBSD: sshd.8,v 1.292 2017/10/25 00:19:47 djm Exp $ +.Dd $Mdocdate: October 25 2017 $ .Dt SSHD 8 .Os .Sh NAME @@ -109,6 +109,7 @@ The keywords are .Dq host , .Dq laddr , .Dq lport , +.Dq rdomain and .Dq addr . All are required and may be supplied in any order, either with multiple diff --git a/sshd.c b/sshd.c index 3ad106f72..1220309d7 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.495 2017/10/25 00:17:08 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.496 2017/10/25 00:19:47 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1404,7 +1404,7 @@ main(int ac, char **av) extern int optind; int r, opt, on = 1, already_daemon, remote_port; int sock_in = -1, sock_out = -1, newsock = -1; - const char *remote_ip; + const char *remote_ip, *rdomain; char *fp, *line, *laddr, *logfile = NULL; int config_s[2] = { -1 , -1 }; u_int i, j; @@ -2022,10 +2022,14 @@ main(int ac, char **av) audit_connection_from(remote_ip, remote_port); #endif + rdomain = ssh_packet_rdomain_in(ssh); + /* Log the connection. */ laddr = get_local_ipaddr(sock_in); - verbose("Connection from %s port %d on %s port %d", - remote_ip, remote_port, laddr, ssh_local_port(ssh)); + verbose("Connection from %s port %d on %s port %d%s%s", + remote_ip, remote_port, laddr, ssh_local_port(ssh), + rdomain == NULL ? "" : " rdomain ", + rdomain == NULL ? "" : rdomain); free(laddr); /* diff --git a/sshd_config.5 b/sshd_config.5 index c216fb75b..0b91f9f74 100644 --- a/sshd_config.5 +++ b/sshd_config.5 @@ -33,7 +33,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd_config.5,v 1.257 2017/10/25 00:17:08 djm Exp $ +.\" $OpenBSD: sshd_config.5,v 1.258 2017/10/25 00:19:47 djm Exp $ .Dd $Mdocdate: October 25 2017 $ .Dt SSHD_CONFIG 5 .Os @@ -1054,8 +1054,15 @@ The available criteria are .Cm Host , .Cm LocalAddress , .Cm LocalPort , +.Cm RDomain , and -.Cm Address . +.Cm Address +(with +.Cm RDomain +representing the +.Xr rdomain 4 +on which the connection was received.) +.Pp The match patterns may consist of single entries or comma-separated lists and may use the wildcard and negation operators described in the .Sx PATTERNS -- cgit v1.2.3 From 43c29bb7cfd46bbbc61e0ffa61a11e74d49a712f Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Wed, 25 Oct 2017 13:10:59 +1100 Subject: provide hooks and fallbacks for rdomain support --- misc.c | 13 +++++++++++++ servconf.c | 7 +++++++ sshd.c | 15 +++++++++++++++ 3 files changed, 35 insertions(+) (limited to 'sshd.c') diff --git a/misc.c b/misc.c index 9b1ea4fa6..2369361b3 100644 --- a/misc.c +++ b/misc.c @@ -184,6 +184,9 @@ set_reuseaddr(int fd) char * get_rdomain(int fd) { +#if defined(HAVE_SYS_GET_RDOMAIN) + return sys_get_rdomain(fd); +#elif defined(__OpenBSD__) int rtable; char *ret; socklen_t len = sizeof(rtable); @@ -195,11 +198,17 @@ get_rdomain(int fd) } xasprintf(&ret, "%d", rtable); return ret; +#else /* defined(__OpenBSD__) */ + return NULL; +#endif } int set_rdomain(int fd, const char *name) { +#if defined(HAVE_SYS_SET_RDOMAIN) + return sys_set_rdomain(fd, name); +#elif defined(__OpenBSD__) int rtable; const char *errstr; @@ -219,6 +228,10 @@ set_rdomain(int fd, const char *name) return -1; } return 0; +#else /* defined(__OpenBSD__) */ + error("Setting routing domain is not supported on this platform"); + return -1; +#endif } /* Characters considered whitespace in strsep calls. */ diff --git a/servconf.c b/servconf.c index 64a865180..288ec0889 100644 --- a/servconf.c +++ b/servconf.c @@ -735,6 +735,9 @@ add_one_listen_addr(ServerOptions *options, const char *addr, static int valid_rdomain(const char *name) { +#if defined(HAVE_SYS_VALID_RDOMAIN) + return valid_rdomain(name) +#elif defined(__OpenBSD__) const char *errstr; long long num; struct rt_tableinfo info; @@ -758,6 +761,10 @@ valid_rdomain(const char *name) return 0; return 1; +#else /* defined(__OpenBSD__) */ + error("Routing domains are not supported on this platform"); + return 0; +#endif } /* diff --git a/sshd.c b/sshd.c index 1220309d7..535cb97f0 100644 --- a/sshd.c +++ b/sshd.c @@ -1372,6 +1372,18 @@ check_ip_options(struct ssh *ssh) static void set_process_rdomain(struct ssh *ssh, const char *name) { +#if defined(HAVE_SYS_SET_PROCESS_RDOMAIN) + if (name == NULL) + return; /* default */ + + if (strcmp(name, "%D") == 0) { + /* "expands" to routing domain of connection */ + if ((name = ssh_packet_rdomain_in(ssh)) == NULL) + return; + } + /* NB. We don't pass 'ssh' to sys_set_process_rdomain() */ + return sys_set_process_rdomain(name); +#elif defined(__OpenBSD__) int rtable, ortable = getrtable(); const char *errstr; @@ -1391,6 +1403,9 @@ set_process_rdomain(struct ssh *ssh, const char *name) fatal("Unable to set routing domain %d: %s", rtable, strerror(errno)); debug("%s: set routing domain %d (was %d)", __func__, rtable, ortable); +#else /* defined(__OpenBSD__) */ + fatal("Unable to set routing domain: not supported in this platform"); +#endif } /* -- cgit v1.2.3 From b77c29a07f5a02c7c1998701c73d92bde7ae1608 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org@openbsd.org" Date: Fri, 27 Oct 2017 00:18:41 +0000 Subject: upstream commit improve printing of rdomain on accept() a little OpenBSD-Commit-ID: 5da58db2243606899cedaa646c70201b2d12247a --- sshd.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'sshd.c') diff --git a/sshd.c b/sshd.c index 535cb97f0..6a8e3762a 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.496 2017/10/25 00:19:47 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.497 2017/10/27 00:18:41 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -2041,10 +2041,11 @@ main(int ac, char **av) /* Log the connection. */ laddr = get_local_ipaddr(sock_in); - verbose("Connection from %s port %d on %s port %d%s%s", + verbose("Connection from %s port %d on %s port %d%s%s%s", remote_ip, remote_port, laddr, ssh_local_port(ssh), - rdomain == NULL ? "" : " rdomain ", - rdomain == NULL ? "" : rdomain); + rdomain == NULL ? "" : " rdomain \"", + rdomain == NULL ? "" : rdomain, + rdomain == NULL ? "" : "\""); free(laddr); /* -- cgit v1.2.3 From 0208a48517b5e8e8b091f32fa4addcd67c31ca9e Mon Sep 17 00:00:00 2001 From: "dtucker@openbsd.org@openbsd.org" Date: Fri, 3 Nov 2017 03:18:53 +0000 Subject: upstream commit When doing a config test with sshd -T, only require the attributes that are actually used in Match criteria rather than (an incomplete list of) all criteria. ok djm@, man page help jmc@ OpenBSD-Commit-ID: b4e773c4212d3dea486d0259ae977551aab2c1fc --- servconf.c | 47 +++++++++++++++++++++++++++-------------------- sshd.8 | 23 ++++++++++++----------- sshd.c | 18 +++++++----------- 3 files changed, 46 insertions(+), 42 deletions(-) (limited to 'sshd.c') diff --git a/servconf.c b/servconf.c index 53d81fb3c..44de35367 100644 --- a/servconf.c +++ b/servconf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: servconf.c,v 1.318 2017/10/25 02:10:39 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.319 2017/11/03 03:18:53 dtucker Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -927,6 +927,13 @@ out: return result; } +static void +match_test_missing_fatal(const char *criteria, const char *attrib) +{ + fatal("'Match %s' in configuration but '%s' not in connection " + "test specification.", criteria, attrib); +} + /* * All of the attributes on a single Match line are ANDed together, so we need * to check every attribute and set the result to zero if any attribute does @@ -964,20 +971,24 @@ match_cfg_line(char **condition, int line, struct connection_info *ci) return -1; } if (strcasecmp(attrib, "user") == 0) { - if (ci == NULL || ci->user == NULL) { + if (ci == NULL) { result = 0; continue; } + if (ci->user == NULL) + match_test_missing_fatal("User", "user"); if (match_pattern_list(ci->user, arg, 0) != 1) result = 0; else debug("user %.100s matched 'User %.100s' at " "line %d", ci->user, arg, line); } else if (strcasecmp(attrib, "group") == 0) { - if (ci == NULL || ci->user == NULL) { + if (ci == NULL) { result = 0; continue; } + if (ci->user == NULL) + match_test_missing_fatal("Group", "user"); switch (match_cfg_line_group(arg, line, ci->user)) { case -1: return -1; @@ -985,20 +996,24 @@ match_cfg_line(char **condition, int line, struct connection_info *ci) result = 0; } } else if (strcasecmp(attrib, "host") == 0) { - if (ci == NULL || ci->host == NULL) { + if (ci == NULL) { result = 0; continue; } + if (ci->host == NULL) + match_test_missing_fatal("Host", "host"); if (match_hostname(ci->host, arg) != 1) result = 0; else debug("connection from %.100s matched 'Host " "%.100s' at line %d", ci->host, arg, line); } else if (strcasecmp(attrib, "address") == 0) { - if (ci == NULL || ci->address == NULL) { + if (ci == NULL) { result = 0; continue; } + if (ci->address == NULL) + match_test_missing_fatal("Address", "addr"); switch (addr_match_list(ci->address, arg)) { case 1: debug("connection from %.100s matched 'Address " @@ -1012,10 +1027,13 @@ match_cfg_line(char **condition, int line, struct connection_info *ci) return -1; } } else if (strcasecmp(attrib, "localaddress") == 0){ - if (ci == NULL || ci->laddress == NULL) { + if (ci == NULL) { result = 0; continue; } + if (ci->laddress == NULL) + match_test_missing_fatal("LocalAddress", + "laddr"); switch (addr_match_list(ci->laddress, arg)) { case 1: debug("connection from %.100s matched " @@ -1035,10 +1053,12 @@ match_cfg_line(char **condition, int line, struct connection_info *ci) arg); return -1; } - if (ci == NULL || ci->lport == 0) { + if (ci == NULL) { result = 0; continue; } + if (ci->lport == 0) + match_test_missing_fatal("LocalPort", "lport"); /* TODO support port lists */ if (port == ci->lport) debug("connection from %.100s matched " @@ -2116,19 +2136,6 @@ int parse_server_match_testspec(struct connection_info *ci, char *spec) return 0; } -/* - * returns 1 for a complete spec, 0 for partial spec and -1 for an - * empty spec. - */ -int server_match_spec_complete(struct connection_info *ci) -{ - if (ci->user && ci->host && ci->address) - return 1; /* complete */ - if (!ci->user && !ci->host && !ci->address) - return -1; /* empty */ - return 0; /* partial */ -} - /* * Copy any supported values that are set. * diff --git a/sshd.8 b/sshd.8 index c16c433ef..76a4474ed 100644 --- a/sshd.8 +++ b/sshd.8 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd.8,v 1.292 2017/10/25 00:19:47 djm Exp $ -.Dd $Mdocdate: October 25 2017 $ +.\" $OpenBSD: sshd.8,v 1.293 2017/11/03 03:18:53 dtucker Exp $ +.Dd $Mdocdate: November 3 2017 $ .Dt SSHD 8 .Os .Sh NAME @@ -100,21 +100,22 @@ Specify the connection parameters to use for the extended test mode. If provided, any .Cm Match -directives in the configuration file -that would apply to the specified user, host, and address will be set before -the configuration is written to standard output. -The connection parameters are supplied as keyword=value pairs. +directives in the configuration file that would apply are applied before the +configuration is written to standard output. +The connection parameters are supplied as keyword=value pairs and may be +supplied in any order, either with multiple +.Fl C +options or as a comma-separated list. The keywords are +.Dq addr, .Dq user , .Dq host , .Dq laddr , .Dq lport , -.Dq rdomain and -.Dq addr . -All are required and may be supplied in any order, either with multiple -.Fl C -options or as a comma-separated list. +.Dq rdomain +and correspond to source address, user, resolved source host name, +local address, local port number and routing domain respectively. .It Fl c Ar host_certificate_file Specifies a path to a certificate file to identify .Nm diff --git a/sshd.c b/sshd.c index 6a8e3762a..73094001b 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.497 2017/10/27 00:18:41 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.498 2017/11/03 03:18:53 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1429,7 +1429,7 @@ main(int ac, char **av) struct sshkey *pubkey; int keytype; Authctxt *authctxt; - struct connection_info *connection_info = get_connection_info(0, 0); + struct connection_info *connection_info = NULL; ssh_malloc_init(); /* must be called before any mallocs */ @@ -1545,6 +1545,7 @@ main(int ac, char **av) test_flag = 2; break; case 'C': + connection_info = get_connection_info(0, 0); if (parse_server_match_testspec(connection_info, optarg) == -1) exit(1); @@ -1613,14 +1614,10 @@ main(int ac, char **av) sensitive_data.have_ssh2_key = 0; /* - * If we're doing an extended config test, make sure we have all of - * the parameters we need. If we're not doing an extended test, - * do not silently ignore connection test params. + * If we're not doing an extended test do not silently ignore connection + * test params. */ - if (test_flag >= 2 && server_match_spec_complete(connection_info) == 0) - fatal("user, host and addr are all required when testing " - "Match configs"); - if (test_flag < 2 && server_match_spec_complete(connection_info) >= 0) + if (test_flag < 2 && connection_info != NULL) fatal("Config test connection parameter (-C) provided without " "test mode (-T)"); @@ -1827,8 +1824,7 @@ main(int ac, char **av) } if (test_flag > 1) { - if (server_match_spec_complete(connection_info) == 1) - parse_server_match_config(&options, connection_info); + parse_server_match_config(&options, connection_info); dump_config(&options); } -- cgit v1.2.3 From 548d3a66feb64c405733932a6b1abeaf7198fa71 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org@openbsd.org" Date: Tue, 14 Nov 2017 00:45:29 +0000 Subject: upstream commit fix problem in configuration parsing when in config dump mode (sshd -T) without providing a full connection specification (sshd -T -C ...) spotted by bluhm@ OpenBSD-Commit-ID: 7125faf5740eaa9d3a2f25400a0bc85e94e28b8f --- sshd.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'sshd.c') diff --git a/sshd.c b/sshd.c index 73094001b..a69537bc8 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.498 2017/11/03 03:18:53 dtucker Exp $ */ +/* $OpenBSD: sshd.c,v 1.499 2017/11/14 00:45:29 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -144,7 +144,12 @@ char *config_file_name = _PATH_SERVER_CONFIG_FILE; */ int debug_flag = 0; -/* Flag indicating that the daemon should only test the configuration and keys. */ +/* + * Indicating that the daemon should only test the configuration and keys. + * If test_flag > 1 ("-T" flag), then sshd will also dump the effective + * configuration, optionally using connection information provided by the + * "-C" flag. + */ int test_flag = 0; /* Flag indicating that the daemon is being started from inetd. */ @@ -1824,6 +1829,12 @@ main(int ac, char **av) } if (test_flag > 1) { + /* + * If no connection info was provided by -C then use + * use a blank one that will cause no predicate to match. + */ + if (connection_info == NULL) + connection_info = get_connection_info(0, 0); parse_server_match_config(&options, connection_info); dump_config(&options); } -- cgit v1.2.3 From d6364f6fb1a3d753d7ca9bf15b2adce961324513 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Tue, 23 Jan 2018 05:01:15 +0000 Subject: upstream commit avoid modifying pw->pw_passwd; let endpwent() clean up for us, but keep a scrubbed copy; bz2777, ok dtucker@ OpenBSD-Commit-ID: 715afc0f59c6b82c4929a73279199ed241ce0752 --- sshd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'sshd.c') diff --git a/sshd.c b/sshd.c index a69537bc8..858dd45a8 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.499 2017/11/14 00:45:29 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.500 2018/01/23 05:01:15 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1695,10 +1695,8 @@ main(int ac, char **av) fatal("Privilege separation user %s does not exist", SSH_PRIVSEP_USER); } else { - explicit_bzero(privsep_pw->pw_passwd, - strlen(privsep_pw->pw_passwd)); privsep_pw = pwcopy(privsep_pw); - free(privsep_pw->pw_passwd); + freezero(privsep_pw->pw_passwd, strlen(privsep_pw->pw_passwd)); privsep_pw->pw_passwd = xstrdup("*"); } endpwent(); -- cgit v1.2.3 From 9e9c4a7e57b96ab29fe6d7545ed09d2e5bddbdec Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Tue, 23 Jan 2018 05:12:12 +0000 Subject: upstream commit unbreak support for clients that advertise a protocol version of "1.99" (indicating both v2 and v1 support). Busted by me during SSHv1 purge in r1.358; bz2810, ok dtucker OpenBSD-Commit-ID: e8f9c2bee11afc16c872bb79d6abe9c555bd0e4b --- sshd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sshd.c') diff --git a/sshd.c b/sshd.c index 858dd45a8..6133135db 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.500 2018/01/23 05:01:15 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.501 2018/01/23 05:12:12 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -455,7 +455,7 @@ sshd_exchange_identification(struct ssh *ssh, int sock_in, int sock_out) debug("Local version string %.200s", server_version_string); if (remote_major != 2 || - (remote_major == 1 && remote_minor != 99)) { + !(remote_major == 1 && remote_minor == 99)) { s = "Protocol major versions differ.\n"; (void) atomicio(vwrite, sock_out, s, strlen(s)); close(sock_in); -- cgit v1.2.3 From 14b5c635d1190633b23ac3372379517fb645b0c2 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Tue, 23 Jan 2018 05:27:21 +0000 Subject: upstream commit Drop compatibility hacks for some ancient SSH implementations, including ssh.com <=2.* and OpenSSH <= 3.*. These versions were all released in or before 2001 and predate the final SSH RFCs. The hacks in question aren't necessary for RFC- compliant SSH implementations. ok markus@ OpenBSD-Commit-ID: 4be81c67db57647f907f4e881fb9341448606138 --- auth2-hostbased.c | 8 +++--- auth2-pubkey.c | 47 +++++++------------------------- auth2.c | 4 +-- authfd.c | 4 +-- channels.c | 45 +++++++++++-------------------- clientloop.c | 15 +++-------- compat.c | 71 +++--------------------------------------------- compat.h | 28 +++++++++---------- kex.c | 8 ++---- monitor.c | 21 ++++++--------- serverloop.c | 8 +++--- ssh-agent.c | 4 +-- ssh-dss.c | 81 +++++++++++++++++++++---------------------------------- ssh.c | 4 +-- sshconnect.c | 5 +--- sshconnect2.c | 55 +++++++------------------------------ sshd.c | 6 +---- 17 files changed, 111 insertions(+), 303 deletions(-) (limited to 'sshd.c') diff --git a/auth2-hostbased.c b/auth2-hostbased.c index 8112cfaa3..8996f7e05 100644 --- a/auth2-hostbased.c +++ b/auth2-hostbased.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-hostbased.c,v 1.32 2017/12/18 02:25:15 djm Exp $ */ +/* $OpenBSD: auth2-hostbased.c,v 1.33 2018/01/23 05:27:21 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -62,7 +62,7 @@ userauth_hostbased(struct ssh *ssh) Authctxt *authctxt = ssh->authctxt; struct sshbuf *b; struct sshkey *key = NULL; - char *pkalg, *cuser, *chost, *service; + char *pkalg, *cuser, *chost; u_char *pkblob, *sig; size_t alen, blen, slen; int r, pktype, authenticated = 0; @@ -118,15 +118,13 @@ userauth_hostbased(struct ssh *ssh) goto done; } - service = ssh->compat & SSH_BUG_HBSERVICE ? "ssh-userauth" : - authctxt->service; if ((b = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); /* reconstruct packet */ if ((r = sshbuf_put_string(b, session_id2, session_id2_len)) != 0 || (r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 || (r = sshbuf_put_cstring(b, authctxt->user)) != 0 || - (r = sshbuf_put_cstring(b, service)) != 0 || + (r = sshbuf_put_cstring(b, authctxt->service)) != 0 || (r = sshbuf_put_cstring(b, "hostbased")) != 0 || (r = sshbuf_put_string(b, pkalg, alen)) != 0 || (r = sshbuf_put_string(b, pkblob, blen)) != 0 || diff --git a/auth2-pubkey.c b/auth2-pubkey.c index 0713a9de8..e64982283 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-pubkey.c,v 1.74 2017/12/21 00:00:28 djm Exp $ */ +/* $OpenBSD: auth2-pubkey.c,v 1.75 2018/01/23 05:27:21 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -100,26 +100,10 @@ userauth_pubkey(struct ssh *ssh) debug2("%s: disabled because of invalid user", __func__); return 0; } - if ((r = sshpkt_get_u8(ssh, &have_sig)) != 0) - fatal("%s: sshpkt_get_u8 failed: %s", __func__, ssh_err(r)); - if (ssh->compat & SSH_BUG_PKAUTH) { - debug2("%s: SSH_BUG_PKAUTH", __func__); - if ((b = sshbuf_new()) == NULL) - fatal("%s: sshbuf_new failed", __func__); - /* no explicit pkalg given */ - /* so we have to extract the pkalg from the pkblob */ - /* XXX use sshbuf_from() */ - if ((r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0 || - (r = sshbuf_put(b, pkblob, blen)) != 0 || - (r = sshbuf_get_cstring(b, &pkalg, NULL)) != 0) - fatal("%s: failed: %s", __func__, ssh_err(r)); - sshbuf_free(b); - } else { - if ((r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 || - (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0) - fatal("%s: sshpkt_get_cstring failed: %s", - __func__, ssh_err(r)); - } + if ((r = sshpkt_get_u8(ssh, &have_sig)) != 0 || + (r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 || + (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0) + fatal("%s: parse request failed: %s", __func__, ssh_err(r)); pktype = sshkey_type_from_name(pkalg); if (pktype == KEY_UNSPEC) { /* this is perfectly legal */ @@ -188,22 +172,11 @@ userauth_pubkey(struct ssh *ssh) authctxt->style ? authctxt->style : ""); if ((r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 || (r = sshbuf_put_cstring(b, userstyle)) != 0 || - (r = sshbuf_put_cstring(b, ssh->compat & SSH_BUG_PKSERVICE ? - "ssh-userauth" : authctxt->service)) != 0) - fatal("%s: build packet failed: %s", - __func__, ssh_err(r)); - if (ssh->compat & SSH_BUG_PKAUTH) { - if ((r = sshbuf_put_u8(b, have_sig)) != 0) - fatal("%s: build packet failed: %s", - __func__, ssh_err(r)); - } else { - if ((r = sshbuf_put_cstring(b, "publickey")) != 0 || - (r = sshbuf_put_u8(b, have_sig)) != 0 || - (r = sshbuf_put_cstring(b, pkalg) != 0)) - fatal("%s: build packet failed: %s", - __func__, ssh_err(r)); - } - if ((r = sshbuf_put_string(b, pkblob, blen)) != 0) + (r = sshbuf_put_cstring(b, authctxt->service)) != 0 || + (r = sshbuf_put_cstring(b, "publickey")) != 0 || + (r = sshbuf_put_u8(b, have_sig)) != 0 || + (r = sshbuf_put_cstring(b, pkalg) != 0) || + (r = sshbuf_put_string(b, pkblob, blen)) != 0) fatal("%s: build packet failed: %s", __func__, ssh_err(r)); #ifdef DEBUG_PK diff --git a/auth2.c b/auth2.c index 862e09960..4f5b481c9 100644 --- a/auth2.c +++ b/auth2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2.c,v 1.143 2017/06/24 06:34:38 djm Exp $ */ +/* $OpenBSD: auth2.c,v 1.144 2018/01/23 05:27:21 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -153,7 +153,7 @@ userauth_banner(void) { char *banner = NULL; - if (options.banner == NULL || (datafellows & SSH_BUG_BANNER) != 0) + if (options.banner == NULL) return; if ((banner = PRIVSEP(auth2_read_banner())) == NULL) diff --git a/authfd.c b/authfd.c index a460fa350..ac635dff9 100644 --- a/authfd.c +++ b/authfd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: authfd.c,v 1.105 2017/07/01 13:50:45 djm Exp $ */ +/* $OpenBSD: authfd.c,v 1.106 2018/01/23 05:27:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -353,8 +353,6 @@ ssh_agent_sign(int sock, const struct sshkey *key, if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE) return SSH_ERR_INVALID_ARGUMENT; - if (compat & SSH_BUG_SIGBLOB) - flags |= SSH_AGENT_OLD_SIGNATURE; if ((msg = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) diff --git a/channels.c b/channels.c index c2706ace7..1c381e0e2 100644 --- a/channels.c +++ b/channels.c @@ -1,4 +1,4 @@ -/* $OpenBSD: channels.c,v 1.377 2017/12/05 01:30:19 djm Exp $ */ +/* $OpenBSD: channels.c,v 1.378 2018/01/23 05:27:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1582,13 +1582,8 @@ channel_post_x11_listener(struct ssh *ssh, Channel *c, SSH_CHANNEL_OPENING, newsock, newsock, -1, c->local_window_max, c->local_maxpacket, 0, buf, 1); open_preamble(ssh, __func__, nc, "x11"); - if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0) { - fatal("%s: channel %i: reply %s", __func__, - c->self, ssh_err(r)); - } - if ((datafellows & SSH_BUG_X11FWD) != 0) - debug2("channel %d: ssh2 x11 bug compat mode", nc->self); - else if ((r = sshpkt_put_u32(ssh, remote_port)) != 0) { + if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 || + (r = sshpkt_put_u32(ssh, remote_port)) != 0) { fatal("%s: channel %i: reply %s", __func__, c->self, ssh_err(r)); } @@ -1824,15 +1819,13 @@ channel_post_connecting(struct ssh *ssh, Channel *c, if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 || (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || - (r = sshpkt_put_u32(ssh, SSH2_OPEN_CONNECT_FAILED)) - != 0) - fatal("%s: channel %i: failure: %s", __func__, - c->self, ssh_err(r)); - if ((datafellows & SSH_BUG_OPENFAILURE) == 0 && - ((r = sshpkt_put_cstring(ssh, strerror(err))) != 0 || - (r = sshpkt_put_cstring(ssh, "")) != 0)) + (r = sshpkt_put_u32(ssh, + SSH2_OPEN_CONNECT_FAILED)) != 0 || + (r = sshpkt_put_cstring(ssh, strerror(err))) != 0 || + (r = sshpkt_put_cstring(ssh, "")) != 0) { fatal("%s: channel %i: failure: %s", __func__, c->self, ssh_err(r)); + } if ((r = sshpkt_send(ssh)) != 0) fatal("%s: channel %i: %s", __func__, c->self, ssh_err(r)); @@ -3110,13 +3103,11 @@ channel_input_open_failure(int type, u_int32_t seq, struct ssh *ssh) error("%s: reason: %s", __func__, ssh_err(r)); packet_disconnect("Invalid open failure message"); } - if ((datafellows & SSH_BUG_OPENFAILURE) == 0) { - /* skip language */ - if ((r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 || - (r = sshpkt_get_string_direct(ssh, NULL, NULL)) != 0) { - error("%s: message/lang: %s", __func__, ssh_err(r)); - packet_disconnect("Invalid open failure message"); - } + /* skip language */ + if ((r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 || + (r = sshpkt_get_string_direct(ssh, NULL, NULL)) != 0) { + error("%s: message/lang: %s", __func__, ssh_err(r)); + packet_disconnect("Invalid open failure message"); } ssh_packet_check_eom(ssh); logit("channel %d: open failed: %s%s%s", c->self, @@ -3664,15 +3655,9 @@ static const char * channel_rfwd_bind_host(const char *listen_host) { if (listen_host == NULL) { - if (datafellows & SSH_BUG_RFWD_ADDR) - return "127.0.0.1"; - else - return "localhost"; + return "localhost"; } else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) { - if (datafellows & SSH_BUG_RFWD_ADDR) - return "0.0.0.0"; - else - return ""; + return ""; } else return listen_host; } diff --git a/clientloop.c b/clientloop.c index c187ae570..ed9bebdfa 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.309 2017/12/18 23:16:23 djm Exp $ */ +/* $OpenBSD: clientloop.c,v 1.310 2018/01/23 05:27:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1541,12 +1541,7 @@ client_request_x11(struct ssh *ssh, const char *request_type, int rchan) return NULL; } originator = packet_get_string(NULL); - if (datafellows & SSH_BUG_X11FWD) { - debug2("buggy server: x11 request w/o originator_port"); - originator_port = 0; - } else { - originator_port = packet_get_int(); - } + originator_port = packet_get_int(); packet_check_eom(); /* XXX check permission */ debug("client_request_x11: request from %s %d", originator, @@ -1678,10 +1673,8 @@ client_input_channel_open(int type, u_int32_t seq, struct ssh *ssh) packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(rchan); packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); - if (!(datafellows & SSH_BUG_OPENFAILURE)) { - packet_put_cstring("open failed"); - packet_put_cstring(""); - } + packet_put_cstring("open failed"); + packet_put_cstring(""); packet_send(); } free(ctype); diff --git a/compat.c b/compat.c index d82135e2b..89b302cca 100644 --- a/compat.c +++ b/compat.c @@ -1,4 +1,4 @@ -/* $OpenBSD: compat.c,v 1.104 2017/07/25 09:22:25 dtucker Exp $ */ +/* $OpenBSD: compat.c,v 1.105 2018/01/23 05:27:21 djm Exp $ */ /* * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. * @@ -50,83 +50,20 @@ compat_datafellows(const char *version) char *pat; int bugs; } check[] = { - { "OpenSSH-2.0*," - "OpenSSH-2.1*," - "OpenSSH_2.1*," - "OpenSSH_2.2*", SSH_OLD_SESSIONID|SSH_BUG_BANNER| - SSH_OLD_DHGEX|SSH_BUG_NOREKEY| - SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR}, - { "OpenSSH_2.3.0*", SSH_BUG_BANNER|SSH_BUG_BIGENDIANAES| - SSH_OLD_DHGEX|SSH_BUG_NOREKEY| - SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR}, - { "OpenSSH_2.3.*", SSH_BUG_BIGENDIANAES|SSH_OLD_DHGEX| - SSH_BUG_NOREKEY|SSH_BUG_EXTEOF| - SSH_OLD_FORWARD_ADDR}, - { "OpenSSH_2.5.0p1*," - "OpenSSH_2.5.1p1*", - SSH_BUG_BIGENDIANAES|SSH_OLD_DHGEX| - SSH_BUG_NOREKEY|SSH_BUG_EXTEOF| - SSH_OLD_FORWARD_ADDR}, - { "OpenSSH_2.5.0*," - "OpenSSH_2.5.1*," - "OpenSSH_2.5.2*", SSH_OLD_DHGEX|SSH_BUG_NOREKEY| - SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR}, - { "OpenSSH_2.5.3*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF| - SSH_OLD_FORWARD_ADDR}, { "OpenSSH_2.*," "OpenSSH_3.0*," "OpenSSH_3.1*", SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR}, { "OpenSSH_3.*", SSH_OLD_FORWARD_ADDR }, { "Sun_SSH_1.0*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF}, - { "OpenSSH_4*", 0 }, + { "OpenSSH_2*," + "OpenSSH_3*," + "OpenSSH_4*", 0 }, { "OpenSSH_5*", SSH_NEW_OPENSSH|SSH_BUG_DYNAMIC_RPORT}, { "OpenSSH_6.6.1*", SSH_NEW_OPENSSH}, { "OpenSSH_6.5*," "OpenSSH_6.6*", SSH_NEW_OPENSSH|SSH_BUG_CURVE25519PAD}, { "OpenSSH*", SSH_NEW_OPENSSH }, { "*MindTerm*", 0 }, - { "2.1.0*", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| - SSH_OLD_SESSIONID|SSH_BUG_DEBUG| - SSH_BUG_RSASIGMD5|SSH_BUG_HBSERVICE| - SSH_BUG_FIRSTKEX }, - { "2.1 *", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| - SSH_OLD_SESSIONID|SSH_BUG_DEBUG| - SSH_BUG_RSASIGMD5|SSH_BUG_HBSERVICE| - SSH_BUG_FIRSTKEX }, - { "2.0.13*," - "2.0.14*," - "2.0.15*," - "2.0.16*," - "2.0.17*," - "2.0.18*," - "2.0.19*", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| - SSH_OLD_SESSIONID|SSH_BUG_DEBUG| - SSH_BUG_PKSERVICE|SSH_BUG_X11FWD| - SSH_BUG_PKOK|SSH_BUG_RSASIGMD5| - SSH_BUG_HBSERVICE|SSH_BUG_OPENFAILURE| - SSH_BUG_DUMMYCHAN|SSH_BUG_FIRSTKEX }, - { "2.0.11*," - "2.0.12*", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| - SSH_OLD_SESSIONID|SSH_BUG_DEBUG| - SSH_BUG_PKSERVICE|SSH_BUG_X11FWD| - SSH_BUG_PKAUTH|SSH_BUG_PKOK| - SSH_BUG_RSASIGMD5|SSH_BUG_OPENFAILURE| - SSH_BUG_DUMMYCHAN|SSH_BUG_FIRSTKEX }, - { "2.0.*", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| - SSH_OLD_SESSIONID|SSH_BUG_DEBUG| - SSH_BUG_PKSERVICE|SSH_BUG_X11FWD| - SSH_BUG_PKAUTH|SSH_BUG_PKOK| - SSH_BUG_RSASIGMD5|SSH_BUG_OPENFAILURE| - SSH_BUG_DERIVEKEY|SSH_BUG_DUMMYCHAN| - SSH_BUG_FIRSTKEX }, - { "2.2.0*," - "2.3.0*", SSH_BUG_HMAC|SSH_BUG_DEBUG| - SSH_BUG_RSASIGMD5|SSH_BUG_FIRSTKEX }, - { "2.3.*", SSH_BUG_DEBUG|SSH_BUG_RSASIGMD5| - SSH_BUG_FIRSTKEX }, - { "2.4", SSH_OLD_SESSIONID }, /* Van Dyke */ - { "2.*", SSH_BUG_DEBUG|SSH_BUG_FIRSTKEX| - SSH_BUG_RFWD_ADDR }, { "3.0.*", SSH_BUG_DEBUG }, { "3.0 SecureCRT*", SSH_OLD_SESSIONID }, { "1.7 SecureFX*", SSH_OLD_SESSIONID }, diff --git a/compat.h b/compat.h index 2e7830f1b..246e6ee4c 100644 --- a/compat.h +++ b/compat.h @@ -1,4 +1,4 @@ -/* $OpenBSD: compat.h,v 1.49 2017/04/30 23:13:25 djm Exp $ */ +/* $OpenBSD: compat.h,v 1.50 2018/01/23 05:27:21 djm Exp $ */ /* * Copyright (c) 1999, 2000, 2001 Markus Friedl. All rights reserved. @@ -32,31 +32,31 @@ #define SSH_PROTO_1_PREFERRED 0x02 #define SSH_PROTO_2 0x04 -#define SSH_BUG_SIGBLOB 0x00000001 -#define SSH_BUG_PKSERVICE 0x00000002 -#define SSH_BUG_HMAC 0x00000004 -#define SSH_BUG_X11FWD 0x00000008 +/* #define unused 0x00000001 */ +/* #define unused 0x00000002 */ +/* #define unused 0x00000004 */ +/* #define unused 0x00000008 */ #define SSH_OLD_SESSIONID 0x00000010 -#define SSH_BUG_PKAUTH 0x00000020 +/* #define unused 0x00000020 */ #define SSH_BUG_DEBUG 0x00000040 -#define SSH_BUG_BANNER 0x00000080 +/* #define unused 0x00000080 */ #define SSH_BUG_IGNOREMSG 0x00000100 -#define SSH_BUG_PKOK 0x00000200 +/* #define unused 0x00000200 */ #define SSH_BUG_PASSWORDPAD 0x00000400 #define SSH_BUG_SCANNER 0x00000800 #define SSH_BUG_BIGENDIANAES 0x00001000 #define SSH_BUG_RSASIGMD5 0x00002000 #define SSH_OLD_DHGEX 0x00004000 #define SSH_BUG_NOREKEY 0x00008000 -#define SSH_BUG_HBSERVICE 0x00010000 -#define SSH_BUG_OPENFAILURE 0x00020000 -#define SSH_BUG_DERIVEKEY 0x00040000 -#define SSH_BUG_DUMMYCHAN 0x00100000 +/* #define unused 0x00010000 */ +/* #define unused 0x00020000 */ +/* #define unused 0x00040000 */ +/* #define unused 0x00100000 */ #define SSH_BUG_EXTEOF 0x00200000 #define SSH_BUG_PROBE 0x00400000 -#define SSH_BUG_FIRSTKEX 0x00800000 +/* #define unused 0x00800000 */ #define SSH_OLD_FORWARD_ADDR 0x01000000 -#define SSH_BUG_RFWD_ADDR 0x02000000 +/* #define unused 0x02000000 */ #define SSH_NEW_OPENSSH 0x04000000 #define SSH_BUG_DYNAMIC_RPORT 0x08000000 #define SSH_BUG_CURVE25519PAD 0x10000000 diff --git a/kex.c b/kex.c index d5d5a9dae..83c6199f3 100644 --- a/kex.c +++ b/kex.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.c,v 1.134 2017/06/13 12:13:59 djm Exp $ */ +/* $OpenBSD: kex.c,v 1.135 2018/01/23 05:27:21 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * @@ -675,9 +675,6 @@ choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server) free(name); return SSH_ERR_INTERNAL_ERROR; } - /* truncate the key */ - if (ssh->compat & SSH_BUG_HMAC) - mac->key_len = 16; mac->name = name; mac->key = NULL; mac->enabled = 0; @@ -866,8 +863,7 @@ kex_choose_conf(struct ssh *ssh) kex->dh_need = dh_need; /* ignore the next message if the proposals do not match */ - if (first_kex_follows && !proposals_match(my, peer) && - !(ssh->compat & SSH_BUG_FIRSTKEX)) + if (first_kex_follows && !proposals_match(my, peer)) ssh->dispatch_skip_packets = 1; r = 0; out: diff --git a/monitor.c b/monitor.c index b0227eee1..89c8c958a 100644 --- a/monitor.c +++ b/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.177 2017/12/21 00:00:28 djm Exp $ */ +/* $OpenBSD: monitor.c,v 1.178 2018/01/23 05:27:21 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -1255,18 +1255,13 @@ monitor_valid_userblob(u_char *data, u_int datalen) free(userstyle); free(cp); buffer_skip_string(&b); - if (datafellows & SSH_BUG_PKAUTH) { - if (!buffer_get_char(&b)) - fail++; - } else { - cp = buffer_get_cstring(&b, NULL); - if (strcmp("publickey", cp) != 0) - fail++; - free(cp); - if (!buffer_get_char(&b)) - fail++; - buffer_skip_string(&b); - } + cp = buffer_get_cstring(&b, NULL); + if (strcmp("publickey", cp) != 0) + fail++; + free(cp); + if (!buffer_get_char(&b)) + fail++; + buffer_skip_string(&b); buffer_skip_string(&b); if (buffer_len(&b) != 0) fail++; diff --git a/serverloop.c b/serverloop.c index 615921c38..feaf35c1a 100644 --- a/serverloop.c +++ b/serverloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: serverloop.c,v 1.202 2017/12/18 23:16:24 djm Exp $ */ +/* $OpenBSD: serverloop.c,v 1.203 2018/01/23 05:27:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -655,10 +655,8 @@ server_input_channel_open(int type, u_int32_t seq, struct ssh *ssh) packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(rchan); packet_put_int(reason); - if (!(datafellows & SSH_BUG_OPENFAILURE)) { - packet_put_cstring(errmsg ? errmsg : "open failed"); - packet_put_cstring(""); - } + packet_put_cstring(errmsg ? errmsg : "open failed"); + packet_put_cstring(""); packet_send(); } free(ctype); diff --git a/ssh-agent.c b/ssh-agent.c index 8cb00f620..39888a72c 100644 --- a/ssh-agent.c +++ b/ssh-agent.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-agent.c,v 1.226 2017/11/15 02:10:16 djm Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.227 2018/01/23 05:27:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -292,8 +292,6 @@ process_sign_request2(SocketEntry *e) goto send; } - if (flags & SSH_AGENT_OLD_SIGNATURE) - compat = SSH_BUG_SIGBLOB; if ((id = lookup_identity(key)) == NULL) { verbose("%s: %s key not found", __func__, sshkey_type(key)); goto send; diff --git a/ssh-dss.c b/ssh-dss.c index 7af59fa6e..cda498a87 100644 --- a/ssh-dss.c +++ b/ssh-dss.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-dss.c,v 1.35 2016/04/21 06:08:02 djm Exp $ */ +/* $OpenBSD: ssh-dss.c,v 1.36 2018/01/23 05:27:21 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -86,38 +86,25 @@ ssh_dss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, BN_bn2bin(sig->r, sigblob + SIGBLOB_LEN - INTBLOB_LEN - rlen); BN_bn2bin(sig->s, sigblob + SIGBLOB_LEN - slen); - if (compat & SSH_BUG_SIGBLOB) { - if (sigp != NULL) { - if ((*sigp = malloc(SIGBLOB_LEN)) == NULL) { - ret = SSH_ERR_ALLOC_FAIL; - goto out; - } - memcpy(*sigp, sigblob, SIGBLOB_LEN); - } - if (lenp != NULL) - *lenp = SIGBLOB_LEN; - ret = 0; - } else { - /* ietf-drafts */ - if ((b = sshbuf_new()) == NULL) { + if ((b = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((ret = sshbuf_put_cstring(b, "ssh-dss")) != 0 || + (ret = sshbuf_put_string(b, sigblob, SIGBLOB_LEN)) != 0) + goto out; + + len = sshbuf_len(b); + if (sigp != NULL) { + if ((*sigp = malloc(len)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } - if ((ret = sshbuf_put_cstring(b, "ssh-dss")) != 0 || - (ret = sshbuf_put_string(b, sigblob, SIGBLOB_LEN)) != 0) - goto out; - len = sshbuf_len(b); - if (sigp != NULL) { - if ((*sigp = malloc(len)) == NULL) { - ret = SSH_ERR_ALLOC_FAIL; - goto out; - } - memcpy(*sigp, sshbuf_ptr(b), len); - } - if (lenp != NULL) - *lenp = len; - ret = 0; + memcpy(*sigp, sshbuf_ptr(b), len); } + if (lenp != NULL) + *lenp = len; + ret = 0; out: explicit_bzero(digest, sizeof(digest)); if (sig != NULL) @@ -146,28 +133,20 @@ ssh_dss_verify(const struct sshkey *key, return SSH_ERR_INTERNAL_ERROR; /* fetch signature */ - if (compat & SSH_BUG_SIGBLOB) { - if ((sigblob = malloc(signaturelen)) == NULL) - return SSH_ERR_ALLOC_FAIL; - memcpy(sigblob, signature, signaturelen); - len = signaturelen; - } else { - /* ietf-drafts */ - if ((b = sshbuf_from(signature, signaturelen)) == NULL) - return SSH_ERR_ALLOC_FAIL; - if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || - sshbuf_get_string(b, &sigblob, &len) != 0) { - ret = SSH_ERR_INVALID_FORMAT; - goto out; - } - if (strcmp("ssh-dss", ktype) != 0) { - ret = SSH_ERR_KEY_TYPE_MISMATCH; - goto out; - } - if (sshbuf_len(b) != 0) { - ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; - goto out; - } + if ((b = sshbuf_from(signature, signaturelen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || + sshbuf_get_string(b, &sigblob, &len) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (strcmp("ssh-dss", ktype) != 0) { + ret = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } + if (sshbuf_len(b) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; } if (len != SIGBLOB_LEN) { diff --git a/ssh.c b/ssh.c index ac85b2bb5..af4597f88 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.470 2018/01/23 05:06:25 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.471 2018/01/23 05:27:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1931,7 +1931,7 @@ ssh_session2(struct ssh *ssh, struct passwd *pw) if (options.control_persist && muxserver_sock == -1) ssh_init_stdio_forwarding(ssh); - if (!no_shell_flag || (datafellows & SSH_BUG_DUMMYCHAN)) + if (!no_shell_flag) id = ssh_session2_open(ssh); else { packet_set_interactive( diff --git a/sshconnect.c b/sshconnect.c index c25e192c8..0e195a31d 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.290 2018/01/23 05:17:04 djm Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.291 2018/01/23 05:27:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -617,9 +617,6 @@ ssh_exchange_identification(int timeout_ms) if (mismatch) fatal("Protocol major versions differ: %d vs. %d", PROTOCOL_MAJOR_2, remote_major); - if ((datafellows & SSH_BUG_DERIVEKEY) != 0) - fatal("Server version \"%.100s\" uses unsafe key agreement; " - "refusing connection", remote_version); if ((datafellows & SSH_BUG_RSASIGMD5) != 0) logit("Server version \"%.100s\" uses unsafe RSA signature " "scheme; disabling use of RSA keys", remote_version); diff --git a/sshconnect2.c b/sshconnect2.c index be9397e48..870c3d270 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect2.c,v 1.266 2017/08/27 00:38:41 dtucker Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.267 2018/01/23 05:27:21 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. @@ -578,7 +578,6 @@ input_userauth_pk_ok(int type, u_int32_t seq, struct ssh *ssh) Authctxt *authctxt = ssh->authctxt; struct sshkey *key = NULL; Identity *id = NULL; - Buffer b; int pktype, sent = 0; u_int alen, blen; char *pkalg, *fp; @@ -586,18 +585,9 @@ input_userauth_pk_ok(int type, u_int32_t seq, struct ssh *ssh) if (authctxt == NULL) fatal("input_userauth_pk_ok: no authentication context"); - if (datafellows & SSH_BUG_PKOK) { - /* this is similar to SSH_BUG_PKAUTH */ - debug2("input_userauth_pk_ok: SSH_BUG_PKOK"); - pkblob = packet_get_string(&blen); - buffer_init(&b); - buffer_append(&b, pkblob, blen); - pkalg = buffer_get_string(&b, &alen); - buffer_free(&b); - } else { - pkalg = packet_get_string(&alen); - pkblob = packet_get_string(&blen); - } + + pkalg = packet_get_string(&alen); + pkblob = packet_get_string(&blen); packet_check_eom(); debug("Server accepts key: pkalg %s blen %u", pkalg, blen); @@ -1100,17 +1090,10 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id) } buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); buffer_put_cstring(&b, authctxt->server_user); - buffer_put_cstring(&b, - datafellows & SSH_BUG_PKSERVICE ? - "ssh-userauth" : - authctxt->service); - if (datafellows & SSH_BUG_PKAUTH) { - buffer_put_char(&b, have_sig); - } else { - buffer_put_cstring(&b, authctxt->method->name); - buffer_put_char(&b, have_sig); - buffer_put_cstring(&b, key_sign_encode(id->key)); - } + buffer_put_cstring(&b, authctxt->service); + buffer_put_cstring(&b, authctxt->method->name); + buffer_put_char(&b, have_sig); + buffer_put_cstring(&b, key_sign_encode(id->key)); buffer_put_string(&b, blob, bloblen); /* @@ -1170,19 +1153,6 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id) #ifdef DEBUG_PK buffer_dump(&b); #endif - if (datafellows & SSH_BUG_PKSERVICE) { - buffer_clear(&b); - buffer_append(&b, session_id2, session_id2_len); - skip = session_id2_len; - buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); - buffer_put_cstring(&b, authctxt->server_user); - buffer_put_cstring(&b, authctxt->service); - buffer_put_cstring(&b, authctxt->method->name); - buffer_put_char(&b, have_sig); - if (!(datafellows & SSH_BUG_PKAUTH)) - buffer_put_cstring(&b, key_ssh_name(id->key)); - buffer_put_string(&b, blob, bloblen); - } free(blob); /* append signature */ @@ -1224,8 +1194,7 @@ send_pubkey_test(Authctxt *authctxt, Identity *id) packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_put_char(have_sig); - if (!(datafellows & SSH_BUG_PKAUTH)) - packet_put_cstring(key_sign_encode(id->key)); + packet_put_cstring(key_sign_encode(id->key)); packet_put_string(blob, bloblen); free(blob); packet_send(); @@ -1741,7 +1710,6 @@ userauth_hostbased(Authctxt *authctxt) struct ssh *ssh = active_state; struct sshkey *private = NULL; struct sshbuf *b = NULL; - const char *service; u_char *sig = NULL, *keyblob = NULL; char *fp = NULL, *chost = NULL, *lname = NULL; size_t siglen = 0, keylen = 0; @@ -1812,9 +1780,6 @@ userauth_hostbased(Authctxt *authctxt) xasprintf(&chost, "%s.", lname); debug2("%s: chost %s", __func__, chost); - service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" : - authctxt->service; - /* construct data */ if ((b = sshbuf_new()) == NULL) { error("%s: sshbuf_new failed", __func__); @@ -1827,7 +1792,7 @@ userauth_hostbased(Authctxt *authctxt) if ((r = sshbuf_put_string(b, session_id2, session_id2_len)) != 0 || (r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 || (r = sshbuf_put_cstring(b, authctxt->server_user)) != 0 || - (r = sshbuf_put_cstring(b, service)) != 0 || + (r = sshbuf_put_cstring(b, authctxt->service)) != 0 || (r = sshbuf_put_cstring(b, authctxt->method->name)) != 0 || (r = sshbuf_put_cstring(b, key_ssh_name(private))) != 0 || (r = sshbuf_put_string(b, keyblob, keylen)) != 0 || diff --git a/sshd.c b/sshd.c index 6133135db..e7e3c99b4 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.501 2018/01/23 05:12:12 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.502 2018/01/23 05:27:21 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -446,10 +446,6 @@ sshd_exchange_identification(struct ssh *ssh, int sock_in, int sock_out) logit("Client version \"%.100s\" uses unsafe RSA signature " "scheme; disabling use of RSA keys", remote_version); } - if ((ssh->compat & SSH_BUG_DERIVEKEY) != 0) { - fatal("Client version \"%.100s\" uses unsafe key agreement; " - "refusing connection", remote_version); - } chop(server_version_string); debug("Local version string %.200s", server_version_string); -- cgit v1.2.3 From c9c1bba06ad1c7cad8548549a68c071bd807af60 Mon Sep 17 00:00:00 2001 From: "stsp@openbsd.org" Date: Tue, 23 Jan 2018 20:00:58 +0000 Subject: upstream commit Fix a logic bug in sshd_exchange_identification which prevented clients using major protocol version 2 from connecting to the server. ok millert@ OpenBSD-Commit-ID: 8668dec04586e27f1c0eb039ef1feb93d80a5ee9 --- sshd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sshd.c') diff --git a/sshd.c b/sshd.c index e7e3c99b4..77231c33b 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.502 2018/01/23 05:27:21 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.503 2018/01/23 20:00:58 stsp Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -450,7 +450,7 @@ sshd_exchange_identification(struct ssh *ssh, int sock_in, int sock_out) chop(server_version_string); debug("Local version string %.200s", server_version_string); - if (remote_major != 2 || + if (remote_major != 2 && !(remote_major == 1 && remote_minor == 99)) { s = "Protocol major versions differ.\n"; (void) atomicio(vwrite, sock_out, s, strlen(s)); -- cgit v1.2.3 From 05046d907c211cb9b4cd21b8eff9e7a46cd6c5ab Mon Sep 17 00:00:00 2001 From: "dtucker@openbsd.org" Date: Sun, 11 Feb 2018 21:16:56 +0000 Subject: upstream Don't reset signal handlers inside handlers. The signal handlers from the original ssh1 code on which OpenSSH is based assume unreliable signals and reinstall their handlers. Since OpenBSD (and pretty much every current system) has reliable signals this is not needed. In the unlikely even that -portable is still being used on such systems we will deal with it in the compat layer. ok deraadt@ OpenBSD-Commit-ID: f53a1015cb6908431b92116130d285d71589612c --- clientloop.c | 3 +-- serverloop.c | 5 +---- ssh.c | 4 +--- sshd.c | 5 +---- 4 files changed, 4 insertions(+), 13 deletions(-) (limited to 'sshd.c') diff --git a/clientloop.c b/clientloop.c index ed9bebdfa..7bcf22e38 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.310 2018/01/23 05:27:21 djm Exp $ */ +/* $OpenBSD: clientloop.c,v 1.311 2018/02/11 21:16:56 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -210,7 +210,6 @@ static void window_change_handler(int sig) { received_window_change_signal = 1; - signal(SIGWINCH, window_change_handler); } /* diff --git a/serverloop.c b/serverloop.c index 9d789a213..e9bd9b5e5 100644 --- a/serverloop.c +++ b/serverloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: serverloop.c,v 1.203 2018/01/23 05:27:21 djm Exp $ */ +/* $OpenBSD: serverloop.c,v 1.204 2018/02/11 21:16:56 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -153,9 +153,6 @@ sigchld_handler(int sig) { int save_errno = errno; child_terminated = 1; -#ifndef _UNICOS - mysignal(SIGCHLD, sigchld_handler); -#endif notify_parent(); errno = save_errno; } diff --git a/ssh.c b/ssh.c index af4597f88..c0bdd38bc 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.471 2018/01/23 05:27:21 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.472 2018/02/11 21:16:56 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -2132,7 +2132,5 @@ main_sigchld_handler(int sig) while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || (pid < 0 && errno == EINTR)) ; - - signal(sig, main_sigchld_handler); errno = save_errno; } diff --git a/sshd.c b/sshd.c index 77231c33b..17931068d 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.503 2018/01/23 20:00:58 stsp Exp $ */ +/* $OpenBSD: sshd.c,v 1.504 2018/02/11 21:16:56 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -283,7 +283,6 @@ sighup_handler(int sig) int save_errno = errno; received_sighup = 1; - signal(SIGHUP, sighup_handler); errno = save_errno; } @@ -333,8 +332,6 @@ main_sigchld_handler(int sig) while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || (pid < 0 && errno == EINTR)) ; - - signal(SIGCHLD, main_sigchld_handler); errno = save_errno; } -- cgit v1.2.3 From ddc0f3814881ea279a6b6d4d98e03afc60ae1ed7 Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Tue, 13 Feb 2018 09:10:46 +1100 Subject: Remove UNICOS support. The code required to support it is quite invasive to the mainline code that is synced with upstream and is an ongoing maintenance burden. Both the hardware and software are literal museum pieces these days and we could not find anyone still running OpenSSH on one. --- auth2.c | 8 - configure.ac | 34 -- defines.h | 24 -- loginrec.c | 6 - openbsd-compat/Makefile.in | 2 +- openbsd-compat/bsd-cray.c | 816 ---------------------------------------- openbsd-compat/bsd-cray.h | 59 --- openbsd-compat/bsd-openpty.c | 11 - openbsd-compat/openbsd-compat.h | 1 - session.c | 23 -- sshd.c | 7 - sshpty.c | 25 -- 12 files changed, 1 insertion(+), 1015 deletions(-) delete mode 100644 openbsd-compat/bsd-cray.c delete mode 100644 openbsd-compat/bsd-cray.h (limited to 'sshd.c') diff --git a/auth2.c b/auth2.c index 67b6b05e8..c80911aeb 100644 --- a/auth2.c +++ b/auth2.c @@ -349,13 +349,6 @@ userauth_finish(struct ssh *ssh, int authenticated, const char *method, } #endif -#ifdef _UNICOS - if (authenticated && cray_access_denied(authctxt->user)) { - authenticated = 0; - fatal("Access denied for user %s.", authctxt->user); - } -#endif /* _UNICOS */ - if (authenticated == 1) { /* turn off userauth */ ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore); @@ -366,7 +359,6 @@ userauth_finish(struct ssh *ssh, int authenticated, const char *method, authctxt->success = 1; ssh_packet_set_log_preamble(ssh, "user %s", authctxt->user); } else { - /* Allow initial try of "none" auth without failure penalty */ if (!partial && !authctxt->server_caused_failure && (authctxt->attempt > 1 || strcmp(method, "none") != 0)) diff --git a/configure.ac b/configure.ac index 753395f10..38600e321 100644 --- a/configure.ac +++ b/configure.ac @@ -1073,40 +1073,6 @@ mips-sony-bsd|mips-sony-newsos4) TEST_SHELL=$SHELL # let configure find us a capable shell SKIP_DISABLE_LASTLOG_DEFINE=yes ;; -*-*-unicosmk*) - AC_DEFINE([NO_SSH_LASTLOG], [1], - [Define if you don't want to use lastlog in session.c]) - AC_DEFINE([SETEUID_BREAKS_SETUID]) - AC_DEFINE([BROKEN_SETREUID]) - AC_DEFINE([BROKEN_SETREGID]) - AC_DEFINE([USE_PIPES]) - AC_DEFINE([DISABLE_FD_PASSING]) - LDFLAGS="$LDFLAGS" - LIBS="$LIBS -lgen -lrsc -lshare -luex -lacm" - MANTYPE=cat - ;; -*-*-unicosmp*) - AC_DEFINE([SETEUID_BREAKS_SETUID]) - AC_DEFINE([BROKEN_SETREUID]) - AC_DEFINE([BROKEN_SETREGID]) - AC_DEFINE([WITH_ABBREV_NO_TTY]) - AC_DEFINE([USE_PIPES]) - AC_DEFINE([DISABLE_FD_PASSING]) - LDFLAGS="$LDFLAGS" - LIBS="$LIBS -lgen -lacid -ldb" - MANTYPE=cat - ;; -*-*-unicos*) - AC_DEFINE([SETEUID_BREAKS_SETUID]) - AC_DEFINE([BROKEN_SETREUID]) - AC_DEFINE([BROKEN_SETREGID]) - AC_DEFINE([USE_PIPES]) - AC_DEFINE([DISABLE_FD_PASSING]) - AC_DEFINE([NO_SSH_LASTLOG]) - LDFLAGS="$LDFLAGS -Wl,-Dmsglevel=334:fatal" - LIBS="$LIBS -lgen -lrsc -lshare -luex -lacm" - MANTYPE=cat - ;; *-dec-osf*) AC_MSG_CHECKING([for Digital Unix SIA]) no_osfsia="" diff --git a/defines.h b/defines.h index f1662edcf..3fa5ec5a9 100644 --- a/defines.h +++ b/defines.h @@ -214,24 +214,12 @@ typedef signed char int8_t; # if (SIZEOF_SHORT_INT == 2) typedef short int int16_t; # else -# ifdef _UNICOS -# if (SIZEOF_SHORT_INT == 4) -typedef short int16_t; -# else -typedef long int16_t; -# endif -# else # error "16 bit int type not found." -# endif /* _UNICOS */ # endif # if (SIZEOF_INT == 4) typedef int int32_t; # else -# ifdef _UNICOS -typedef long int32_t; -# else # error "32 bit int type not found." -# endif /* _UNICOS */ # endif #endif @@ -247,24 +235,12 @@ typedef unsigned char u_int8_t; # if (SIZEOF_SHORT_INT == 2) typedef unsigned short int u_int16_t; # else -# ifdef _UNICOS -# if (SIZEOF_SHORT_INT == 4) -typedef unsigned short u_int16_t; -# else -typedef unsigned long u_int16_t; -# endif -# else # error "16 bit int type not found." -# endif # endif # if (SIZEOF_INT == 4) typedef unsigned int u_int32_t; # else -# ifdef _UNICOS -typedef unsigned long u_int32_t; -# else # error "32 bit int type not found." -# endif # endif # endif #define __BIT_TYPES_DEFINED__ diff --git a/loginrec.c b/loginrec.c index 788553e92..bdbc9bbf4 100644 --- a/loginrec.c +++ b/loginrec.c @@ -663,15 +663,9 @@ construct_utmp(struct logininfo *li, switch (li->type) { case LTYPE_LOGIN: ut->ut_type = USER_PROCESS; -#ifdef _UNICOS - cray_set_tmpdir(ut); -#endif break; case LTYPE_LOGOUT: ut->ut_type = DEAD_PROCESS; -#ifdef _UNICOS - cray_retain_utmp(ut, li->pid); -#endif break; } # endif diff --git a/openbsd-compat/Makefile.in b/openbsd-compat/Makefile.in index 213ded4d4..62dbf8566 100644 --- a/openbsd-compat/Makefile.in +++ b/openbsd-compat/Makefile.in @@ -18,7 +18,7 @@ LDFLAGS=-L. @LDFLAGS@ OPENBSD=base64.o basename.o bcrypt_pbkdf.o bindresvport.o blowfish.o daemon.o dirname.o fmt_scaled.o getcwd.o getgrouplist.o getopt_long.o getrrsetbyname.o glob.o inet_aton.o inet_ntoa.o inet_ntop.o mktemp.o pwcache.o readpassphrase.o reallocarray.o realpath.o recallocarray.o rresvport.o setenv.o setproctitle.o sha1.o sha2.o rmd160.o md5.o sigact.o strcasestr.o strlcat.o strlcpy.o strmode.o strnlen.o strptime.o strsep.o strtonum.o strtoll.o strtoul.o strtoull.o timingsafe_bcmp.o vis.o blowfish.o bcrypt_pbkdf.o explicit_bzero.o freezero.o -COMPAT=arc4random.o bsd-asprintf.o bsd-closefrom.o bsd-cray.o bsd-cygwin_util.o bsd-getpeereid.o getrrsetbyname-ldns.o bsd-err.o bsd-getpagesize.o bsd-misc.o bsd-nextstep.o bsd-openpty.o bsd-poll.o bsd-malloc.o bsd-setres_id.o bsd-signal.o bsd-snprintf.o bsd-statvfs.o bsd-waitpid.o fake-rfc2553.o openssl-compat.o xcrypt.o kludge-fd_set.o +COMPAT=arc4random.o bsd-asprintf.o bsd-closefrom.o bsd-cygwin_util.o bsd-getpeereid.o getrrsetbyname-ldns.o bsd-err.o bsd-getpagesize.o bsd-misc.o bsd-nextstep.o bsd-openpty.o bsd-poll.o bsd-malloc.o bsd-setres_id.o bsd-signal.o bsd-snprintf.o bsd-statvfs.o bsd-waitpid.o fake-rfc2553.o openssl-compat.o xcrypt.o kludge-fd_set.o PORTS=port-aix.o port-irix.o port-linux.o port-solaris.o port-net.o port-uw.o diff --git a/openbsd-compat/bsd-cray.c b/openbsd-compat/bsd-cray.c deleted file mode 100644 index c02e63261..000000000 --- a/openbsd-compat/bsd-cray.c +++ /dev/null @@ -1,816 +0,0 @@ -/* - * - * bsd-cray.c - * - * Copyright (c) 2002, Cray Inc. (Wendy Palm ) - * Significant portions provided by - * Wayne Schroeder, SDSC - * William Jones, UTexas - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Created: Apr 22 16.34:00 2002 wp - * - * This file contains functions required for proper execution - * on UNICOS systems. - * - */ -#ifdef _UNICOS - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ssh.h" - -#include "includes.h" -#include "sys/types.h" - -#ifndef HAVE_STRUCT_SOCKADDR_STORAGE -# define _SS_MAXSIZE 128 /* Implementation specific max size */ -# define _SS_PADSIZE (_SS_MAXSIZE - sizeof (struct sockaddr)) - -# define ss_family ss_sa.sa_family -#endif /* !HAVE_STRUCT_SOCKADDR_STORAGE */ - -#ifndef IN6_IS_ADDR_LOOPBACK -# define IN6_IS_ADDR_LOOPBACK(a) \ - (((u_int32_t *) (a))[0] == 0 && ((u_int32_t *) (a))[1] == 0 && \ - ((u_int32_t *) (a))[2] == 0 && ((u_int32_t *) (a))[3] == htonl (1)) -#endif /* !IN6_IS_ADDR_LOOPBACK */ - -#ifndef AF_INET6 -/* Define it to something that should never appear */ -#define AF_INET6 AF_MAX -#endif - -#include "log.h" -#include "servconf.h" -#include "bsd-cray.h" - -#define MAXACID 80 - -extern ServerOptions options; - -char cray_tmpdir[TPATHSIZ + 1]; /* job TMPDIR path */ - -struct sysv sysv; /* system security structure */ -struct usrv usrv; /* user security structure */ - -/* - * Functions. - */ -void cray_retain_utmp(struct utmp *, int); -void cray_delete_tmpdir(char *, int, uid_t); -void cray_init_job(struct passwd *); -void cray_set_tmpdir(struct utmp *); -void cray_login_failure(char *, int); -int cray_setup(uid_t, char *, const char *); -int cray_access_denied(char *); - -void -cray_login_failure(char *username, int errcode) -{ - struct udb *ueptr; /* UDB pointer for username */ - ia_failure_t fsent; /* ia_failure structure */ - ia_failure_ret_t fret; /* ia_failure return stuff */ - struct jtab jtab; /* job table structure */ - int jid = 0; /* job id */ - - if ((jid = getjtab(&jtab)) < 0) - debug("cray_login_failure(): getjtab error"); - - getsysudb(); - if ((ueptr = getudbnam(username)) == UDB_NULL) - debug("cray_login_failure(): getudbname() returned NULL"); - endudb(); - - memset(&fsent, '\0', sizeof(fsent)); - fsent.revision = 0; - fsent.uname = username; - fsent.host = (char *)get_canonical_hostname(options.use_dns); - fsent.ttyn = "sshd"; - fsent.caller = IA_SSHD; - fsent.flags = IA_INTERACTIVE; - fsent.ueptr = ueptr; - fsent.jid = jid; - fsent.errcode = errcode; - fsent.pwdp = NULL; - fsent.exitcode = 0; /* dont exit in ia_failure() */ - - fret.revision = 0; - fret.normal = 0; - - /* - * Call ia_failure because of an login failure. - */ - ia_failure(&fsent, &fret); -} - -/* - * Cray access denied - */ -int -cray_access_denied(char *username) -{ - struct udb *ueptr; /* UDB pointer for username */ - int errcode; /* IA errorcode */ - - errcode = 0; - getsysudb(); - if ((ueptr = getudbnam(username)) == UDB_NULL) - debug("cray_login_failure(): getudbname() returned NULL"); - endudb(); - - if (ueptr != NULL && ueptr->ue_disabled) - errcode = IA_DISABLED; - if (errcode) - cray_login_failure(username, errcode); - - return (errcode); -} - -/* - * record_failed_login: generic "login failed" interface function - */ -void -record_failed_login(const char *user, const char *hostname, const char *ttyname) -{ - cray_login_failure((char *)user, IA_UDBERR); -} - -int -cray_setup (uid_t uid, char *username, const char *command) -{ - extern struct udb *getudb(); - extern char *setlimits(); - - int err; /* error return */ - time_t system_time; /* current system clock */ - time_t expiration_time; /* password expiration time */ - int maxattempts; /* maximum no. of failed login attempts */ - int SecureSys; /* unicos security flag */ - int minslevel = 0; /* system minimum security level */ - int i, j; - int valid_acct = -1; /* flag for reading valid acct */ - char acct_name[MAXACID] = { "" }; /* used to read acct name */ - struct jtab jtab; /* Job table struct */ - struct udb ue; /* udb entry for logging-in user */ - struct udb *up; /* pointer to UDB entry */ - struct secstat secinfo; /* file security attributes */ - struct servprov init_info; /* used for sesscntl() call */ - int jid; /* job ID */ - int pid; /* process ID */ - char *sr; /* status return from setlimits() */ - char *ttyn = NULL; /* ttyname or command name*/ - char hostname[MAXHOSTNAMELEN]; - /* passwd stuff for ia_user */ - passwd_t pwdacm, pwddialup, pwdudb, pwdwal, pwddce; - ia_user_ret_t uret; /* stuff returned from ia_user */ - ia_user_t usent; /* ia_user main structure */ - int ia_rcode; /* ia_user return code */ - ia_failure_t fsent; /* ia_failure structure */ - ia_failure_ret_t fret; /* ia_failure return stuff */ - ia_success_t ssent; /* ia_success structure */ - ia_success_ret_t sret; /* ia_success return stuff */ - int ia_mlsrcode; /* ia_mlsuser return code */ - int secstatrc; /* [f]secstat return code */ - - if (SecureSys = (int)sysconf(_SC_CRAY_SECURE_SYS)) { - getsysv(&sysv, sizeof(struct sysv)); - minslevel = sysv.sy_minlvl; - if (getusrv(&usrv) < 0) - fatal("getusrv() failed, errno = %d", errno); - } - hostname[0] = '\0'; - strlcpy(hostname, - (char *)get_canonical_hostname(options.use_dns), - MAXHOSTNAMELEN); - /* - * Fetch user's UDB entry. - */ - getsysudb(); - if ((up = getudbnam(username)) == UDB_NULL) - fatal("cannot fetch user's UDB entry"); - - /* - * Prevent any possible fudging so perform a data - * safety check and compare the supplied uid against - * the udb's uid. - */ - if (up->ue_uid != uid) - fatal("IA uid missmatch"); - endudb(); - - if ((jid = getjtab(&jtab)) < 0) { - debug("getjtab"); - return(-1); - } - pid = getpid(); - ttyn = ttyname(0); - if (SecureSys) { - if (ttyn != NULL) - secstatrc = secstat(ttyn, &secinfo); - else - secstatrc = fsecstat(1, &secinfo); - - if (secstatrc == 0) - debug("[f]secstat() successful"); - else - fatal("[f]secstat() error, rc = %d", secstatrc); - } - if ((ttyn == NULL) && ((char *)command != NULL)) - ttyn = (char *)command; - /* - * Initialize all structures to call ia_user - */ - usent.revision = 0; - usent.uname = username; - usent.host = hostname; - usent.ttyn = ttyn; - usent.caller = IA_SSHD; - usent.pswdlist = &pwdacm; - usent.ueptr = &ue; - usent.flags = IA_INTERACTIVE | IA_FFLAG; - pwdacm.atype = IA_SECURID; - pwdacm.pwdp = NULL; - pwdacm.next = &pwdudb; - - pwdudb.atype = IA_UDB; - pwdudb.pwdp = NULL; - pwdudb.next = &pwddce; - - pwddce.atype = IA_DCE; - pwddce.pwdp = NULL; - pwddce.next = &pwddialup; - - pwddialup.atype = IA_DIALUP; - pwddialup.pwdp = NULL; - /* pwddialup.next = &pwdwal; */ - pwddialup.next = NULL; - - pwdwal.atype = IA_WAL; - pwdwal.pwdp = NULL; - pwdwal.next = NULL; - - uret.revision = 0; - uret.pswd = NULL; - uret.normal = 0; - - ia_rcode = ia_user(&usent, &uret); - switch (ia_rcode) { - /* - * These are acceptable return codes from ia_user() - */ - case IA_UDBWEEK: /* Password Expires in 1 week */ - expiration_time = ue.ue_pwage.time + ue.ue_pwage.maxage; - printf ("WARNING - your current password will expire %s\n", - ctime((const time_t *)&expiration_time)); - break; - case IA_UDBEXPIRED: - if (ttyname(0) != NULL) { - /* Force a password change */ - printf("Your password has expired; Choose a new one.\n"); - execl("/bin/passwd", "passwd", username, 0); - exit(9); - } - break; - case IA_NORMAL: /* Normal Return Code */ - break; - case IA_BACKDOOR: - /* XXX: can we memset it to zero here so save some of this */ - strlcpy(ue.ue_name, "root", sizeof(ue.ue_name)); - strlcpy(ue.ue_dir, "/", sizeof(ue.ue_dir)); - strlcpy(ue.ue_shell, "/bin/sh", sizeof(ue.ue_shell)); - - ue.ue_passwd[0] = '\0'; - ue.ue_age[0] = '\0'; - ue.ue_comment[0] = '\0'; - ue.ue_loghost[0] = '\0'; - ue.ue_logline[0] = '\0'; - - ue.ue_uid = -1; - ue.ue_nice[UDBRC_INTER] = 0; - - for (i = 0; i < MAXVIDS; i++) - ue.ue_gids[i] = 0; - - ue.ue_logfails = 0; - ue.ue_minlvl = ue.ue_maxlvl = ue.ue_deflvl = minslevel; - ue.ue_defcomps = 0; - ue.ue_comparts = 0; - ue.ue_permits = 0; - ue.ue_trap = 0; - ue.ue_disabled = 0; - ue.ue_logtime = 0; - break; - case IA_CONSOLE: /* Superuser not from Console */ - case IA_TRUSTED: /* Trusted user */ - if (options.permit_root_login > PERMIT_NO) - break; /* Accept root login */ - default: - /* - * These are failed return codes from ia_user() - */ - switch (ia_rcode) - { - case IA_BADAUTH: - printf("Bad authorization, access denied.\n"); - break; - case IA_DISABLED: - printf("Your login has been disabled. Contact the system "); - printf("administrator for assistance.\n"); - break; - case IA_GETSYSV: - printf("getsysv() failed - errno = %d\n", errno); - break; - case IA_MAXLOGS: - printf("Maximum number of failed login attempts exceeded.\n"); - printf("Access denied.\n"); - break; - case IA_UDBPWDNULL: - if (SecureSys) - printf("NULL Password not allowed on MLS systems.\n"); - break; - default: - break; - } - - /* - * Authentication failed. - */ - printf("sshd: Login incorrect, (0%o)\n", - ia_rcode-IA_ERRORCODE); - - /* - * Initialize structure for ia_failure - * which will exit. - */ - fsent.revision = 0; - fsent.uname = username; - fsent.host = hostname; - fsent.ttyn = ttyn; - fsent.caller = IA_SSHD; - fsent.flags = IA_INTERACTIVE; - fsent.ueptr = &ue; - fsent.jid = jid; - fsent.errcode = ia_rcode; - fsent.pwdp = uret.pswd; - fsent.exitcode = 1; - - fret.revision = 0; - fret.normal = 0; - - /* - * Call ia_failure because of an IA failure. - * There is no return because ia_failure exits. - */ - ia_failure(&fsent, &fret); - - exit(1); - } - - ia_mlsrcode = IA_NORMAL; - if (SecureSys) { - debug("calling ia_mlsuser()"); - ia_mlsrcode = ia_mlsuser(&ue, &secinfo, &usrv, NULL, 0); - } - if (ia_mlsrcode != IA_NORMAL) { - printf("sshd: Login incorrect, (0%o)\n", - ia_mlsrcode-IA_ERRORCODE); - /* - * Initialize structure for ia_failure - * which will exit. - */ - fsent.revision = 0; - fsent.uname = username; - fsent.host = hostname; - fsent.ttyn = ttyn; - fsent.caller = IA_SSHD; - fsent.flags = IA_INTERACTIVE; - fsent.ueptr = &ue; - fsent.jid = jid; - fsent.errcode = ia_mlsrcode; - fsent.pwdp = uret.pswd; - fsent.exitcode = 1; - fret.revision = 0; - fret.normal = 0; - - /* - * Call ia_failure because of an IA failure. - * There is no return because ia_failure exits. - */ - ia_failure(&fsent,&fret); - exit(1); - } - - /* Provide login status information */ - if (options.print_lastlog && ue.ue_logtime != 0) { - printf("Last successful login was : %.*s ", 19, - (char *)ctime(&ue.ue_logtime)); - - if (*ue.ue_loghost != '\0') { - printf("from %.*s\n", sizeof(ue.ue_loghost), - ue.ue_loghost); - } else { - printf("on %.*s\n", sizeof(ue.ue_logline), - ue.ue_logline); - } - - if (SecureSys && (ue.ue_logfails != 0)) { - printf(" followed by %d failed attempts\n", - ue.ue_logfails); - } - } - - /* - * Call ia_success to process successful I/A. - */ - ssent.revision = 0; - ssent.uname = username; - ssent.host = hostname; - ssent.ttyn = ttyn; - ssent.caller = IA_SSHD; - ssent.flags = IA_INTERACTIVE; - ssent.ueptr = &ue; - ssent.jid = jid; - ssent.errcode = ia_rcode; - ssent.us = NULL; - ssent.time = 1; /* Set ue_logtime */ - - sret.revision = 0; - sret.normal = 0; - - ia_success(&ssent, &sret); - - /* - * Query for account, iff > 1 valid acid & askacid permbit - */ - if (((ue.ue_permbits & PERMBITS_ACCTID) || - (ue.ue_acids[0] >= 0) && (ue.ue_acids[1] >= 0)) && - ue.ue_permbits & PERMBITS_ASKACID) { - if (ttyname(0) != NULL) { - debug("cray_setup: ttyname true case, %.100s", ttyname); - while (valid_acct == -1) { - printf("Account (? for available accounts)" - " [%s]: ", acid2nam(ue.ue_acids[0])); - fgets(acct_name, MAXACID, stdin); - switch (acct_name[0]) { - case EOF: - exit(0); - break; - case '\0': - valid_acct = ue.ue_acids[0]; - strlcpy(acct_name, acid2nam(valid_acct), MAXACID); - break; - case '?': - /* Print the list 3 wide */ - for (i = 0, j = 0; i < MAXVIDS; i++) { - if (ue.ue_acids[i] == -1) { - printf("\n"); - break; - } - if (++j == 4) { - j = 1; - printf("\n"); - } - printf(" %s", - acid2nam(ue.ue_acids[i])); - } - if (ue.ue_permbits & PERMBITS_ACCTID) { - printf("\"acctid\" permbit also allows" - " you to select any valid " - "account name.\n"); - } - printf("\n"); - break; - default: - valid_acct = nam2acid(acct_name); - if (valid_acct == -1) - printf( - "Account id not found for" - " account name \"%s\"\n\n", - acct_name); - break; - } - /* - * If an account was given, search the user's - * acids array to verify they can use this account. - */ - if ((valid_acct != -1) && - !(ue.ue_permbits & PERMBITS_ACCTID)) { - for (i = 0; i < MAXVIDS; i++) { - if (ue.ue_acids[i] == -1) - break; - if (valid_acct == ue.ue_acids[i]) - break; - } - if (i == MAXVIDS || - ue.ue_acids[i] == -1) { - fprintf(stderr, "Cannot set" - " account name to " - "\"%s\", permission " - "denied\n\n", acct_name); - valid_acct = -1; - } - } - } - } else { - /* - * The client isn't connected to a terminal and can't - * respond to an acid prompt. Use default acid. - */ - debug("cray_setup: ttyname false case, %.100s", - ttyname); - valid_acct = ue.ue_acids[0]; - } - } else { - /* - * The user doesn't have the askacid permbit set or - * only has one valid account to use. - */ - valid_acct = ue.ue_acids[0]; - } - if (acctid(0, valid_acct) < 0) { - printf ("Bad account id: %d\n", valid_acct); - exit(1); - } - - /* - * Now set shares, quotas, limits, including CPU time for the - * (interactive) job and process, and set up permissions - * (for chown etc), etc. - */ - if (setshares(ue.ue_uid, valid_acct, printf, 0, 0)) { - printf("Unable to give %d shares to <%s>(%d/%d)\n", - ue.ue_shares, ue.ue_name, ue.ue_uid, valid_acct); - exit(1); - } - - sr = setlimits(username, C_PROC, pid, UDBRC_INTER); - if (sr != NULL) { - debug("%.200s", sr); - exit(1); - } - sr = setlimits(username, C_JOB, jid, UDBRC_INTER); - if (sr != NULL) { - debug("%.200s", sr); - exit(1); - } - /* - * Place the service provider information into - * the session table (Unicos) or job table (Unicos/mk). - * There exist double defines for the job/session table in - * unicos/mk (jtab.h) so no need for a compile time switch. - */ - memset(&init_info, '\0', sizeof(init_info)); - init_info.s_sessinit.si_id = URM_SPT_LOGIN; - init_info.s_sessinit.si_pid = getpid(); - init_info.s_sessinit.si_sid = jid; - sesscntl(0, S_SETSERVPO, (int)&init_info); - - /* - * Set user and controlling tty security attributes. - */ - if (SecureSys) { - if (setusrv(&usrv) == -1) { - debug("setusrv() failed, errno = %d",errno); - exit(1); - } - } - - return (0); -} - -/* - * The rc.* and /etc/sdaemon methods of starting a program on unicos/unicosmk - * can have pal privileges that sshd can inherit which - * could allow a user to su to root with out a password. - * This subroutine clears all privileges. - */ -void -drop_cray_privs() -{ -#if defined(_SC_CRAY_PRIV_SU) - priv_proc_t *privstate; - int result; - extern int priv_set_proc(); - extern priv_proc_t *priv_init_proc(); - - /* - * If ether of theses two flags are not set - * then don't allow this version of ssh to run. - */ - if (!sysconf(_SC_CRAY_PRIV_SU)) - fatal("Not PRIV_SU system."); - if (!sysconf(_SC_CRAY_POSIX_PRIV)) - fatal("Not POSIX_PRIV."); - - debug("Setting MLS labels.");; - - if (sysconf(_SC_CRAY_SECURE_MAC)) { - usrv.sv_minlvl = SYSLOW; - usrv.sv_actlvl = SYSHIGH; - usrv.sv_maxlvl = SYSHIGH; - } else { - usrv.sv_minlvl = sysv.sy_minlvl; - usrv.sv_actlvl = sysv.sy_minlvl; - usrv.sv_maxlvl = sysv.sy_maxlvl; - } - usrv.sv_actcmp = 0; - usrv.sv_valcmp = sysv.sy_valcmp; - - usrv.sv_intcat = TFM_SYSTEM; - usrv.sv_valcat |= (TFM_SYSTEM | TFM_SYSFILE); - - if (setusrv(&usrv) < 0) { - fatal("%s(%d): setusrv(): %s", __FILE__, __LINE__, - strerror(errno)); - } - - if ((privstate = priv_init_proc()) != NULL) { - result = priv_set_proc(privstate); - if (result != 0 ) { - fatal("%s(%d): priv_set_proc(): %s", - __FILE__, __LINE__, strerror(errno)); - } - priv_free_proc(privstate); - } - debug ("Privileges should be cleared..."); -#else - /* XXX: do this differently */ -# error Cray systems must be run with _SC_CRAY_PRIV_SU on! -#endif -} - - -/* - * Retain utmp/wtmp information - used by cray accounting. - */ -void -cray_retain_utmp(struct utmp *ut, int pid) -{ - int fd; - struct utmp utmp; - - if ((fd = open(UTMP_FILE, O_RDONLY)) != -1) { - /* XXX use atomicio */ - while (read(fd, (char *)&utmp, sizeof(utmp)) == sizeof(utmp)) { - if (pid == utmp.ut_pid) { - ut->ut_jid = utmp.ut_jid; - strncpy(ut->ut_tpath, utmp.ut_tpath, sizeof(utmp.ut_tpath)); - strncpy(ut->ut_host, utmp.ut_host, sizeof(utmp.ut_host)); - strncpy(ut->ut_name, utmp.ut_name, sizeof(utmp.ut_name)); - break; - } - } - close(fd); - } else - fatal("Unable to open utmp file"); -} - -/* - * tmpdir support. - */ - -/* - * find and delete jobs tmpdir. - */ -void -cray_delete_tmpdir(char *login, int jid, uid_t uid) -{ - static char jtmp[TPATHSIZ]; - struct stat statbuf; - int child, c, wstat; - - for (c = 'a'; c <= 'z'; c++) { - snprintf(jtmp, TPATHSIZ, "%s/jtmp.%06d%c", JTMPDIR, jid, c); - if (stat(jtmp, &statbuf) == 0 && statbuf.st_uid == uid) - break; - } - - if (c > 'z') - return; - - if ((child = fork()) == 0) { - execl(CLEANTMPCMD, CLEANTMPCMD, login, jtmp, (char *)NULL); - fatal("cray_delete_tmpdir: execl of CLEANTMPCMD failed"); - } - - while (waitpid(child, &wstat, 0) == -1 && errno == EINTR) - ; -} - -/* - * Remove tmpdir on job termination. - */ -void -cray_job_termination_handler(int sig) -{ - int jid; - char *login = NULL; - struct jtab jtab; - - if ((jid = waitjob(&jtab)) == -1 || - (login = uid2nam(jtab.j_uid)) == NULL) - return; - - cray_delete_tmpdir(login, jid, jtab.j_uid); -} - -/* - * Set job id and create tmpdir directory. - */ -void -cray_init_job(struct passwd *pw) -{ - int jid; - int c; - - jid = setjob(pw->pw_uid, WJSIGNAL); - if (jid < 0) - fatal("System call setjob failure"); - - for (c = 'a'; c <= 'z'; c++) { - snprintf(cray_tmpdir, TPATHSIZ, "%s/jtmp.%06d%c", JTMPDIR, jid, c); - if (mkdir(cray_tmpdir, JTMPMODE) != 0) - continue; - if (chown(cray_tmpdir, pw->pw_uid, pw->pw_gid) != 0) { - rmdir(cray_tmpdir); - continue; - } - break; - } - - if (c > 'z') - cray_tmpdir[0] = '\0'; -} - -void -cray_set_tmpdir(struct utmp *ut) -{ - int jid; - struct jtab jbuf; - - if ((jid = getjtab(&jbuf)) < 0) - return; - - /* - * Set jid and tmpdir in utmp record. - */ - ut->ut_jid = jid; - strncpy(ut->ut_tpath, cray_tmpdir, TPATHSIZ); -} -#endif /* UNICOS */ - -#ifdef _UNICOSMP -#include -/* - * Set job id and create tmpdir directory. - */ -void -cray_init_job(struct passwd *pw) -{ - initrm_silent(pw->pw_uid); - return; -} -#endif /* _UNICOSMP */ diff --git a/openbsd-compat/bsd-cray.h b/openbsd-compat/bsd-cray.h deleted file mode 100644 index ca626a021..000000000 --- a/openbsd-compat/bsd-cray.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2002, Cray Inc. (Wendy Palm ) - * Significant portions provided by - * Wayne Schroeder, SDSC - * William Jones, UTexas - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Created: Apr 22 16.34:00 2002 wp - * - * This file contains functions required for proper execution - * on UNICOS systems. - * - */ - -#ifndef _BSD_CRAY_H -#define _BSD_CRAY_H - -#ifdef _UNICOS - -void cray_init_job(struct passwd *); -void cray_job_termination_handler(int); -void cray_login_failure(char *, int ); -int cray_access_denied(char *); -extern char cray_tmpdir[]; - -#define CUSTOM_FAILED_LOGIN 1 - -#ifndef IA_SSHD -# define IA_SSHD IA_LOGIN -#endif -#ifndef MAXHOSTNAMELEN -# define MAXHOSTNAMELEN 64 -#endif -#ifndef _CRAYT3E -# define TIOCGPGRP (tIOC|20) -#endif - -#endif /* UNICOS */ - -#endif /* _BSD_CRAY_H */ diff --git a/openbsd-compat/bsd-openpty.c b/openbsd-compat/bsd-openpty.c index b28235860..48fb6059e 100644 --- a/openbsd-compat/bsd-openpty.c +++ b/openbsd-compat/bsd-openpty.c @@ -147,17 +147,6 @@ openpty(int *amaster, int *aslave, char *name, struct termios *termp, } return (0); -#elif defined(_UNICOS) - char ptbuf[64], ttbuf[64]; - int i; - int highpty; - - highpty = 128; -#ifdef _SC_CRAY_NPTY - if ((highpty = sysconf(_SC_CRAY_NPTY)) == -1) - highpty = 128; -#endif /* _SC_CRAY_NPTY */ - for (i = 0; i < highpty; i++) { snprintf(ptbuf, sizeof(ptbuf), "/dev/pty/%03d", i); snprintf(ttbuf, sizeof(ttbuf), "/dev/ttyp%03d", i); diff --git a/openbsd-compat/openbsd-compat.h b/openbsd-compat/openbsd-compat.h index c7f660609..8155a0dd7 100644 --- a/openbsd-compat/openbsd-compat.h +++ b/openbsd-compat/openbsd-compat.h @@ -316,7 +316,6 @@ char *shadow_pw(struct passwd *pw); #include "fake-rfc2553.h" /* Routines for a single OS platform */ -#include "bsd-cray.h" #include "bsd-cygwin_util.h" #include "port-aix.h" diff --git a/session.c b/session.c index e93012679..51c5ea0ec 100644 --- a/session.c +++ b/session.c @@ -450,11 +450,6 @@ do_exec_no_pty(struct ssh *ssh, Session *s, const char *command) close(err[0]); #endif - -#ifdef _UNICOS - cray_init_job(s->pw); /* set up cray jid and tmpdir */ -#endif - /* Do processing for the child (exec command etc). */ do_child(ssh, s, command); /* NOTREACHED */ @@ -462,9 +457,6 @@ do_exec_no_pty(struct ssh *ssh, Session *s, const char *command) break; } -#ifdef _UNICOS - signal(WJSIGNAL, cray_job_termination_handler); -#endif /* _UNICOS */ #ifdef HAVE_CYGWIN cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); #endif @@ -576,9 +568,6 @@ do_exec_pty(struct ssh *ssh, Session *s, const char *command) close(ttyfd); /* record login, etc. similar to login(1) */ -#ifdef _UNICOS - cray_init_job(s->pw); /* set up cray jid and tmpdir */ -#endif /* _UNICOS */ #ifndef HAVE_OSF_SIA do_login(ssh, s, command); #endif @@ -592,9 +581,6 @@ do_exec_pty(struct ssh *ssh, Session *s, const char *command) break; } -#ifdef _UNICOS - signal(WJSIGNAL, cray_job_termination_handler); -#endif /* _UNICOS */ #ifdef HAVE_CYGWIN cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); #endif @@ -1080,11 +1066,6 @@ do_setup_env(struct ssh *ssh, Session *s, const char *shell) child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND", original_command); -#ifdef _UNICOS - if (cray_tmpdir[0] != '\0') - child_set_env(&env, &envsize, "TMPDIR", cray_tmpdir); -#endif /* _UNICOS */ - /* * Since we clear KRB5CCNAME at startup, if it's set now then it * must have been set by a native authentication method (eg AIX or @@ -1485,10 +1466,6 @@ do_child(struct ssh *ssh, Session *s, const char *command) exit(1); } -#ifdef _UNICOS - cray_setup(pw->pw_uid, pw->pw_name, command); -#endif /* _UNICOS */ - /* * Login(1) does this as well, and it needs uid 0 for the "-h" * switch, so we let login(1) to this for us. diff --git a/sshd.c b/sshd.c index 17931068d..7466d5a44 100644 --- a/sshd.c +++ b/sshd.c @@ -1602,13 +1602,6 @@ main(int ac, char **av) if (getenv("KRB5CCNAME") != NULL) (void) unsetenv("KRB5CCNAME"); -#ifdef _UNICOS - /* Cray can define user privs drop all privs now! - * Not needed on PRIV_SU systems! - */ - drop_cray_privs(); -#endif - sensitive_data.have_ssh2_key = 0; /* diff --git a/sshpty.c b/sshpty.c index fe2fb5aa2..4da84d05f 100644 --- a/sshpty.c +++ b/sshpty.c @@ -100,30 +100,6 @@ pty_make_controlling_tty(int *ttyfd, const char *tty) { int fd; -#ifdef _UNICOS - if (setsid() < 0) - error("setsid: %.100s", strerror(errno)); - - fd = open(tty, O_RDWR|O_NOCTTY); - if (fd != -1) { - signal(SIGHUP, SIG_IGN); - ioctl(fd, TCVHUP, (char *)NULL); - signal(SIGHUP, SIG_DFL); - setpgid(0, 0); - close(fd); - } else { - error("Failed to disconnect from controlling tty."); - } - - debug("Setting controlling tty using TCSETCTTY."); - ioctl(*ttyfd, TCSETCTTY, NULL); - fd = open("/dev/tty", O_RDWR); - if (fd < 0) - error("%.100s: %.100s", tty, strerror(errno)); - close(*ttyfd); - *ttyfd = fd; -#else /* _UNICOS */ - /* First disconnect from the old controlling tty. */ #ifdef TIOCNOTTY fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); @@ -167,7 +143,6 @@ pty_make_controlling_tty(int *ttyfd, const char *tty) strerror(errno)); else close(fd); -#endif /* _UNICOS */ } /* Changes the window size associated with the pty. */ -- cgit v1.2.3 From 1b11ea7c58cd5c59838b5fa574cd456d6047b2d4 Mon Sep 17 00:00:00 2001 From: "markus@openbsd.org" Date: Fri, 23 Feb 2018 15:58:37 +0000 Subject: upstream: Add experimental support for PQC XMSS keys (Extended Hash-Based Signatures) The code is not compiled in by default (see WITH_XMSS in Makefile.inc) Joint work with stefan-lukas_gazdag at genua.eu See https://tools.ietf.org/html/draft-irtf-cfrg-xmss-hash-based-signatures-12 ok djm@ OpenBSD-Commit-ID: ef3eccb96762a5d6f135d7daeef608df7776a7ac --- Makefile.in | 12 +- authfd.c | 39 +- authfd.h | 5 +- authfile.c | 8 +- cipher.c | 4 +- dns.c | 7 +- dns.h | 5 +- pathnames.h | 4 +- readconf.c | 3 +- servconf.c | 4 +- ssh-add.c | 74 +++- ssh-agent.c | 24 +- ssh-keygen.c | 19 +- ssh-keyscan.c | 12 +- ssh-keysign.c | 5 +- ssh-xmss.c | 188 +++++++++ ssh.c | 15 +- sshconnect.c | 5 +- sshd.c | 6 +- sshkey-xmss.c | 1048 ++++++++++++++++++++++++++++++++++++++++++++++++ sshkey-xmss.h | 56 +++ sshkey.c | 410 ++++++++++++++++++- sshkey.h | 35 +- xmss_commons.c | 27 ++ xmss_commons.h | 15 + xmss_fast.c | 1099 +++++++++++++++++++++++++++++++++++++++++++++++++++ xmss_fast.h | 109 +++++ xmss_hash.c | 133 +++++++ xmss_hash.h | 19 + xmss_hash_address.c | 59 +++ xmss_hash_address.h | 37 ++ xmss_wots.c | 185 +++++++++ xmss_wots.h | 59 +++ 33 files changed, 3657 insertions(+), 73 deletions(-) create mode 100644 ssh-xmss.c create mode 100644 sshkey-xmss.c create mode 100644 sshkey-xmss.h create mode 100644 xmss_commons.c create mode 100644 xmss_commons.h create mode 100644 xmss_fast.c create mode 100644 xmss_fast.h create mode 100644 xmss_hash.c create mode 100644 xmss_hash.h create mode 100644 xmss_hash_address.c create mode 100644 xmss_hash_address.h create mode 100644 xmss_wots.c create mode 100644 xmss_wots.h (limited to 'sshd.c') diff --git a/Makefile.in b/Makefile.in index 2d26e83af..25d45eafb 100644 --- a/Makefile.in +++ b/Makefile.in @@ -62,6 +62,15 @@ MKDIR_P=@MKDIR_P@ 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) +XMSS_OBJS=\ + ssh-xmss.c \ + sshkey-xmss.c \ + xmss_commons.c \ + xmss_fast.c \ + xmss_hash.c \ + xmss_hash_address.c \ + xmss_wots.c + LIBOPENSSH_OBJS=\ ssh_api.o \ ssherr.o \ @@ -71,7 +80,8 @@ LIBOPENSSH_OBJS=\ sshbuf-misc.o \ sshbuf-getput-crypto.o \ krl.o \ - bitmap.o + bitmap.o \ + ${XMSS_OBJS} LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ authfd.o authfile.o bufaux.o bufbn.o bufec.o buffer.o \ diff --git a/authfd.c b/authfd.c index 148bc9bfb..1eff7ba94 100644 --- a/authfd.c +++ b/authfd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: authfd.c,v 1.107 2018/02/10 09:25:34 djm Exp $ */ +/* $OpenBSD: authfd.c,v 1.108 2018/02/23 15:58:37 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -129,7 +129,7 @@ ssh_request_reply(int sock, struct sshbuf *request, struct sshbuf *reply) /* Get the length of the message, and format it in the buffer. */ len = sshbuf_len(request); - put_u32(buf, len); + POKE_U32(buf, len); /* Send the length and then the packet to the agent. */ if (atomicio(vwrite, sock, buf, 4) != 4 || @@ -144,7 +144,7 @@ ssh_request_reply(int sock, struct sshbuf *request, struct sshbuf *reply) return SSH_ERR_AGENT_COMMUNICATION; /* Extract the length, and check it for sanity. */ - len = get_u32(buf); + len = PEEK_U32(buf); if (len > MAX_AGENT_REPLY_LEN) return SSH_ERR_INVALID_FORMAT; @@ -391,19 +391,7 @@ ssh_agent_sign(int sock, const struct sshkey *key, static int -ssh_encode_identity_ssh2(struct sshbuf *b, const struct sshkey *key, - const char *comment) -{ - int r; - - if ((r = sshkey_private_serialize(key, b)) != 0 || - (r = sshbuf_put_cstring(b, comment)) != 0) - return r; - return 0; -} - -static int -encode_constraints(struct sshbuf *m, u_int life, u_int confirm) +encode_constraints(struct sshbuf *m, u_int life, u_int confirm, u_int maxsign) { int r; @@ -416,6 +404,11 @@ encode_constraints(struct sshbuf *m, u_int life, u_int confirm) if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_CONFIRM)) != 0) goto out; } + if (maxsign != 0) { + if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_MAXSIGN)) != 0 || + (r = sshbuf_put_u32(m, maxsign)) != 0) + goto out; + } r = 0; out: return r; @@ -427,10 +420,10 @@ encode_constraints(struct sshbuf *m, u_int life, u_int confirm) */ int ssh_add_identity_constrained(int sock, const struct sshkey *key, - const char *comment, u_int life, u_int confirm) + const char *comment, u_int life, u_int confirm, u_int maxsign) { struct sshbuf *msg; - int r, constrained = (life || confirm); + int r, constrained = (life || confirm || maxsign); u_char type; if ((msg = sshbuf_new()) == NULL) @@ -447,11 +440,15 @@ ssh_add_identity_constrained(int sock, const struct sshkey *key, #endif case KEY_ED25519: case KEY_ED25519_CERT: + case KEY_XMSS: + case KEY_XMSS_CERT: type = constrained ? SSH2_AGENTC_ADD_ID_CONSTRAINED : SSH2_AGENTC_ADD_IDENTITY; if ((r = sshbuf_put_u8(msg, type)) != 0 || - (r = ssh_encode_identity_ssh2(msg, key, comment)) != 0) + (r = sshkey_private_serialize_maxsign(key, msg, maxsign, + NULL)) != 0 || + (r = sshbuf_put_cstring(msg, comment)) != 0) goto out; break; default: @@ -459,7 +456,7 @@ ssh_add_identity_constrained(int sock, const struct sshkey *key, goto out; } if (constrained && - (r = encode_constraints(msg, life, confirm)) != 0) + (r = encode_constraints(msg, life, confirm, maxsign)) != 0) goto out; if ((r = ssh_request_reply(sock, msg, msg)) != 0) goto out; @@ -537,7 +534,7 @@ ssh_update_card(int sock, int add, const char *reader_id, const char *pin, (r = sshbuf_put_cstring(msg, pin)) != 0) goto out; if (constrained && - (r = encode_constraints(msg, life, confirm)) != 0) + (r = encode_constraints(msg, life, confirm, 0)) != 0) goto out; if ((r = ssh_request_reply(sock, msg, msg)) != 0) goto out; diff --git a/authfd.h b/authfd.h index 41997ce6d..ab954ffc0 100644 --- a/authfd.h +++ b/authfd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: authfd.h,v 1.42 2018/02/10 09:25:34 djm Exp $ */ +/* $OpenBSD: authfd.h,v 1.43 2018/02/23 15:58:37 markus Exp $ */ /* * Author: Tatu Ylonen @@ -30,7 +30,7 @@ int ssh_lock_agent(int sock, int lock, const char *password); int ssh_fetch_identitylist(int sock, struct ssh_identitylist **idlp); void ssh_free_identitylist(struct ssh_identitylist *idl); int ssh_add_identity_constrained(int sock, const struct sshkey *key, - const char *comment, u_int life, u_int confirm); + const char *comment, u_int life, u_int confirm, u_int maxsign); int ssh_remove_identity(int sock, struct sshkey *key); int ssh_update_card(int sock, int add, const char *reader_id, const char *pin, u_int life, u_int confirm); @@ -77,6 +77,7 @@ int ssh_agent_sign(int sock, const struct sshkey *key, #define SSH_AGENT_CONSTRAIN_LIFETIME 1 #define SSH_AGENT_CONSTRAIN_CONFIRM 2 +#define SSH_AGENT_CONSTRAIN_MAXSIGN 3 /* extended failure messages */ #define SSH2_AGENT_FAILURE 30 diff --git a/authfile.c b/authfile.c index d09b700d2..57dcd808c 100644 --- a/authfile.c +++ b/authfile.c @@ -1,4 +1,4 @@ -/* $OpenBSD: authfile.c,v 1.127 2017/07/01 13:50:45 djm Exp $ */ +/* $OpenBSD: authfile.c,v 1.128 2018/02/23 15:58:37 markus Exp $ */ /* * Copyright (c) 2000, 2013 Markus Friedl. All rights reserved. * @@ -191,6 +191,8 @@ sshkey_load_private_type(int type, const char *filename, const char *passphrase, *perm_ok = 1; r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp); + if (r == 0 && keyp && *keyp) + r = sshkey_set_filename(*keyp, filename); out: close(fd); return r; @@ -249,6 +251,9 @@ sshkey_load_private(const char *filename, const char *passphrase, (r = sshkey_parse_private_fileblob(buffer, passphrase, keyp, commentp)) != 0) goto out; + if (keyp && *keyp && + (r = sshkey_set_filename(*keyp, filename)) != 0) + goto out; r = 0; out: close(fd); @@ -397,6 +402,7 @@ sshkey_load_private_cert(int type, const char *filename, const char *passphrase, case KEY_ECDSA: #endif /* WITH_OPENSSL */ case KEY_ED25519: + case KEY_XMSS: case KEY_UNSPEC: break; default: diff --git a/cipher.c b/cipher.c index 9f4546759..578763616 100644 --- a/cipher.c +++ b/cipher.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cipher.c,v 1.110 2018/02/13 03:36:56 djm Exp $ */ +/* $OpenBSD: cipher.c,v 1.111 2018/02/23 15:58:37 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -401,7 +401,7 @@ cipher_get_length(struct sshcipher_ctx *cc, u_int *plenp, u_int seqnr, cp, len); if (len < 4) return SSH_ERR_MESSAGE_INCOMPLETE; - *plenp = get_u32(cp); + *plenp = PEEK_U32(cp); return 0; } diff --git a/dns.c b/dns.c index 6e1abb530..ff1a2c41c 100644 --- a/dns.c +++ b/dns.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dns.c,v 1.37 2017/09/14 04:32:21 djm Exp $ */ +/* $OpenBSD: dns.c,v 1.38 2018/02/23 15:58:37 markus Exp $ */ /* * Copyright (c) 2003 Wesley Griffin. All rights reserved. @@ -105,6 +105,11 @@ dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, if (!*digest_type) *digest_type = SSHFP_HASH_SHA256; break; + case KEY_XMSS: + *algorithm = SSHFP_KEY_XMSS; + if (!*digest_type) + *digest_type = SSHFP_HASH_SHA256; + break; default: *algorithm = SSHFP_KEY_RESERVED; /* 0 */ *digest_type = SSHFP_HASH_RESERVED; /* 0 */ diff --git a/dns.h b/dns.h index 68443f7cb..91f3c632d 100644 --- a/dns.h +++ b/dns.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dns.h,v 1.17 2017/09/14 04:32:21 djm Exp $ */ +/* $OpenBSD: dns.h,v 1.18 2018/02/23 15:58:37 markus Exp $ */ /* * Copyright (c) 2003 Wesley Griffin. All rights reserved. @@ -33,7 +33,8 @@ enum sshfp_types { SSHFP_KEY_RSA = 1, SSHFP_KEY_DSA = 2, SSHFP_KEY_ECDSA = 3, - SSHFP_KEY_ED25519 = 4 + SSHFP_KEY_ED25519 = 4, + SSHFP_KEY_XMSS = 5 }; enum sshfp_hashes { diff --git a/pathnames.h b/pathnames.h index 1c221b01b..cb44caa4d 100644 --- a/pathnames.h +++ b/pathnames.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pathnames.h,v 1.27 2017/05/05 10:42:49 naddy Exp $ */ +/* $OpenBSD: pathnames.h,v 1.28 2018/02/23 15:58:37 markus Exp $ */ /* * Author: Tatu Ylonen @@ -39,6 +39,7 @@ #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" @@ -75,6 +76,7 @@ #define _PATH_SSH_CLIENT_ID_ECDSA _PATH_SSH_USER_DIR "/id_ecdsa" #define _PATH_SSH_CLIENT_ID_RSA _PATH_SSH_USER_DIR "/id_rsa" #define _PATH_SSH_CLIENT_ID_ED25519 _PATH_SSH_USER_DIR "/id_ed25519" +#define _PATH_SSH_CLIENT_ID_XMSS _PATH_SSH_USER_DIR "/id_xmss" /* * Configuration file in user's home directory. This file need not be diff --git a/readconf.c b/readconf.c index 56bff850a..88051db57 100644 --- a/readconf.c +++ b/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.282 2018/02/23 02:34:33 djm Exp $ */ +/* $OpenBSD: readconf.c,v 1.283 2018/02/23 15:58:37 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1943,6 +1943,7 @@ fill_default_options(Options * options) #endif add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_ED25519, 0); + add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_XMSS, 0); } if (options->escape_char == -1) options->escape_char = '~'; diff --git a/servconf.c b/servconf.c index bf8ad671d..c87a9a2b6 100644 --- a/servconf.c +++ b/servconf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: servconf.c,v 1.324 2018/02/16 02:32:40 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.325 2018/02/23 15:58:37 markus Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -253,6 +253,8 @@ fill_default_server_options(ServerOptions *options) #endif servconf_add_hostkey("[default]", 0, options, _PATH_HOST_ED25519_KEY_FILE); + servconf_add_hostkey("[default]", 0, options, + _PATH_HOST_XMSS_KEY_FILE); } /* No certificates by default */ if (options->num_ports == 0) diff --git a/ssh-add.c b/ssh-add.c index 2afd48330..adcc45998 100644 --- a/ssh-add.c +++ b/ssh-add.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-add.c,v 1.134 2017/08/29 09:42:29 dlg Exp $ */ +/* $OpenBSD: ssh-add.c,v 1.135 2018/02/23 15:58:37 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -78,6 +78,7 @@ static char *default_files[] = { #endif #endif /* WITH_OPENSSL */ _PATH_SSH_CLIENT_ID_ED25519, + _PATH_SSH_CLIENT_ID_XMSS, NULL }; @@ -89,6 +90,10 @@ static int lifetime = 0; /* User has to confirm key use */ static int confirm = 0; +/* Maximum number of signatures (XMSS) */ +static u_int maxsign = 0; +static u_int minleft = 0; + /* we keep a cache of one passphrase */ static char *pass = NULL; static void @@ -190,7 +195,10 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag) char *comment = NULL; char msg[1024], *certpath = NULL; int r, fd, ret = -1; + size_t i; + u_int32_t left; struct sshbuf *keyblob; + struct ssh_identitylist *idlist; if (strcmp(filename, "-") == 0) { fd = STDIN_FILENO; @@ -268,8 +276,40 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag) comment = xstrdup(filename); sshbuf_free(keyblob); + /* For XMSS */ + if ((r = sshkey_set_filename(private, filename)) != 0) { + fprintf(stderr, "Could not add filename to private key: %s (%s)\n", + filename, comment); + goto out; + } + if (maxsign && minleft && + (r = ssh_fetch_identitylist(agent_fd, &idlist)) == 0) { + for (i = 0; i < idlist->nkeys; i++) { + if (!sshkey_equal_public(idlist->keys[i], private)) + continue; + left = sshkey_signatures_left(idlist->keys[i]); + if (left < minleft) { + fprintf(stderr, + "Only %d signatures left.\n", left); + break; + } + fprintf(stderr, "Skipping update: "); + if (left == minleft) { + fprintf(stderr, + "required signatures left (%d).\n", left); + } else { + fprintf(stderr, + "more signatures left (%d) than" + " required (%d).\n", left, minleft); + } + ssh_free_identitylist(idlist); + goto out; + } + ssh_free_identitylist(idlist); + } + if ((r = ssh_add_identity_constrained(agent_fd, private, comment, - lifetime, confirm)) == 0) { + lifetime, confirm, maxsign)) == 0) { fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); ret = 0; if (lifetime != 0) @@ -317,7 +357,7 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag) sshkey_free(cert); if ((r = ssh_add_identity_constrained(agent_fd, private, comment, - lifetime, confirm)) != 0) { + lifetime, confirm, maxsign)) != 0) { error("Certificate %s (%s) add failed: %s", certpath, private->cert->key_id, ssh_err(r)); goto out; @@ -368,6 +408,7 @@ list_identities(int agent_fd, int do_fp) char *fp; int r; struct ssh_identitylist *idlist; + u_int32_t left; size_t i; if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) { @@ -392,7 +433,12 @@ list_identities(int agent_fd, int do_fp) ssh_err(r)); continue; } - fprintf(stdout, " %s\n", idlist->comments[i]); + fprintf(stdout, " %s", idlist->comments[i]); + left = sshkey_signatures_left(idlist->keys[i]); + if (left > 0) + fprintf(stdout, + " [signatures left %d]", left); + fprintf(stdout, "\n"); } } ssh_free_identitylist(idlist); @@ -454,6 +500,8 @@ usage(void) fprintf(stderr, " -L List public key parameters of all identities.\n"); fprintf(stderr, " -k Load only keys and not certificates.\n"); fprintf(stderr, " -c Require confirmation to sign using identities\n"); + fprintf(stderr, " -m minleft Maxsign is only changed if less than minleft are left (for XMSS)\n"); + fprintf(stderr, " -M maxsign Maximum number of signatures allowed (for XMSS)\n"); fprintf(stderr, " -t life Set lifetime (in seconds) when adding identities.\n"); fprintf(stderr, " -d Delete identity.\n"); fprintf(stderr, " -D Delete all identities.\n"); @@ -500,7 +548,7 @@ main(int argc, char **argv) exit(2); } - while ((ch = getopt(argc, argv, "klLcdDxXE:e:qs:t:")) != -1) { + while ((ch = getopt(argc, argv, "klLcdDxXE:e:M:m:qs:t:")) != -1) { switch (ch) { case 'E': fingerprint_hash = ssh_digest_alg_by_name(optarg); @@ -525,6 +573,22 @@ main(int argc, char **argv) case 'c': confirm = 1; break; + case 'm': + minleft = (int)strtonum(optarg, 1, UINT_MAX, NULL); + if (minleft == 0) { + usage(); + ret = 1; + goto done; + } + break; + case 'M': + maxsign = (int)strtonum(optarg, 1, UINT_MAX, NULL); + if (maxsign == 0) { + usage(); + ret = 1; + goto done; + } + break; case 'd': deleting = 1; break; diff --git a/ssh-agent.c b/ssh-agent.c index 39888a72c..2a4578b03 100644 --- a/ssh-agent.c +++ b/ssh-agent.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-agent.c,v 1.227 2018/01/23 05:27:21 djm Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.228 2018/02/23 15:58:37 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -245,7 +245,8 @@ process_request_identities(SocketEntry *e) (r = sshbuf_put_u32(msg, idtab->nentries)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); TAILQ_FOREACH(id, &idtab->idlist, next) { - if ((r = sshkey_puts(id->key, msg)) != 0 || + if ((r = sshkey_puts_opts(id->key, msg, SSHKEY_SERIALIZE_INFO)) + != 0 || (r = sshbuf_put_cstring(msg, id->comment)) != 0) { error("%s: put key/comment: %s", __func__, ssh_err(r)); @@ -402,7 +403,7 @@ process_add_identity(SocketEntry *e) { Identity *id; int success = 0, confirm = 0; - u_int seconds; + u_int seconds, maxsign; char *comment = NULL; time_t death = 0; struct sshkey *k = NULL; @@ -433,6 +434,18 @@ process_add_identity(SocketEntry *e) case SSH_AGENT_CONSTRAIN_CONFIRM: confirm = 1; break; + case SSH_AGENT_CONSTRAIN_MAXSIGN: + if ((r = sshbuf_get_u32(e->request, &maxsign)) != 0) { + error("%s: bad maxsign constraint: %s", + __func__, ssh_err(r)); + goto err; + } + if ((r = sshkey_enable_maxsign(k, maxsign)) != 0) { + error("%s: cannot enable maxsign: %s", + __func__, ssh_err(r)); + goto err; + } + break; default: error("%s: Unknown constraint %d", __func__, ctype); err: @@ -448,14 +461,15 @@ process_add_identity(SocketEntry *e) death = monotime() + lifetime; if ((id = lookup_identity(k)) == NULL) { id = xcalloc(1, sizeof(Identity)); - id->key = k; TAILQ_INSERT_TAIL(&idtab->idlist, id, next); /* Increment the number of identities. */ idtab->nentries++; } else { - sshkey_free(k); + /* key state might have been updated */ + sshkey_free(id->key); free(id->comment); } + id->key = k; id->comment = comment; id->death = death; id->confirm = confirm; diff --git a/ssh-keygen.c b/ssh-keygen.c index 9812c0d2a..d80930eeb 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.312 2018/02/10 05:48:46 djm Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.313 2018/02/23 15:58:38 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland @@ -275,6 +275,10 @@ ask_filename(struct passwd *pw, const char *prompt) case KEY_ED25519_CERT: name = _PATH_SSH_CLIENT_ID_ED25519; break; + case KEY_XMSS: + case KEY_XMSS_CERT: + name = _PATH_SSH_CLIENT_ID_XMSS; + break; default: fatal("bad key type"); } @@ -969,6 +973,9 @@ do_gen_all_hostkeys(struct passwd *pw) #endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ { "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE }, +#ifdef WITH_XMSS + { "xmss", "XMSS",_PATH_HOST_XMSS_KEY_FILE }, +#endif /* WITH_XMSS */ { NULL, NULL, NULL } }; @@ -1455,7 +1462,8 @@ do_change_comment(struct passwd *pw) } } - if (private->type != KEY_ED25519 && !use_new_format) { + if (private->type != KEY_ED25519 && private->type != KEY_XMSS && + !use_new_format) { error("Comments are only supported for keys stored in " "the new format (-o)."); explicit_bzero(passphrase, strlen(passphrase)); @@ -1705,7 +1713,8 @@ do_ca_sign(struct passwd *pw, int argc, char **argv) fatal("%s: unable to open \"%s\": %s", __func__, tmp, ssh_err(r)); if (public->type != KEY_RSA && public->type != KEY_DSA && - public->type != KEY_ECDSA && public->type != KEY_ED25519) + public->type != KEY_ECDSA && public->type != KEY_ED25519 && + public->type != KEY_XMSS) fatal("%s: key \"%s\" type %s cannot be certified", __func__, tmp, sshkey_type(public)); @@ -2405,7 +2414,7 @@ main(int argc, char **argv) gen_all_hostkeys = 1; break; case 'b': - bits = (u_int32_t)strtonum(optarg, 256, 32768, &errstr); + bits = (u_int32_t)strtonum(optarg, 10, 32768, &errstr); if (errstr) fatal("Bits has bad value %s (%s)", optarg, errstr); @@ -2683,6 +2692,8 @@ main(int argc, char **argv) _PATH_HOST_ECDSA_KEY_FILE, rr_hostname); n += do_print_resource_record(pw, _PATH_HOST_ED25519_KEY_FILE, rr_hostname); + n += do_print_resource_record(pw, + _PATH_HOST_XMSS_KEY_FILE, rr_hostname); if (n == 0) fatal("no keys found."); exit(0); diff --git a/ssh-keyscan.c b/ssh-keyscan.c index 15059f6fa..535368602 100644 --- a/ssh-keyscan.c +++ b/ssh-keyscan.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keyscan.c,v 1.117 2018/02/23 05:14:05 djm Exp $ */ +/* $OpenBSD: ssh-keyscan.c,v 1.118 2018/02/23 15:58:38 markus Exp $ */ /* * Copyright 1995, 1996 by David Mazieres . * @@ -58,9 +58,10 @@ int ssh_port = SSH_DEFAULT_PORT; #define KT_RSA (1<<1) #define KT_ECDSA (1<<2) #define KT_ED25519 (1<<3) +#define KT_XMSS (1<<4) #define KT_MIN KT_DSA -#define KT_MAX KT_ED25519 +#define KT_MAX KT_XMSS int get_cert = 0; int get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519; @@ -238,6 +239,10 @@ keygrab_ssh2(con *c) myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? "ssh-ed25519-cert-v01@openssh.com" : "ssh-ed25519"; break; + case KT_XMSS: + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? + "ssh-xmss-cert-v01@openssh.com" : "ssh-xmss@openssh.com"; + break; case KT_ECDSA: myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? "ecdsa-sha2-nistp256-cert-v01@openssh.com," @@ -718,6 +723,9 @@ main(int argc, char **argv) case KEY_ED25519: get_keytypes |= KT_ED25519; break; + case KEY_XMSS: + get_keytypes |= KT_XMSS; + break; case KEY_UNSPEC: default: fatal("Unknown key type \"%s\"", tname); diff --git a/ssh-keysign.c b/ssh-keysign.c index 17e87a281..78bb66b08 100644 --- a/ssh-keysign.c +++ b/ssh-keysign.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keysign.c,v 1.53 2018/02/07 22:52:45 dtucker Exp $ */ +/* $OpenBSD: ssh-keysign.c,v 1.54 2018/02/23 15:58:38 markus Exp $ */ /* * Copyright (c) 2002 Markus Friedl. All rights reserved. * @@ -171,7 +171,7 @@ main(int argc, char **argv) { struct sshbuf *b; Options options; -#define NUM_KEYTYPES 4 +#define NUM_KEYTYPES 5 struct sshkey *keys[NUM_KEYTYPES], *key = NULL; struct passwd *pw; int r, key_fd[NUM_KEYTYPES], i, found, version = 2, fd; @@ -198,6 +198,7 @@ main(int argc, char **argv) key_fd[i++] = open(_PATH_HOST_DSA_KEY_FILE, O_RDONLY); key_fd[i++] = open(_PATH_HOST_ECDSA_KEY_FILE, O_RDONLY); key_fd[i++] = open(_PATH_HOST_ED25519_KEY_FILE, O_RDONLY); + key_fd[i++] = open(_PATH_HOST_XMSS_KEY_FILE, O_RDONLY); key_fd[i++] = open(_PATH_HOST_RSA_KEY_FILE, O_RDONLY); original_real_uid = getuid(); /* XXX readconf.c needs this */ diff --git a/ssh-xmss.c b/ssh-xmss.c new file mode 100644 index 000000000..d9dafd762 --- /dev/null +++ b/ssh-xmss.c @@ -0,0 +1,188 @@ +/* $OpenBSD: ssh-xmss.c,v 1.1 2018/02/23 15:58:38 markus Exp $*/ +/* + * Copyright (c) 2017 Stefan-Lukas Gazdag. + * Copyright (c) 2017 Markus Friedl. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#define SSHKEY_INTERNAL +#include +#include + +#include +#include +#include + +#include "log.h" +#include "sshbuf.h" +#include "sshkey.h" +#include "sshkey-xmss.h" +#include "ssherr.h" +#include "ssh.h" + +#include "xmss_fast.h" + +int +ssh_xmss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat) +{ + u_char *sig = NULL; + size_t slen = 0, len = 0, required_siglen; + unsigned long long smlen; + int r, ret; + struct sshbuf *b = NULL; + + if (lenp != NULL) + *lenp = 0; + if (sigp != NULL) + *sigp = NULL; + + if (key == NULL || + sshkey_type_plain(key->type) != KEY_XMSS || + key->xmss_sk == NULL || + sshkey_xmss_params(key) == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((r = sshkey_xmss_siglen(key, &required_siglen)) != 0) + return r; + if (datalen >= INT_MAX - required_siglen) + return SSH_ERR_INVALID_ARGUMENT; + smlen = slen = datalen + required_siglen; + if ((sig = malloc(slen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshkey_xmss_get_state(key, error)) != 0) + goto out; + if ((ret = xmss_sign(key->xmss_sk, sshkey_xmss_bds_state(key), sig, &smlen, + data, datalen, sshkey_xmss_params(key))) != 0 || smlen <= datalen) { + r = SSH_ERR_INVALID_ARGUMENT; /* XXX better error? */ + goto out; + } + /* encode signature */ + if ((b = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_put_cstring(b, "ssh-xmss@openssh.com")) != 0 || + (r = sshbuf_put_string(b, sig, smlen - datalen)) != 0) + goto out; + len = sshbuf_len(b); + if (sigp != NULL) { + if ((*sigp = malloc(len)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(*sigp, sshbuf_ptr(b), len); + } + if (lenp != NULL) + *lenp = len; + /* success */ + r = 0; + out: + if ((ret = sshkey_xmss_update_state(key, error)) != 0) { + /* discard signature since we cannot update the state */ + if (r == 0 && sigp != NULL && *sigp != NULL) { + explicit_bzero(*sigp, len); + free(*sigp); + } + if (sigp != NULL) + *sigp = NULL; + if (lenp != NULL) + *lenp = 0; + r = ret; + } + sshbuf_free(b); + if (sig != NULL) { + explicit_bzero(sig, slen); + free(sig); + } + + return r; +} + +int +ssh_xmss_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat) +{ + struct sshbuf *b = NULL; + char *ktype = NULL; + const u_char *sigblob; + u_char *sm = NULL, *m = NULL; + size_t len, required_siglen; + unsigned long long smlen = 0, mlen = 0; + int r, ret; + + if (key == NULL || + sshkey_type_plain(key->type) != KEY_XMSS || + key->xmss_pk == NULL || + sshkey_xmss_params(key) == NULL || + signature == NULL || signaturelen == 0) + return SSH_ERR_INVALID_ARGUMENT; + if ((r = sshkey_xmss_siglen(key, &required_siglen)) != 0) + return r; + if (datalen >= INT_MAX - required_siglen) + return SSH_ERR_INVALID_ARGUMENT; + + if ((b = sshbuf_from(signature, signaturelen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 || + (r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0) + goto out; + if (strcmp("ssh-xmss@openssh.com", ktype) != 0) { + r = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } + if (sshbuf_len(b) != 0) { + r = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; + } + if (len != required_siglen) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (datalen >= SIZE_MAX - len) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + smlen = len + datalen; + mlen = smlen; + if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(sm, sigblob, len); + memcpy(sm+len, data, datalen); + if ((ret = xmss_sign_open(m, &mlen, sm, smlen, + key->xmss_pk, sshkey_xmss_params(key))) != 0) { + debug2("%s: crypto_sign_xmss_open failed: %d", + __func__, ret); + } + if (ret != 0 || mlen != datalen) { + r = SSH_ERR_SIGNATURE_INVALID; + goto out; + } + /* XXX compare 'm' and 'data' ? */ + /* success */ + r = 0; + out: + if (sm != NULL) { + explicit_bzero(sm, smlen); + free(sm); + } + if (m != NULL) { + explicit_bzero(m, smlen); /* NB mlen may be invalid if r != 0 */ + free(m); + } + sshbuf_free(b); + free(ktype); + return r; +} diff --git a/ssh.c b/ssh.c index fa57290be..d3619fe29 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.474 2018/02/23 02:34:33 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.475 2018/02/23 15:58:38 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1384,7 +1384,7 @@ main(int ac, char **av) sensitive_data.keys = NULL; sensitive_data.external_keysign = 0; if (options.hostbased_authentication) { - sensitive_data.nkeys = 9; + sensitive_data.nkeys = 11; sensitive_data.keys = xcalloc(sensitive_data.nkeys, sizeof(struct sshkey)); /* XXX */ for (i = 0; i < sensitive_data.nkeys; i++) @@ -1411,6 +1411,10 @@ main(int ac, char **av) _PATH_HOST_RSA_KEY_FILE, "", NULL, NULL); sensitive_data.keys[8] = key_load_private_type(KEY_DSA, _PATH_HOST_DSA_KEY_FILE, "", NULL, NULL); + sensitive_data.keys[9] = key_load_private_cert(KEY_XMSS, + _PATH_HOST_XMSS_KEY_FILE, "", NULL); + sensitive_data.keys[10] = key_load_private_type(KEY_XMSS, + _PATH_HOST_XMSS_KEY_FILE, "", NULL, NULL); PRIV_END; if (options.hostbased_authentication == 1 && @@ -1418,7 +1422,8 @@ main(int ac, char **av) sensitive_data.keys[5] == NULL && sensitive_data.keys[6] == NULL && sensitive_data.keys[7] == NULL && - sensitive_data.keys[8] == NULL) { + sensitive_data.keys[8] == NULL && + sensitive_data.keys[9] == NULL) { #ifdef OPENSSL_HAS_ECC sensitive_data.keys[1] = key_load_cert( _PATH_HOST_ECDSA_KEY_FILE); @@ -1439,6 +1444,10 @@ main(int ac, char **av) _PATH_HOST_RSA_KEY_FILE, NULL); sensitive_data.keys[8] = key_load_public( _PATH_HOST_DSA_KEY_FILE, NULL); + sensitive_data.keys[9] = key_load_cert( + _PATH_HOST_XMSS_KEY_FILE); + sensitive_data.keys[10] = key_load_public( + _PATH_HOST_XMSS_KEY_FILE, NULL); sensitive_data.external_keysign = 1; } } diff --git a/sshconnect.c b/sshconnect.c index 07eae09f6..3805d35d9 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.296 2018/02/23 04:18:46 dtucker Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.297 2018/02/23 15:58:38 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1475,6 +1475,7 @@ show_other_keys(struct hostkeys *hostkeys, struct sshkey *key) KEY_DSA, KEY_ECDSA, KEY_ED25519, + KEY_XMSS, -1 }; int i, ret = 0; @@ -1592,7 +1593,7 @@ maybe_add_key_to_agent(char *authfile, const struct sshkey *private, } if ((r = ssh_add_identity_constrained(auth_sock, private, comment, 0, - (options.add_keys_to_agent == 3))) == 0) + (options.add_keys_to_agent == 3), 0)) == 0) debug("identity added to agent: %s", authfile); else debug("could not add identity to agent: %s (%d)", authfile, r); diff --git a/sshd.c b/sshd.c index 7466d5a44..0b9a7ec46 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.504 2018/02/11 21:16:56 dtucker Exp $ */ +/* $OpenBSD: sshd.c,v 1.505 2018/02/23 15:58:38 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -705,6 +705,7 @@ list_hostkey_types(void) case KEY_DSA: case KEY_ECDSA: case KEY_ED25519: + case KEY_XMSS: if (buffer_len(&b) > 0) buffer_append(&b, ",", 1); p = key_ssh_name(key); @@ -726,6 +727,7 @@ list_hostkey_types(void) case KEY_DSA_CERT: case KEY_ECDSA_CERT: case KEY_ED25519_CERT: + case KEY_XMSS_CERT: if (buffer_len(&b) > 0) buffer_append(&b, ",", 1); p = key_ssh_name(key); @@ -752,6 +754,7 @@ get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh) case KEY_DSA_CERT: case KEY_ECDSA_CERT: case KEY_ED25519_CERT: + case KEY_XMSS_CERT: key = sensitive_data.host_certificates[i]; break; default: @@ -1734,6 +1737,7 @@ main(int ac, char **av) case KEY_DSA: case KEY_ECDSA: case KEY_ED25519: + case KEY_XMSS: if (have_agent || key != NULL) sensitive_data.have_ssh2_key = 1; break; diff --git a/sshkey-xmss.c b/sshkey-xmss.c new file mode 100644 index 000000000..41cc1bade --- /dev/null +++ b/sshkey-xmss.c @@ -0,0 +1,1048 @@ +/* $OpenBSD: sshkey-xmss.c,v 1.1 2018/02/23 15:58:38 markus Exp $ */ +/* + * Copyright (c) 2017 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "ssh2.h" +#include "ssherr.h" +#include "sshbuf.h" +#include "cipher.h" +#include "sshkey.h" +#include "sshkey-xmss.h" +#include "atomicio.h" + +#include "xmss_fast.h" + +/* opaque internal XMSS state */ +#define XMSS_MAGIC "xmss-state-v1" +#define XMSS_CIPHERNAME "aes256-gcm@openssh.com" +struct ssh_xmss_state { + xmss_params params; + u_int32_t n, w, h, k; + + bds_state bds; + u_char *stack; + u_int32_t stackoffset; + u_char *stacklevels; + u_char *auth; + u_char *keep; + u_char *th_nodes; + u_char *retain; + treehash_inst *treehash; + + u_int32_t idx; /* state read from file */ + u_int32_t maxidx; /* resticted # of signatures */ + int have_state; /* .state file exists */ + int lockfd; /* locked in sshkey_xmss_get_state() */ + int allow_update; /* allow sshkey_xmss_update_state() */ + char *enc_ciphername;/* encrypt state with cipher */ + u_char *enc_keyiv; /* encrypt state with key */ + u_int32_t enc_keyiv_len; /* length of enc_keyiv */ +}; + +int sshkey_xmss_init_bds_state(struct sshkey *); +int sshkey_xmss_init_enc_key(struct sshkey *, const char *); +void sshkey_xmss_free_bds(struct sshkey *); +int sshkey_xmss_get_state_from_file(struct sshkey *, const char *, + int *, sshkey_printfn *); +int sshkey_xmss_encrypt_state(const struct sshkey *, struct sshbuf *, + struct sshbuf **); +int sshkey_xmss_decrypt_state(const struct sshkey *, struct sshbuf *, + struct sshbuf **); +int sshkey_xmss_serialize_enc_key(const struct sshkey *, struct sshbuf *); +int sshkey_xmss_deserialize_enc_key(struct sshkey *, struct sshbuf *); + +#define PRINT(s...) do { if (pr) pr(s); } while (0) + +int +sshkey_xmss_init(struct sshkey *key, const char *name) +{ + struct ssh_xmss_state *state; + + if (key->xmss_state != NULL) + return SSH_ERR_INVALID_FORMAT; + if (name == NULL) + return SSH_ERR_INVALID_FORMAT; + state = calloc(sizeof(struct ssh_xmss_state), 1); + if (state == NULL) + return SSH_ERR_ALLOC_FAIL; + if (strcmp(name, XMSS_SHA2_256_W16_H10_NAME) == 0) { + state->n = 32; + state->w = 16; + state->h = 10; + } else if (strcmp(name, XMSS_SHA2_256_W16_H16_NAME) == 0) { + state->n = 32; + state->w = 16; + state->h = 16; + } else if (strcmp(name, XMSS_SHA2_256_W16_H20_NAME) == 0) { + state->n = 32; + state->w = 16; + state->h = 20; + } else { + free(state); + return SSH_ERR_KEY_TYPE_UNKNOWN; + } + if ((key->xmss_name = strdup(name)) == NULL) { + free(state); + return SSH_ERR_ALLOC_FAIL; + } + state->k = 2; /* XXX hardcoded */ + state->lockfd = -1; + if (xmss_set_params(&state->params, state->n, state->h, state->w, + state->k) != 0) { + free(state); + return SSH_ERR_INVALID_FORMAT; + } + key->xmss_state = state; + return 0; +} + +void +sshkey_xmss_free_state(struct sshkey *key) +{ + struct ssh_xmss_state *state = key->xmss_state; + + sshkey_xmss_free_bds(key); + if (state) { + if (state->enc_keyiv) { + explicit_bzero(state->enc_keyiv, state->enc_keyiv_len); + free(state->enc_keyiv); + } + free(state->enc_ciphername); + free(state); + } + key->xmss_state = NULL; +} + +#define SSH_XMSS_K2_MAGIC "k=2" +#define num_stack(x) ((x->h+1)*(x->n)) +#define num_stacklevels(x) (x->h+1) +#define num_auth(x) ((x->h)*(x->n)) +#define num_keep(x) ((x->h >> 1)*(x->n)) +#define num_th_nodes(x) ((x->h - x->k)*(x->n)) +#define num_retain(x) (((1ULL << x->k) - x->k - 1) * (x->n)) +#define num_treehash(x) ((x->h) - (x->k)) + +int +sshkey_xmss_init_bds_state(struct sshkey *key) +{ + struct ssh_xmss_state *state = key->xmss_state; + u_int32_t i; + + state->stackoffset = 0; + if ((state->stack = calloc(num_stack(state), 1)) == NULL || + (state->stacklevels = calloc(num_stacklevels(state), 1))== NULL || + (state->auth = calloc(num_auth(state), 1)) == NULL || + (state->keep = calloc(num_keep(state), 1)) == NULL || + (state->th_nodes = calloc(num_th_nodes(state), 1)) == NULL || + (state->retain = calloc(num_retain(state), 1)) == NULL || + (state->treehash = calloc(num_treehash(state), + sizeof(treehash_inst))) == NULL) { + sshkey_xmss_free_bds(key); + return SSH_ERR_ALLOC_FAIL; + } + for (i = 0; i < state->h - state->k; i++) + state->treehash[i].node = &state->th_nodes[state->n*i]; + xmss_set_bds_state(&state->bds, state->stack, state->stackoffset, + state->stacklevels, state->auth, state->keep, state->treehash, + state->retain, 0); + return 0; +} + +void +sshkey_xmss_free_bds(struct sshkey *key) +{ + struct ssh_xmss_state *state = key->xmss_state; + + if (state == NULL) + return; + free(state->stack); + free(state->stacklevels); + free(state->auth); + free(state->keep); + free(state->th_nodes); + free(state->retain); + free(state->treehash); + state->stack = NULL; + state->stacklevels = NULL; + state->auth = NULL; + state->keep = NULL; + state->th_nodes = NULL; + state->retain = NULL; + state->treehash = NULL; +} + +void * +sshkey_xmss_params(const struct sshkey *key) +{ + struct ssh_xmss_state *state = key->xmss_state; + + if (state == NULL) + return NULL; + return &state->params; +} + +void * +sshkey_xmss_bds_state(const struct sshkey *key) +{ + struct ssh_xmss_state *state = key->xmss_state; + + if (state == NULL) + return NULL; + return &state->bds; +} + +int +sshkey_xmss_siglen(const struct sshkey *key, size_t *lenp) +{ + struct ssh_xmss_state *state = key->xmss_state; + + if (lenp == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (state == NULL) + return SSH_ERR_INVALID_FORMAT; + *lenp = 4 + state->n + + state->params.wots_par.keysize + + state->h * state->n; + return 0; +} + +size_t +sshkey_xmss_pklen(const struct sshkey *key) +{ + struct ssh_xmss_state *state = key->xmss_state; + + if (state == NULL) + return 0; + return state->n * 2; +} + +size_t +sshkey_xmss_sklen(const struct sshkey *key) +{ + struct ssh_xmss_state *state = key->xmss_state; + + if (state == NULL) + return 0; + return state->n * 4 + 4; +} + +int +sshkey_xmss_init_enc_key(struct sshkey *k, const char *ciphername) +{ + struct ssh_xmss_state *state = k->xmss_state; + const struct sshcipher *cipher; + size_t keylen = 0, ivlen = 0; + + if (state == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((cipher = cipher_by_name(ciphername)) == NULL) + return SSH_ERR_INTERNAL_ERROR; + if ((state->enc_ciphername = strdup(ciphername)) == NULL) + return SSH_ERR_ALLOC_FAIL; + keylen = cipher_keylen(cipher); + ivlen = cipher_ivlen(cipher); + state->enc_keyiv_len = keylen + ivlen; + if ((state->enc_keyiv = calloc(state->enc_keyiv_len, 1)) == NULL) { + free(state->enc_ciphername); + state->enc_ciphername = NULL; + return SSH_ERR_ALLOC_FAIL; + } + arc4random_buf(state->enc_keyiv, state->enc_keyiv_len); + return 0; +} + +int +sshkey_xmss_serialize_enc_key(const struct sshkey *k, struct sshbuf *b) +{ + struct ssh_xmss_state *state = k->xmss_state; + int r; + + if (state == NULL || state->enc_keyiv == NULL || + state->enc_ciphername == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((r = sshbuf_put_cstring(b, state->enc_ciphername)) != 0 || + (r = sshbuf_put_string(b, state->enc_keyiv, + state->enc_keyiv_len)) != 0) + return r; + return 0; +} + +int +sshkey_xmss_deserialize_enc_key(struct sshkey *k, struct sshbuf *b) +{ + struct ssh_xmss_state *state = k->xmss_state; + size_t len; + int r; + + if (state == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((r = sshbuf_get_cstring(b, &state->enc_ciphername, NULL)) != 0 || + (r = sshbuf_get_string(b, &state->enc_keyiv, &len)) != 0) + return r; + state->enc_keyiv_len = len; + return 0; +} + +int +sshkey_xmss_serialize_pk_info(const struct sshkey *k, struct sshbuf *b, + enum sshkey_serialize_rep opts) +{ + struct ssh_xmss_state *state = k->xmss_state; + u_char have_info = 1; + u_int32_t idx; + int r; + + if (state == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (opts != SSHKEY_SERIALIZE_INFO) + return 0; + idx = k->xmss_sk ? PEEK_U32(k->xmss_sk) : state->idx; + if ((r = sshbuf_put_u8(b, have_info)) != 0 || + (r = sshbuf_put_u32(b, idx)) != 0 || + (r = sshbuf_put_u32(b, state->maxidx)) != 0) + return r; + return 0; +} + +int +sshkey_xmss_deserialize_pk_info(struct sshkey *k, struct sshbuf *b) +{ + struct ssh_xmss_state *state = k->xmss_state; + u_char have_info; + int r; + + if (state == NULL) + return SSH_ERR_INVALID_ARGUMENT; + /* optional */ + if (sshbuf_len(b) == 0) + return 0; + if ((r = sshbuf_get_u8(b, &have_info)) != 0) + return r; + if (have_info != 1) + return SSH_ERR_INVALID_ARGUMENT; + if ((r = sshbuf_get_u32(b, &state->idx)) != 0 || + (r = sshbuf_get_u32(b, &state->maxidx)) != 0) + return r; + return 0; +} + +int +sshkey_xmss_generate_private_key(struct sshkey *k, u_int bits) +{ + int r; + const char *name; + + if (bits == 10) { + name = XMSS_SHA2_256_W16_H10_NAME; + } else if (bits == 16) { + name = XMSS_SHA2_256_W16_H16_NAME; + } else if (bits == 20) { + name = XMSS_SHA2_256_W16_H20_NAME; + } else { + name = XMSS_DEFAULT_NAME; + } + if ((r = sshkey_xmss_init(k, name)) != 0 || + (r = sshkey_xmss_init_bds_state(k)) != 0 || + (r = sshkey_xmss_init_enc_key(k, XMSS_CIPHERNAME)) != 0) + return r; + if ((k->xmss_pk = malloc(sshkey_xmss_pklen(k))) == NULL || + (k->xmss_sk = malloc(sshkey_xmss_sklen(k))) == NULL) { + return SSH_ERR_ALLOC_FAIL; + } + xmss_keypair(k->xmss_pk, k->xmss_sk, sshkey_xmss_bds_state(k), + sshkey_xmss_params(k)); + return 0; +} + +int +sshkey_xmss_get_state_from_file(struct sshkey *k, const char *filename, + int *have_file, sshkey_printfn *pr) +{ + struct sshbuf *b = NULL, *enc = NULL; + int ret = SSH_ERR_SYSTEM_ERROR, r, fd = -1; + u_int32_t len; + unsigned char buf[4], *data = NULL; + + *have_file = 0; + if ((fd = open(filename, O_RDONLY)) >= 0) { + *have_file = 1; + if (atomicio(read, fd, buf, sizeof(buf)) != sizeof(buf)) { + PRINT("%s: corrupt state file: %s", __func__, filename); + goto done; + } + len = PEEK_U32(buf); + if ((data = calloc(len, 1)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto done; + } + if (atomicio(read, fd, data, len) != len) { + PRINT("%s: cannot read blob: %s", __func__, filename); + goto done; + } + if ((enc = sshbuf_from(data, len)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto done; + } + sshkey_xmss_free_bds(k); + if ((r = sshkey_xmss_decrypt_state(k, enc, &b)) != 0) { + ret = r; + goto done; + } + if ((r = sshkey_xmss_deserialize_state(k, b)) != 0) { + ret = r; + goto done; + } + ret = 0; + } +done: + if (fd != -1) + close(fd); + free(data); + sshbuf_free(enc); + sshbuf_free(b); + return ret; +} + +int +sshkey_xmss_get_state(const struct sshkey *k, sshkey_printfn *pr) +{ + struct ssh_xmss_state *state = k->xmss_state; + u_int32_t idx = 0; + char *filename = NULL; + char *statefile = NULL, *ostatefile = NULL, *lockfile = NULL; + int lockfd = -1, have_state = 0, have_ostate, tries = 0; + int ret = SSH_ERR_INVALID_ARGUMENT, r; + + if (state == NULL) + goto done; + /* + * If maxidx is set, then we are allowed a limited number + * of signatures, but don't need to access the disk. + * Otherwise we need to deal with the on-disk state. + */ + if (state->maxidx) { + /* xmss_sk always contains the current state */ + idx = PEEK_U32(k->xmss_sk); + if (idx < state->maxidx) { + state->allow_update = 1; + return 0; + } + return SSH_ERR_INVALID_ARGUMENT; + } + if ((filename = k->xmss_filename) == NULL) + goto done; + if (asprintf(&lockfile, "%s.lock", filename) < 0 || + asprintf(&statefile, "%s.state", filename) < 0 || + asprintf(&ostatefile, "%s.ostate", filename) < 0) { + ret = SSH_ERR_ALLOC_FAIL; + goto done; + } + if ((lockfd = open(lockfile, O_CREAT|O_RDONLY, 0600)) < 0) { + ret = SSH_ERR_SYSTEM_ERROR; + PRINT("%s: cannot open/create: %s", __func__, lockfile); + goto done; + } + while (flock(lockfd, LOCK_EX|LOCK_NB) < 0) { + if (errno != EWOULDBLOCK) { + ret = SSH_ERR_SYSTEM_ERROR; + PRINT("%s: cannot lock: %s", __func__, lockfile); + goto done; + } + if (++tries > 10) { + ret = SSH_ERR_SYSTEM_ERROR; + PRINT("%s: giving up on: %s", __func__, lockfile); + goto done; + } + usleep(1000*100*tries); + } + /* XXX no longer const */ + if ((r = sshkey_xmss_get_state_from_file((struct sshkey *)k, + statefile, &have_state, pr)) != 0) { + if ((r = sshkey_xmss_get_state_from_file((struct sshkey *)k, + ostatefile, &have_ostate, pr)) == 0) { + state->allow_update = 1; + r = sshkey_xmss_forward_state(k, 1); + state->idx = PEEK_U32(k->xmss_sk); + state->allow_update = 0; + } + } + if (!have_state && !have_ostate) { + /* check that bds state is initialized */ + if (state->bds.auth == NULL) + goto done; + PRINT("%s: start from scratch idx 0: %u", __func__, state->idx); + } else if (r != 0) { + ret = r; + goto done; + } + if (state->idx + 1 < state->idx) { + PRINT("%s: state wrap: %u", __func__, state->idx); + goto done; + } + state->have_state = have_state; + state->lockfd = lockfd; + state->allow_update = 1; + lockfd = -1; + ret = 0; +done: + if (lockfd != -1) + close(lockfd); + free(lockfile); + free(statefile); + free(ostatefile); + return ret; +} + +int +sshkey_xmss_forward_state(const struct sshkey *k, u_int32_t reserve) +{ + struct ssh_xmss_state *state = k->xmss_state; + u_char *sig = NULL; + size_t required_siglen; + unsigned long long smlen; + u_char data; + int ret, r; + + if (state == NULL || !state->allow_update) + return SSH_ERR_INVALID_ARGUMENT; + if (reserve == 0) + return SSH_ERR_INVALID_ARGUMENT; + if (state->idx + reserve <= state->idx) + return SSH_ERR_INVALID_ARGUMENT; + if ((r = sshkey_xmss_siglen(k, &required_siglen)) != 0) + return r; + if ((sig = malloc(required_siglen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + while (reserve-- > 0) { + state->idx = PEEK_U32(k->xmss_sk); + smlen = required_siglen; + if ((ret = xmss_sign(k->xmss_sk, sshkey_xmss_bds_state(k), + sig, &smlen, &data, 0, sshkey_xmss_params(k))) != 0) { + r = SSH_ERR_INVALID_ARGUMENT; + break; + } + } + free(sig); + return r; +} + +int +sshkey_xmss_update_state(const struct sshkey *k, sshkey_printfn *pr) +{ + struct ssh_xmss_state *state = k->xmss_state; + struct sshbuf *b = NULL, *enc = NULL; + u_int32_t idx = 0; + unsigned char buf[4]; + char *filename = NULL; + char *statefile = NULL, *ostatefile = NULL, *nstatefile = NULL; + int fd = -1; + int ret = SSH_ERR_INVALID_ARGUMENT; + + if (state == NULL || !state->allow_update) + return ret; + if (state->maxidx) { + /* no update since the number of signatures is limited */ + ret = 0; + goto done; + } + idx = PEEK_U32(k->xmss_sk); + if (idx == state->idx) { + /* no signature happend, no need to update */ + ret = 0; + goto done; + } else if (idx != state->idx + 1) { + PRINT("%s: more than one signature happened: idx %u state %u", + __func__, idx, state->idx); + goto done; + } + state->idx = idx; + if ((filename = k->xmss_filename) == NULL) + goto done; + if (asprintf(&statefile, "%s.state", filename) < 0 || + asprintf(&ostatefile, "%s.ostate", filename) < 0 || + asprintf(&nstatefile, "%s.nstate", filename) < 0) { + ret = SSH_ERR_ALLOC_FAIL; + goto done; + } + unlink(nstatefile); + if ((b = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto done; + } + if ((ret = sshkey_xmss_serialize_state(k, b)) != 0) { + PRINT("%s: SERLIALIZE FAILED: %d", __func__, ret); + goto done; + } + if ((ret = sshkey_xmss_encrypt_state(k, b, &enc)) != 0) { + PRINT("%s: ENCRYPT FAILED: %d", __func__, ret); + goto done; + } + if ((fd = open(nstatefile, O_CREAT|O_WRONLY|O_EXCL, 0600)) < 0) { + ret = SSH_ERR_SYSTEM_ERROR; + PRINT("%s: open new state file: %s", __func__, nstatefile); + goto done; + } + POKE_U32(buf, sshbuf_len(enc)); + if (atomicio(vwrite, fd, buf, sizeof(buf)) != sizeof(buf)) { + ret = SSH_ERR_SYSTEM_ERROR; + PRINT("%s: write new state file hdr: %s", __func__, nstatefile); + close(fd); + goto done; + } + if (atomicio(vwrite, fd, (void *)sshbuf_ptr(enc), sshbuf_len(enc)) != + sshbuf_len(enc)) { + ret = SSH_ERR_SYSTEM_ERROR; + PRINT("%s: write new state file data: %s", __func__, nstatefile); + close(fd); + goto done; + } + if (fsync(fd) < 0) { + ret = SSH_ERR_SYSTEM_ERROR; + PRINT("%s: sync new state file: %s", __func__, nstatefile); + close(fd); + goto done; + } + if (close(fd) < 0) { + ret = SSH_ERR_SYSTEM_ERROR; + PRINT("%s: close new state file: %s", __func__, nstatefile); + goto done; + } + if (state->have_state) { + unlink(ostatefile); + if (link(statefile, ostatefile)) { + ret = SSH_ERR_SYSTEM_ERROR; + PRINT("%s: backup state %s to %s", __func__, statefile, + ostatefile); + goto done; + } + } + if (rename(nstatefile, statefile) < 0) { + ret = SSH_ERR_SYSTEM_ERROR; + PRINT("%s: rename %s to %s", __func__, nstatefile, statefile); + goto done; + } + ret = 0; +done: + if (state->lockfd != -1) { + close(state->lockfd); + state->lockfd = -1; + } + if (nstatefile) + unlink(nstatefile); + free(statefile); + free(ostatefile); + free(nstatefile); + sshbuf_free(b); + sshbuf_free(enc); + return ret; +} + +int +sshkey_xmss_serialize_state(const struct sshkey *k, struct sshbuf *b) +{ + struct ssh_xmss_state *state = k->xmss_state; + treehash_inst *th; + u_int32_t i, node; + int r; + + if (state == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (state->stack == NULL) + return SSH_ERR_INVALID_ARGUMENT; + state->stackoffset = state->bds.stackoffset; /* copy back */ + if ((r = sshbuf_put_cstring(b, SSH_XMSS_K2_MAGIC)) != 0 || + (r = sshbuf_put_u32(b, state->idx)) != 0 || + (r = sshbuf_put_string(b, state->stack, num_stack(state))) != 0 || + (r = sshbuf_put_u32(b, state->stackoffset)) != 0 || + (r = sshbuf_put_string(b, state->stacklevels, num_stacklevels(state))) != 0 || + (r = sshbuf_put_string(b, state->auth, num_auth(state))) != 0 || + (r = sshbuf_put_string(b, state->keep, num_keep(state))) != 0 || + (r = sshbuf_put_string(b, state->th_nodes, num_th_nodes(state))) != 0 || + (r = sshbuf_put_string(b, state->retain, num_retain(state))) != 0 || + (r = sshbuf_put_u32(b, num_treehash(state))) != 0) + return r; + for (i = 0; i < num_treehash(state); i++) { + th = &state->treehash[i]; + node = th->node - state->th_nodes; + if ((r = sshbuf_put_u32(b, th->h)) != 0 || + (r = sshbuf_put_u32(b, th->next_idx)) != 0 || + (r = sshbuf_put_u32(b, th->stackusage)) != 0 || + (r = sshbuf_put_u8(b, th->completed)) != 0 || + (r = sshbuf_put_u32(b, node)) != 0) + return r; + } + return 0; +} + +int +sshkey_xmss_serialize_state_opt(const struct sshkey *k, struct sshbuf *b, + enum sshkey_serialize_rep opts) +{ + struct ssh_xmss_state *state = k->xmss_state; + int r = SSH_ERR_INVALID_ARGUMENT; + + if (state == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((r = sshbuf_put_u8(b, opts)) != 0) + return r; + switch (opts) { + case SSHKEY_SERIALIZE_STATE: + r = sshkey_xmss_serialize_state(k, b); + break; + case SSHKEY_SERIALIZE_FULL: + if ((r = sshkey_xmss_serialize_enc_key(k, b)) != 0) + break; + r = sshkey_xmss_serialize_state(k, b); + break; + case SSHKEY_SERIALIZE_DEFAULT: + r = 0; + break; + default: + r = SSH_ERR_INVALID_ARGUMENT; + break; + } + return r; +} + +int +sshkey_xmss_deserialize_state(struct sshkey *k, struct sshbuf *b) +{ + struct ssh_xmss_state *state = k->xmss_state; + treehash_inst *th; + u_int32_t i, lh, node; + size_t ls, lsl, la, lk, ln, lr; + char *magic; + int r; + + if (state == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (k->xmss_sk == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((state->treehash = calloc(num_treehash(state), + sizeof(treehash_inst))) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_get_cstring(b, &magic, NULL)) != 0 || + (r = sshbuf_get_u32(b, &state->idx)) != 0 || + (r = sshbuf_get_string(b, &state->stack, &ls)) != 0 || + (r = sshbuf_get_u32(b, &state->stackoffset)) != 0 || + (r = sshbuf_get_string(b, &state->stacklevels, &lsl)) != 0 || + (r = sshbuf_get_string(b, &state->auth, &la)) != 0 || + (r = sshbuf_get_string(b, &state->keep, &lk)) != 0 || + (r = sshbuf_get_string(b, &state->th_nodes, &ln)) != 0 || + (r = sshbuf_get_string(b, &state->retain, &lr)) != 0 || + (r = sshbuf_get_u32(b, &lh)) != 0) + return r; + if (strcmp(magic, SSH_XMSS_K2_MAGIC) != 0) + return SSH_ERR_INVALID_ARGUMENT; + /* XXX check stackoffset */ + if (ls != num_stack(state) || + lsl != num_stacklevels(state) || + la != num_auth(state) || + lk != num_keep(state) || + ln != num_th_nodes(state) || + lr != num_retain(state) || + lh != num_treehash(state)) + return SSH_ERR_INVALID_ARGUMENT; + for (i = 0; i < num_treehash(state); i++) { + th = &state->treehash[i]; + if ((r = sshbuf_get_u32(b, &th->h)) != 0 || + (r = sshbuf_get_u32(b, &th->next_idx)) != 0 || + (r = sshbuf_get_u32(b, &th->stackusage)) != 0 || + (r = sshbuf_get_u8(b, &th->completed)) != 0 || + (r = sshbuf_get_u32(b, &node)) != 0) + return r; + if (node < num_th_nodes(state)) + th->node = &state->th_nodes[node]; + } + POKE_U32(k->xmss_sk, state->idx); + xmss_set_bds_state(&state->bds, state->stack, state->stackoffset, + state->stacklevels, state->auth, state->keep, state->treehash, + state->retain, 0); + return 0; +} + +int +sshkey_xmss_deserialize_state_opt(struct sshkey *k, struct sshbuf *b) +{ + enum sshkey_serialize_rep opts; + u_char have_state; + int r; + + if ((r = sshbuf_get_u8(b, &have_state)) != 0) + return r; + + opts = have_state; + switch (opts) { + case SSHKEY_SERIALIZE_DEFAULT: + r = 0; + break; + case SSHKEY_SERIALIZE_STATE: + if ((r = sshkey_xmss_deserialize_state(k, b)) != 0) + return r; + break; + case SSHKEY_SERIALIZE_FULL: + if ((r = sshkey_xmss_deserialize_enc_key(k, b)) != 0 || + (r = sshkey_xmss_deserialize_state(k, b)) != 0) + return r; + break; + default: + r = SSH_ERR_INVALID_FORMAT; + break; + } + return r; +} + +int +sshkey_xmss_encrypt_state(const struct sshkey *k, struct sshbuf *b, + struct sshbuf **retp) +{ + struct ssh_xmss_state *state = k->xmss_state; + struct sshbuf *encrypted = NULL, *encoded = NULL, *padded = NULL; + struct sshcipher_ctx *ciphercontext = NULL; + const struct sshcipher *cipher; + u_char *cp, *key, *iv = NULL; + size_t i, keylen, ivlen, blocksize, authlen, encrypted_len, aadlen; + int r = SSH_ERR_INTERNAL_ERROR; + + if (retp != NULL) + *retp = NULL; + if (state == NULL || + state->enc_keyiv == NULL || + state->enc_ciphername == NULL) + return SSH_ERR_INTERNAL_ERROR; + if ((cipher = cipher_by_name(state->enc_ciphername)) == NULL) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + blocksize = cipher_blocksize(cipher); + keylen = cipher_keylen(cipher); + ivlen = cipher_ivlen(cipher); + authlen = cipher_authlen(cipher); + if (state->enc_keyiv_len != keylen + ivlen) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + key = state->enc_keyiv; + if ((encrypted = sshbuf_new()) == NULL || + (encoded = sshbuf_new()) == NULL || + (padded = sshbuf_new()) == NULL || + (iv = malloc(ivlen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + /* replace first 4 bytes of IV with index to ensure uniqueness */ + memcpy(iv, key + keylen, ivlen); + POKE_U32(iv, state->idx); + + if ((r = sshbuf_put(encoded, XMSS_MAGIC, sizeof(XMSS_MAGIC))) != 0 || + (r = sshbuf_put_u32(encoded, state->idx)) != 0) + goto out; + + /* padded state will be encrypted */ + if ((r = sshbuf_putb(padded, b)) != 0) + goto out; + i = 0; + while (sshbuf_len(padded) % blocksize) { + if ((r = sshbuf_put_u8(padded, ++i & 0xff)) != 0) + goto out; + } + encrypted_len = sshbuf_len(padded); + + /* header including the length of state is used as AAD */ + if ((r = sshbuf_put_u32(encoded, encrypted_len)) != 0) + goto out; + aadlen = sshbuf_len(encoded); + + /* concat header and state */ + if ((r = sshbuf_putb(encoded, padded)) != 0) + goto out; + + /* reserve space for encryption of encoded data plus auth tag */ + /* encrypt at offset addlen */ + if ((r = sshbuf_reserve(encrypted, + encrypted_len + aadlen + authlen, &cp)) != 0 || + (r = cipher_init(&ciphercontext, cipher, key, keylen, + iv, ivlen, 1)) != 0 || + (r = cipher_crypt(ciphercontext, 0, cp, sshbuf_ptr(encoded), + encrypted_len, aadlen, authlen)) != 0) + goto out; + + /* success */ + r = 0; + out: + if (retp != NULL) { + *retp = encrypted; + encrypted = NULL; + } + sshbuf_free(padded); + sshbuf_free(encoded); + sshbuf_free(encrypted); + cipher_free(ciphercontext); + free(iv); + return r; +} + +int +sshkey_xmss_decrypt_state(const struct sshkey *k, struct sshbuf *encoded, + struct sshbuf **retp) +{ + struct ssh_xmss_state *state = k->xmss_state; + struct sshbuf *copy = NULL, *decrypted = NULL; + struct sshcipher_ctx *ciphercontext = NULL; + const struct sshcipher *cipher = NULL; + u_char *key, *iv = NULL, *dp; + size_t keylen, ivlen, authlen, aadlen; + u_int blocksize, encrypted_len, index; + int r = SSH_ERR_INTERNAL_ERROR; + + if (retp != NULL) + *retp = NULL; + if (state == NULL || + state->enc_keyiv == NULL || + state->enc_ciphername == NULL) + return SSH_ERR_INTERNAL_ERROR; + if ((cipher = cipher_by_name(state->enc_ciphername)) == NULL) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + blocksize = cipher_blocksize(cipher); + keylen = cipher_keylen(cipher); + ivlen = cipher_ivlen(cipher); + authlen = cipher_authlen(cipher); + if (state->enc_keyiv_len != keylen + ivlen) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + key = state->enc_keyiv; + + if ((copy = sshbuf_fromb(encoded)) == NULL || + (decrypted = sshbuf_new()) == NULL || + (iv = malloc(ivlen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + /* check magic */ + if (sshbuf_len(encoded) < sizeof(XMSS_MAGIC) || + memcmp(sshbuf_ptr(encoded), XMSS_MAGIC, sizeof(XMSS_MAGIC))) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* parse public portion */ + if ((r = sshbuf_consume(encoded, sizeof(XMSS_MAGIC))) != 0 || + (r = sshbuf_get_u32(encoded, &index)) != 0 || + (r = sshbuf_get_u32(encoded, &encrypted_len)) != 0) + goto out; + + /* check size of encrypted key blob */ + if (encrypted_len < blocksize || (encrypted_len % blocksize) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* check that an appropriate amount of auth data is present */ + if (sshbuf_len(encoded) < encrypted_len + authlen) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + + aadlen = sshbuf_len(copy) - sshbuf_len(encoded); + + /* replace first 4 bytes of IV with index to ensure uniqueness */ + memcpy(iv, key + keylen, ivlen); + POKE_U32(iv, index); + + /* decrypt private state of key */ + if ((r = sshbuf_reserve(decrypted, aadlen + encrypted_len, &dp)) != 0 || + (r = cipher_init(&ciphercontext, cipher, key, keylen, + iv, ivlen, 0)) != 0 || + (r = cipher_crypt(ciphercontext, 0, dp, sshbuf_ptr(copy), + encrypted_len, aadlen, authlen)) != 0) + goto out; + + /* there should be no trailing data */ + if ((r = sshbuf_consume(encoded, encrypted_len + authlen)) != 0) + goto out; + if (sshbuf_len(encoded) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + + /* remove AAD */ + if ((r = sshbuf_consume(decrypted, aadlen)) != 0) + goto out; + /* XXX encrypted includes unchecked padding */ + + /* success */ + r = 0; + if (retp != NULL) { + *retp = decrypted; + decrypted = NULL; + } + out: + cipher_free(ciphercontext); + sshbuf_free(copy); + sshbuf_free(decrypted); + free(iv); + return r; +} + +u_int32_t +sshkey_xmss_signatures_left(const struct sshkey *k) +{ + struct ssh_xmss_state *state = k->xmss_state; + u_int32_t idx; + + if (sshkey_type_plain(k->type) == KEY_XMSS && state && + state->maxidx) { + idx = k->xmss_sk ? PEEK_U32(k->xmss_sk) : state->idx; + if (idx < state->maxidx) + return state->maxidx - idx; + } + return 0; +} + +int +sshkey_xmss_enable_maxsign(struct sshkey *k, u_int32_t maxsign) +{ + struct ssh_xmss_state *state = k->xmss_state; + + if (sshkey_type_plain(k->type) != KEY_XMSS) + return SSH_ERR_INVALID_ARGUMENT; + if (maxsign == 0) + return 0; + if (state->idx + maxsign < state->idx) + return SSH_ERR_INVALID_ARGUMENT; + state->maxidx = state->idx + maxsign; + return 0; +} diff --git a/sshkey-xmss.h b/sshkey-xmss.h new file mode 100644 index 000000000..b9f8ead10 --- /dev/null +++ b/sshkey-xmss.h @@ -0,0 +1,56 @@ +/* $OpenBSD: sshkey-xmss.h,v 1.1 2018/02/23 15:58:38 markus Exp $ */ +/* + * Copyright (c) 2017 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef SSHKEY_XMSS_H +#define SSHKEY_XMSS_H + +#define XMSS_SHA2_256_W16_H10_NAME "XMSS_SHA2-256_W16_H10" +#define XMSS_SHA2_256_W16_H16_NAME "XMSS_SHA2-256_W16_H16" +#define XMSS_SHA2_256_W16_H20_NAME "XMSS_SHA2-256_W16_H20" +#define XMSS_DEFAULT_NAME XMSS_SHA2_256_W16_H10_NAME + +size_t sshkey_xmss_pklen(const struct sshkey *); +size_t sshkey_xmss_sklen(const struct sshkey *); +int sshkey_xmss_init(struct sshkey *, const char *); +void sshkey_xmss_free_state(struct sshkey *); +int sshkey_xmss_generate_private_key(struct sshkey *, u_int); +int sshkey_xmss_serialize_state(const struct sshkey *, struct sshbuf *); +int sshkey_xmss_serialize_state_opt(const struct sshkey *, struct sshbuf *, + enum sshkey_serialize_rep); +int sshkey_xmss_serialize_pk_info(const struct sshkey *, struct sshbuf *, + enum sshkey_serialize_rep); +int sshkey_xmss_deserialize_state(struct sshkey *, struct sshbuf *); +int sshkey_xmss_deserialize_state_opt(struct sshkey *, struct sshbuf *); +int sshkey_xmss_deserialize_pk_info(struct sshkey *, struct sshbuf *); + +int sshkey_xmss_siglen(const struct sshkey *, size_t *); +void *sshkey_xmss_params(const struct sshkey *); +void *sshkey_xmss_bds_state(const struct sshkey *); +int sshkey_xmss_get_state(const struct sshkey *, sshkey_printfn *); +int sshkey_xmss_enable_maxsign(struct sshkey *, u_int32_t); +int sshkey_xmss_forward_state(const struct sshkey *, u_int32_t); +int sshkey_xmss_update_state(const struct sshkey *, sshkey_printfn *); +u_int32_t sshkey_xmss_signatures_left(const struct sshkey *); + +#endif /* SSHKEY_XMSS_H */ diff --git a/sshkey.c b/sshkey.c index 0e146d4d6..d8ee70ca0 100644 --- a/sshkey.c +++ b/sshkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshkey.c,v 1.61 2018/02/14 16:03:32 jsing Exp $ */ +/* $OpenBSD: sshkey.c,v 1.62 2018/02/23 15:58:38 markus Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2008 Alexander von Gernler. All rights reserved. @@ -55,8 +55,11 @@ #include "digest.h" #define SSHKEY_INTERNAL #include "sshkey.h" +#include "sshkey-xmss.h" #include "match.h" +#include "xmss_fast.h" + /* openssh private key file format */ #define MARK_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----\n" #define MARK_END "-----END OPENSSH PRIVATE KEY-----\n" @@ -71,6 +74,8 @@ /* Version identification string for SSH v1 identity files. */ #define LEGACY_BEGIN "SSH PRIVATE KEY FILE FORMAT 1.1\n" +int sshkey_private_serialize_opt(const struct sshkey *key, + struct sshbuf *buf, enum sshkey_serialize_rep); static int sshkey_from_blob_internal(struct sshbuf *buf, struct sshkey **keyp, int allow_cert); @@ -87,6 +92,11 @@ static const struct keytype keytypes[] = { { "ssh-ed25519", "ED25519", KEY_ED25519, 0, 0, 0 }, { "ssh-ed25519-cert-v01@openssh.com", "ED25519-CERT", KEY_ED25519_CERT, 0, 1, 0 }, +#ifdef WITH_XMSS + { "ssh-xmss@openssh.com", "XMSS", KEY_XMSS, 0, 0, 0 }, + { "ssh-xmss-cert-v01@openssh.com", "XMSS-CERT", + KEY_XMSS_CERT, 0, 1, 0 }, +#endif /* WITH_XMSS */ #ifdef WITH_OPENSSL { "ssh-rsa", "RSA", KEY_RSA, 0, 0, 0 }, { "rsa-sha2-256", "RSA", KEY_RSA, 0, 0, 1 }, @@ -274,6 +284,8 @@ sshkey_size(const struct sshkey *k) #endif /* WITH_OPENSSL */ case KEY_ED25519: case KEY_ED25519_CERT: + case KEY_XMSS: + case KEY_XMSS_CERT: return 256; /* XXX */ } return 0; @@ -287,6 +299,7 @@ sshkey_type_is_valid_ca(int type) case KEY_DSA: case KEY_ECDSA: case KEY_ED25519: + case KEY_XMSS: return 1; default: return 0; @@ -314,6 +327,8 @@ sshkey_type_plain(int type) return KEY_ECDSA; case KEY_ED25519_CERT: return KEY_ED25519; + case KEY_XMSS_CERT: + return KEY_XMSS; default: return type; } @@ -461,6 +476,8 @@ sshkey_new(int type) k->cert = NULL; k->ed25519_sk = NULL; k->ed25519_pk = NULL; + k->xmss_sk = NULL; + k->xmss_pk = NULL; switch (k->type) { #ifdef WITH_OPENSSL case KEY_RSA: @@ -494,6 +511,8 @@ sshkey_new(int type) #endif /* WITH_OPENSSL */ case KEY_ED25519: case KEY_ED25519_CERT: + case KEY_XMSS: + case KEY_XMSS_CERT: /* no need to prealloc */ break; case KEY_UNSPEC: @@ -542,6 +561,8 @@ sshkey_add_private(struct sshkey *k) #endif /* WITH_OPENSSL */ case KEY_ED25519: case KEY_ED25519_CERT: + case KEY_XMSS: + case KEY_XMSS_CERT: /* no need to prealloc */ break; case KEY_UNSPEC: @@ -598,6 +619,20 @@ sshkey_free(struct sshkey *k) freezero(k->ed25519_sk, ED25519_SK_SZ); k->ed25519_sk = NULL; break; +#ifdef WITH_XMSS + case KEY_XMSS: + case KEY_XMSS_CERT: + freezero(k->xmss_pk, sshkey_xmss_pklen(k)); + k->xmss_pk = NULL; + freezero(k->xmss_sk, sshkey_xmss_sklen(k)); + k->xmss_sk = NULL; + sshkey_xmss_free_state(k); + free(k->xmss_name); + k->xmss_name = NULL; + free(k->xmss_filename); + k->xmss_filename = NULL; + break; +#endif /* WITH_XMSS */ case KEY_UNSPEC: break; default: @@ -677,6 +712,13 @@ sshkey_equal_public(const struct sshkey *a, const struct sshkey *b) case KEY_ED25519_CERT: return a->ed25519_pk != NULL && b->ed25519_pk != NULL && memcmp(a->ed25519_pk, b->ed25519_pk, ED25519_PK_SZ) == 0; +#ifdef WITH_XMSS + case KEY_XMSS: + case KEY_XMSS_CERT: + return a->xmss_pk != NULL && b->xmss_pk != NULL && + sshkey_xmss_pklen(a) == sshkey_xmss_pklen(b) && + memcmp(a->xmss_pk, b->xmss_pk, sshkey_xmss_pklen(a)) == 0; +#endif /* WITH_XMSS */ default: return 0; } @@ -696,7 +738,8 @@ sshkey_equal(const struct sshkey *a, const struct sshkey *b) } static int -to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain) +to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain, + enum sshkey_serialize_rep opts) { int type, ret = SSH_ERR_INTERNAL_ERROR; const char *typename; @@ -720,6 +763,9 @@ to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain) case KEY_RSA_CERT: #endif /* WITH_OPENSSL */ case KEY_ED25519_CERT: +#ifdef WITH_XMSS + case KEY_XMSS_CERT: +#endif /* WITH_XMSS */ /* Use the existing blob */ /* XXX modified flag? */ if ((ret = sshbuf_putb(b, key->cert->certblob)) != 0) @@ -764,6 +810,19 @@ to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain) key->ed25519_pk, ED25519_PK_SZ)) != 0) return ret; break; +#ifdef WITH_XMSS + case KEY_XMSS: + if (key->xmss_name == NULL || key->xmss_pk == NULL || + sshkey_xmss_pklen(key) == 0) + return SSH_ERR_INVALID_ARGUMENT; + if ((ret = sshbuf_put_cstring(b, typename)) != 0 || + (ret = sshbuf_put_cstring(b, key->xmss_name)) != 0 || + (ret = sshbuf_put_string(b, + key->xmss_pk, sshkey_xmss_pklen(key))) != 0 || + (ret = sshkey_xmss_serialize_pk_info(key, b, opts)) != 0) + return ret; + break; +#endif /* WITH_XMSS */ default: return SSH_ERR_KEY_TYPE_UNKNOWN; } @@ -773,32 +832,40 @@ to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain) int sshkey_putb(const struct sshkey *key, struct sshbuf *b) { - return to_blob_buf(key, b, 0); + return to_blob_buf(key, b, 0, SSHKEY_SERIALIZE_DEFAULT); } int -sshkey_puts(const struct sshkey *key, struct sshbuf *b) +sshkey_puts_opts(const struct sshkey *key, struct sshbuf *b, + enum sshkey_serialize_rep opts) { struct sshbuf *tmp; int r; if ((tmp = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; - r = to_blob_buf(key, tmp, 0); + r = to_blob_buf(key, tmp, 0, opts); if (r == 0) r = sshbuf_put_stringb(b, tmp); sshbuf_free(tmp); return r; } +int +sshkey_puts(const struct sshkey *key, struct sshbuf *b) +{ + return sshkey_puts_opts(key, b, SSHKEY_SERIALIZE_DEFAULT); +} + int sshkey_putb_plain(const struct sshkey *key, struct sshbuf *b) { - return to_blob_buf(key, b, 1); + return to_blob_buf(key, b, 1, SSHKEY_SERIALIZE_DEFAULT); } static int -to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain) +to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain, + enum sshkey_serialize_rep opts) { int ret = SSH_ERR_INTERNAL_ERROR; size_t len; @@ -810,7 +877,7 @@ to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain) *blobp = NULL; if ((b = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; - if ((ret = to_blob_buf(key, b, force_plain)) != 0) + if ((ret = to_blob_buf(key, b, force_plain, opts)) != 0) goto out; len = sshbuf_len(b); if (lenp != NULL) @@ -831,13 +898,13 @@ to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain) int sshkey_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp) { - return to_blob(key, blobp, lenp, 0); + return to_blob(key, blobp, lenp, 0, SSHKEY_SERIALIZE_DEFAULT); } int sshkey_plain_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp) { - return to_blob(key, blobp, lenp, 1); + return to_blob(key, blobp, lenp, 1, SSHKEY_SERIALIZE_DEFAULT); } int @@ -856,7 +923,8 @@ sshkey_fingerprint_raw(const struct sshkey *k, int dgst_alg, r = SSH_ERR_INVALID_ARGUMENT; goto out; } - if ((r = to_blob(k, &blob, &blob_len, 1)) != 0) + if ((r = to_blob(k, &blob, &blob_len, 1, SSHKEY_SERIALIZE_DEFAULT)) + != 0) goto out; if ((ret = calloc(1, SSH_DIGEST_MAX_LENGTH)) == NULL) { r = SSH_ERR_ALLOC_FAIL; @@ -1173,6 +1241,10 @@ sshkey_read(struct sshkey *ret, char **cpp) case KEY_ECDSA_CERT: case KEY_RSA_CERT: case KEY_ED25519_CERT: +#ifdef WITH_XMSS + case KEY_XMSS: + case KEY_XMSS_CERT: +#endif /* WITH_XMSS */ space = strchr(cp, ' '); if (space == NULL) return SSH_ERR_INVALID_FORMAT; @@ -1270,6 +1342,25 @@ sshkey_read(struct sshkey *ret, char **cpp) /* XXX */ #endif break; +#ifdef WITH_XMSS + case KEY_XMSS: + free(ret->xmss_pk); + ret->xmss_pk = k->xmss_pk; + k->xmss_pk = NULL; + free(ret->xmss_state); + ret->xmss_state = k->xmss_state; + k->xmss_state = NULL; + free(ret->xmss_name); + ret->xmss_name = k->xmss_name; + k->xmss_name = NULL; + free(ret->xmss_filename); + ret->xmss_filename = k->xmss_filename; + k->xmss_filename = NULL; +#ifdef DEBUG_PK + /* XXX */ +#endif + break; +#endif /* WITH_XMSS */ } *cpp = ep; retval = 0; @@ -1528,6 +1619,11 @@ sshkey_generate(int type, u_int bits, struct sshkey **keyp) crypto_sign_ed25519_keypair(k->ed25519_pk, k->ed25519_sk); ret = 0; break; +#ifdef WITH_XMSS + case KEY_XMSS: + ret = sshkey_xmss_generate_private_key(k, bits); + break; +#endif /* WITH_XMSS */ #ifdef WITH_OPENSSL case KEY_DSA: ret = dsa_generate_private_key(bits, &k->dsa); @@ -1671,6 +1767,29 @@ sshkey_from_private(const struct sshkey *k, struct sshkey **pkp) memcpy(n->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ); } break; +#ifdef WITH_XMSS + case KEY_XMSS: + case KEY_XMSS_CERT: + if ((n = sshkey_new(k->type)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((ret = sshkey_xmss_init(n, k->xmss_name)) != 0) { + sshkey_free(n); + return ret; + } + if (k->xmss_pk != NULL) { + size_t pklen = sshkey_xmss_pklen(k); + if (pklen == 0 || sshkey_xmss_pklen(n) != pklen) { + sshkey_free(n); + return SSH_ERR_INTERNAL_ERROR; + } + if ((n->xmss_pk = malloc(pklen)) == NULL) { + sshkey_free(n); + return SSH_ERR_ALLOC_FAIL; + } + memcpy(n->xmss_pk, k->xmss_pk, pklen); + } + break; +#endif /* WITH_XMSS */ default: return SSH_ERR_KEY_TYPE_UNKNOWN; } @@ -1812,7 +1931,7 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp, int allow_cert) { int type, ret = SSH_ERR_INTERNAL_ERROR; - char *ktype = NULL, *curve = NULL; + char *ktype = NULL, *curve = NULL, *xmss_name = NULL; struct sshkey *key = NULL; size_t len; u_char *pk = NULL; @@ -1963,6 +2082,36 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp, key->ed25519_pk = pk; pk = NULL; break; +#ifdef WITH_XMSS + case KEY_XMSS_CERT: + /* Skip nonce */ + if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* FALLTHROUGH */ + case KEY_XMSS: + if ((ret = sshbuf_get_cstring(b, &xmss_name, NULL)) != 0) + goto out; + if ((key = sshkey_new(type)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((ret = sshkey_xmss_init(key, xmss_name)) != 0) + goto out; + if ((ret = sshbuf_get_string(b, &pk, &len)) != 0) + goto out; + if (len == 0 || len != sshkey_xmss_pklen(key)) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + key->xmss_pk = pk; + pk = NULL; + if (type != KEY_XMSS_CERT && + (ret = sshkey_xmss_deserialize_pk_info(key, b)) != 0) + goto out; + break; +#endif /* WITH_XMSS */ case KEY_UNSPEC: default: ret = SSH_ERR_KEY_TYPE_UNKNOWN; @@ -1985,6 +2134,7 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp, out: sshbuf_free(copy); sshkey_free(key); + free(xmss_name); free(ktype); free(curve); free(pk); @@ -2079,6 +2229,11 @@ sshkey_sign(const struct sshkey *key, case KEY_ED25519: case KEY_ED25519_CERT: return ssh_ed25519_sign(key, sigp, lenp, data, datalen, compat); +#ifdef WITH_XMSS + case KEY_XMSS: + case KEY_XMSS_CERT: + return ssh_xmss_sign(key, sigp, lenp, data, datalen, compat); +#endif /* WITH_XMSS */ default: return SSH_ERR_KEY_TYPE_UNKNOWN; } @@ -2112,6 +2267,11 @@ sshkey_verify(const struct sshkey *key, case KEY_ED25519: case KEY_ED25519_CERT: return ssh_ed25519_verify(key, sig, siglen, data, dlen, compat); +#ifdef WITH_XMSS + case KEY_XMSS: + case KEY_XMSS_CERT: + return ssh_xmss_verify(key, sig, siglen, data, dlen, compat); +#endif /* WITH_XMSS */ default: return SSH_ERR_KEY_TYPE_UNKNOWN; } @@ -2135,6 +2295,8 @@ sshkey_demote(const struct sshkey *k, struct sshkey **dkp) pk->rsa = NULL; pk->ed25519_pk = NULL; pk->ed25519_sk = NULL; + pk->xmss_pk = NULL; + pk->xmss_sk = NULL; switch (k->type) { #ifdef WITH_OPENSSL @@ -2196,6 +2358,29 @@ sshkey_demote(const struct sshkey *k, struct sshkey **dkp) memcpy(pk->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ); } break; +#ifdef WITH_XMSS + case KEY_XMSS_CERT: + if ((ret = sshkey_cert_copy(k, pk)) != 0) + goto fail; + /* FALLTHROUGH */ + case KEY_XMSS: + if ((ret = sshkey_xmss_init(pk, k->xmss_name)) != 0) + goto fail; + if (k->xmss_pk != NULL) { + size_t pklen = sshkey_xmss_pklen(k); + + if (pklen == 0 || sshkey_xmss_pklen(pk) != pklen) { + ret = SSH_ERR_INTERNAL_ERROR; + goto fail; + } + if ((pk->xmss_pk = malloc(pklen)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto fail; + } + memcpy(pk->xmss_pk, k->xmss_pk, pklen); + } + break; +#endif /* WITH_XMSS */ default: ret = SSH_ERR_KEY_TYPE_UNKNOWN; fail: @@ -2227,6 +2412,11 @@ sshkey_to_certified(struct sshkey *k) case KEY_ED25519: newtype = KEY_ED25519_CERT; break; +#ifdef WITH_XMSS + case KEY_XMSS: + newtype = KEY_XMSS_CERT; + break; +#endif /* WITH_XMSS */ default: return SSH_ERR_INVALID_ARGUMENT; } @@ -2311,6 +2501,18 @@ sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg, k->ed25519_pk, ED25519_PK_SZ)) != 0) goto out; break; +#ifdef WITH_XMSS + case KEY_XMSS_CERT: + if (k->xmss_name == NULL) { + ret = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((ret = sshbuf_put_cstring(cert, k->xmss_name)) || + (ret = sshbuf_put_string(cert, + k->xmss_pk, sshkey_xmss_pklen(k))) != 0) + goto out; + break; +#endif /* WITH_XMSS */ default: ret = SSH_ERR_INVALID_ARGUMENT; goto out; @@ -2468,7 +2670,8 @@ sshkey_format_cert_validity(const struct sshkey_cert *cert, char *s, size_t l) } int -sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b) +sshkey_private_serialize_opt(const struct sshkey *key, struct sshbuf *b, + enum sshkey_serialize_rep opts) { int r = SSH_ERR_INTERNAL_ERROR; @@ -2554,6 +2757,36 @@ sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b) ED25519_SK_SZ)) != 0) goto out; break; +#ifdef WITH_XMSS + case KEY_XMSS: + if (key->xmss_name == NULL) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = sshbuf_put_cstring(b, key->xmss_name)) != 0 || + (r = sshbuf_put_string(b, key->xmss_pk, + sshkey_xmss_pklen(key))) != 0 || + (r = sshbuf_put_string(b, key->xmss_sk, + sshkey_xmss_sklen(key))) != 0 || + (r = sshkey_xmss_serialize_state_opt(key, b, opts)) != 0) + goto out; + break; + case KEY_XMSS_CERT: + if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0 || + key->xmss_name == NULL) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || + (r = sshbuf_put_cstring(b, key->xmss_name)) != 0 || + (r = sshbuf_put_string(b, key->xmss_pk, + sshkey_xmss_pklen(key))) != 0 || + (r = sshbuf_put_string(b, key->xmss_sk, + sshkey_xmss_sklen(key))) != 0 || + (r = sshkey_xmss_serialize_state_opt(key, b, opts)) != 0) + goto out; + break; +#endif /* WITH_XMSS */ default: r = SSH_ERR_INVALID_ARGUMENT; goto out; @@ -2564,14 +2797,22 @@ sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b) return r; } +int +sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b) +{ + return sshkey_private_serialize_opt(key, b, + SSHKEY_SERIALIZE_DEFAULT); +} + int sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) { - char *tname = NULL, *curve = NULL; + char *tname = NULL, *curve = NULL, *xmss_name = NULL; struct sshkey *k = NULL; size_t pklen = 0, sklen = 0; int type, r = SSH_ERR_INTERNAL_ERROR; u_char *ed25519_pk = NULL, *ed25519_sk = NULL; + u_char *xmss_pk = NULL, *xmss_sk = NULL; #ifdef WITH_OPENSSL BIGNUM *exponent = NULL; #endif /* WITH_OPENSSL */ @@ -2716,6 +2957,48 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) k->ed25519_sk = ed25519_sk; ed25519_pk = ed25519_sk = NULL; break; +#ifdef WITH_XMSS + case KEY_XMSS: + if ((k = sshkey_new_private(type)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_get_cstring(buf, &xmss_name, NULL)) != 0 || + (r = sshkey_xmss_init(k, xmss_name)) != 0 || + (r = sshbuf_get_string(buf, &xmss_pk, &pklen)) != 0 || + (r = sshbuf_get_string(buf, &xmss_sk, &sklen)) != 0) + goto out; + if (pklen != sshkey_xmss_pklen(k) || + sklen != sshkey_xmss_sklen(k)) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + k->xmss_pk = xmss_pk; + k->xmss_sk = xmss_sk; + xmss_pk = xmss_sk = NULL; + /* optional internal state */ + if ((r = sshkey_xmss_deserialize_state_opt(k, buf)) != 0) + goto out; + break; + case KEY_XMSS_CERT: + if ((r = sshkey_froms(buf, &k)) != 0 || + (r = sshkey_add_private(k)) != 0 || + (r = sshbuf_get_string(buf, &xmss_pk, &pklen)) != 0 || + (r = sshbuf_get_string(buf, &xmss_sk, &sklen)) != 0) + goto out; + if (pklen != sshkey_xmss_pklen(k) || + sklen != sshkey_xmss_sklen(k)) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + k->xmss_pk = xmss_pk; + k->xmss_sk = xmss_sk; + xmss_pk = xmss_sk = NULL; + /* optional internal state */ + if ((r = sshkey_xmss_deserialize_state_opt(k, buf)) != 0) + goto out; + break; +#endif /* WITH_XMSS */ default: r = SSH_ERR_KEY_TYPE_UNKNOWN; goto out; @@ -2747,6 +3030,9 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) sshkey_free(k); freezero(ed25519_pk, pklen); freezero(ed25519_sk, sklen); + free(xmss_name); + freezero(xmss_pk, pklen); + freezero(xmss_sk, sklen); return r; } @@ -3001,7 +3287,8 @@ sshkey_private_to_blob2(const struct sshkey *prv, struct sshbuf *blob, goto out; /* append private key and comment*/ - if ((r = sshkey_private_serialize(prv, encrypted)) != 0 || + if ((r = sshkey_private_serialize_opt(prv, encrypted, + SSHKEY_SERIALIZE_FULL)) != 0 || (r = sshbuf_put_cstring(encrypted, comment)) != 0) goto out; @@ -3362,6 +3649,9 @@ sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob, passphrase, comment); #endif /* WITH_OPENSSL */ case KEY_ED25519: +#ifdef WITH_XMSS + case KEY_XMSS: +#endif /* WITH_XMSS */ return sshkey_private_to_blob2(key, blob, passphrase, comment, new_format_cipher, new_format_rounds); default: @@ -3545,6 +3835,9 @@ sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type, passphrase, keyp); #endif /* WITH_OPENSSL */ case KEY_ED25519: +#ifdef WITH_XMSS + case KEY_XMSS: +#endif /* WITH_XMSS */ return sshkey_parse_private2(blob, type, passphrase, keyp, commentp); case KEY_UNSPEC: @@ -3576,3 +3869,90 @@ sshkey_parse_private_fileblob(struct sshbuf *buffer, const char *passphrase, return sshkey_parse_private_fileblob_type(buffer, KEY_UNSPEC, passphrase, keyp, commentp); } + +#ifdef WITH_XMSS +/* + * serialize the key with the current state and forward the state + * maxsign times. + */ +int +sshkey_private_serialize_maxsign(const struct sshkey *k, struct sshbuf *b, + u_int32_t maxsign, sshkey_printfn *pr) +{ + int r, rupdate; + + if (maxsign == 0 || + sshkey_type_plain(k->type) != KEY_XMSS) + return sshkey_private_serialize_opt(k, b, + SSHKEY_SERIALIZE_DEFAULT); + if ((r = sshkey_xmss_get_state(k, pr)) != 0 || + (r = sshkey_private_serialize_opt(k, b, + SSHKEY_SERIALIZE_STATE)) != 0 || + (r = sshkey_xmss_forward_state(k, maxsign)) != 0) + goto out; + r = 0; +out: + if ((rupdate = sshkey_xmss_update_state(k, pr)) != 0) { + if (r == 0) + r = rupdate; + } + return r; +} + +u_int32_t +sshkey_signatures_left(const struct sshkey *k) +{ + if (sshkey_type_plain(k->type) == KEY_XMSS) + return sshkey_xmss_signatures_left(k); + return 0; +} + +int +sshkey_enable_maxsign(struct sshkey *k, u_int32_t maxsign) +{ + if (sshkey_type_plain(k->type) != KEY_XMSS) + return SSH_ERR_INVALID_ARGUMENT; + return sshkey_xmss_enable_maxsign(k, maxsign); +} + +int +sshkey_set_filename(struct sshkey *k, const char *filename) +{ + if (k == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (sshkey_type_plain(k->type) != KEY_XMSS) + return 0; + if (filename == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((k->xmss_filename = strdup(filename)) == NULL) + return SSH_ERR_ALLOC_FAIL; + return 0; +} +#else +int +sshkey_private_serialize_maxsign(const struct sshkey *k, struct sshbuf *b, + u_int32_t maxsign, sshkey_printfn *pr) +{ + return sshkey_private_serialize_opt(k, b, SSHKEY_SERIALIZE_DEFAULT); +} + +u_int32_t +sshkey_signatures_left(const struct sshkey *k) +{ + return 0; +} + +int +sshkey_enable_maxsign(struct sshkey *k, u_int32_t maxsign) +{ + return SSH_ERR_INVALID_ARGUMENT; +} + +int +sshkey_set_filename(struct sshkey *k, const char *filename) +{ + if (k == NULL) + return SSH_ERR_INVALID_ARGUMENT; + return 0; +} +#endif /* WITH_XMSS */ diff --git a/sshkey.h b/sshkey.h index 7efa16ff9..c795815fa 100644 --- a/sshkey.h +++ b/sshkey.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sshkey.h,v 1.23 2017/12/18 02:25:15 djm Exp $ */ +/* $OpenBSD: sshkey.h,v 1.24 2018/02/23 15:58:38 markus Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -61,6 +61,8 @@ enum sshkey_types { KEY_DSA_CERT, KEY_ECDSA_CERT, KEY_ED25519_CERT, + KEY_XMSS, + KEY_XMSS_CERT, KEY_UNSPEC }; @@ -76,6 +78,14 @@ enum sshkey_fp_rep { SSH_FP_RANDOMART }; +/* Private key serialisation formats, used on the wire */ +enum sshkey_serialize_rep { + SSHKEY_SERIALIZE_DEFAULT = 0, + SSHKEY_SERIALIZE_STATE = 1, + SSHKEY_SERIALIZE_FULL = 2, + SSHKEY_SERIALIZE_INFO = 254, +}; + /* key is stored in external hardware */ #define SSHKEY_FLAG_EXT 0x0001 @@ -104,6 +114,11 @@ struct sshkey { EC_KEY *ecdsa; u_char *ed25519_sk; u_char *ed25519_pk; + char *xmss_name; + char *xmss_filename; /* for state file updates */ + void *xmss_state; /* depends on xmss_name, opaque */ + u_char *xmss_sk; + u_char *xmss_pk; struct sshkey_cert *cert; }; @@ -171,6 +186,8 @@ int sshkey_to_blob(const struct sshkey *, u_char **, size_t *); int sshkey_to_base64(const struct sshkey *, char **); int sshkey_putb(const struct sshkey *, struct sshbuf *); int sshkey_puts(const struct sshkey *, struct sshbuf *); +int sshkey_puts_opts(const struct sshkey *, struct sshbuf *, + enum sshkey_serialize_rep); int sshkey_plain_to_blob(const struct sshkey *, u_char **, size_t *); int sshkey_putb_plain(const struct sshkey *, struct sshbuf *); @@ -186,6 +203,8 @@ void sshkey_dump_ec_key(const EC_KEY *); /* private key parsing and serialisation */ int sshkey_private_serialize(const struct sshkey *key, struct sshbuf *buf); +int sshkey_private_serialize_opt(const struct sshkey *key, struct sshbuf *buf, + enum sshkey_serialize_rep); int sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **keyp); /* private key file format parsing and serialisation */ @@ -200,6 +219,15 @@ int sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type, /* XXX should be internal, but used by ssh-keygen */ int ssh_rsa_generate_additional_parameters(struct sshkey *); +/* stateful keys (e.g. XMSS) */ +typedef void sshkey_printfn(const char *, ...) __attribute__((format(printf, 1, 2))); +int sshkey_set_filename(struct sshkey *, const char *); +int sshkey_enable_maxsign(struct sshkey *, u_int32_t); +u_int32_t sshkey_signatures_left(const struct sshkey *); +int sshkey_forward_state(const struct sshkey *, u_int32_t, sshkey_printfn *); +int sshkey_private_serialize_maxsign(const struct sshkey *key, struct sshbuf *buf, + u_int32_t maxsign, sshkey_printfn *pr); + #ifdef SSHKEY_INTERNAL int ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, @@ -222,6 +250,11 @@ int ssh_ed25519_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, int ssh_ed25519_verify(const struct sshkey *key, const u_char *signature, size_t signaturelen, const u_char *data, size_t datalen, u_int compat); +int ssh_xmss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat); +int ssh_xmss_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat); #endif #if !defined(WITH_OPENSSL) diff --git a/xmss_commons.c b/xmss_commons.c new file mode 100644 index 000000000..51171af91 --- /dev/null +++ b/xmss_commons.c @@ -0,0 +1,27 @@ +/* +xmss_commons.c 20160722 +Andreas Hülsing +Joost Rijneveld +Public domain. +*/ + +#include "xmss_commons.h" +#include +#include +#include + +void to_byte(unsigned char *out, unsigned long long in, uint32_t bytes) +{ + int32_t i; + for (i = bytes-1; i >= 0; i--) { + out[i] = in & 0xff; + in = in >> 8; + } +} + +void hexdump(const unsigned char *a, size_t len) +{ + size_t i; + for (i = 0; i < len; i++) + printf("%02x", a[i]); +} \ No newline at end of file diff --git a/xmss_commons.h b/xmss_commons.h new file mode 100644 index 000000000..32fd4e2dc --- /dev/null +++ b/xmss_commons.h @@ -0,0 +1,15 @@ +/* +xmss_commons.h 20160722 +Andreas Hülsing +Joost Rijneveld +Public domain. +*/ +#ifndef XMSS_COMMONS_H +#define XMSS_COMMONS_H + +#include +#include + +void to_byte(unsigned char *output, unsigned long long in, uint32_t bytes); +void hexdump(const unsigned char *a, size_t len); +#endif \ No newline at end of file diff --git a/xmss_fast.c b/xmss_fast.c new file mode 100644 index 000000000..7ddc92f83 --- /dev/null +++ b/xmss_fast.c @@ -0,0 +1,1099 @@ +/* +xmss_fast.c version 20160722 +Andreas Hülsing +Joost Rijneveld +Public domain. +*/ + +#include "xmss_fast.h" +#include +#include +#include + +#include "crypto_api.h" +#include "xmss_wots.h" +#include "xmss_hash.h" + +#include "xmss_commons.h" +#include "xmss_hash_address.h" +// For testing +#include "stdio.h" + + + +/** + * Used for pseudorandom keygeneration, + * generates the seed for the WOTS keypair at address addr + * + * takes n byte sk_seed and returns n byte seed using 32 byte address addr. + */ +static void get_seed(unsigned char *seed, const unsigned char *sk_seed, int n, uint32_t addr[8]) +{ + unsigned char bytes[32]; + // Make sure that chain addr, hash addr, and key bit are 0! + setChainADRS(addr,0); + setHashADRS(addr,0); + setKeyAndMask(addr,0); + // Generate pseudorandom value + addr_to_byte(bytes, addr); + prf(seed, bytes, sk_seed, n); +} + +/** + * Initialize xmss params struct + * parameter names are the same as in the draft + * parameter k is K as used in the BDS algorithm + */ +int xmss_set_params(xmss_params *params, int n, int h, int w, int k) +{ + if (k >= h || k < 2 || (h - k) % 2) { + fprintf(stderr, "For BDS traversal, H - K must be even, with H > K >= 2!\n"); + return 1; + } + params->h = h; + params->n = n; + params->k = k; + wots_params wots_par; + wots_set_params(&wots_par, n, w); + params->wots_par = wots_par; + return 0; +} + +/** + * Initialize BDS state struct + * parameter names are the same as used in the description of the BDS traversal + */ +void xmss_set_bds_state(bds_state *state, unsigned char *stack, int stackoffset, unsigned char *stacklevels, unsigned char *auth, unsigned char *keep, treehash_inst *treehash, unsigned char *retain, int next_leaf) +{ + state->stack = stack; + state->stackoffset = stackoffset; + state->stacklevels = stacklevels; + state->auth = auth; + state->keep = keep; + state->treehash = treehash; + state->retain = retain; + state->next_leaf = next_leaf; +} + +/** + * Initialize xmssmt_params struct + * parameter names are the same as in the draft + * + * Especially h is the total tree height, i.e. the XMSS trees have height h/d + */ +int xmssmt_set_params(xmssmt_params *params, int n, int h, int d, int w, int k) +{ + if (h % d) { + fprintf(stderr, "d must divide h without remainder!\n"); + return 1; + } + params->h = h; + params->d = d; + params->n = n; + params->index_len = (h + 7) / 8; + xmss_params xmss_par; + if (xmss_set_params(&xmss_par, n, (h/d), w, k)) { + return 1; + } + params->xmss_par = xmss_par; + return 0; +} + +/** + * Computes a leaf from a WOTS public key using an L-tree. + */ +static void l_tree(unsigned char *leaf, unsigned char *wots_pk, const xmss_params *params, const unsigned char *pub_seed, uint32_t addr[8]) +{ + unsigned int l = params->wots_par.len; + unsigned int n = params->n; + uint32_t i = 0; + uint32_t height = 0; + uint32_t bound; + + //ADRS.setTreeHeight(0); + setTreeHeight(addr, height); + + while (l > 1) { + bound = l >> 1; //floor(l / 2); + for (i = 0; i < bound; i++) { + //ADRS.setTreeIndex(i); + setTreeIndex(addr, i); + //wots_pk[i] = RAND_HASH(pk[2i], pk[2i + 1], SEED, ADRS); + hash_h(wots_pk+i*n, wots_pk+i*2*n, pub_seed, addr, n); + } + //if ( l % 2 == 1 ) { + if (l & 1) { + //pk[floor(l / 2) + 1] = pk[l]; + memcpy(wots_pk+(l>>1)*n, wots_pk+(l-1)*n, n); + //l = ceil(l / 2); + l=(l>>1)+1; + } + else { + //l = ceil(l / 2); + l=(l>>1); + } + //ADRS.setTreeHeight(ADRS.getTreeHeight() + 1); + height++; + setTreeHeight(addr, height); + } + //return pk[0]; + memcpy(leaf, wots_pk, n); +} + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, then computes leaf using l_tree. As this happens position independent, we only require that addr encodes the right ltree-address. + */ +static void gen_leaf_wots(unsigned char *leaf, const unsigned char *sk_seed, const xmss_params *params, const unsigned char *pub_seed, uint32_t ltree_addr[8], uint32_t ots_addr[8]) +{ + unsigned char seed[params->n]; + unsigned char pk[params->wots_par.keysize]; + + get_seed(seed, sk_seed, params->n, ots_addr); + wots_pkgen(pk, seed, &(params->wots_par), pub_seed, ots_addr); + + l_tree(leaf, pk, params, pub_seed, ltree_addr); +} + +static int treehash_minheight_on_stack(bds_state* state, const xmss_params *params, const treehash_inst *treehash) { + unsigned int r = params->h, i; + for (i = 0; i < treehash->stackusage; i++) { + if (state->stacklevels[state->stackoffset - i - 1] < r) { + r = state->stacklevels[state->stackoffset - i - 1]; + } + } + return r; +} + +/** + * Merkle's TreeHash algorithm. The address only needs to initialize the first 78 bits of addr. Everything else will be set by treehash. + * Currently only used for key generation. + * + */ +static void treehash_setup(unsigned char *node, int height, int index, bds_state *state, const unsigned char *sk_seed, const xmss_params *params, const unsigned char *pub_seed, const uint32_t addr[8]) +{ + unsigned int idx = index; + unsigned int n = params->n; + unsigned int h = params->h; + unsigned int k = params->k; + // use three different addresses because at this point we use all three formats in parallel + uint32_t ots_addr[8]; + uint32_t ltree_addr[8]; + uint32_t node_addr[8]; + // only copy layer and tree address parts + memcpy(ots_addr, addr, 12); + // type = ots + setType(ots_addr, 0); + memcpy(ltree_addr, addr, 12); + setType(ltree_addr, 1); + memcpy(node_addr, addr, 12); + setType(node_addr, 2); + + uint32_t lastnode, i; + unsigned char stack[(height+1)*n]; + unsigned int stacklevels[height+1]; + unsigned int stackoffset=0; + unsigned int nodeh; + + lastnode = idx+(1<treehash[i].h = i; + state->treehash[i].completed = 1; + state->treehash[i].stackusage = 0; + } + + i = 0; + for (; idx < lastnode; idx++) { + setLtreeADRS(ltree_addr, idx); + setOTSADRS(ots_addr, idx); + gen_leaf_wots(stack+stackoffset*n, sk_seed, params, pub_seed, ltree_addr, ots_addr); + stacklevels[stackoffset] = 0; + stackoffset++; + if (h - k > 0 && i == 3) { + memcpy(state->treehash[0].node, stack+stackoffset*n, n); + } + while (stackoffset>1 && stacklevels[stackoffset-1] == stacklevels[stackoffset-2]) + { + nodeh = stacklevels[stackoffset-1]; + if (i >> nodeh == 1) { + memcpy(state->auth + nodeh*n, stack+(stackoffset-1)*n, n); + } + else { + if (nodeh < h - k && i >> nodeh == 3) { + memcpy(state->treehash[nodeh].node, stack+(stackoffset-1)*n, n); + } + else if (nodeh >= h - k) { + memcpy(state->retain + ((1 << (h - 1 - nodeh)) + nodeh - h + (((i >> nodeh) - 3) >> 1)) * n, stack+(stackoffset-1)*n, n); + } + } + setTreeHeight(node_addr, stacklevels[stackoffset-1]); + setTreeIndex(node_addr, (idx >> (stacklevels[stackoffset-1]+1))); + hash_h(stack+(stackoffset-2)*n, stack+(stackoffset-2)*n, pub_seed, + node_addr, n); + stacklevels[stackoffset-2]++; + stackoffset--; + } + i++; + } + + for (i = 0; i < n; i++) + node[i] = stack[i]; +} + +static void treehash_update(treehash_inst *treehash, bds_state *state, const unsigned char *sk_seed, const xmss_params *params, const unsigned char *pub_seed, const uint32_t addr[8]) { + int n = params->n; + + uint32_t ots_addr[8]; + uint32_t ltree_addr[8]; + uint32_t node_addr[8]; + // only copy layer and tree address parts + memcpy(ots_addr, addr, 12); + // type = ots + setType(ots_addr, 0); + memcpy(ltree_addr, addr, 12); + setType(ltree_addr, 1); + memcpy(node_addr, addr, 12); + setType(node_addr, 2); + + setLtreeADRS(ltree_addr, treehash->next_idx); + setOTSADRS(ots_addr, treehash->next_idx); + + unsigned char nodebuffer[2 * n]; + unsigned int nodeheight = 0; + gen_leaf_wots(nodebuffer, sk_seed, params, pub_seed, ltree_addr, ots_addr); + while (treehash->stackusage > 0 && state->stacklevels[state->stackoffset-1] == nodeheight) { + memcpy(nodebuffer + n, nodebuffer, n); + memcpy(nodebuffer, state->stack + (state->stackoffset-1)*n, n); + setTreeHeight(node_addr, nodeheight); + setTreeIndex(node_addr, (treehash->next_idx >> (nodeheight+1))); + hash_h(nodebuffer, nodebuffer, pub_seed, node_addr, n); + nodeheight++; + treehash->stackusage--; + state->stackoffset--; + } + if (nodeheight == treehash->h) { // this also implies stackusage == 0 + memcpy(treehash->node, nodebuffer, n); + treehash->completed = 1; + } + else { + memcpy(state->stack + state->stackoffset*n, nodebuffer, n); + treehash->stackusage++; + state->stacklevels[state->stackoffset] = nodeheight; + state->stackoffset++; + treehash->next_idx++; + } +} + +/** + * Computes a root node given a leaf and an authapth + */ +static void validate_authpath(unsigned char *root, const unsigned char *leaf, unsigned long leafidx, const unsigned char *authpath, const xmss_params *params, const unsigned char *pub_seed, uint32_t addr[8]) +{ + unsigned int n = params->n; + + uint32_t i, j; + unsigned char buffer[2*n]; + + // If leafidx is odd (last bit = 1), current path element is a right child and authpath has to go to the left. + // Otherwise, it is the other way around + if (leafidx & 1) { + for (j = 0; j < n; j++) + buffer[n+j] = leaf[j]; + for (j = 0; j < n; j++) + buffer[j] = authpath[j]; + } + else { + for (j = 0; j < n; j++) + buffer[j] = leaf[j]; + for (j = 0; j < n; j++) + buffer[n+j] = authpath[j]; + } + authpath += n; + + for (i=0; i < params->h-1; i++) { + setTreeHeight(addr, i); + leafidx >>= 1; + setTreeIndex(addr, leafidx); + if (leafidx&1) { + hash_h(buffer+n, buffer, pub_seed, addr, n); + for (j = 0; j < n; j++) + buffer[j] = authpath[j]; + } + else { + hash_h(buffer, buffer, pub_seed, addr, n); + for (j = 0; j < n; j++) + buffer[j+n] = authpath[j]; + } + authpath += n; + } + setTreeHeight(addr, (params->h-1)); + leafidx >>= 1; + setTreeIndex(addr, leafidx); + hash_h(root, buffer, pub_seed, addr, n); +} + +/** + * Performs one treehash update on the instance that needs it the most. + * Returns 1 if such an instance was not found + **/ +static char bds_treehash_update(bds_state *state, unsigned int updates, const unsigned char *sk_seed, const xmss_params *params, unsigned char *pub_seed, const uint32_t addr[8]) { + uint32_t i, j; + unsigned int level, l_min, low; + unsigned int h = params->h; + unsigned int k = params->k; + unsigned int used = 0; + + for (j = 0; j < updates; j++) { + l_min = h; + level = h - k; + for (i = 0; i < h - k; i++) { + if (state->treehash[i].completed) { + low = h; + } + else if (state->treehash[i].stackusage == 0) { + low = i; + } + else { + low = treehash_minheight_on_stack(state, params, &(state->treehash[i])); + } + if (low < l_min) { + level = i; + l_min = low; + } + } + if (level == h - k) { + break; + } + treehash_update(&(state->treehash[level]), state, sk_seed, params, pub_seed, addr); + used++; + } + return updates - used; +} + +/** + * Updates the state (typically NEXT_i) by adding a leaf and updating the stack + * Returns 1 if all leaf nodes have already been processed + **/ +static char bds_state_update(bds_state *state, const unsigned char *sk_seed, const xmss_params *params, unsigned char *pub_seed, const uint32_t addr[8]) { + uint32_t ltree_addr[8]; + uint32_t node_addr[8]; + uint32_t ots_addr[8]; + + int n = params->n; + int h = params->h; + int k = params->k; + + int nodeh; + int idx = state->next_leaf; + if (idx == 1 << h) { + return 1; + } + + // only copy layer and tree address parts + memcpy(ots_addr, addr, 12); + // type = ots + setType(ots_addr, 0); + memcpy(ltree_addr, addr, 12); + setType(ltree_addr, 1); + memcpy(node_addr, addr, 12); + setType(node_addr, 2); + + setOTSADRS(ots_addr, idx); + setLtreeADRS(ltree_addr, idx); + + gen_leaf_wots(state->stack+state->stackoffset*n, sk_seed, params, pub_seed, ltree_addr, ots_addr); + + state->stacklevels[state->stackoffset] = 0; + state->stackoffset++; + if (h - k > 0 && idx == 3) { + memcpy(state->treehash[0].node, state->stack+state->stackoffset*n, n); + } + while (state->stackoffset>1 && state->stacklevels[state->stackoffset-1] == state->stacklevels[state->stackoffset-2]) { + nodeh = state->stacklevels[state->stackoffset-1]; + if (idx >> nodeh == 1) { + memcpy(state->auth + nodeh*n, state->stack+(state->stackoffset-1)*n, n); + } + else { + if (nodeh < h - k && idx >> nodeh == 3) { + memcpy(state->treehash[nodeh].node, state->stack+(state->stackoffset-1)*n, n); + } + else if (nodeh >= h - k) { + memcpy(state->retain + ((1 << (h - 1 - nodeh)) + nodeh - h + (((idx >> nodeh) - 3) >> 1)) * n, state->stack+(state->stackoffset-1)*n, n); + } + } + setTreeHeight(node_addr, state->stacklevels[state->stackoffset-1]); + setTreeIndex(node_addr, (idx >> (state->stacklevels[state->stackoffset-1]+1))); + hash_h(state->stack+(state->stackoffset-2)*n, state->stack+(state->stackoffset-2)*n, pub_seed, node_addr, n); + + state->stacklevels[state->stackoffset-2]++; + state->stackoffset--; + } + state->next_leaf++; + return 0; +} + +/** + * Returns the auth path for node leaf_idx and computes the auth path for the + * next leaf node, using the algorithm described by Buchmann, Dahmen and Szydlo + * in "Post Quantum Cryptography", Springer 2009. + */ +static void bds_round(bds_state *state, const unsigned long leaf_idx, const unsigned char *sk_seed, const xmss_params *params, unsigned char *pub_seed, uint32_t addr[8]) +{ + unsigned int i; + unsigned int n = params->n; + unsigned int h = params->h; + unsigned int k = params->k; + + unsigned int tau = h; + unsigned int startidx; + unsigned int offset, rowidx; + unsigned char buf[2 * n]; + + uint32_t ots_addr[8]; + uint32_t ltree_addr[8]; + uint32_t node_addr[8]; + // only copy layer and tree address parts + memcpy(ots_addr, addr, 12); + // type = ots + setType(ots_addr, 0); + memcpy(ltree_addr, addr, 12); + setType(ltree_addr, 1); + memcpy(node_addr, addr, 12); + setType(node_addr, 2); + + for (i = 0; i < h; i++) { + if (! ((leaf_idx >> i) & 1)) { + tau = i; + break; + } + } + + if (tau > 0) { + memcpy(buf, state->auth + (tau-1) * n, n); + // we need to do this before refreshing state->keep to prevent overwriting + memcpy(buf + n, state->keep + ((tau-1) >> 1) * n, n); + } + if (!((leaf_idx >> (tau + 1)) & 1) && (tau < h - 1)) { + memcpy(state->keep + (tau >> 1)*n, state->auth + tau*n, n); + } + if (tau == 0) { + setLtreeADRS(ltree_addr, leaf_idx); + setOTSADRS(ots_addr, leaf_idx); + gen_leaf_wots(state->auth, sk_seed, params, pub_seed, ltree_addr, ots_addr); + } + else { + setTreeHeight(node_addr, (tau-1)); + setTreeIndex(node_addr, leaf_idx >> tau); + hash_h(state->auth + tau * n, buf, pub_seed, node_addr, n); + for (i = 0; i < tau; i++) { + if (i < h - k) { + memcpy(state->auth + i * n, state->treehash[i].node, n); + } + else { + offset = (1 << (h - 1 - i)) + i - h; + rowidx = ((leaf_idx >> i) - 1) >> 1; + memcpy(state->auth + i * n, state->retain + (offset + rowidx) * n, n); + } + } + + for (i = 0; i < ((tau < h - k) ? tau : (h - k)); i++) { + startidx = leaf_idx + 1 + 3 * (1 << i); + if (startidx < 1U << h) { + state->treehash[i].h = i; + state->treehash[i].next_idx = startidx; + state->treehash[i].completed = 0; + state->treehash[i].stackusage = 0; + } + } + } +} + +/* + * Generates a XMSS key pair for a given parameter set. + * Format sk: [(32bit) idx || SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] omitting algo oid. + */ +int xmss_keypair(unsigned char *pk, unsigned char *sk, bds_state *state, xmss_params *params) +{ + unsigned int n = params->n; + // Set idx = 0 + sk[0] = 0; + sk[1] = 0; + sk[2] = 0; + sk[3] = 0; + // Init SK_SEED (n byte), SK_PRF (n byte), and PUB_SEED (n byte) + randombytes(sk+4, 3*n); + // Copy PUB_SEED to public key + memcpy(pk+n, sk+4+2*n, n); + + uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + // Compute root + treehash_setup(pk, params->h, 0, state, sk+4, params, sk+4+2*n, addr); + // copy root to sk + memcpy(sk+4+3*n, pk, n); + return 0; +} + +/** + * Signs a message. + * Returns + * 1. an array containing the signature followed by the message AND + * 2. an updated secret key! + * + */ +int xmss_sign(unsigned char *sk, bds_state *state, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg, unsigned long long msglen, const xmss_params *params) +{ + unsigned int h = params->h; + unsigned int n = params->n; + unsigned int k = params->k; + uint16_t i = 0; + + // Extract SK + unsigned long idx = ((unsigned long)sk[0] << 24) | ((unsigned long)sk[1] << 16) | ((unsigned long)sk[2] << 8) | sk[3]; + unsigned char sk_seed[n]; + memcpy(sk_seed, sk+4, n); + unsigned char sk_prf[n]; + memcpy(sk_prf, sk+4+n, n); + unsigned char pub_seed[n]; + memcpy(pub_seed, sk+4+2*n, n); + + // index as 32 bytes string + unsigned char idx_bytes_32[32]; + to_byte(idx_bytes_32, idx, 32); + + unsigned char hash_key[3*n]; + + // Update SK + sk[0] = ((idx + 1) >> 24) & 255; + sk[1] = ((idx + 1) >> 16) & 255; + sk[2] = ((idx + 1) >> 8) & 255; + sk[3] = (idx + 1) & 255; + // -- Secret key for this non-forward-secure version is now updated. + // -- A productive implementation should use a file handle instead and write the updated secret key at this point! + + // Init working params + unsigned char R[n]; + unsigned char msg_h[n]; + unsigned char ots_seed[n]; + uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + // --------------------------------- + // Message Hashing + // --------------------------------- + + // Message Hash: + // First compute pseudorandom value + prf(R, idx_bytes_32, sk_prf, n); + // Generate hash key (R || root || idx) + memcpy(hash_key, R, n); + memcpy(hash_key+n, sk+4+3*n, n); + to_byte(hash_key+2*n, idx, n); + // Then use it for message digest + h_msg(msg_h, msg, msglen, hash_key, 3*n, n); + + // Start collecting signature + *sig_msg_len = 0; + + // Copy index to signature + sig_msg[0] = (idx >> 24) & 255; + sig_msg[1] = (idx >> 16) & 255; + sig_msg[2] = (idx >> 8) & 255; + sig_msg[3] = idx & 255; + + sig_msg += 4; + *sig_msg_len += 4; + + // Copy R to signature + for (i = 0; i < n; i++) + sig_msg[i] = R[i]; + + sig_msg += n; + *sig_msg_len += n; + + // ---------------------------------- + // Now we start to "really sign" + // ---------------------------------- + + // Prepare Address + setType(ots_addr, 0); + setOTSADRS(ots_addr, idx); + + // Compute seed for OTS key pair + get_seed(ots_seed, sk_seed, n, ots_addr); + + // Compute WOTS signature + wots_sign(sig_msg, msg_h, ots_seed, &(params->wots_par), pub_seed, ots_addr); + + sig_msg += params->wots_par.keysize; + *sig_msg_len += params->wots_par.keysize; + + // the auth path was already computed during the previous round + memcpy(sig_msg, state->auth, h*n); + + if (idx < (1U << h) - 1) { + bds_round(state, idx, sk_seed, params, pub_seed, ots_addr); + bds_treehash_update(state, (h - k) >> 1, sk_seed, params, pub_seed, ots_addr); + } + +/* TODO: save key/bds state here! */ + + sig_msg += params->h*n; + *sig_msg_len += params->h*n; + + //Whipe secret elements? + //zerobytes(tsk, CRYPTO_SECRETKEYBYTES); + + + memcpy(sig_msg, msg, msglen); + *sig_msg_len += msglen; + + return 0; +} + +/** + * Verifies a given message signature pair under a given public key. + */ +int xmss_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk, const xmss_params *params) +{ + unsigned int n = params->n; + + unsigned long long i, m_len; + unsigned long idx=0; + unsigned char wots_pk[params->wots_par.keysize]; + unsigned char pkhash[n]; + unsigned char root[n]; + unsigned char msg_h[n]; + unsigned char hash_key[3*n]; + + unsigned char pub_seed[n]; + memcpy(pub_seed, pk+n, n); + + // Init addresses + uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t ltree_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t node_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + setType(ots_addr, 0); + setType(ltree_addr, 1); + setType(node_addr, 2); + + // Extract index + idx = ((unsigned long)sig_msg[0] << 24) | ((unsigned long)sig_msg[1] << 16) | ((unsigned long)sig_msg[2] << 8) | sig_msg[3]; + printf("verify:: idx = %lu\n", idx); + + // Generate hash key (R || root || idx) + memcpy(hash_key, sig_msg+4,n); + memcpy(hash_key+n, pk, n); + to_byte(hash_key+2*n, idx, n); + + sig_msg += (n+4); + sig_msg_len -= (n+4); + + // hash message + unsigned long long tmp_sig_len = params->wots_par.keysize+params->h*n; + m_len = sig_msg_len - tmp_sig_len; + h_msg(msg_h, sig_msg + tmp_sig_len, m_len, hash_key, 3*n, n); + + //----------------------- + // Verify signature + //----------------------- + + // Prepare Address + setOTSADRS(ots_addr, idx); + // Check WOTS signature + wots_pkFromSig(wots_pk, sig_msg, msg_h, &(params->wots_par), pub_seed, ots_addr); + + sig_msg += params->wots_par.keysize; + sig_msg_len -= params->wots_par.keysize; + + // Compute Ltree + setLtreeADRS(ltree_addr, idx); + l_tree(pkhash, wots_pk, params, pub_seed, ltree_addr); + + // Compute root + validate_authpath(root, pkhash, idx, sig_msg, params, pub_seed, node_addr); + + sig_msg += params->h*n; + sig_msg_len -= params->h*n; + + for (i = 0; i < n; i++) + if (root[i] != pk[i]) + goto fail; + + *msglen = sig_msg_len; + for (i = 0; i < *msglen; i++) + msg[i] = sig_msg[i]; + + return 0; + + +fail: + *msglen = sig_msg_len; + for (i = 0; i < *msglen; i++) + msg[i] = 0; + *msglen = -1; + return -1; +} + +/* + * Generates a XMSSMT key pair for a given parameter set. + * Format sk: [(ceil(h/8) bit) idx || SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] omitting algo oid. + */ +int xmssmt_keypair(unsigned char *pk, unsigned char *sk, bds_state *states, unsigned char *wots_sigs, xmssmt_params *params) +{ + unsigned int n = params->n; + unsigned int i; + unsigned char ots_seed[params->n]; + // Set idx = 0 + for (i = 0; i < params->index_len; i++) { + sk[i] = 0; + } + // Init SK_SEED (n byte), SK_PRF (n byte), and PUB_SEED (n byte) + randombytes(sk+params->index_len, 3*n); + // Copy PUB_SEED to public key + memcpy(pk+n, sk+params->index_len+2*n, n); + + // Set address to point on the single tree on layer d-1 + uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + setLayerADRS(addr, (params->d-1)); + // Set up state and compute wots signatures for all but topmost tree root + for (i = 0; i < params->d - 1; i++) { + // Compute seed for OTS key pair + treehash_setup(pk, params->xmss_par.h, 0, states + i, sk+params->index_len, &(params->xmss_par), pk+n, addr); + setLayerADRS(addr, (i+1)); + get_seed(ots_seed, sk+params->index_len, n, addr); + wots_sign(wots_sigs + i*params->xmss_par.wots_par.keysize, pk, ots_seed, &(params->xmss_par.wots_par), pk+n, addr); + } + treehash_setup(pk, params->xmss_par.h, 0, states + i, sk+params->index_len, &(params->xmss_par), pk+n, addr); + memcpy(sk+params->index_len+3*n, pk, n); + return 0; +} + +/** + * Signs a message. + * Returns + * 1. an array containing the signature followed by the message AND + * 2. an updated secret key! + * + */ +int xmssmt_sign(unsigned char *sk, bds_state *states, unsigned char *wots_sigs, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg, unsigned long long msglen, const xmssmt_params *params) +{ + unsigned int n = params->n; + + unsigned int tree_h = params->xmss_par.h; + unsigned int h = params->h; + unsigned int k = params->xmss_par.k; + unsigned int idx_len = params->index_len; + uint64_t idx_tree; + uint32_t idx_leaf; + uint64_t i, j; + int needswap_upto = -1; + unsigned int updates; + + unsigned char sk_seed[n]; + unsigned char sk_prf[n]; + unsigned char pub_seed[n]; + // Init working params + unsigned char R[n]; + unsigned char msg_h[n]; + unsigned char hash_key[3*n]; + unsigned char ots_seed[n]; + uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + unsigned char idx_bytes_32[32]; + bds_state tmp; + + // Extract SK + unsigned long long idx = 0; + for (i = 0; i < idx_len; i++) { + idx |= ((unsigned long long)sk[i]) << 8*(idx_len - 1 - i); + } + + memcpy(sk_seed, sk+idx_len, n); + memcpy(sk_prf, sk+idx_len+n, n); + memcpy(pub_seed, sk+idx_len+2*n, n); + + // Update SK + for (i = 0; i < idx_len; i++) { + sk[i] = ((idx + 1) >> 8*(idx_len - 1 - i)) & 255; + } + // -- Secret key for this non-forward-secure version is now updated. + // -- A productive implementation should use a file handle instead and write the updated secret key at this point! + + + // --------------------------------- + // Message Hashing + // --------------------------------- + + // Message Hash: + // First compute pseudorandom value + to_byte(idx_bytes_32, idx, 32); + prf(R, idx_bytes_32, sk_prf, n); + // Generate hash key (R || root || idx) + memcpy(hash_key, R, n); + memcpy(hash_key+n, sk+idx_len+3*n, n); + to_byte(hash_key+2*n, idx, n); + + // Then use it for message digest + h_msg(msg_h, msg, msglen, hash_key, 3*n, n); + + // Start collecting signature + *sig_msg_len = 0; + + // Copy index to signature + for (i = 0; i < idx_len; i++) { + sig_msg[i] = (idx >> 8*(idx_len - 1 - i)) & 255; + } + + sig_msg += idx_len; + *sig_msg_len += idx_len; + + // Copy R to signature + for (i = 0; i < n; i++) + sig_msg[i] = R[i]; + + sig_msg += n; + *sig_msg_len += n; + + // ---------------------------------- + // Now we start to "really sign" + // ---------------------------------- + + // Handle lowest layer separately as it is slightly different... + + // Prepare Address + setType(ots_addr, 0); + idx_tree = idx >> tree_h; + idx_leaf = (idx & ((1 << tree_h)-1)); + setLayerADRS(ots_addr, 0); + setTreeADRS(ots_addr, idx_tree); + setOTSADRS(ots_addr, idx_leaf); + + // Compute seed for OTS key pair + get_seed(ots_seed, sk_seed, n, ots_addr); + + // Compute WOTS signature + wots_sign(sig_msg, msg_h, ots_seed, &(params->xmss_par.wots_par), pub_seed, ots_addr); + + sig_msg += params->xmss_par.wots_par.keysize; + *sig_msg_len += params->xmss_par.wots_par.keysize; + + memcpy(sig_msg, states[0].auth, tree_h*n); + sig_msg += tree_h*n; + *sig_msg_len += tree_h*n; + + // prepare signature of remaining layers + for (i = 1; i < params->d; i++) { + // put WOTS signature in place + memcpy(sig_msg, wots_sigs + (i-1)*params->xmss_par.wots_par.keysize, params->xmss_par.wots_par.keysize); + + sig_msg += params->xmss_par.wots_par.keysize; + *sig_msg_len += params->xmss_par.wots_par.keysize; + + // put AUTH nodes in place + memcpy(sig_msg, states[i].auth, tree_h*n); + sig_msg += tree_h*n; + *sig_msg_len += tree_h*n; + } + + updates = (tree_h - k) >> 1; + + setTreeADRS(addr, (idx_tree + 1)); + // mandatory update for NEXT_0 (does not count towards h-k/2) if NEXT_0 exists + if ((1 + idx_tree) * (1 << tree_h) + idx_leaf < (1ULL << h)) { + bds_state_update(&states[params->d], sk_seed, &(params->xmss_par), pub_seed, addr); + } + + for (i = 0; i < params->d; i++) { + // check if we're not at the end of a tree + if (! (((idx + 1) & ((1ULL << ((i+1)*tree_h)) - 1)) == 0)) { + idx_leaf = (idx >> (tree_h * i)) & ((1 << tree_h)-1); + idx_tree = (idx >> (tree_h * (i+1))); + setLayerADRS(addr, i); + setTreeADRS(addr, idx_tree); + if (i == (unsigned int) (needswap_upto + 1)) { + bds_round(&states[i], idx_leaf, sk_seed, &(params->xmss_par), pub_seed, addr); + } + updates = bds_treehash_update(&states[i], updates, sk_seed, &(params->xmss_par), pub_seed, addr); + setTreeADRS(addr, (idx_tree + 1)); + // if a NEXT-tree exists for this level; + if ((1 + idx_tree) * (1 << tree_h) + idx_leaf < (1ULL << (h - tree_h * i))) { + if (i > 0 && updates > 0 && states[params->d + i].next_leaf < (1ULL << h)) { + bds_state_update(&states[params->d + i], sk_seed, &(params->xmss_par), pub_seed, addr); + updates--; + } + } + } + else if (idx < (1ULL << h) - 1) { + memcpy(&tmp, states+params->d + i, sizeof(bds_state)); + memcpy(states+params->d + i, states + i, sizeof(bds_state)); + memcpy(states + i, &tmp, sizeof(bds_state)); + + setLayerADRS(ots_addr, (i+1)); + setTreeADRS(ots_addr, ((idx + 1) >> ((i+2) * tree_h))); + setOTSADRS(ots_addr, (((idx >> ((i+1) * tree_h)) + 1) & ((1 << tree_h)-1))); + + get_seed(ots_seed, sk+params->index_len, n, ots_addr); + wots_sign(wots_sigs + i*params->xmss_par.wots_par.keysize, states[i].stack, ots_seed, &(params->xmss_par.wots_par), pub_seed, ots_addr); + + states[params->d + i].stackoffset = 0; + states[params->d + i].next_leaf = 0; + + updates--; // WOTS-signing counts as one update + needswap_upto = i; + for (j = 0; j < tree_h-k; j++) { + states[i].treehash[j].completed = 1; + } + } + } + + //Whipe secret elements? + //zerobytes(tsk, CRYPTO_SECRETKEYBYTES); + + memcpy(sig_msg, msg, msglen); + *sig_msg_len += msglen; + + return 0; +} + +/** + * Verifies a given message signature pair under a given public key. + */ +int xmssmt_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk, const xmssmt_params *params) +{ + unsigned int n = params->n; + + unsigned int tree_h = params->xmss_par.h; + unsigned int idx_len = params->index_len; + uint64_t idx_tree; + uint32_t idx_leaf; + + unsigned long long i, m_len; + unsigned long long idx=0; + unsigned char wots_pk[params->xmss_par.wots_par.keysize]; + unsigned char pkhash[n]; + unsigned char root[n]; + unsigned char msg_h[n]; + unsigned char hash_key[3*n]; + + unsigned char pub_seed[n]; + memcpy(pub_seed, pk+n, n); + + // Init addresses + uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t ltree_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t node_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + // Extract index + for (i = 0; i < idx_len; i++) { + idx |= ((unsigned long long)sig_msg[i]) << (8*(idx_len - 1 - i)); + } + printf("verify:: idx = %llu\n", idx); + sig_msg += idx_len; + sig_msg_len -= idx_len; + + // Generate hash key (R || root || idx) + memcpy(hash_key, sig_msg,n); + memcpy(hash_key+n, pk, n); + to_byte(hash_key+2*n, idx, n); + + sig_msg += n; + sig_msg_len -= n; + + + // hash message (recall, R is now on pole position at sig_msg + unsigned long long tmp_sig_len = (params->d * params->xmss_par.wots_par.keysize) + (params->h * n); + m_len = sig_msg_len - tmp_sig_len; + h_msg(msg_h, sig_msg + tmp_sig_len, m_len, hash_key, 3*n, n); + + + //----------------------- + // Verify signature + //----------------------- + + // Prepare Address + idx_tree = idx >> tree_h; + idx_leaf = (idx & ((1 << tree_h)-1)); + setLayerADRS(ots_addr, 0); + setTreeADRS(ots_addr, idx_tree); + setType(ots_addr, 0); + + memcpy(ltree_addr, ots_addr, 12); + setType(ltree_addr, 1); + + memcpy(node_addr, ltree_addr, 12); + setType(node_addr, 2); + + setOTSADRS(ots_addr, idx_leaf); + + // Check WOTS signature + wots_pkFromSig(wots_pk, sig_msg, msg_h, &(params->xmss_par.wots_par), pub_seed, ots_addr); + + sig_msg += params->xmss_par.wots_par.keysize; + sig_msg_len -= params->xmss_par.wots_par.keysize; + + // Compute Ltree + setLtreeADRS(ltree_addr, idx_leaf); + l_tree(pkhash, wots_pk, &(params->xmss_par), pub_seed, ltree_addr); + + // Compute root + validate_authpath(root, pkhash, idx_leaf, sig_msg, &(params->xmss_par), pub_seed, node_addr); + + sig_msg += tree_h*n; + sig_msg_len -= tree_h*n; + + for (i = 1; i < params->d; i++) { + // Prepare Address + idx_leaf = (idx_tree & ((1 << tree_h)-1)); + idx_tree = idx_tree >> tree_h; + + setLayerADRS(ots_addr, i); + setTreeADRS(ots_addr, idx_tree); + setType(ots_addr, 0); + + memcpy(ltree_addr, ots_addr, 12); + setType(ltree_addr, 1); + + memcpy(node_addr, ltree_addr, 12); + setType(node_addr, 2); + + setOTSADRS(ots_addr, idx_leaf); + + // Check WOTS signature + wots_pkFromSig(wots_pk, sig_msg, root, &(params->xmss_par.wots_par), pub_seed, ots_addr); + + sig_msg += params->xmss_par.wots_par.keysize; + sig_msg_len -= params->xmss_par.wots_par.keysize; + + // Compute Ltree + setLtreeADRS(ltree_addr, idx_leaf); + l_tree(pkhash, wots_pk, &(params->xmss_par), pub_seed, ltree_addr); + + // Compute root + validate_authpath(root, pkhash, idx_leaf, sig_msg, &(params->xmss_par), pub_seed, node_addr); + + sig_msg += tree_h*n; + sig_msg_len -= tree_h*n; + + } + + for (i = 0; i < n; i++) + if (root[i] != pk[i]) + goto fail; + + *msglen = sig_msg_len; + for (i = 0; i < *msglen; i++) + msg[i] = sig_msg[i]; + + return 0; + + +fail: + *msglen = sig_msg_len; + for (i = 0; i < *msglen; i++) + msg[i] = 0; + *msglen = -1; + return -1; +} diff --git a/xmss_fast.h b/xmss_fast.h new file mode 100644 index 000000000..657cd27f4 --- /dev/null +++ b/xmss_fast.h @@ -0,0 +1,109 @@ +/* +xmss_fast.h version 20160722 +Andreas Hülsing +Joost Rijneveld +Public domain. +*/ + +#include "xmss_wots.h" + +#ifndef XMSS_H +#define XMSS_H +typedef struct{ + unsigned int level; + unsigned long long subtree; + unsigned int subleaf; +} leafaddr; + +typedef struct{ + wots_params wots_par; + unsigned int n; + unsigned int h; + unsigned int k; +} xmss_params; + +typedef struct{ + xmss_params xmss_par; + unsigned int n; + unsigned int h; + unsigned int d; + unsigned int index_len; +} xmssmt_params; + +typedef struct{ + unsigned int h; + unsigned int next_idx; + unsigned int stackusage; + unsigned char completed; + unsigned char *node; +} treehash_inst; + +typedef struct { + unsigned char *stack; + unsigned int stackoffset; + unsigned char *stacklevels; + unsigned char *auth; + unsigned char *keep; + treehash_inst *treehash; + unsigned char *retain; + unsigned int next_leaf; +} bds_state; + +/** + * Initialize BDS state struct + * parameter names are the same as used in the description of the BDS traversal + */ +void xmss_set_bds_state(bds_state *state, unsigned char *stack, int stackoffset, unsigned char *stacklevels, unsigned char *auth, unsigned char *keep, treehash_inst *treehash, unsigned char *retain, int next_leaf); +/** + * Initializes parameter set. + * Needed, for any of the other methods. + */ +int xmss_set_params(xmss_params *params, int n, int h, int w, int k); +/** + * Initialize xmssmt_params struct + * parameter names are the same as in the draft + * + * Especially h is the total tree height, i.e. the XMSS trees have height h/d + */ +int xmssmt_set_params(xmssmt_params *params, int n, int h, int d, int w, int k); +/** + * Generates a XMSS key pair for a given parameter set. + * Format sk: [(32bit) idx || SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] omitting algo oid. + */ +int xmss_keypair(unsigned char *pk, unsigned char *sk, bds_state *state, xmss_params *params); +/** + * Signs a message. + * Returns + * 1. an array containing the signature followed by the message AND + * 2. an updated secret key! + * + */ +int xmss_sign(unsigned char *sk, bds_state *state, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg,unsigned long long msglen, const xmss_params *params); +/** + * Verifies a given message signature pair under a given public key. + * + * Note: msg and msglen are pure outputs which carry the message in case verification succeeds. The (input) message is assumed to be within sig_msg which has the form (sig||msg). + */ +int xmss_sign_open(unsigned char *msg,unsigned long long *msglen, const unsigned char *sig_msg,unsigned long long sig_msg_len, const unsigned char *pk, const xmss_params *params); + +/* + * Generates a XMSSMT key pair for a given parameter set. + * Format sk: [(ceil(h/8) bit) idx || SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] omitting algo oid. + */ +int xmssmt_keypair(unsigned char *pk, unsigned char *sk, bds_state *states, unsigned char *wots_sigs, xmssmt_params *params); +/** + * Signs a message. + * Returns + * 1. an array containing the signature followed by the message AND + * 2. an updated secret key! + * + */ +int xmssmt_sign(unsigned char *sk, bds_state *state, unsigned char *wots_sigs, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg, unsigned long long msglen, const xmssmt_params *params); +/** + * Verifies a given message signature pair under a given public key. + */ +int xmssmt_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk, const xmssmt_params *params); +#endif + diff --git a/xmss_hash.c b/xmss_hash.c new file mode 100644 index 000000000..963b584b9 --- /dev/null +++ b/xmss_hash.c @@ -0,0 +1,133 @@ +/* +hash.c version 20160722 +Andreas Hülsing +Joost Rijneveld +Public domain. +*/ + +#include "xmss_hash_address.h" +#include "xmss_commons.h" +#include "xmss_hash.h" + +#include +#include +#include +#include +#include +#include +#include + +int core_hash_SHA2(unsigned char *, const unsigned int, const unsigned char *, + unsigned int, const unsigned char *, unsigned long long, unsigned int); + +unsigned char* addr_to_byte(unsigned char *bytes, const uint32_t addr[8]){ +#if IS_LITTLE_ENDIAN==1 + int i = 0; + for(i=0;i<8;i++) + to_byte(bytes+i*4, addr[i],4); + return bytes; +#else + memcpy(bytes, addr, 32); + return bytes; +#endif +} + +int core_hash_SHA2(unsigned char *out, const unsigned int type, const unsigned char *key, unsigned int keylen, const unsigned char *in, unsigned long long inlen, unsigned int n){ + unsigned long long i = 0; + unsigned char buf[inlen + n + keylen]; + + // Input is (toByte(X, 32) || KEY || M) + + // set toByte + to_byte(buf, type, n); + + for (i=0; i < keylen; i++) { + buf[i+n] = key[i]; + } + + for (i=0; i < inlen; i++) { + buf[keylen + n + i] = in[i]; + } + + if (n == 32) { + SHA256(buf, inlen + keylen + n, out); + return 0; + } + else { + if (n == 64) { + SHA512(buf, inlen + keylen + n, out); + return 0; + } + } + return 1; +} + +/** + * Implements PRF + */ +int prf(unsigned char *out, const unsigned char *in, const unsigned char *key, unsigned int keylen) +{ + return core_hash_SHA2(out, 3, key, keylen, in, 32, keylen); +} + +/* + * Implemts H_msg + */ +int h_msg(unsigned char *out, const unsigned char *in, unsigned long long inlen, const unsigned char *key, const unsigned int keylen, const unsigned int n) +{ + if (keylen != 3*n){ + // H_msg takes 3n-bit keys, but n does not match the keylength of keylen + return -1; + } + return core_hash_SHA2(out, 2, key, keylen, in, inlen, n); +} + +/** + * We assume the left half is in in[0]...in[n-1] + */ +int hash_h(unsigned char *out, const unsigned char *in, const unsigned char *pub_seed, uint32_t addr[8], const unsigned int n) +{ + + unsigned char buf[2*n]; + unsigned char key[n]; + unsigned char bitmask[2*n]; + unsigned char byte_addr[32]; + unsigned int i; + + setKeyAndMask(addr, 0); + addr_to_byte(byte_addr, addr); + prf(key, byte_addr, pub_seed, n); + // Use MSB order + setKeyAndMask(addr, 1); + addr_to_byte(byte_addr, addr); + prf(bitmask, byte_addr, pub_seed, n); + setKeyAndMask(addr, 2); + addr_to_byte(byte_addr, addr); + prf(bitmask+n, byte_addr, pub_seed, n); + for (i = 0; i < 2*n; i++) { + buf[i] = in[i] ^ bitmask[i]; + } + return core_hash_SHA2(out, 1, key, n, buf, 2*n, n); +} + +int hash_f(unsigned char *out, const unsigned char *in, const unsigned char *pub_seed, uint32_t addr[8], const unsigned int n) +{ + unsigned char buf[n]; + unsigned char key[n]; + unsigned char bitmask[n]; + unsigned char byte_addr[32]; + unsigned int i; + + setKeyAndMask(addr, 0); + addr_to_byte(byte_addr, addr); + prf(key, byte_addr, pub_seed, n); + + setKeyAndMask(addr, 1); + addr_to_byte(byte_addr, addr); + prf(bitmask, byte_addr, pub_seed, n); + + for (i = 0; i < n; i++) { + buf[i] = in[i] ^ bitmask[i]; + } + return core_hash_SHA2(out, 0, key, n, buf, n, n); +} diff --git a/xmss_hash.h b/xmss_hash.h new file mode 100644 index 000000000..2fed73009 --- /dev/null +++ b/xmss_hash.h @@ -0,0 +1,19 @@ +/* +hash.h version 20160722 +Andreas Hülsing +Joost Rijneveld +Public domain. +*/ + +#ifndef HASH_H +#define HASH_H + +#define IS_LITTLE_ENDIAN 1 + +unsigned char* addr_to_byte(unsigned char *bytes, const uint32_t addr[8]); +int prf(unsigned char *out, const unsigned char *in, const unsigned char *key, unsigned int keylen); +int h_msg(unsigned char *out,const unsigned char *in,unsigned long long inlen, const unsigned char *key, const unsigned int keylen, const unsigned int n); +int hash_h(unsigned char *out, const unsigned char *in, const unsigned char *pub_seed, uint32_t addr[8], const unsigned int n); +int hash_f(unsigned char *out, const unsigned char *in, const unsigned char *pub_seed, uint32_t addr[8], const unsigned int n); + +#endif diff --git a/xmss_hash_address.c b/xmss_hash_address.c new file mode 100644 index 000000000..223c6f8ab --- /dev/null +++ b/xmss_hash_address.c @@ -0,0 +1,59 @@ +/* +hash_address.c version 20160722 +Andreas Hülsing +Joost Rijneveld +Public domain. +*/ +#include +#include "xmss_hash_address.h" /* prototypes */ + +void setLayerADRS(uint32_t adrs[8], uint32_t layer){ + adrs[0] = layer; +} + +void setTreeADRS(uint32_t adrs[8], uint64_t tree){ + adrs[1] = (uint32_t) (tree >> 32); + adrs[2] = (uint32_t) tree; +} + +void setType(uint32_t adrs[8], uint32_t type){ + adrs[3] = type; + int i; + for(i = 4; i < 8; i++){ + adrs[i] = 0; + } +} + +void setKeyAndMask(uint32_t adrs[8], uint32_t keyAndMask){ + adrs[7] = keyAndMask; +} + +// OTS + +void setOTSADRS(uint32_t adrs[8], uint32_t ots){ + adrs[4] = ots; +} + +void setChainADRS(uint32_t adrs[8], uint32_t chain){ + adrs[5] = chain; +} + +void setHashADRS(uint32_t adrs[8], uint32_t hash){ + adrs[6] = hash; +} + +// L-tree + +void setLtreeADRS(uint32_t adrs[8], uint32_t ltree){ + adrs[4] = ltree; +} + +// Hash Tree & L-tree + +void setTreeHeight(uint32_t adrs[8], uint32_t treeHeight){ + adrs[5] = treeHeight; +} + +void setTreeIndex(uint32_t adrs[8], uint32_t treeIndex){ + adrs[6] = treeIndex; +} diff --git a/xmss_hash_address.h b/xmss_hash_address.h new file mode 100644 index 000000000..73cbfd61c --- /dev/null +++ b/xmss_hash_address.h @@ -0,0 +1,37 @@ +/* +hash_address.h version 20160722 +Andreas Hülsing +Joost Rijneveld +Public domain. +*/ + +#include + +void setLayerADRS(uint32_t adrs[8], uint32_t layer); + +void setTreeADRS(uint32_t adrs[8], uint64_t tree); + +void setType(uint32_t adrs[8], uint32_t type); + +void setKeyAndMask(uint32_t adrs[8], uint32_t keyAndMask); + +// OTS + +void setOTSADRS(uint32_t adrs[8], uint32_t ots); + +void setChainADRS(uint32_t adrs[8], uint32_t chain); + +void setHashADRS(uint32_t adrs[8], uint32_t hash); + +// L-tree + +void setLtreeADRS(uint32_t adrs[8], uint32_t ltree); + +// Hash Tree & L-tree + +void setTreeHeight(uint32_t adrs[8], uint32_t treeHeight); + +void setTreeIndex(uint32_t adrs[8], uint32_t treeIndex); + + + diff --git a/xmss_wots.c b/xmss_wots.c new file mode 100644 index 000000000..fcd033405 --- /dev/null +++ b/xmss_wots.c @@ -0,0 +1,185 @@ +/* +wots.c version 20160722 +Andreas Hülsing +Joost Rijneveld +Public domain. +*/ + +#include +#include +#include +#include "xmss_commons.h" +#include "xmss_hash.h" +#include "xmss_wots.h" +#include "xmss_hash_address.h" + + +/* libm-free version of log2() for wots */ +static inline int +wots_log2(uint32_t v) +{ + int b; + + for (b = sizeof (v) * CHAR_BIT - 1; b >= 0; b--) { + if ((1U << b) & v) { + return b; + } + } + return 0; +} + +void +wots_set_params(wots_params *params, int n, int w) +{ + params->n = n; + params->w = w; + params->log_w = wots_log2(params->w); + params->len_1 = (CHAR_BIT * n) / params->log_w; + params->len_2 = (wots_log2(params->len_1 * (w - 1)) / params->log_w) + 1; + params->len = params->len_1 + params->len_2; + params->keysize = params->len * params->n; +} + +/** + * Helper method for pseudorandom key generation + * Expands an n-byte array into a len*n byte array + * this is done using PRF + */ +static void expand_seed(unsigned char *outseeds, const unsigned char *inseed, const wots_params *params) +{ + uint32_t i = 0; + unsigned char ctr[32]; + for(i = 0; i < params->len; i++){ + to_byte(ctr, i, 32); + prf((outseeds + (i*params->n)), ctr, inseed, params->n); + } +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays + * + * interpretes in as start-th value of the chain + * addr has to contain the address of the chain + */ +static void gen_chain(unsigned char *out, const unsigned char *in, unsigned int start, unsigned int steps, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]) +{ + uint32_t i, j; + for (j = 0; j < params->n; j++) + out[j] = in[j]; + + for (i = start; i < (start+steps) && i < params->w; i++) { + setHashADRS(addr, i); + hash_f(out, out, pub_seed, addr, params->n); + } +} + +/** + * base_w algorithm as described in draft. + * + * + */ +static void base_w(int *output, const int out_len, const unsigned char *input, const wots_params *params) +{ + int in = 0; + int out = 0; + uint32_t total = 0; + int bits = 0; + int consumed = 0; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= params->log_w; + output[out] = (total >> bits) & (params->w - 1); + out++; + } +} + +void wots_pkgen(unsigned char *pk, const unsigned char *sk, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]) +{ + uint32_t i; + expand_seed(pk, sk, params); + for (i=0; i < params->len; i++) { + setChainADRS(addr, i); + gen_chain(pk+i*params->n, pk+i*params->n, 0, params->w-1, params, pub_seed, addr); + } +} + + +int wots_sign(unsigned char *sig, const unsigned char *msg, const unsigned char *sk, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]) +{ + //int basew[params->len]; + int csum = 0; + uint32_t i = 0; + int *basew = calloc(params->len, sizeof(int)); + if (basew == NULL) + return -1; + + base_w(basew, params->len_1, msg, params); + + for (i=0; i < params->len_1; i++) { + csum += params->w - 1 - basew[i]; + } + + csum = csum << (8 - ((params->len_2 * params->log_w) % 8)); + + int len_2_bytes = ((params->len_2 * params->log_w) + 7) / 8; + + unsigned char csum_bytes[len_2_bytes]; + to_byte(csum_bytes, csum, len_2_bytes); + + int csum_basew[params->len_2]; + base_w(csum_basew, params->len_2, csum_bytes, params); + + for (i = 0; i < params->len_2; i++) { + basew[params->len_1 + i] = csum_basew[i]; + } + + expand_seed(sig, sk, params); + + for (i = 0; i < params->len; i++) { + setChainADRS(addr, i); + gen_chain(sig+i*params->n, sig+i*params->n, 0, basew[i], params, pub_seed, addr); + } + free(basew); + return 0; +} + +int wots_pkFromSig(unsigned char *pk, const unsigned char *sig, const unsigned char *msg, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]) +{ + int csum = 0; + uint32_t i = 0; + int *basew = calloc(params->len, sizeof(int)); + if (basew == NULL) + return -1; + + base_w(basew, params->len_1, msg, params); + + for (i=0; i < params->len_1; i++) { + csum += params->w - 1 - basew[i]; + } + + csum = csum << (8 - ((params->len_2 * params->log_w) % 8)); + + int len_2_bytes = ((params->len_2 * params->log_w) + 7) / 8; + + unsigned char csum_bytes[len_2_bytes]; + to_byte(csum_bytes, csum, len_2_bytes); + + int csum_basew[params->len_2]; + base_w(csum_basew, params->len_2, csum_bytes, params); + + for (i = 0; i < params->len_2; i++) { + basew[params->len_1 + i] = csum_basew[i]; + } + for (i=0; i < params->len; i++) { + setChainADRS(addr, i); + gen_chain(pk+i*params->n, sig+i*params->n, basew[i], params->w-1-basew[i], params, pub_seed, addr); + } + free(basew); + return 0; +} diff --git a/xmss_wots.h b/xmss_wots.h new file mode 100644 index 000000000..495431087 --- /dev/null +++ b/xmss_wots.h @@ -0,0 +1,59 @@ +/* +wots.h version 20160722 +Andreas Hülsing +Joost Rijneveld +Public domain. +*/ + +#ifndef WOTS_H +#define WOTS_H + +#include "stdint.h" + +/** + * WOTS parameter set + * + * Meaning as defined in draft-irtf-cfrg-xmss-hash-based-signatures-02 + */ +typedef struct { + uint32_t len_1; + uint32_t len_2; + uint32_t len; + uint32_t n; + uint32_t w; + uint32_t log_w; + uint32_t keysize; +} wots_params; + +/** + * Set the WOTS parameters, + * only m, n, w are required as inputs, + * len, len_1, and len_2 are computed from those. + * + * Assumes w is a power of 2 + */ +void wots_set_params(wots_params *params, int n, int w); + +/** + * WOTS key generation. Takes a 32byte seed for the secret key, expands it to a full WOTS secret key and computes the corresponding public key. + * For this it takes the seed pub_seed which is used to generate bitmasks and hash keys and the address of this WOTS key pair addr + * + * params, must have been initialized before using wots_set params for params ! This is not done in this function + * + * Places the computed public key at address pk. + */ +void wots_pkgen(unsigned char *pk, const unsigned char *sk, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a m-byte message and the 32-byte seed for the secret key to compute a signature that is placed at "sig". + * + */ +int wots_sign(unsigned char *sig, const unsigned char *msg, const unsigned char *sk, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a WOTS signature, a m-byte message and computes a WOTS public key that it places at pk. + * + */ +int wots_pkFromSig(unsigned char *pk, const unsigned char *sig, const unsigned char *msg, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]); + +#endif -- cgit v1.2.3 From 7c856857607112a3dfe6414696bf4c7ab7fb0cb3 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Sat, 3 Mar 2018 03:15:51 +0000 Subject: upstream: switch over to the new authorized_keys options API and remove the legacy one. Includes a fairly big refactor of auth2-pubkey.c to retain less state between key file lines. feedback and ok markus@ OpenBSD-Commit-ID: dece6cae0f47751b9892080eb13d6625599573df --- .depend | 7 +- auth-options.c | 650 +-------------------------------------------------------- auth-options.h | 46 ++-- auth-pam.c | 4 +- auth-pam.h | 4 +- auth-passwd.c | 30 ++- auth.c | 180 +++++++++++++++- auth.h | 28 ++- auth2-none.c | 4 +- auth2-passwd.c | 4 +- auth2-pubkey.c | 532 +++++++++++++++++++++++++++++++--------------- auth2.c | 4 +- misc.c | 3 +- monitor.c | 70 ++++--- monitor_wrap.c | 44 ++-- monitor_wrap.h | 11 +- serverloop.c | 33 +-- session.c | 85 +++++--- sshd.c | 12 +- 19 files changed, 767 insertions(+), 984 deletions(-) (limited to 'sshd.c') diff --git a/.depend b/.depend index 4c6b49d54..0893a87ab 100644 --- a/.depend +++ b/.depend @@ -7,8 +7,7 @@ audit-linux.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-c audit.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h auth-bsdauth.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h auth-krb5.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h xmalloc.h ssh.h packet.h openbsd-compat/sys-queue.h dispatch.h opacket.h log.h misc.h servconf.h uidswap.h key.h sshkey.h hostfile.h auth.h auth-pam.h audit.h loginrec.h -auth-options.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h openbsd-compat/sys-queue.h key.h sshkey.h xmalloc.h match.h ssherr.h log.h canohost.h packet.h dispatch.h opacket.h misc.h channels.h servconf.h auth-options.h hostfile.h auth.h auth-pam.h -auth-options.o: audit.h loginrec.h +auth-options.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h openbsd-compat/sys-queue.h xmalloc.h ssherr.h log.h misc.h sshkey.h match.h ssh2.h auth-options.h auth-pam.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h auth-passwd.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h packet.h openbsd-compat/sys-queue.h dispatch.h opacket.h log.h misc.h servconf.h key.h sshkey.h hostfile.h auth.h auth-pam.h audit.h loginrec.h auth-options.h auth-rhosts.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h packet.h openbsd-compat/sys-queue.h dispatch.h opacket.h uidswap.h pathnames.h log.h misc.h key.h sshkey.h servconf.h canohost.h hostfile.h auth.h auth-pam.h audit.h loginrec.h @@ -16,7 +15,7 @@ auth-shadow.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-c auth-sia.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h auth-skey.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h auth.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h xmalloc.h match.h groupaccess.h log.h misc.h servconf.h key.h sshkey.h hostfile.h auth.h auth-pam.h audit.h loginrec.h auth-options.h canohost.h uidswap.h packet.h openbsd-compat/sys-queue.h -auth.o: dispatch.h opacket.h authfile.h monitor_wrap.h ssherr.h compat.h +auth.o: dispatch.h opacket.h authfile.h monitor_wrap.h ssherr.h compat.h channels.h auth2-chall.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h xmalloc.h ssh2.h key.h sshkey.h hostfile.h auth.h auth-pam.h audit.h loginrec.h packet.h openbsd-compat/sys-queue.h dispatch.h opacket.h log.h misc.h servconf.h auth2-gss.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h auth2-hostbased.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h xmalloc.h ssh2.h packet.h openbsd-compat/sys-queue.h dispatch.h opacket.h log.h misc.h servconf.h compat.h sshkey.h hostfile.h auth.h auth-pam.h audit.h loginrec.h canohost.h @@ -161,7 +160,7 @@ sshconnect.o: ssh2.h version.h authfile.h ssherr.h authfd.h sshconnect2.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h openbsd-compat/sys-queue.h xmalloc.h ssh.h ssh2.h packet.h dispatch.h opacket.h compat.h cipher.h cipher-chachapoly.h chacha.h poly1305.h cipher-aesctr.h rijndael.h key.h sshkey.h kex.h mac.h sshconnect2.o: myproposal.h sshconnect.h authfile.h dh.h authfd.h log.h misc.h readconf.h match.h canohost.h msg.h pathnames.h uidswap.h hostfile.h ssherr.h utf8.h sshd.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h ./openbsd-compat/sys-tree.h openbsd-compat/sys-queue.h xmalloc.h ssh.h ssh2.h sshpty.h packet.h dispatch.h opacket.h log.h misc.h match.h servconf.h uidswap.h compat.h cipher.h cipher-chachapoly.h -sshd.o: chacha.h poly1305.h cipher-aesctr.h rijndael.h digest.h key.h sshkey.h kex.h mac.h myproposal.h authfile.h pathnames.h atomicio.h canohost.h hostfile.h auth.h auth-pam.h audit.h loginrec.h authfd.h msg.h channels.h session.h monitor.h monitor_wrap.h ssh-sandbox.h version.h ssherr.h +sshd.o: chacha.h poly1305.h cipher-aesctr.h rijndael.h digest.h key.h sshkey.h kex.h mac.h myproposal.h authfile.h pathnames.h atomicio.h canohost.h hostfile.h auth.h auth-pam.h audit.h loginrec.h authfd.h msg.h channels.h session.h monitor.h monitor_wrap.h ssh-sandbox.h auth-options.h version.h ssherr.h ssherr.o: ssherr.h sshkey-xmss.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h sshkey.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h crypto_api.h ssh2.h ssherr.h misc.h cipher.h cipher-chachapoly.h chacha.h poly1305.h cipher-aesctr.h rijndael.h digest.h sshkey.h sshkey-xmss.h match.h xmss_fast.h diff --git a/auth-options.c b/auth-options.c index 8b93b51e5..484e44b74 100644 --- a/auth-options.c +++ b/auth-options.c @@ -1,14 +1,4 @@ -/* $OpenBSD: auth-options.c,v 1.75 2018/03/03 03:06:02 djm Exp $ */ -/* - * Author: Tatu Ylonen - * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - * All rights reserved - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - */ +/* $OpenBSD: auth-options.c,v 1.76 2018/03/03 03:15:51 djm Exp $ */ /* * Copyright (c) 2018 Damien Miller * @@ -39,649 +29,15 @@ #include "openbsd-compat/sys-queue.h" -#include "key.h" /* XXX for typedef */ -#include "buffer.h" /* XXX for typedef */ #include "xmalloc.h" -#include "match.h" #include "ssherr.h" -#include "ssh2.h" #include "log.h" -#include "canohost.h" -#include "packet.h" #include "sshbuf.h" #include "misc.h" -#include "channels.h" -#include "servconf.h" #include "sshkey.h" +#include "match.h" +#include "ssh2.h" #include "auth-options.h" -#include "hostfile.h" -#include "auth.h" - -/* Flags set authorized_keys flags */ -int no_port_forwarding_flag = 0; -int no_agent_forwarding_flag = 0; -int no_x11_forwarding_flag = 0; -int no_pty_flag = 0; -int no_user_rc = 0; -int key_is_cert_authority = 0; - -/* "command=" option. */ -char *forced_command = NULL; - -/* "environment=" options. */ -struct envstring *custom_environment = NULL; - -/* "tunnel=" option. */ -int forced_tun_device = -1; - -/* "principals=" option. */ -char *authorized_principals = NULL; - -extern ServerOptions options; - -/* XXX refactor to be stateless */ - -void -auth_clear_options(void) -{ - struct ssh *ssh = active_state; /* XXX */ - - no_agent_forwarding_flag = 0; - no_port_forwarding_flag = 0; - no_pty_flag = 0; - no_x11_forwarding_flag = 0; - no_user_rc = 0; - key_is_cert_authority = 0; - while (custom_environment) { - struct envstring *ce = custom_environment; - custom_environment = ce->next; - free(ce->s); - free(ce); - } - free(forced_command); - forced_command = NULL; - free(authorized_principals); - authorized_principals = NULL; - forced_tun_device = -1; - channel_clear_permitted_opens(ssh); -} - -/* - * Match flag 'opt' in *optsp, and if allow_negate is set then also match - * 'no-opt'. Returns -1 if option not matched, 1 if option matches or 0 - * if negated option matches. - * If the option or negated option matches, then *optsp is updated to - * point to the first character after the option and, if 'msg' is not NULL - * then a message based on it added via auth_debug_add(). - */ -static int -match_flag(const char *opt, int allow_negate, char **optsp, const char *msg) -{ - size_t opt_len = strlen(opt); - char *opts = *optsp; - int negate = 0; - - if (allow_negate && strncasecmp(opts, "no-", 3) == 0) { - opts += 3; - negate = 1; - } - if (strncasecmp(opts, opt, opt_len) == 0) { - *optsp = opts + opt_len; - if (msg != NULL) { - auth_debug_add("%s %s.", msg, - negate ? "disabled" : "enabled"); - } - return negate ? 0 : 1; - } - return -1; -} - -/* - * return 1 if access is granted, 0 if not. - * side effect: sets key option flags - * XXX remove side effects; fill structure instead. - */ -int -auth_parse_options(struct passwd *pw, char *opts, const char *file, - u_long linenum) -{ - struct ssh *ssh = active_state; /* XXX */ - const char *cp; - int i, r; - - /* reset options */ - auth_clear_options(); - - if (!opts) - return 1; - - while (*opts && *opts != ' ' && *opts != '\t') { - if ((r = match_flag("cert-authority", 0, &opts, NULL)) != -1) { - key_is_cert_authority = r; - goto next_option; - } - if ((r = match_flag("restrict", 0, &opts, NULL)) != -1) { - auth_debug_add("Key is restricted."); - no_port_forwarding_flag = 1; - no_agent_forwarding_flag = 1; - no_x11_forwarding_flag = 1; - no_pty_flag = 1; - no_user_rc = 1; - goto next_option; - } - if ((r = match_flag("port-forwarding", 1, &opts, - "Port forwarding")) != -1) { - no_port_forwarding_flag = r != 1; - goto next_option; - } - if ((r = match_flag("agent-forwarding", 1, &opts, - "Agent forwarding")) != -1) { - no_agent_forwarding_flag = r != 1; - goto next_option; - } - if ((r = match_flag("x11-forwarding", 1, &opts, - "X11 forwarding")) != -1) { - no_x11_forwarding_flag = r != 1; - goto next_option; - } - if ((r = match_flag("pty", 1, &opts, - "PTY allocation")) != -1) { - no_pty_flag = r != 1; - goto next_option; - } - if ((r = match_flag("user-rc", 1, &opts, - "User rc execution")) != -1) { - no_user_rc = r != 1; - goto next_option; - } - cp = "command=\""; - if (strncasecmp(opts, cp, strlen(cp)) == 0) { - opts += strlen(cp); - free(forced_command); - forced_command = xmalloc(strlen(opts) + 1); - i = 0; - while (*opts) { - if (*opts == '"') - break; - if (*opts == '\\' && opts[1] == '"') { - opts += 2; - forced_command[i++] = '"'; - continue; - } - forced_command[i++] = *opts++; - } - if (!*opts) { - debug("%.100s, line %lu: missing end quote", - file, linenum); - auth_debug_add("%.100s, line %lu: missing end quote", - file, linenum); - free(forced_command); - forced_command = NULL; - goto bad_option; - } - forced_command[i] = '\0'; - auth_debug_add("Forced command."); - opts++; - goto next_option; - } - cp = "principals=\""; - if (strncasecmp(opts, cp, strlen(cp)) == 0) { - opts += strlen(cp); - free(authorized_principals); - authorized_principals = xmalloc(strlen(opts) + 1); - i = 0; - while (*opts) { - if (*opts == '"') - break; - if (*opts == '\\' && opts[1] == '"') { - opts += 2; - authorized_principals[i++] = '"'; - continue; - } - authorized_principals[i++] = *opts++; - } - if (!*opts) { - debug("%.100s, line %lu: missing end quote", - file, linenum); - auth_debug_add("%.100s, line %lu: missing end quote", - file, linenum); - free(authorized_principals); - authorized_principals = NULL; - goto bad_option; - } - authorized_principals[i] = '\0'; - auth_debug_add("principals: %.900s", - authorized_principals); - opts++; - goto next_option; - } - cp = "environment=\""; - if (strncasecmp(opts, cp, strlen(cp)) == 0) { - char *s; - struct envstring *new_envstring; - - opts += strlen(cp); - s = xmalloc(strlen(opts) + 1); - i = 0; - while (*opts) { - if (*opts == '"') - break; - if (*opts == '\\' && opts[1] == '"') { - opts += 2; - s[i++] = '"'; - continue; - } - s[i++] = *opts++; - } - if (!*opts) { - debug("%.100s, line %lu: missing end quote", - file, linenum); - auth_debug_add("%.100s, line %lu: missing end quote", - file, linenum); - free(s); - goto bad_option; - } - s[i] = '\0'; - opts++; - if (options.permit_user_env) { - auth_debug_add("Adding to environment: " - "%.900s", s); - debug("Adding to environment: %.900s", s); - new_envstring = xcalloc(1, - sizeof(*new_envstring)); - new_envstring->s = s; - new_envstring->next = custom_environment; - custom_environment = new_envstring; - s = NULL; - } - free(s); - goto next_option; - } - cp = "from=\""; - if (strncasecmp(opts, cp, strlen(cp)) == 0) { - const char *remote_ip = ssh_remote_ipaddr(ssh); - const char *remote_host = auth_get_canonical_hostname( - ssh, options.use_dns); - char *patterns = xmalloc(strlen(opts) + 1); - - opts += strlen(cp); - i = 0; - while (*opts) { - if (*opts == '"') - break; - if (*opts == '\\' && opts[1] == '"') { - opts += 2; - patterns[i++] = '"'; - continue; - } - patterns[i++] = *opts++; - } - if (!*opts) { - debug("%.100s, line %lu: missing end quote", - file, linenum); - auth_debug_add("%.100s, line %lu: missing end quote", - file, linenum); - free(patterns); - goto bad_option; - } - patterns[i] = '\0'; - opts++; - switch (match_host_and_ip(remote_host, remote_ip, - patterns)) { - case 1: - free(patterns); - /* Host name matches. */ - goto next_option; - case -1: - debug("%.100s, line %lu: invalid criteria", - file, linenum); - auth_debug_add("%.100s, line %lu: " - "invalid criteria", file, linenum); - /* FALLTHROUGH */ - case 0: - free(patterns); - logit("Authentication tried for %.100s with " - "correct key but not from a permitted " - "host (host=%.200s, ip=%.200s).", - pw->pw_name, remote_host, remote_ip); - auth_debug_add("Your host '%.200s' is not " - "permitted to use this key for login.", - remote_host); - break; - } - /* deny access */ - return 0; - } - cp = "permitopen=\""; - if (strncasecmp(opts, cp, strlen(cp)) == 0) { - char *host, *p; - int port; - char *patterns = xmalloc(strlen(opts) + 1); - - opts += strlen(cp); - i = 0; - while (*opts) { - if (*opts == '"') - break; - if (*opts == '\\' && opts[1] == '"') { - opts += 2; - patterns[i++] = '"'; - continue; - } - patterns[i++] = *opts++; - } - if (!*opts) { - debug("%.100s, line %lu: missing end quote", - file, linenum); - auth_debug_add("%.100s, line %lu: missing " - "end quote", file, linenum); - free(patterns); - goto bad_option; - } - patterns[i] = '\0'; - opts++; - p = patterns; - /* XXX - add streamlocal support */ - host = hpdelim(&p); - if (host == NULL || strlen(host) >= NI_MAXHOST) { - debug("%.100s, line %lu: Bad permitopen " - "specification <%.100s>", file, linenum, - patterns); - auth_debug_add("%.100s, line %lu: " - "Bad permitopen specification", file, - linenum); - free(patterns); - goto bad_option; - } - host = cleanhostname(host); - if (p == NULL || (port = permitopen_port(p)) < 0) { - debug("%.100s, line %lu: Bad permitopen port " - "<%.100s>", file, linenum, p ? p : ""); - auth_debug_add("%.100s, line %lu: " - "Bad permitopen port", file, linenum); - free(patterns); - goto bad_option; - } - if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0) - channel_add_permitted_opens(ssh, host, port); - free(patterns); - goto next_option; - } - cp = "tunnel=\""; - if (strncasecmp(opts, cp, strlen(cp)) == 0) { - char *tun = NULL; - opts += strlen(cp); - tun = xmalloc(strlen(opts) + 1); - i = 0; - while (*opts) { - if (*opts == '"') - break; - tun[i++] = *opts++; - } - if (!*opts) { - debug("%.100s, line %lu: missing end quote", - file, linenum); - auth_debug_add("%.100s, line %lu: missing end quote", - file, linenum); - free(tun); - forced_tun_device = -1; - goto bad_option; - } - tun[i] = '\0'; - forced_tun_device = a2tun(tun, NULL); - free(tun); - if (forced_tun_device == SSH_TUNID_ERR) { - debug("%.100s, line %lu: invalid tun device", - file, linenum); - auth_debug_add("%.100s, line %lu: invalid tun device", - file, linenum); - forced_tun_device = -1; - goto bad_option; - } - auth_debug_add("Forced tun device: %d", forced_tun_device); - opts++; - goto next_option; - } -next_option: - /* - * Skip the comma, and move to the next option - * (or break out if there are no more). - */ - if (!*opts) - fatal("Bugs in auth-options.c option processing."); - if (*opts == ' ' || *opts == '\t') - break; /* End of options. */ - if (*opts != ',') - goto bad_option; - opts++; - /* Process the next option. */ - } - - /* grant access */ - return 1; - -bad_option: - logit("Bad options in %.100s file, line %lu: %.50s", - file, linenum, opts); - auth_debug_add("Bad options in %.100s file, line %lu: %.50s", - file, linenum, opts); - - /* deny access */ - return 0; -} - -#define OPTIONS_CRITICAL 1 -#define OPTIONS_EXTENSIONS 2 -static int -parse_option_list(struct sshbuf *oblob, struct passwd *pw, - u_int which, int crit, - int *cert_no_port_forwarding_flag, - int *cert_no_agent_forwarding_flag, - int *cert_no_x11_forwarding_flag, - int *cert_no_pty_flag, - int *cert_no_user_rc, - char **cert_forced_command, - int *cert_source_address_done) -{ - struct ssh *ssh = active_state; /* XXX */ - char *command, *allowed; - const char *remote_ip; - char *name = NULL; - struct sshbuf *c = NULL, *data = NULL; - int r, ret = -1, result, found; - - if ((c = sshbuf_fromb(oblob)) == NULL) { - error("%s: sshbuf_fromb failed", __func__); - goto out; - } - - while (sshbuf_len(c) > 0) { - sshbuf_free(data); - data = NULL; - if ((r = sshbuf_get_cstring(c, &name, NULL)) != 0 || - (r = sshbuf_froms(c, &data)) != 0) { - error("Unable to parse certificate options: %s", - ssh_err(r)); - goto out; - } - debug3("found certificate option \"%.100s\" len %zu", - name, sshbuf_len(data)); - found = 0; - if ((which & OPTIONS_EXTENSIONS) != 0) { - if (strcmp(name, "permit-X11-forwarding") == 0) { - *cert_no_x11_forwarding_flag = 0; - found = 1; - } else if (strcmp(name, - "permit-agent-forwarding") == 0) { - *cert_no_agent_forwarding_flag = 0; - found = 1; - } else if (strcmp(name, - "permit-port-forwarding") == 0) { - *cert_no_port_forwarding_flag = 0; - found = 1; - } else if (strcmp(name, "permit-pty") == 0) { - *cert_no_pty_flag = 0; - found = 1; - } else if (strcmp(name, "permit-user-rc") == 0) { - *cert_no_user_rc = 0; - found = 1; - } - } - if (!found && (which & OPTIONS_CRITICAL) != 0) { - if (strcmp(name, "force-command") == 0) { - if ((r = sshbuf_get_cstring(data, &command, - NULL)) != 0) { - error("Unable to parse \"%s\" " - "section: %s", name, ssh_err(r)); - goto out; - } - if (*cert_forced_command != NULL) { - error("Certificate has multiple " - "force-command options"); - free(command); - goto out; - } - *cert_forced_command = command; - found = 1; - } - if (strcmp(name, "source-address") == 0) { - if ((r = sshbuf_get_cstring(data, &allowed, - NULL)) != 0) { - error("Unable to parse \"%s\" " - "section: %s", name, ssh_err(r)); - goto out; - } - if ((*cert_source_address_done)++) { - error("Certificate has multiple " - "source-address options"); - free(allowed); - goto out; - } - remote_ip = ssh_remote_ipaddr(ssh); - result = addr_match_cidr_list(remote_ip, - allowed); - free(allowed); - switch (result) { - case 1: - /* accepted */ - break; - case 0: - /* no match */ - logit("Authentication tried for %.100s " - "with valid certificate but not " - "from a permitted host " - "(ip=%.200s).", pw->pw_name, - remote_ip); - auth_debug_add("Your address '%.200s' " - "is not permitted to use this " - "certificate for login.", - remote_ip); - goto out; - case -1: - default: - error("Certificate source-address " - "contents invalid"); - goto out; - } - found = 1; - } - } - - if (!found) { - if (crit) { - error("Certificate critical option \"%s\" " - "is not supported", name); - goto out; - } else { - logit("Certificate extension \"%s\" " - "is not supported", name); - } - } else if (sshbuf_len(data) != 0) { - error("Certificate option \"%s\" corrupt " - "(extra data)", name); - goto out; - } - free(name); - name = NULL; - } - /* successfully parsed all options */ - ret = 0; - - out: - if (ret != 0 && - cert_forced_command != NULL && - *cert_forced_command != NULL) { - free(*cert_forced_command); - *cert_forced_command = NULL; - } - free(name); - sshbuf_free(data); - sshbuf_free(c); - return ret; -} - -/* - * Set options from critical certificate options. These supersede user key - * options so this must be called after auth_parse_options(). - */ -int -auth_cert_options(struct sshkey *k, struct passwd *pw, const char **reason) -{ - int cert_no_port_forwarding_flag = 1; - int cert_no_agent_forwarding_flag = 1; - int cert_no_x11_forwarding_flag = 1; - int cert_no_pty_flag = 1; - int cert_no_user_rc = 1; - char *cert_forced_command = NULL; - int cert_source_address_done = 0; - - *reason = "invalid certificate options"; - - /* Separate options and extensions for v01 certs */ - if (parse_option_list(k->cert->critical, pw, - OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL, - &cert_forced_command, - &cert_source_address_done) == -1) - return -1; - if (parse_option_list(k->cert->extensions, pw, - OPTIONS_EXTENSIONS, 0, - &cert_no_port_forwarding_flag, - &cert_no_agent_forwarding_flag, - &cert_no_x11_forwarding_flag, - &cert_no_pty_flag, - &cert_no_user_rc, - NULL, NULL) == -1) - return -1; - - no_port_forwarding_flag |= cert_no_port_forwarding_flag; - no_agent_forwarding_flag |= cert_no_agent_forwarding_flag; - no_x11_forwarding_flag |= cert_no_x11_forwarding_flag; - no_pty_flag |= cert_no_pty_flag; - no_user_rc |= cert_no_user_rc; - /* - * Only permit both CA and key option forced-command if they match. - * Otherwise refuse the certificate. - */ - if (cert_forced_command != NULL && forced_command != NULL) { - if (strcmp(forced_command, cert_forced_command) == 0) { - free(forced_command); - forced_command = cert_forced_command; - } else { - *reason = "certificate and key options forced command " - "do not match"; - free(cert_forced_command); - return -1; - } - } else if (cert_forced_command != NULL) - forced_command = cert_forced_command; - /* success */ - *reason = NULL; - return 0; -} - -/* - * authorized_keys options processing. - */ /* * Match flag 'opt' in *optsp, and if allow_negate is set then also match diff --git a/auth-options.h b/auth-options.h index 0dbfc325e..16871d754 100644 --- a/auth-options.h +++ b/auth-options.h @@ -1,15 +1,19 @@ -/* $OpenBSD: auth-options.h,v 1.24 2018/03/03 03:06:02 djm Exp $ */ +/* $OpenBSD: auth-options.h,v 1.25 2018/03/03 03:15:51 djm Exp $ */ /* - * Author: Tatu Ylonen - * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - * All rights reserved + * Copyright (c) 2018 Damien Miller * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef AUTH_OPTIONS_H @@ -18,30 +22,6 @@ struct passwd; struct sshkey; -/* Linked list of custom environment strings */ -struct envstring { - struct envstring *next; - char *s; -}; - -/* Flags that may be set in authorized_keys options. */ -extern int no_port_forwarding_flag; -extern int no_agent_forwarding_flag; -extern int no_x11_forwarding_flag; -extern int no_pty_flag; -extern int no_user_rc; -extern char *forced_command; -extern struct envstring *custom_environment; -extern int forced_tun_device; -extern int key_is_cert_authority; -extern char *authorized_principals; - -int auth_parse_options(struct passwd *, char *, const char *, u_long); -void auth_clear_options(void); -int auth_cert_options(struct sshkey *, struct passwd *, const char **); - -/* authorized_keys options handling */ - /* * sshauthopt represents key options parsed from authorized_keys or * from certificate extensions/options. diff --git a/auth-pam.c b/auth-pam.c index de29c04c9..50d2073d7 100644 --- a/auth-pam.c +++ b/auth-pam.c @@ -1077,7 +1077,7 @@ do_pam_chauthtok(void) } void -do_pam_session(void) +do_pam_session(struct ssh *ssh) { debug3("PAM: opening session"); @@ -1093,7 +1093,7 @@ do_pam_session(void) sshpam_session_open = 1; else { sshpam_session_open = 0; - disable_forwarding(); + auth_restrict_session(ssh); error("PAM: pam_open_session(): %s", pam_strerror(sshpam_handle, sshpam_err)); } diff --git a/auth-pam.h b/auth-pam.h index c47b442e4..419860745 100644 --- a/auth-pam.h +++ b/auth-pam.h @@ -25,10 +25,12 @@ #include "includes.h" #ifdef USE_PAM +struct ssh; + void start_pam(Authctxt *); void finish_pam(void); u_int do_pam_account(void); -void do_pam_session(void); +void do_pam_session(struct ssh *); void do_pam_setcred(int ); void do_pam_chauthtok(void); int do_pam_putenv(char *, char *); diff --git a/auth-passwd.c b/auth-passwd.c index 996c2cf71..6097fdd24 100644 --- a/auth-passwd.c +++ b/auth-passwd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-passwd.c,v 1.45 2016/07/21 01:39:35 dtucker Exp $ */ +/* $OpenBSD: auth-passwd.c,v 1.46 2018/03/03 03:15:51 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -68,22 +68,15 @@ extern login_cap_t *lc; #define MAX_PASSWORD_LEN 1024 -void -disable_forwarding(void) -{ - no_port_forwarding_flag = 1; - no_agent_forwarding_flag = 1; - no_x11_forwarding_flag = 1; -} - /* * Tries to authenticate the user using password. Returns true if * authentication succeeds. */ int -auth_password(Authctxt *authctxt, const char *password) +auth_password(struct ssh *ssh, const char *password) { - struct passwd * pw = authctxt->pw; + Authctxt *authctxt = ssh->authctxt; + struct passwd *pw = authctxt->pw; int result, ok = authctxt->valid; #if defined(USE_SHADOW) && defined(HAS_SHADOW_EXPIRE) static int expire_checked = 0; @@ -128,9 +121,9 @@ auth_password(Authctxt *authctxt, const char *password) authctxt->force_pwchange = 1; } #endif - result = sys_auth_passwd(authctxt, password); + result = sys_auth_passwd(ssh, password); if (authctxt->force_pwchange) - disable_forwarding(); + auth_restrict_session(ssh); return (result && ok); } @@ -170,19 +163,19 @@ warn_expiry(Authctxt *authctxt, auth_session_t *as) } int -sys_auth_passwd(Authctxt *authctxt, const char *password) +sys_auth_passwd(struct ssh *ssh, const char *password) { - struct passwd *pw = authctxt->pw; + Authctxt *authctxt = ssh->authctxt; auth_session_t *as; static int expire_checked = 0; - as = auth_usercheck(pw->pw_name, authctxt->style, "auth-ssh", + as = auth_usercheck(authctxt->pw->pw_name, authctxt->style, "auth-ssh", (char *)password); if (as == NULL) return (0); if (auth_getstate(as) & AUTH_PWEXPIRED) { auth_close(as); - disable_forwarding(); + auth_restrict_session(ssh); authctxt->force_pwchange = 1; return (1); } else { @@ -195,8 +188,9 @@ sys_auth_passwd(Authctxt *authctxt, const char *password) } #elif !defined(CUSTOM_SYS_AUTH_PASSWD) int -sys_auth_passwd(Authctxt *authctxt, const char *password) +sys_auth_passwd(struct ssh *ssh, const char *password) { + Authctxt *authctxt = ssh->authctxt; struct passwd *pw = authctxt->pw; char *encrypted_password, *salt = NULL; diff --git a/auth.c b/auth.c index fd02eff07..041a09e3f 100644 --- a/auth.c +++ b/auth.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.c,v 1.125 2018/01/08 15:21:49 markus Exp $ */ +/* $OpenBSD: auth.c,v 1.126 2018/03/03 03:15:51 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -74,12 +74,14 @@ #include "authfile.h" #include "ssherr.h" #include "compat.h" +#include "channels.h" /* import */ extern ServerOptions options; extern int use_privsep; extern Buffer loginmsg; extern struct passwd *privsep_pw; +extern struct sshauthopt *auth_opts; /* Debugging messages */ Buffer auth_debug; @@ -386,10 +388,8 @@ auth_maxtries_exceeded(Authctxt *authctxt) * Check whether root logins are disallowed. */ int -auth_root_allowed(const char *method) +auth_root_allowed(struct ssh *ssh, const char *method) { - struct ssh *ssh = active_state; /* XXX */ - switch (options.permit_root_login) { case PERMIT_YES: return 1; @@ -400,7 +400,7 @@ auth_root_allowed(const char *method) return 1; break; case PERMIT_FORCED_ONLY: - if (forced_command) { + if (auth_opts->force_command != NULL) { logit("Root login accepted for forced command."); return 1; } @@ -993,3 +993,173 @@ subprocess(const char *tag, struct passwd *pw, const char *command, *child = f; return pid; } + +/* These functions link key/cert options to the auth framework */ + +/* Log sshauthopt options locally and (optionally) for remote transmission */ +void +auth_log_authopts(const char *loc, const struct sshauthopt *opts, int do_remote) +{ + int do_env = options.permit_user_env && opts->nenv > 0; + int do_permitopen = opts->npermitopen > 0 && + (options.allow_tcp_forwarding & FORWARD_LOCAL) != 0; + size_t i; + char msg[1024], tbuf[32]; + + snprintf(tbuf, sizeof(tbuf), "%d", opts->force_tun_device); + /* Try to keep this alphabetically sorted */ + snprintf(msg, sizeof(msg), "key options:%s%s%s%s%s%s%s%s%s%s%s", + opts->permit_agent_forwarding_flag ? " agent-forwarding" : "", + opts->force_command == NULL ? "" : " command", + do_env ? " environment" : "", + do_permitopen ? " permitopen" : "", + opts->permit_port_forwarding_flag ? " port-forwarding" : "", + opts->cert_principals == NULL ? "" : " principals", + opts->permit_pty_flag ? " pty" : "", + opts->force_tun_device == -1 ? "" : " tun=", + opts->force_tun_device == -1 ? "" : tbuf, + opts->permit_user_rc ? " user-rc" : "", + opts->permit_x11_forwarding_flag ? " x11-forwarding" : ""); + + debug("%s: %s", loc, msg); + if (do_remote) + auth_debug_add("%s: %s", loc, msg); + + if (options.permit_user_env) { + for (i = 0; i < opts->nenv; i++) { + debug("%s: environment: %s", loc, opts->env[i]); + if (do_remote) { + auth_debug_add("%s: environment: %s", + loc, opts->env[i]); + } + } + } + + /* Go into a little more details for the local logs. */ + if (opts->cert_principals != NULL) { + debug("%s: authorized principals: \"%s\"", + loc, opts->cert_principals); + } + if (opts->force_command != NULL) + debug("%s: forced command: \"%s\"", loc, opts->force_command); + if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0) { + for (i = 0; i < opts->npermitopen; i++) { + debug("%s: permitted open: %s", + loc, opts->permitopen[i]); + } + } +} + +/* Activate a new set of key/cert options; merging with what is there. */ +int +auth_activate_options(struct ssh *ssh, struct sshauthopt *opts) +{ + struct sshauthopt *old = auth_opts; + const char *emsg = NULL; + + debug("%s: setting new authentication options", __func__); + if ((auth_opts = sshauthopt_merge(old, opts, &emsg)) == NULL) { + error("Inconsistent authentication options: %s", emsg); + return -1; + } + return 0; +} + +/* Disable forwarding, etc for the session */ +void +auth_restrict_session(struct ssh *ssh) +{ + struct sshauthopt *restricted; + + debug("%s: restricting session", __func__); + + /* A blank sshauthopt defaults to permitting nothing */ + restricted = sshauthopt_new(); + restricted->restricted = 1; + + if (auth_activate_options(ssh, restricted) != 0) + fatal("%s: failed to restrict session", __func__); + sshauthopt_free(restricted); +} + +int +auth_authorise_keyopts(struct ssh *ssh, struct passwd *pw, + struct sshauthopt *opts, int allow_cert_authority, const char *loc) +{ + const char *remote_ip = ssh_remote_ipaddr(ssh); + const char *remote_host = auth_get_canonical_hostname(ssh, + options.use_dns); + + /* Consistency checks */ + if (opts->cert_principals != NULL && !opts->cert_authority) { + debug("%s: principals on non-CA key", loc); + auth_debug_add("%s: principals on non-CA key", loc); + /* deny access */ + return -1; + } + /* cert-authority flag isn't valid in authorized_principals files */ + if (!allow_cert_authority && opts->cert_authority) { + debug("%s: cert-authority flag invalid here", loc); + auth_debug_add("%s: cert-authority flag invalid here", loc); + /* deny access */ + return -1; + } + + /* Perform from= checks */ + if (opts->required_from_host_keys != NULL) { + switch (match_host_and_ip(remote_host, remote_ip, + opts->required_from_host_keys )) { + case 1: + /* Host name matches. */ + break; + case -1: + default: + debug("%s: invalid from criteria", loc); + auth_debug_add("%s: invalid from criteria", loc); + /* FALLTHROUGH */ + case 0: + logit("%s: Authentication tried for %.100s with " + "correct key but not from a permitted " + "host (host=%.200s, ip=%.200s, required=%.200s).", + loc, pw->pw_name, remote_host, remote_ip, + opts->required_from_host_keys); + auth_debug_add("%s: Your host '%.200s' is not " + "permitted to use this key for login.", + loc, remote_host); + /* deny access */ + return -1; + } + } + /* Check source-address restriction from certificate */ + if (opts->required_from_host_cert != NULL) { + switch (addr_match_cidr_list(remote_ip, + opts->required_from_host_cert)) { + case 1: + /* accepted */ + break; + case -1: + default: + /* invalid */ + error("%s: Certificate source-address invalid", + loc); + /* FALLTHROUGH */ + case 0: + logit("%s: Authentication tried for %.100s with valid " + "certificate but not from a permitted source " + "address (%.200s).", loc, pw->pw_name, remote_ip); + auth_debug_add("%s: Your address '%.200s' is not " + "permitted to use this certificate for login.", + loc, remote_ip); + return -1; + } + } + /* + * + * XXX this is spammy. We should report remotely only for keys + * that are successful in actual auth attempts, and not PK_OK + * tests. + */ + auth_log_authopts(loc, opts, 1); + + return 0; +} diff --git a/auth.h b/auth.h index 64f3c2eb5..23ce67caf 100644 --- a/auth.h +++ b/auth.h @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.h,v 1.94 2018/01/08 15:21:49 markus Exp $ */ +/* $OpenBSD: auth.h,v 1.95 2018/03/03 03:15:51 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -42,9 +42,11 @@ #include #endif +struct passwd; struct ssh; -struct sshkey; struct sshbuf; +struct sshkey; +struct sshauthopt; typedef struct Authctxt Authctxt; typedef struct Authmethod Authmethod; @@ -128,11 +130,12 @@ struct KbdintDevice int auth_rhosts2(struct passwd *, const char *, const char *, const char *); -int auth_password(Authctxt *, const char *); +int auth_password(struct ssh *, const char *); int hostbased_key_allowed(struct passwd *, const char *, char *, struct sshkey *); -int user_key_allowed(struct passwd *, struct sshkey *, int); +int user_key_allowed(struct ssh *, struct passwd *, struct sshkey *, int, + struct sshauthopt **); int auth2_key_already_used(Authctxt *, const struct sshkey *); /* @@ -163,14 +166,12 @@ int auth_shadow_pwexpired(Authctxt *); #include "audit.h" void remove_kbdint_device(const char *); -void disable_forwarding(void); - void do_authentication2(Authctxt *); void auth_log(Authctxt *, int, int, const char *, const char *); void auth_maxtries_exceeded(Authctxt *) __attribute__((noreturn)); void userauth_finish(struct ssh *, int, const char *, const char *); -int auth_root_allowed(const char *); +int auth_root_allowed(struct ssh *, const char *); void userauth_send_banner(const char *); @@ -214,8 +215,17 @@ int get_hostkey_index(struct sshkey *, int, struct ssh *); int sshd_hostkey_sign(struct sshkey *, struct sshkey *, u_char **, size_t *, const u_char *, size_t, const char *, u_int); +/* Key / cert options linkage to auth layer */ +const struct sshauthopt *auth_options(struct ssh *); +int auth_activate_options(struct ssh *, struct sshauthopt *); +void auth_restrict_session(struct ssh *); +int auth_authorise_keyopts(struct ssh *, struct passwd *pw, + struct sshauthopt *, int, const char *); +void auth_log_authopts(const char *, const struct sshauthopt *, int); + /* debug messages during authentication */ -void auth_debug_add(const char *fmt,...) __attribute__((format(printf, 1, 2))); +void auth_debug_add(const char *fmt,...) + __attribute__((format(printf, 1, 2))); void auth_debug_send(void); void auth_debug_reset(void); @@ -227,7 +237,7 @@ struct passwd *fakepw(void); pid_t subprocess(const char *, struct passwd *, const char *, int, char **, FILE **, u_int flags); -int sys_auth_passwd(Authctxt *, const char *); +int sys_auth_passwd(struct ssh *, const char *); #define SKEY_PROMPT "\nS/Key Password: " diff --git a/auth2-none.c b/auth2-none.c index 35d25fa63..8d4e9bb8c 100644 --- a/auth2-none.c +++ b/auth2-none.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-none.c,v 1.20 2017/05/30 14:29:59 markus Exp $ */ +/* $OpenBSD: auth2-none.c,v 1.21 2018/03/03 03:15:51 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -68,7 +68,7 @@ userauth_none(struct ssh *ssh) if ((r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); if (options.permit_empty_passwd && options.password_authentication) - return (PRIVSEP(auth_password(ssh->authctxt, ""))); + return (PRIVSEP(auth_password(ssh, ""))); return (0); } diff --git a/auth2-passwd.c b/auth2-passwd.c index 5f7ba3244..445016aec 100644 --- a/auth2-passwd.c +++ b/auth2-passwd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-passwd.c,v 1.14 2017/05/30 14:29:59 markus Exp $ */ +/* $OpenBSD: auth2-passwd.c,v 1.15 2018/03/03 03:15:51 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -63,7 +63,7 @@ userauth_passwd(struct ssh *ssh) if (change) logit("password change not supported"); - else if (PRIVSEP(auth_password(ssh->authctxt, password)) == 1) + else if (PRIVSEP(auth_password(ssh, password)) == 1) authenticated = 1; explicit_bzero(password, len); free(password); diff --git a/auth2-pubkey.c b/auth2-pubkey.c index 8fb7ffe71..8024b1d6a 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-pubkey.c,v 1.76 2018/02/07 22:52:45 dtucker Exp $ */ +/* $OpenBSD: auth2-pubkey.c,v 1.77 2018/03/03 03:15:51 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -88,6 +88,7 @@ static int userauth_pubkey(struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; + struct passwd *pw = authctxt->pw; struct sshbuf *b; struct sshkey *key = NULL; char *pkalg, *userstyle = NULL, *key_s = NULL, *ca_s = NULL; @@ -95,6 +96,7 @@ userauth_pubkey(struct ssh *ssh) size_t blen, slen; int r, pktype; int authenticated = 0; + struct sshauthopt *authopts = NULL; if (!authctxt->valid) { debug2("%s: disabled because of invalid user", __func__); @@ -185,7 +187,7 @@ userauth_pubkey(struct ssh *ssh) /* test for correct signature */ authenticated = 0; - if (PRIVSEP(user_key_allowed(authctxt->pw, key, 1)) && + if (PRIVSEP(user_key_allowed(ssh, pw, key, 1, &authopts)) && PRIVSEP(sshkey_verify(key, sig, slen, sshbuf_ptr(b), sshbuf_len(b), NULL, ssh->compat)) == 0) { authenticated = 1; @@ -210,7 +212,7 @@ userauth_pubkey(struct ssh *ssh) * if a user is not allowed to login. is this an * issue? -markus */ - if (PRIVSEP(user_key_allowed(authctxt->pw, key, 0))) { + if (PRIVSEP(user_key_allowed(ssh, pw, key, 0, NULL))) { if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_PK_OK)) != 0 || (r = sshpkt_put_cstring(ssh, pkalg)) != 0 || @@ -221,10 +223,14 @@ userauth_pubkey(struct ssh *ssh) authctxt->postponed = 1; } } - if (authenticated != 1) - auth_clear_options(); done: + if (authenticated == 1 && auth_activate_options(ssh, authopts) != 0) { + debug("%s: key options inconsistent with existing", __func__); + authenticated = 0; + } debug2("%s: authenticated %d pkalg %s", __func__, authenticated, pkalg); + + sshauthopt_free(authopts); sshkey_free(key); free(userstyle); free(pkalg); @@ -254,18 +260,77 @@ match_principals_option(const char *principal_list, struct sshkey_cert *cert) return 0; } +/* + * Process a single authorized_principals format line. Returns 0 and sets + * authoptsp is principal is authorised, -1 otherwise. "loc" is used as a + * log preamble for file/line information. + */ +static int +check_principals_line(struct ssh *ssh, char *cp, const struct sshkey_cert *cert, + const char *loc, struct sshauthopt **authoptsp) +{ + u_int i, found = 0; + char *ep, *line_opts; + const char *reason = NULL; + struct sshauthopt *opts = NULL; + + if (authoptsp != NULL) + *authoptsp = NULL; + + /* Trim trailing whitespace. */ + ep = cp + strlen(cp) - 1; + while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t')) + *ep-- = '\0'; + + /* + * If the line has internal whitespace then assume it has + * key options. + */ + line_opts = NULL; + if ((ep = strrchr(cp, ' ')) != NULL || + (ep = strrchr(cp, '\t')) != NULL) { + for (; *ep == ' ' || *ep == '\t'; ep++) + ; + line_opts = cp; + cp = ep; + } + if ((opts = sshauthopt_parse(line_opts, &reason)) == NULL) { + debug("%s: bad principals options: %s", loc, reason); + auth_debug_add("%s: bad principals options: %s", loc, reason); + return -1; + } + /* Check principals in cert against those on line */ + for (i = 0; i < cert->nprincipals; i++) { + if (strcmp(cp, cert->principals[i]) != 0) + continue; + debug3("%s: matched principal \"%.100s\"", + loc, cert->principals[i]); + found = 1; + } + if (found && authoptsp != NULL) { + *authoptsp = opts; + opts = NULL; + } + sshauthopt_free(opts); + return found ? 0 : -1; +} + static int -process_principals(FILE *f, const char *file, struct passwd *pw, - const struct sshkey_cert *cert) +process_principals(struct ssh *ssh, FILE *f, const char *file, + const struct sshkey_cert *cert, struct sshauthopt **authoptsp) { - char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts; + char loc[256], line[SSH_MAX_PUBKEY_BYTES], *cp, *ep; u_long linenum = 0; - u_int i, found_principal = 0; + u_int found_principal = 0; + + if (authoptsp != NULL) + *authoptsp = NULL; while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { /* Always consume entire input */ if (found_principal) continue; + /* Skip leading whitespace. */ for (cp = line; *cp == ' ' || *cp == '\t'; cp++) ; @@ -274,50 +339,33 @@ process_principals(FILE *f, const char *file, struct passwd *pw, *ep = '\0'; if (!*cp || *cp == '\n') continue; - /* Trim trailing whitespace. */ - ep = cp + strlen(cp) - 1; - while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t')) - *ep-- = '\0'; - /* - * If the line has internal whitespace then assume it has - * key options. - */ - line_opts = NULL; - if ((ep = strrchr(cp, ' ')) != NULL || - (ep = strrchr(cp, '\t')) != NULL) { - for (; *ep == ' ' || *ep == '\t'; ep++) - ; - line_opts = cp; - cp = ep; - } - for (i = 0; i < cert->nprincipals; i++) { - if (strcmp(cp, cert->principals[i]) == 0) { - debug3("%s:%lu: matched principal \"%.100s\"", - file, linenum, cert->principals[i]); - if (auth_parse_options(pw, line_opts, - file, linenum) != 1) - continue; - found_principal = 1; - continue; - } - } + + snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum); + if (check_principals_line(ssh, cp, cert, loc, authoptsp) == 0) + found_principal = 1; } return found_principal; } +/* XXX remove pw args here and elsewhere once ssh->authctxt is guaranteed */ + static int -match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert) +match_principals_file(struct ssh *ssh, struct passwd *pw, char *file, + struct sshkey_cert *cert, struct sshauthopt **authoptsp) { FILE *f; int success; + if (authoptsp != NULL) + *authoptsp = NULL; + temporarily_use_uid(pw); debug("trying authorized principals file %s", file); if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) { restore_uid(); return 0; } - success = process_principals(f, file, pw, cert); + success = process_principals(ssh, f, file, cert, authoptsp); fclose(f); restore_uid(); return success; @@ -328,12 +376,13 @@ match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert) * returns 1 if the principal is allowed or 0 otherwise. */ static int -match_principals_command(struct passwd *user_pw, const struct sshkey *key) +match_principals_command(struct ssh *ssh, struct passwd *user_pw, + const struct sshkey *key, struct sshauthopt **authoptsp) { + struct passwd *runas_pw = NULL; const struct sshkey_cert *cert = key->cert; FILE *f = NULL; int r, ok, found_principal = 0; - struct passwd *pw; int i, ac = 0, uid_swapped = 0; pid_t pid; char *tmp, *username = NULL, *command = NULL, **av = NULL; @@ -341,6 +390,8 @@ match_principals_command(struct passwd *user_pw, const struct sshkey *key) char serial_s[16]; void (*osigchld)(int); + if (authoptsp != NULL) + *authoptsp = NULL; if (options.authorized_principals_command == NULL) return 0; if (options.authorized_principals_command_user == NULL) { @@ -358,8 +409,8 @@ match_principals_command(struct passwd *user_pw, const struct sshkey *key) /* Prepare and verify the user for the command */ username = percent_expand(options.authorized_principals_command_user, "u", user_pw->pw_name, (char *)NULL); - pw = getpwnam(username); - if (pw == NULL) { + runas_pw = getpwnam(username); + if (runas_pw == NULL) { error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s", username, strerror(errno)); goto out; @@ -417,15 +468,15 @@ match_principals_command(struct passwd *user_pw, const struct sshkey *key) /* Prepare a printable command for logs, etc. */ command = argv_assemble(ac, av); - if ((pid = subprocess("AuthorizedPrincipalsCommand", pw, command, + if ((pid = subprocess("AuthorizedPrincipalsCommand", runas_pw, command, ac, av, &f, SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD)) == 0) goto out; uid_swapped = 1; - temporarily_use_uid(pw); + temporarily_use_uid(runas_pw); - ok = process_principals(f, "(command)", pw, cert); + ok = process_principals(ssh, f, "(command)", cert, authoptsp); fclose(f); f = NULL; @@ -452,130 +503,225 @@ match_principals_command(struct passwd *user_pw, const struct sshkey *key) free(keytext); return found_principal; } + +static void +skip_space(char **cpp) +{ + char *cp; + + for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) + ; + *cpp = cp; +} + +/* + * Advanced *cpp past the end of key options, defined as the first unquoted + * whitespace character. Returns 0 on success or -1 on failure (e.g. + * unterminated quotes). + */ +static int +advance_past_options(char **cpp) +{ + char *cp = *cpp; + int quoted = 0; + + for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { + if (*cp == '\\' && cp[1] == '"') + cp++; /* Skip both */ + else if (*cp == '"') + quoted = !quoted; + } + *cpp = cp; + /* return failure for unterminated quotes */ + return (*cp == '\0' && quoted) ? -1 : 0; +} + +/* + * Check a single line of an authorized_keys-format file. Returns 0 if key + * matches, -1 otherwise. Will return key/cert options via *authoptsp + * on success. "loc" is used as file/line location in log messages. + */ +static int +check_authkey_line(struct ssh *ssh, struct passwd *pw, struct sshkey *key, + char *cp, const char *loc, struct sshauthopt **authoptsp) +{ + int want_keytype = sshkey_is_cert(key) ? KEY_UNSPEC : key->type; + struct sshkey *found = NULL; + struct sshauthopt *keyopts = NULL, *certopts = NULL, *finalopts = NULL; + char *key_options = NULL, *fp = NULL; + const char *reason = NULL; + int ret = -1; + + if (authoptsp != NULL) + *authoptsp = NULL; + + if ((found = sshkey_new(want_keytype)) == NULL) { + debug3("%s: keytype %d failed", __func__, want_keytype); + goto out; + } + + /* XXX djm: peek at key type in line and skip if unwanted */ + + if (sshkey_read(found, &cp) != 0) { + /* no key? check for options */ + debug2("%s: check options: '%s'", loc, cp); + key_options = cp; + if (advance_past_options(&cp) != 0) { + reason = "invalid key option string"; + goto fail_reason; + } + skip_space(&cp); + if (sshkey_read(found, &cp) != 0) { + /* still no key? advance to next line*/ + debug2("%s: advance: '%s'", loc, cp); + goto out; + } + } + /* Parse key options now; we need to know if this is a CA key */ + if ((keyopts = sshauthopt_parse(key_options, &reason)) == NULL) { + debug("%s: bad key options: %s", loc, reason); + auth_debug_add("%s: bad key options: %s", loc, reason); + goto out; + } + /* Ignore keys that don't match or incorrectly marked as CAs */ + if (sshkey_is_cert(key)) { + /* Certificate; check signature key against CA */ + if (!sshkey_equal(found, key->cert->signature_key) || + !keyopts->cert_authority) + goto out; + } else { + /* Plain key: check it against key found in file */ + if (!sshkey_equal(found, key) || keyopts->cert_authority) + goto out; + } + + /* We have a candidate key, perform authorisation checks */ + if ((fp = sshkey_fingerprint(found, + options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) + fatal("%s: fingerprint failed", __func__); + + debug("%s: matching %s found: %s %s", loc, + sshkey_is_cert(key) ? "CA" : "key", sshkey_type(found), fp); + + if (auth_authorise_keyopts(ssh, pw, keyopts, + sshkey_is_cert(key), loc) != 0) { + reason = "Refused by key options"; + goto fail_reason; + } + /* That's all we need for plain keys. */ + if (!sshkey_is_cert(key)) { + verbose("Accepted key %s %s found at %s", + sshkey_type(found), fp, loc); + finalopts = keyopts; + keyopts = NULL; + goto success; + } + + /* + * Additional authorisation for certificates. + */ + + /* Parse and check options present in certificate */ + if ((certopts = sshauthopt_from_cert(key)) == NULL) { + reason = "Invalid certificate options"; + goto fail_reason; + } + if (auth_authorise_keyopts(ssh, pw, certopts, 0, loc) != 0) { + reason = "Refused by certificate options"; + goto fail_reason; + } + if ((finalopts = sshauthopt_merge(keyopts, certopts, &reason)) == NULL) + goto fail_reason; + + /* + * If the user has specified a list of principals as + * a key option, then prefer that list to matching + * their username in the certificate principals list. + */ + if (keyopts->cert_principals != NULL && + !match_principals_option(keyopts->cert_principals, key->cert)) { + reason = "Certificate does not contain an authorized principal"; + goto fail_reason; + } + if (sshkey_cert_check_authority(key, 0, 0, + keyopts->cert_principals == NULL ? pw->pw_name : NULL, &reason) != 0) + goto fail_reason; + + verbose("Accepted certificate ID \"%s\" (serial %llu) " + "signed by CA %s %s found at %s", + key->cert->key_id, + (unsigned long long)key->cert->serial, + sshkey_type(found), fp, loc); + + success: + if (finalopts == NULL) + fatal("%s: internal error: missing options", __func__); + if (authoptsp != NULL) { + *authoptsp = finalopts; + finalopts = NULL; + } + /* success */ + ret = 0; + goto out; + + fail_reason: + error("%s", reason); + auth_debug_add("%s", reason); + out: + free(fp); + sshauthopt_free(keyopts); + sshauthopt_free(certopts); + sshauthopt_free(finalopts); + sshkey_free(found); + return ret; +} + /* * Checks whether key is allowed in authorized_keys-format file, * returns 1 if the key is allowed or 0 otherwise. */ static int -check_authkeys_file(FILE *f, char *file, struct sshkey *key, struct passwd *pw) +check_authkeys_file(struct ssh *ssh, struct passwd *pw, FILE *f, + char *file, struct sshkey *key, struct sshauthopt **authoptsp) { - char line[SSH_MAX_PUBKEY_BYTES]; + char *cp, line[SSH_MAX_PUBKEY_BYTES], loc[256]; int found_key = 0; u_long linenum = 0; - struct sshkey *found = NULL; - while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { - char *cp, *key_options = NULL, *fp = NULL; - const char *reason = NULL; + if (authoptsp != NULL) + *authoptsp = NULL; + while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { /* Always consume entire file */ if (found_key) continue; - sshkey_free(found); - found = sshkey_new(sshkey_is_cert(key) ? KEY_UNSPEC : key->type); - if (found == NULL) - goto done; - auth_clear_options(); /* Skip leading whitespace, empty and comment lines. */ - for (cp = line; *cp == ' ' || *cp == '\t'; cp++) - ; + cp = line; + skip_space(&cp); if (!*cp || *cp == '\n' || *cp == '#') continue; - - if (sshkey_read(found, &cp) != 0) { - /* no key? check if there are options for this key */ - int quoted = 0; - debug2("user_key_allowed: check options: '%s'", cp); - key_options = cp; - for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { - if (*cp == '\\' && cp[1] == '"') - cp++; /* Skip both */ - else if (*cp == '"') - quoted = !quoted; - } - /* Skip remaining whitespace. */ - for (; *cp == ' ' || *cp == '\t'; cp++) - ; - if (sshkey_read(found, &cp) != 0) { - debug2("user_key_allowed: advance: '%s'", cp); - /* still no key? advance to next line*/ - continue; - } - } - if (sshkey_is_cert(key)) { - if (!sshkey_equal(found, key->cert->signature_key)) - continue; - if (auth_parse_options(pw, key_options, file, - linenum) != 1) - continue; - if (!key_is_cert_authority) - continue; - if ((fp = sshkey_fingerprint(found, - options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) - continue; - debug("matching CA found: file %s, line %lu, %s %s", - file, linenum, sshkey_type(found), fp); - /* - * If the user has specified a list of principals as - * a key option, then prefer that list to matching - * their username in the certificate principals list. - */ - if (authorized_principals != NULL && - !match_principals_option(authorized_principals, - key->cert)) { - reason = "Certificate does not contain an " - "authorized principal"; - fail_reason: - free(fp); - error("%s", reason); - auth_debug_add("%s", reason); - continue; - } - if (sshkey_cert_check_authority(key, 0, 0, - authorized_principals == NULL ? pw->pw_name : NULL, - &reason) != 0) - goto fail_reason; - if (auth_cert_options(key, pw, &reason) != 0) - goto fail_reason; - verbose("Accepted certificate ID \"%s\" (serial %llu) " - "signed by %s CA %s via %s", key->cert->key_id, - (unsigned long long)key->cert->serial, - sshkey_type(found), fp, file); - free(fp); - found_key = 1; - break; - } else if (sshkey_equal(found, key)) { - if (auth_parse_options(pw, key_options, file, - linenum) != 1) - continue; - if (key_is_cert_authority) - continue; - if ((fp = sshkey_fingerprint(found, - options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) - continue; - debug("matching key found: file %s, line %lu %s %s", - file, linenum, sshkey_type(found), fp); - free(fp); + snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum); + if (check_authkey_line(ssh, pw, key, cp, loc, authoptsp) == 0) found_key = 1; - continue; - } } - done: - sshkey_free(found); - if (!found_key) - debug2("key not found"); return found_key; } /* Authenticate a certificate key against TrustedUserCAKeys */ static int -user_cert_trusted_ca(struct passwd *pw, struct sshkey *key) +user_cert_trusted_ca(struct ssh *ssh, struct passwd *pw, struct sshkey *key, + struct sshauthopt **authoptsp) { char *ca_fp, *principals_file = NULL; const char *reason; + struct sshauthopt *principals_opts = NULL, *cert_opts = NULL; + struct sshauthopt *final_opts = NULL; int r, ret = 0, found_principal = 0, use_authorized_principals; + if (authoptsp != NULL) + *authoptsp = NULL; + if (!sshkey_is_cert(key) || options.trusted_user_ca_keys == NULL) return 0; @@ -596,36 +742,69 @@ user_cert_trusted_ca(struct passwd *pw, struct sshkey *key) * against the username. */ if ((principals_file = authorized_principals_file(pw)) != NULL) { - if (match_principals_file(principals_file, pw, key->cert)) + if (match_principals_file(ssh, pw, principals_file, + key->cert, &principals_opts)) found_principal = 1; } /* Try querying command if specified */ - if (!found_principal && match_principals_command(pw, key)) + if (!found_principal && match_principals_command(ssh, pw, key, + &principals_opts)) found_principal = 1; /* If principals file or command is specified, then require a match */ use_authorized_principals = principals_file != NULL || options.authorized_principals_command != NULL; if (!found_principal && use_authorized_principals) { reason = "Certificate does not contain an authorized principal"; - fail_reason: - error("%s", reason); - auth_debug_add("%s", reason); - goto out; + goto fail_reason; } + if (use_authorized_principals && principals_opts == NULL) + fatal("%s: internal error: missing principals_opts", __func__); if (sshkey_cert_check_authority(key, 0, 1, use_authorized_principals ? NULL : pw->pw_name, &reason) != 0) goto fail_reason; - if (auth_cert_options(key, pw, &reason) != 0) + + /* Check authority from options in key and from principals file/cmd */ + if ((cert_opts = sshauthopt_from_cert(key)) == NULL) { + reason = "Invalid certificate options"; + goto fail_reason; + } + if (auth_authorise_keyopts(ssh, pw, cert_opts, 0, "cert") != 0) { + reason = "Refused by certificate options"; goto fail_reason; + } + if (principals_opts == NULL) { + final_opts = cert_opts; + cert_opts = NULL; + } else { + if (auth_authorise_keyopts(ssh, pw, principals_opts, 0, + "principals") != 0) { + reason = "Refused by certificate principals options"; + goto fail_reason; + } + if ((final_opts = sshauthopt_merge(principals_opts, + cert_opts, &reason)) == NULL) { + fail_reason: + error("%s", reason); + auth_debug_add("%s", reason); + goto out; + } + } + /* Success */ verbose("Accepted certificate ID \"%s\" (serial %llu) signed by " "%s CA %s via %s", key->cert->key_id, (unsigned long long)key->cert->serial, sshkey_type(key->cert->signature_key), ca_fp, options.trusted_user_ca_keys); + if (authoptsp != NULL) { + *authoptsp = final_opts; + final_opts = NULL; + } ret = 1; - out: + sshauthopt_free(principals_opts); + sshauthopt_free(cert_opts); + sshauthopt_free(final_opts); free(principals_file); free(ca_fp); return ret; @@ -636,17 +815,22 @@ user_cert_trusted_ca(struct passwd *pw, struct sshkey *key) * returns 1 if the key is allowed or 0 otherwise. */ static int -user_key_allowed2(struct passwd *pw, struct sshkey *key, char *file) +user_key_allowed2(struct ssh *ssh, struct passwd *pw, struct sshkey *key, + char *file, struct sshauthopt **authoptsp) { FILE *f; int found_key = 0; + if (authoptsp != NULL) + *authoptsp = NULL; + /* Temporarily use the user's uid. */ temporarily_use_uid(pw); debug("trying public key file %s", file); if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) { - found_key = check_authkeys_file(f, file, key, pw); + found_key = check_authkeys_file(ssh, pw, f, file, + key, authoptsp); fclose(f); } @@ -659,17 +843,20 @@ user_key_allowed2(struct passwd *pw, struct sshkey *key, char *file) * returns 1 if the key is allowed or 0 otherwise. */ static int -user_key_command_allowed2(struct passwd *user_pw, struct sshkey *key) +user_key_command_allowed2(struct ssh *ssh, struct passwd *user_pw, + struct sshkey *key, struct sshauthopt **authoptsp) { + struct passwd *runas_pw = NULL; FILE *f = NULL; int r, ok, found_key = 0; - struct passwd *pw; int i, uid_swapped = 0, ac = 0; pid_t pid; char *username = NULL, *key_fp = NULL, *keytext = NULL; char *tmp, *command = NULL, **av = NULL; void (*osigchld)(int); + if (authoptsp != NULL) + *authoptsp = NULL; if (options.authorized_keys_command == NULL) return 0; if (options.authorized_keys_command_user == NULL) { @@ -686,8 +873,8 @@ user_key_command_allowed2(struct passwd *user_pw, struct sshkey *key) /* Prepare and verify the user for the command */ username = percent_expand(options.authorized_keys_command_user, "u", user_pw->pw_name, (char *)NULL); - pw = getpwnam(username); - if (pw == NULL) { + runas_pw = getpwnam(username); + if (runas_pw == NULL) { error("AuthorizedKeysCommandUser \"%s\" not found: %s", username, strerror(errno)); goto out; @@ -745,15 +932,16 @@ user_key_command_allowed2(struct passwd *user_pw, struct sshkey *key) xasprintf(&command, "%s %s", av[0], av[1]); } - if ((pid = subprocess("AuthorizedKeysCommand", pw, command, + if ((pid = subprocess("AuthorizedKeysCommand", runas_pw, command, ac, av, &f, SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD)) == 0) goto out; uid_swapped = 1; - temporarily_use_uid(pw); + temporarily_use_uid(runas_pw); - ok = check_authkeys_file(f, options.authorized_keys_command, key, pw); + ok = check_authkeys_file(ssh, user_pw, f, + options.authorized_keys_command, key, authoptsp); fclose(f); f = NULL; @@ -783,10 +971,14 @@ user_key_command_allowed2(struct passwd *user_pw, struct sshkey *key) * Check whether key authenticates and authorises the user. */ int -user_key_allowed(struct passwd *pw, struct sshkey *key, int auth_attempt) +user_key_allowed(struct ssh *ssh, struct passwd *pw, struct sshkey *key, + int auth_attempt, struct sshauthopt **authoptsp) { u_int success, i; char *file; + struct sshauthopt *opts = NULL; + if (authoptsp != NULL) + *authoptsp = NULL; if (auth_key_is_revoked(key)) return 0; @@ -794,25 +986,31 @@ user_key_allowed(struct passwd *pw, struct sshkey *key, int auth_attempt) auth_key_is_revoked(key->cert->signature_key)) return 0; - success = user_cert_trusted_ca(pw, key); - if (success) - return success; + if ((success = user_cert_trusted_ca(ssh, pw, key, &opts)) != 0) + goto out; + sshauthopt_free(opts); + opts = NULL; - success = user_key_command_allowed2(pw, key); - if (success > 0) - return success; + if ((success = user_key_command_allowed2(ssh, pw, key, &opts)) != 0) + goto out; + sshauthopt_free(opts); + opts = NULL; for (i = 0; !success && i < options.num_authkeys_files; i++) { - if (strcasecmp(options.authorized_keys_files[i], "none") == 0) continue; file = expand_authorized_keys( options.authorized_keys_files[i], pw); - - success = user_key_allowed2(pw, key, file); + success = user_key_allowed2(ssh, pw, key, file, &opts); free(file); } + out: + if (success && authoptsp != NULL) { + *authoptsp = opts; + opts = NULL; + } + sshauthopt_free(opts); return success; } diff --git a/auth2.c b/auth2.c index c80911aeb..e0034229a 100644 --- a/auth2.c +++ b/auth2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2.c,v 1.144 2018/01/23 05:27:21 djm Exp $ */ +/* $OpenBSD: auth2.c,v 1.145 2018/03/03 03:15:51 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -310,7 +310,7 @@ userauth_finish(struct ssh *ssh, int authenticated, const char *method, /* Special handling for root */ if (authenticated && authctxt->pw->pw_uid == 0 && - !auth_root_allowed(method)) { + !auth_root_allowed(ssh, method)) { authenticated = 0; #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED)); diff --git a/misc.c b/misc.c index b45a9ec63..1e660b021 100644 --- a/misc.c +++ b/misc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.c,v 1.124 2018/03/02 03:02:11 djm Exp $ */ +/* $OpenBSD: misc.c,v 1.125 2018/03/03 03:15:51 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2005,2006 Damien Miller. All rights reserved. @@ -1921,6 +1921,7 @@ child_set_env(char ***envp, u_int *envsizep, const char *name, } /* Allocate space and format the variable in the appropriate slot. */ + /* XXX xasprintf */ env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1); snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); } diff --git a/monitor.c b/monitor.c index e4ac3ccfd..c68e1b0d9 100644 --- a/monitor.c +++ b/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.179 2018/02/05 05:37:46 tb Exp $ */ +/* $OpenBSD: monitor.c,v 1.180 2018/03/03 03:15:51 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -116,6 +116,7 @@ extern u_char session_id[]; extern Buffer auth_debug; extern int auth_debug_init; extern Buffer loginmsg; +extern struct sshauthopt *auth_opts; /* XXX move to permanent ssh->authctxt? */ /* State exported from the child */ static struct sshbuf *child_state; @@ -172,6 +173,7 @@ static Authctxt *authctxt; static u_char *key_blob = NULL; static u_int key_bloblen = 0; static int key_blobtype = MM_NOKEY; +static struct sshauthopt *key_opts = NULL; static char *hostbased_cuser = NULL; static char *hostbased_chost = NULL; static char *auth_method = "unknown"; @@ -252,7 +254,6 @@ struct mon_table mon_dispatch_postauth20[] = { struct mon_table *mon_dispatch; /* Specifies if a certain message is allowed at the moment */ - static void monitor_permit(struct mon_table *ent, enum monitor_reqtype type, int permit) { @@ -297,6 +298,7 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor) authctxt = _authctxt; memset(authctxt, 0, sizeof(*authctxt)); + ssh->authctxt = authctxt; authctxt->loginmsg = &loginmsg; @@ -331,7 +333,7 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor) fatal("%s: unexpected authentication from %d", __func__, ent->type); if (authctxt->pw->pw_uid == 0 && - !auth_root_allowed(auth_method)) + !auth_root_allowed(ssh, auth_method)) authenticated = 0; #ifdef USE_PAM /* PAM needs to perform account checks after auth */ @@ -365,6 +367,7 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor) debug("%s: %s has been authenticated by privileged process", __func__, authctxt->user); + ssh->authctxt = NULL; ssh_packet_set_log_preamble(ssh, "user %s", authctxt->user); mm_get_keystate(pmonitor); @@ -413,7 +416,7 @@ monitor_child_postauth(struct monitor *pmonitor) monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); - if (!no_pty_flag) { + if (auth_opts->permit_pty_flag) { monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); monitor_permit(mon_dispatch, MONITOR_REQ_PTYCLEANUP, 1); } @@ -558,9 +561,11 @@ monitor_reset_key_state(void) free(key_blob); free(hostbased_cuser); free(hostbased_chost); + sshauthopt_free(key_opts); key_blob = NULL; key_bloblen = 0; key_blobtype = MM_NOKEY; + key_opts = NULL; hostbased_cuser = NULL; hostbased_chost = NULL; } @@ -828,6 +833,7 @@ mm_answer_authserv(int sock, Buffer *m) int mm_answer_authpassword(int sock, Buffer *m) { + struct ssh *ssh = active_state; /* XXX */ static int call_count; char *passwd; int authenticated; @@ -838,7 +844,7 @@ mm_answer_authpassword(int sock, Buffer *m) passwd = buffer_get_string(m, &plen); /* Only authenticate if the context is valid */ authenticated = options.password_authentication && - auth_password(authctxt, passwd); + auth_password(ssh, passwd); explicit_bzero(passwd, strlen(passwd)); free(passwd); @@ -1129,15 +1135,16 @@ mm_answer_pam_free_ctx(int sock, Buffer *m) int mm_answer_keyallowed(int sock, Buffer *m) { + struct ssh *ssh = active_state; /* XXX */ struct sshkey *key; char *cuser, *chost; u_char *blob; u_int bloblen, pubkey_auth_attempt; enum mm_keytype type = 0; - int allowed = 0; + int r, allowed = 0; + struct sshauthopt *opts = NULL; debug3("%s entering", __func__); - type = buffer_get_int(m); cuser = buffer_get_string(m, NULL); chost = buffer_get_string(m, NULL); @@ -1156,28 +1163,31 @@ mm_answer_keyallowed(int sock, Buffer *m) switch (type) { case MM_USERKEY: - allowed = options.pubkey_authentication && - !auth2_key_already_used(authctxt, key) && - match_pattern_list(sshkey_ssh_name(key), - options.pubkey_key_types, 0) == 1 && - user_key_allowed(authctxt->pw, key, - pubkey_auth_attempt); auth_method = "publickey"; - if (options.pubkey_authentication && - (!pubkey_auth_attempt || allowed != 1)) - auth_clear_options(); + if (!options.pubkey_authentication) + break; + if (auth2_key_already_used(authctxt, key)) + break; + if (match_pattern_list(sshkey_ssh_name(key), + options.pubkey_key_types, 0) != 1) + break; + allowed = user_key_allowed(ssh, authctxt->pw, key, + pubkey_auth_attempt, &opts); break; case MM_HOSTKEY: - allowed = options.hostbased_authentication && - !auth2_key_already_used(authctxt, key) && - match_pattern_list(sshkey_ssh_name(key), - options.hostbased_key_types, 0) == 1 && - hostbased_key_allowed(authctxt->pw, + auth_method = "hostbased"; + if (!options.hostbased_authentication) + break; + if (auth2_key_already_used(authctxt, key)) + break; + if (match_pattern_list(sshkey_ssh_name(key), + options.hostbased_key_types, 0) != 1) + break; + allowed = hostbased_key_allowed(authctxt->pw, cuser, chost, key); auth2_record_info(authctxt, "client user \"%.100s\", client host \"%.100s\"", cuser, chost); - auth_method = "hostbased"; break; default: fatal("%s: unknown key type %d", __func__, type); @@ -1185,7 +1195,10 @@ mm_answer_keyallowed(int sock, Buffer *m) } } - debug3("%s: key is %s", __func__, allowed ? "allowed" : "not allowed"); + debug3("%s: %s authentication%s: %s key is %s", __func__, + auth_method, pubkey_auth_attempt ? "" : " test", + (key == NULL || !authctxt->valid) ? "invalid" : sshkey_type(key), + allowed ? "allowed" : "not allowed"); auth2_record_key(authctxt, 0, key); sshkey_free(key); @@ -1198,6 +1211,7 @@ mm_answer_keyallowed(int sock, Buffer *m) key_blob = blob; key_bloblen = bloblen; key_blobtype = type; + key_opts = opts; hostbased_cuser = cuser; hostbased_chost = chost; } else { @@ -1210,10 +1224,13 @@ mm_answer_keyallowed(int sock, Buffer *m) buffer_clear(m); buffer_put_int(m, allowed); - buffer_put_int(m, forced_command != NULL); - + if (opts != NULL && (r = sshauthopt_serialise(opts, m, 1)) != 0) + fatal("%s: sshauthopt_serialise: %s", __func__, ssh_err(r)); mm_request_send(sock, MONITOR_ANS_KEYALLOWED, m); + if (!allowed) + sshauthopt_free(opts); + return (0); } @@ -1336,6 +1353,7 @@ monitor_valid_hostbasedblob(u_char *data, u_int datalen, char *cuser, int mm_answer_keyverify(int sock, struct sshbuf *m) { + struct ssh *ssh = active_state; /* XXX */ struct sshkey *key; u_char *signature, *data, *blob; char *sigalg; @@ -1390,6 +1408,8 @@ mm_answer_keyverify(int sock, struct sshbuf *m) free(data); free(sigalg); + if (key_blobtype == MM_USERKEY) + auth_activate_options(ssh, key_opts); monitor_reset_key_state(); sshkey_free(key); diff --git a/monitor_wrap.c b/monitor_wrap.c index cce318bc5..9666bda4b 100644 --- a/monitor_wrap.c +++ b/monitor_wrap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor_wrap.c,v 1.98 2018/01/08 15:14:44 markus Exp $ */ +/* $OpenBSD: monitor_wrap.c,v 1.99 2018/03/03 03:15:51 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -351,7 +351,7 @@ mm_inform_authserv(char *service, char *style) /* Do the password authentication */ int -mm_auth_password(Authctxt *authctxt, char *password) +mm_auth_password(struct ssh *ssh, char *password) { Buffer m; int authenticated = 0; @@ -378,34 +378,38 @@ mm_auth_password(Authctxt *authctxt, char *password) } int -mm_user_key_allowed(struct passwd *pw, struct sshkey *key, - int pubkey_auth_attempt) +mm_user_key_allowed(struct ssh *ssh, struct passwd *pw, struct sshkey *key, + int pubkey_auth_attempt, struct sshauthopt **authoptp) { return (mm_key_allowed(MM_USERKEY, NULL, NULL, key, - pubkey_auth_attempt)); + pubkey_auth_attempt, authoptp)); } int mm_hostbased_key_allowed(struct passwd *pw, const char *user, const char *host, struct sshkey *key) { - return (mm_key_allowed(MM_HOSTKEY, user, host, key, 0)); + return (mm_key_allowed(MM_HOSTKEY, user, host, key, 0, NULL)); } int mm_key_allowed(enum mm_keytype type, const char *user, const char *host, - struct sshkey *key, int pubkey_auth_attempt) + struct sshkey *key, int pubkey_auth_attempt, struct sshauthopt **authoptp) { Buffer m; u_char *blob; u_int len; - int allowed = 0, have_forced = 0; + int r, allowed = 0; + struct sshauthopt *opts = NULL; debug3("%s entering", __func__); + if (authoptp != NULL) + *authoptp = NULL; + /* Convert the key to a blob and the pass it over */ if (!key_to_blob(key, &blob, &len)) - return (0); + return 0; buffer_init(&m); buffer_put_int(&m, type); @@ -418,18 +422,24 @@ mm_key_allowed(enum mm_keytype type, const char *user, const char *host, mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_KEYALLOWED, &m); debug3("%s: waiting for MONITOR_ANS_KEYALLOWED", __func__); - mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_KEYALLOWED, &m); + mm_request_receive_expect(pmonitor->m_recvfd, + MONITOR_ANS_KEYALLOWED, &m); allowed = buffer_get_int(&m); - - /* fake forced command */ - auth_clear_options(); - have_forced = buffer_get_int(&m); - forced_command = have_forced ? xstrdup("true") : NULL; - + if (allowed && type == MM_USERKEY) { + if ((r = sshauthopt_deserialise(&m, &opts)) != 0) + fatal("%s: sshauthopt_deserialise: %s", + __func__, ssh_err(r)); + } buffer_free(&m); - return (allowed); + if (authoptp != NULL) { + *authoptp = opts; + opts = NULL; + } + sshauthopt_free(opts); + + return allowed; } /* diff --git a/monitor_wrap.h b/monitor_wrap.h index f5af1e819..762332704 100644 --- a/monitor_wrap.h +++ b/monitor_wrap.h @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor_wrap.h,v 1.36 2017/12/18 02:25:15 djm Exp $ */ +/* $OpenBSD: monitor_wrap.h,v 1.37 2018/03/03 03:15:51 djm Exp $ */ /* * Copyright 2002 Niels Provos @@ -35,6 +35,8 @@ enum mm_keytype { MM_NOKEY, MM_HOSTKEY, MM_USERKEY }; struct monitor; struct Authctxt; +struct sshkey; +struct sshauthopt; void mm_log_handler(LogLevel, const char *, void *); int mm_is_monitor(void); @@ -44,10 +46,11 @@ int mm_key_sign(struct sshkey *, u_char **, u_int *, const u_char *, u_int, void mm_inform_authserv(char *, char *); struct passwd *mm_getpwnamallow(const char *); char *mm_auth2_read_banner(void); -int mm_auth_password(struct Authctxt *, char *); +int mm_auth_password(struct ssh *, char *); int mm_key_allowed(enum mm_keytype, const char *, const char *, struct sshkey *, - int); -int mm_user_key_allowed(struct passwd *, struct sshkey *, int); + int, struct sshauthopt **); +int mm_user_key_allowed(struct ssh *, struct passwd *, struct sshkey *, int, + struct sshauthopt **); int mm_hostbased_key_allowed(struct passwd *, const char *, const char *, struct sshkey *); int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t, diff --git a/serverloop.c b/serverloop.c index 7e2abd52f..d6fe24cc1 100644 --- a/serverloop.c +++ b/serverloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: serverloop.c,v 1.204 2018/02/11 21:16:56 dtucker Exp $ */ +/* $OpenBSD: serverloop.c,v 1.205 2018/03/03 03:15:51 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -82,6 +82,7 @@ extern ServerOptions options; /* XXX */ extern Authctxt *the_authctxt; +extern struct sshauthopt *auth_opts; extern int use_privsep; static int no_more_sessions = 0; /* Disallow further sessions. */ @@ -456,12 +457,13 @@ server_request_direct_tcpip(struct ssh *ssh, int *reason, const char **errmsg) originator_port = packet_get_int(); packet_check_eom(); - debug("server_request_direct_tcpip: originator %s port %d, target %s " - "port %d", originator, originator_port, target, target_port); + debug("%s: originator %s port %d, target %s port %d", __func__, + originator, originator_port, target, target_port); /* XXX fine grained permissions */ if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0 && - !no_port_forwarding_flag && !options.disable_forwarding) { + auth_opts->permit_port_forwarding_flag && + !options.disable_forwarding) { c = channel_connect_to_port(ssh, target, target_port, "direct-tcpip", "direct-tcpip", reason, errmsg); } else { @@ -487,20 +489,20 @@ server_request_direct_streamlocal(struct ssh *ssh) struct passwd *pw = the_authctxt->pw; if (pw == NULL || !the_authctxt->valid) - fatal("server_input_global_request: no/invalid user"); + fatal("%s: no/invalid user", __func__); target = packet_get_string(NULL); originator = packet_get_string(NULL); originator_port = packet_get_int(); packet_check_eom(); - debug("server_request_direct_streamlocal: originator %s port %d, target %s", + debug("%s: originator %s port %d, target %s", __func__, originator, originator_port, target); /* XXX fine grained permissions */ if ((options.allow_streamlocal_forwarding & FORWARD_LOCAL) != 0 && - !no_port_forwarding_flag && !options.disable_forwarding && - (pw->pw_uid == 0 || use_privsep)) { + auth_opts->permit_port_forwarding_flag && + !options.disable_forwarding && (pw->pw_uid == 0 || use_privsep)) { c = channel_connect_to_path(ssh, target, "direct-streamlocal@openssh.com", "direct-streamlocal"); } else { @@ -519,8 +521,7 @@ static Channel * server_request_tun(struct ssh *ssh) { Channel *c = NULL; - int mode, tun; - int sock; + int mode, tun, sock; char *tmp, *ifname = NULL; mode = packet_get_int(); @@ -539,10 +540,10 @@ server_request_tun(struct ssh *ssh) } tun = packet_get_int(); - if (forced_tun_device != -1) { - if (tun != SSH_TUNID_ANY && forced_tun_device != tun) + if (auth_opts->force_tun_device != -1) { + if (tun != SSH_TUNID_ANY && auth_opts->force_tun_device != tun) goto done; - tun = forced_tun_device; + tun = auth_opts->force_tun_device; } sock = tun_open(tun, mode, &ifname); if (sock < 0) @@ -767,7 +768,8 @@ server_input_global_request(int type, u_int32_t seq, struct ssh *ssh) /* check permissions */ if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0 || - no_port_forwarding_flag || options.disable_forwarding || + !auth_opts->permit_port_forwarding_flag || + options.disable_forwarding || (!want_reply && fwd.listen_port == 0) || (fwd.listen_port != 0 && !bind_permitted(fwd.listen_port, pw->pw_uid))) { @@ -805,7 +807,8 @@ server_input_global_request(int type, u_int32_t seq, struct ssh *ssh) /* check permissions */ if ((options.allow_streamlocal_forwarding & FORWARD_REMOTE) == 0 - || no_port_forwarding_flag || options.disable_forwarding || + || !auth_opts->permit_port_forwarding_flag || + options.disable_forwarding || (pw->pw_uid != 0 && !use_privsep)) { success = 0; packet_send_debug("Server has disabled " diff --git a/session.c b/session.c index 51c5ea0ec..58826db16 100644 --- a/session.c +++ b/session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.293 2017/10/23 05:08:00 djm Exp $ */ +/* $OpenBSD: session.c,v 1.294 2018/03/03 03:15:51 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -140,6 +140,7 @@ extern u_int utmp_len; extern int startup_pipe; extern void destroy_sensitive_data(void); extern Buffer loginmsg; +extern struct sshauthopt *auth_opts; char *tun_fwd_ifnames; /* serverloop.c */ /* original command from peer. */ @@ -288,14 +289,42 @@ prepare_auth_info_file(struct passwd *pw, struct sshbuf *info) restore_uid(); } +static void +set_permitopen_from_authopts(struct ssh *ssh, const struct sshauthopt *opts) +{ + char *tmp, *cp, *host; + int port; + size_t i; + + if ((options.allow_tcp_forwarding & FORWARD_LOCAL) == 0) + return; + channel_clear_permitted_opens(ssh); + for (i = 0; i < auth_opts->npermitopen; i++) { + tmp = cp = xstrdup(auth_opts->permitopen[i]); + /* This shouldn't fail as it has already been checked */ + if ((host = hpdelim(&cp)) == NULL) + fatal("%s: internal error: hpdelim", __func__); + host = cleanhostname(host); + if (cp == NULL || (port = permitopen_port(cp)) < 0) + fatal("%s: internal error: permitopen port", + __func__); + channel_add_permitted_opens(ssh, host, port); + free(tmp); + } +} + void do_authenticated(struct ssh *ssh, Authctxt *authctxt) { setproctitle("%s", authctxt->pw->pw_name); + auth_log_authopts("active", auth_opts, 0); + /* setup the channel layer */ /* XXX - streamlocal? */ - if (no_port_forwarding_flag || options.disable_forwarding || + set_permitopen_from_authopts(ssh, auth_opts); + if (!auth_opts->permit_port_forwarding_flag || + options.disable_forwarding || (options.allow_tcp_forwarding & FORWARD_LOCAL) == 0) channel_disable_adm_local_opens(ssh); else @@ -642,9 +671,9 @@ do_exec(struct ssh *ssh, Session *s, const char *command) original_command = command; command = options.adm_forced_command; forced = "(config)"; - } else if (forced_command) { + } else if (auth_opts->force_command != NULL) { original_command = command; - command = forced_command; + command = auth_opts->force_command; forced = "(key-option)"; } if (forced != NULL) { @@ -947,8 +976,9 @@ static char ** do_setup_env(struct ssh *ssh, Session *s, const char *shell) { char buf[256]; + size_t n; u_int i, envsize; - char **env, *laddr; + char *ocp, *cp, **env, *laddr; struct passwd *pw = s->pw; #if !defined (HAVE_LOGIN_CAP) && !defined (HAVE_CYGWIN) char *path = NULL; @@ -1023,20 +1053,17 @@ do_setup_env(struct ssh *ssh, Session *s, const char *shell) if (getenv("TZ")) child_set_env(&env, &envsize, "TZ", getenv("TZ")); - /* Set custom environment options from RSA authentication. */ - while (custom_environment) { - struct envstring *ce = custom_environment; - char *str = ce->s; - - for (i = 0; str[i] != '=' && str[i]; i++) - ; - if (str[i] == '=') { - str[i] = 0; - child_set_env(&env, &envsize, str, str + i + 1); + /* Set custom environment options from pubkey authentication. */ + if (options.permit_user_env) { + for (n = 0 ; n < auth_opts->nenv; n++) { + ocp = xstrdup(auth_opts->env[n]); + cp = strchr(ocp, '='); + if (*cp == '=') { + *cp = '\0'; + child_set_env(&env, &envsize, ocp, cp + 1); + } + free(ocp); } - custom_environment = ce->next; - free(ce->s); - free(ce); } /* SSH_CLIENT deprecated */ @@ -1138,7 +1165,7 @@ do_setup_env(struct ssh *ssh, Session *s, const char *shell) * first in this order). */ static void -do_rc_files(Session *s, const char *shell) +do_rc_files(struct ssh *ssh, Session *s, const char *shell) { FILE *f = NULL; char cmd[1024]; @@ -1150,7 +1177,7 @@ do_rc_files(Session *s, const char *shell) /* ignore _PATH_SSH_USER_RC for subsystems and admin forced commands */ if (!s->is_subsystem && options.adm_forced_command == NULL && - !no_user_rc && options.permit_user_rc && + auth_opts->permit_user_rc && options.permit_user_rc && stat(_PATH_SSH_USER_RC, &st) >= 0) { snprintf(cmd, sizeof cmd, "%s -c '%s %s'", shell, _PATH_BSHELL, _PATH_SSH_USER_RC); @@ -1570,7 +1597,7 @@ do_child(struct ssh *ssh, Session *s, const char *command) closefrom(STDERR_FILENO + 1); - do_rc_files(s, shell); + do_rc_files(ssh, s, shell); /* restore SIGPIPE for child */ signal(SIGPIPE, SIG_DFL); @@ -1833,8 +1860,8 @@ session_pty_req(struct ssh *ssh, Session *s) u_int len; int n_bytes; - if (no_pty_flag || !options.permit_tty) { - debug("Allocating a pty not permitted for this authentication."); + if (!auth_opts->permit_pty_flag || !options.permit_tty) { + debug("Allocating a pty not permitted for this connection."); return 0; } if (s->ttyfd != -1) { @@ -2022,9 +2049,11 @@ static int session_auth_agent_req(struct ssh *ssh, Session *s) { static int called = 0; + packet_check_eom(); - if (no_agent_forwarding_flag || !options.allow_agent_forwarding) { - debug("session_auth_agent_req: no_agent_forwarding_flag"); + if (!auth_opts->permit_agent_forwarding_flag || + !options.allow_agent_forwarding) { + debug("%s: agent forwarding disabled", __func__); return 0; } if (called) { @@ -2402,8 +2431,8 @@ session_setup_x11fwd(struct ssh *ssh, Session *s) char hostname[NI_MAXHOST]; u_int i; - if (no_x11_forwarding_flag) { - packet_send_debug("X11 forwarding disabled in user configuration file."); + if (!auth_opts->permit_x11_forwarding_flag) { + packet_send_debug("X11 forwarding disabled by key options."); return 0; } if (!options.x11_forwarding) { @@ -2412,7 +2441,7 @@ session_setup_x11fwd(struct ssh *ssh, Session *s) } if (options.xauth_location == NULL || (stat(options.xauth_location, &st) == -1)) { - packet_send_debug("No xauth program; cannot forward with spoofing."); + packet_send_debug("No xauth program; cannot forward X11."); return 0; } if (s->display != NULL) { diff --git a/sshd.c b/sshd.c index 0b9a7ec46..fd95b681b 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.505 2018/02/23 15:58:38 markus Exp $ */ +/* $OpenBSD: sshd.c,v 1.506 2018/03/03 03:15:51 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -119,6 +119,7 @@ #endif #include "monitor_wrap.h" #include "ssh-sandbox.h" +#include "auth-options.h" #include "version.h" #include "ssherr.h" @@ -232,6 +233,9 @@ static int privsep_chroot = 1; /* global authentication context */ Authctxt *the_authctxt = NULL; +/* global key/cert auth options. XXX move to permanent ssh->authctxt? */ +struct sshauthopt *auth_opts = NULL; + /* sshd_config buffer */ Buffer cfg; @@ -2066,6 +2070,10 @@ main(int ac, char **av) /* XXX global for cleanup, access from other modules */ the_authctxt = authctxt; + /* Set default key authentication options */ + if ((auth_opts = sshauthopt_new_with_keys_defaults()) == NULL) + fatal("allocation failed"); + /* prepare buffer to collect messages to display to user after login */ buffer_init(&loginmsg); auth_debug_reset(); @@ -2122,7 +2130,7 @@ main(int ac, char **av) #ifdef USE_PAM if (options.use_pam) { do_pam_setcred(1); - do_pam_session(); + do_pam_session(ssh); } #endif -- cgit v1.2.3