From 1f0311c7c7d10c94ff7f823de9c5b2ed79368b14 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Thu, 15 May 2014 14:24:09 +1000 Subject: - markus@cvs.openbsd.org 2014/04/29 18:01:49 [auth.c authfd.c authfile.c bufaux.c cipher.c cipher.h hostfile.c] [kex.c key.c mac.c monitor.c monitor_wrap.c myproposal.h packet.c] [roaming_client.c ssh-agent.c ssh-keygen.c ssh-keyscan.c ssh-keysign.c] [ssh-pkcs11.h ssh.c sshconnect.c sshconnect2.c sshd.c] make compiling against OpenSSL optional (make OPENSSL=no); reduces algorithms to curve25519, aes-ctr, chacha, ed25519; allows us to explore further options; with and ok djm --- auth.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'auth.c') diff --git a/auth.c b/auth.c index 9a36f1dac..fcb314cbd 100644 --- a/auth.c +++ b/auth.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.c,v 1.103 2013/05/19 02:42:42 djm Exp $ */ +/* $OpenBSD: auth.c,v 1.104 2014/04/29 18:01:49 markus Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -659,6 +659,7 @@ getpwnamallow(const char *user) int auth_key_is_revoked(Key *key) { +#ifdef WITH_OPENSSL char *key_fp; if (options.revoked_keys_file == NULL) @@ -671,6 +672,7 @@ auth_key_is_revoked(Key *key) default: goto revoked; } +#endif debug3("%s: treating %s as a key list", __func__, options.revoked_keys_file); switch (key_in_file(key, options.revoked_keys_file, 0)) { @@ -682,6 +684,7 @@ auth_key_is_revoked(Key *key) error("Revoked keys file is unreadable: refusing public key " "authentication"); return 1; +#ifdef WITH_OPENSSL case 1: revoked: /* Key revoked */ @@ -690,6 +693,7 @@ auth_key_is_revoked(Key *key) "%s key %s ", key_type(key), key_fp); free(key_fp); return 1; +#endif } fatal("key_in_file returned junk"); } -- cgit v1.2.3 From 686feb560ec43a06ba04da82b50f3c183c947309 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Thu, 3 Jul 2014 21:29:38 +1000 Subject: - djm@cvs.openbsd.org 2014/07/03 11:16:55 [auth.c auth.h auth1.c auth2.c] make the "Too many authentication failures" message include the user, source address, port and protocol in a format similar to the authentication success / failure messages; bz#2199, ok dtucker --- ChangeLog | 5 +++++ auth.c | 16 +++++++++++++++- auth.h | 5 ++--- auth1.c | 4 ++-- auth2.c | 4 ++-- 5 files changed, 26 insertions(+), 8 deletions(-) (limited to 'auth.c') diff --git a/ChangeLog b/ChangeLog index 788d91729..48f19a385 100644 --- a/ChangeLog +++ b/ChangeLog @@ -58,6 +58,11 @@ - jmc@cvs.openbsd.org 2014/07/03 07:45:27 [ssh_config.5] escape %C since groff thinks it part of an Rs/Re block; + - djm@cvs.openbsd.org 2014/07/03 11:16:55 + [auth.c auth.h auth1.c auth2.c] + make the "Too many authentication failures" message include the + user, source address, port and protocol in a format similar to the + authentication success / failure messages; bz#2199, ok dtucker 20140702 - OpenBSD CVS Sync diff --git a/auth.c b/auth.c index fcb314cbd..890dde046 100644 --- a/auth.c +++ b/auth.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.c,v 1.104 2014/04/29 18:01:49 markus Exp $ */ +/* $OpenBSD: auth.c,v 1.105 2014/07/03 11:16:55 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -326,6 +326,20 @@ auth_log(Authctxt *authctxt, int authenticated, int partial, #endif } + +void +auth_maxtries_exceeded(Authctxt *authctxt) +{ + packet_disconnect("Too many authentication failures for " + "%s%.100s from %.200s port %d %s", + authctxt->valid ? "" : "invalid user ", + authctxt->user, + get_remote_ipaddr(), + get_remote_port(), + compat20 ? "ssh2" : "ssh1"); + /* NOTREACHED */ +} + /* * Check whether root logins are disallowed. */ diff --git a/auth.h b/auth.h index 124e59743..d081c94a6 100644 --- a/auth.h +++ b/auth.h @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.h,v 1.77 2014/01/29 06:18:35 djm Exp $ */ +/* $OpenBSD: auth.h,v 1.78 2014/07/03 11:16:55 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -154,6 +154,7 @@ void auth_info(Authctxt *authctxt, const char *, ...) __attribute__((__format__ (printf, 2, 3))) __attribute__((__nonnull__ (2))); void auth_log(Authctxt *, int, int, const char *, const char *); +void auth_maxtries_exceeded(Authctxt *) __attribute__((noreturn)); void userauth_finish(Authctxt *, int, const char *, const char *); int auth_root_allowed(const char *); @@ -210,8 +211,6 @@ struct passwd *fakepw(void); int sys_auth_passwd(Authctxt *, const char *); -#define AUTH_FAIL_MSG "Too many authentication failures for %.100s" - #define SKEY_PROMPT "\nS/Key Password: " #if defined(KRB5) && !defined(HEIMDAL) diff --git a/auth1.c b/auth1.c index 0f870b3b6..d758a3d69 100644 --- a/auth1.c +++ b/auth1.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth1.c,v 1.80 2014/02/02 03:44:31 djm Exp $ */ +/* $OpenBSD: auth1.c,v 1.81 2014/07/03 11:16:55 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -363,7 +363,7 @@ do_authloop(Authctxt *authctxt) #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES)); #endif - packet_disconnect(AUTH_FAIL_MSG, authctxt->user); + auth_maxtries_exceeded(authctxt); } packet_start(SSH_SMSG_FAILURE); diff --git a/auth2.c b/auth2.c index a5490c009..6572381cb 100644 --- a/auth2.c +++ b/auth2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2.c,v 1.130 2014/01/29 06:18:35 djm Exp $ */ +/* $OpenBSD: auth2.c,v 1.131 2014/07/03 11:16:55 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -362,7 +362,7 @@ userauth_finish(Authctxt *authctxt, int authenticated, const char *method, #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES)); #endif - packet_disconnect(AUTH_FAIL_MSG, authctxt->user); + auth_maxtries_exceeded(authctxt); } methods = authmethods_get(authctxt); debug3("%s: failure partial=%d next methods=\"%s\"", __func__, -- cgit v1.2.3 From 7acefbbcbeab725420ea07397ae35992f505f702 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Fri, 18 Jul 2014 14:11:24 +1000 Subject: - millert@cvs.openbsd.org 2014/07/15 15:54:14 [PROTOCOL auth-options.c auth-passwd.c auth-rh-rsa.c auth-rhosts.c] [auth-rsa.c auth.c auth1.c auth2-hostbased.c auth2-kbdint.c auth2-none.c] [auth2-passwd.c auth2-pubkey.c auth2.c canohost.c channels.c channels.h] [clientloop.c misc.c misc.h monitor.c mux.c packet.c readconf.c] [readconf.h servconf.c servconf.h serverloop.c session.c ssh-agent.c] [ssh.c ssh_config.5 sshconnect.c sshconnect1.c sshconnect2.c sshd.c] [sshd_config.5 sshlogin.c] Add support for Unix domain socket forwarding. A remote TCP port may be forwarded to a local Unix domain socket and vice versa or both ends may be a Unix domain socket. This is a reimplementation of the streamlocal patches by William Ahern from: http://www.25thandclement.com/~william/projects/streamlocal.html OK djm@ markus@ --- ChangeLog | 17 ++ PROTOCOL | 52 ++++- auth-chall.c | 1 + auth-krb5.c | 1 + auth-options.c | 5 +- auth-passwd.c | 3 +- auth-rh-rsa.c | 3 +- auth-rhosts.c | 4 +- auth-rsa.c | 4 +- auth.c | 4 +- auth1.c | 3 +- auth2-chall.c | 1 + auth2-hostbased.c | 3 +- auth2-kbdint.c | 3 +- auth2-none.c | 3 +- auth2-passwd.c | 3 +- auth2-pubkey.c | 4 +- auth2.c | 3 +- canohost.c | 12 +- channels.c | 600 ++++++++++++++++++++++++++++++++++++++++++----------- channels.h | 28 +-- clientloop.c | 78 ++++--- misc.c | 49 ++++- misc.h | 25 ++- monitor.c | 4 +- mux.c | 203 ++++++++++-------- packet.c | 4 +- platform.c | 3 +- readconf.c | 224 ++++++++++++++++---- readconf.h | 25 +-- sandbox-systrace.c | 2 +- servconf.c | 55 ++++- servconf.h | 5 +- serverloop.c | 107 ++++++++-- session.c | 34 +-- ssh-agent.c | 23 +- ssh.c | 62 +++--- ssh_config.5 | 31 ++- sshconnect.c | 4 +- sshconnect1.c | 4 +- sshconnect2.c | 4 +- sshd.c | 4 +- sshd_config.5 | 51 ++++- sshlogin.c | 3 +- 44 files changed, 1312 insertions(+), 449 deletions(-) (limited to 'auth.c') diff --git a/ChangeLog b/ChangeLog index d133c5b73..f3f83afe1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +20140718 + - OpenBSD CVS Sync + - millert@cvs.openbsd.org 2014/07/15 15:54:14 + [PROTOCOL auth-options.c auth-passwd.c auth-rh-rsa.c auth-rhosts.c] + [auth-rsa.c auth.c auth1.c auth2-hostbased.c auth2-kbdint.c auth2-none.c] + [auth2-passwd.c auth2-pubkey.c auth2.c canohost.c channels.c channels.h] + [clientloop.c misc.c misc.h monitor.c mux.c packet.c readconf.c] + [readconf.h servconf.c servconf.h serverloop.c session.c ssh-agent.c] + [ssh.c ssh_config.5 sshconnect.c sshconnect1.c sshconnect2.c sshd.c] + [sshd_config.5 sshlogin.c] + Add support for Unix domain socket forwarding. A remote TCP port + may be forwarded to a local Unix domain socket and vice versa or + both ends may be a Unix domain socket. This is a reimplementation + of the streamlocal patches by William Ahern from: + http://www.25thandclement.com/~william/projects/streamlocal.html + OK djm@ markus@ + 20140717 - (djm) [digest-openssl.c] Preserve array order when disabling digests. Reported by Petr Lautrbach. diff --git a/PROTOCOL b/PROTOCOL index 4a5088f90..aa59f584e 100644 --- a/PROTOCOL +++ b/PROTOCOL @@ -232,6 +232,56 @@ The contents of the "data" field for layer 2 packets is: The "frame" field contains an IEEE 802.3 Ethernet frame, including header. +2.4. connection: Unix domain socket forwarding + +OpenSSH supports local and remote Unix domain socket forwarding +using the "streamlocal" extension. Forwarding is initiated as per +TCP sockets but with a single path instead of a host and port. + +Similar to direct-tcpip, direct-streamlocal is sent by the client +to request that the server make a connection to a Unix domain socket. + + byte SSH_MSG_CHANNEL_OPEN + string "direct-streamlocal@openssh.com" + uint32 sender channel + uint32 initial window size + uint32 maximum packet size + string socket path + string reserved for future use + +Similar to forwarded-tcpip, forwarded-streamlocal is sent by the +server when the client has previously send the server a streamlocal-forward +GLOBAL_REQUEST. + + byte SSH_MSG_CHANNEL_OPEN + string "forwarded-streamlocal@openssh.com" + uint32 sender channel + uint32 initial window size + uint32 maximum packet size + string socket path + string reserved for future use + +The reserved field is not currently defined and is ignored on the +remote end. It is intended to be used in the future to pass +information about the socket file, such as ownership and mode. +The client currently sends the empty string for this field. + +Similar to tcpip-forward, streamlocal-forward is sent by the client +to request remote forwarding of a Unix domain socket. + + byte SSH2_MSG_GLOBAL_REQUEST + string "streamlocal-forward@openssh.com" + boolean TRUE + string socket path + +Similar to cancel-tcpip-forward, cancel-streamlocal-forward is sent +by the client cancel the forwarding of a Unix domain socket. + + byte SSH2_MSG_GLOBAL_REQUEST + string "cancel-streamlocal-forward@openssh.com" + boolean FALSE + string socket path + 3. SFTP protocol changes 3.1. sftp: Reversal of arguments to SSH_FXP_SYMLINK @@ -356,4 +406,4 @@ respond with a SSH_FXP_STATUS message. This extension is advertised in the SSH_FXP_VERSION hello with version "1". -$OpenBSD: PROTOCOL,v 1.23 2013/12/01 23:19:05 djm Exp $ +$OpenBSD: PROTOCOL,v 1.24 2014/07/15 15:54:14 millert Exp $ diff --git a/auth-chall.c b/auth-chall.c index cb3d522d9..5c26a403d 100644 --- a/auth-chall.c +++ b/auth-chall.c @@ -37,6 +37,7 @@ #include "hostfile.h" #include "auth.h" #include "log.h" +#include "misc.h" #include "servconf.h" /* limited protocol v1 interface to kbd-interactive authentication */ diff --git a/auth-krb5.c b/auth-krb5.c index 6c62bdf54..0089b1844 100644 --- a/auth-krb5.c +++ b/auth-krb5.c @@ -40,6 +40,7 @@ #include "packet.h" #include "log.h" #include "buffer.h" +#include "misc.h" #include "servconf.h" #include "uidswap.h" #include "key.h" diff --git a/auth-options.c b/auth-options.c index 9a3c270e9..f3d9c9df8 100644 --- a/auth-options.c +++ b/auth-options.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-options.c,v 1.63 2014/06/24 01:13:21 djm Exp $ */ +/* $OpenBSD: auth-options.c,v 1.64 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -26,9 +26,9 @@ #include "log.h" #include "canohost.h" #include "buffer.h" +#include "misc.h" #include "channels.h" #include "servconf.h" -#include "misc.h" #include "key.h" #include "auth-options.h" #include "hostfile.h" @@ -325,6 +325,7 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) 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 " diff --git a/auth-passwd.c b/auth-passwd.c index 68bbd18dd..63ccf3cab 100644 --- a/auth-passwd.c +++ b/auth-passwd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-passwd.c,v 1.43 2007/09/21 08:15:29 djm Exp $ */ +/* $OpenBSD: auth-passwd.c,v 1.44 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -48,6 +48,7 @@ #include "packet.h" #include "buffer.h" #include "log.h" +#include "misc.h" #include "servconf.h" #include "key.h" #include "hostfile.h" diff --git a/auth-rh-rsa.c b/auth-rh-rsa.c index b21a0f4a2..b7fd064e7 100644 --- a/auth-rh-rsa.c +++ b/auth-rh-rsa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-rh-rsa.c,v 1.43 2010/03/04 10:36:03 djm Exp $ */ +/* $OpenBSD: auth-rh-rsa.c,v 1.44 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -24,6 +24,7 @@ #include "uidswap.h" #include "log.h" #include "buffer.h" +#include "misc.h" #include "servconf.h" #include "key.h" #include "hostfile.h" diff --git a/auth-rhosts.c b/auth-rhosts.c index 06ae7f0b9..b5bedee8d 100644 --- a/auth-rhosts.c +++ b/auth-rhosts.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-rhosts.c,v 1.44 2010/03/07 11:57:13 dtucker Exp $ */ +/* $OpenBSD: auth-rhosts.c,v 1.45 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -34,12 +34,12 @@ #include "uidswap.h" #include "pathnames.h" #include "log.h" +#include "misc.h" #include "servconf.h" #include "canohost.h" #include "key.h" #include "hostfile.h" #include "auth.h" -#include "misc.h" /* import */ extern ServerOptions options; diff --git a/auth-rsa.c b/auth-rsa.c index 1bddfa02f..e9f4ede26 100644 --- a/auth-rsa.c +++ b/auth-rsa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-rsa.c,v 1.87 2014/06/24 01:13:21 djm Exp $ */ +/* $OpenBSD: auth-rsa.c,v 1.88 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -35,6 +35,7 @@ #include "buffer.h" #include "pathnames.h" #include "log.h" +#include "misc.h" #include "servconf.h" #include "key.h" #include "auth-options.h" @@ -45,7 +46,6 @@ #endif #include "monitor_wrap.h" #include "ssh.h" -#include "misc.h" #include "digest.h" diff --git a/auth.c b/auth.c index 890dde046..5e60682ce 100644 --- a/auth.c +++ b/auth.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.c,v 1.105 2014/07/03 11:16:55 djm Exp $ */ +/* $OpenBSD: auth.c,v 1.106 2014/07/15 15:54:14 millert Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -56,6 +56,7 @@ #include "groupaccess.h" #include "log.h" #include "buffer.h" +#include "misc.h" #include "servconf.h" #include "key.h" #include "hostfile.h" @@ -63,7 +64,6 @@ #include "auth-options.h" #include "canohost.h" #include "uidswap.h" -#include "misc.h" #include "packet.h" #include "loginrec.h" #ifdef GSSAPI diff --git a/auth1.c b/auth1.c index d758a3d69..50388285c 100644 --- a/auth1.c +++ b/auth1.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth1.c,v 1.81 2014/07/03 11:16:55 djm Exp $ */ +/* $OpenBSD: auth1.c,v 1.82 2014/07/15 15:54:14 millert Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -27,6 +27,7 @@ #include "packet.h" #include "buffer.h" #include "log.h" +#include "misc.h" #include "servconf.h" #include "compat.h" #include "key.h" diff --git a/auth2-chall.c b/auth2-chall.c index 980250a91..ea4eb6952 100644 --- a/auth2-chall.c +++ b/auth2-chall.c @@ -41,6 +41,7 @@ #include "packet.h" #include "dispatch.h" #include "log.h" +#include "misc.h" #include "servconf.h" /* import */ diff --git a/auth2-hostbased.c b/auth2-hostbased.c index 488008f62..6787e4ca4 100644 --- a/auth2-hostbased.c +++ b/auth2-hostbased.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-hostbased.c,v 1.17 2013/12/30 23:52:27 djm Exp $ */ +/* $OpenBSD: auth2-hostbased.c,v 1.18 2014/07/15 15:54:14 millert Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -36,6 +36,7 @@ #include "packet.h" #include "buffer.h" #include "log.h" +#include "misc.h" #include "servconf.h" #include "compat.h" #include "key.h" diff --git a/auth2-kbdint.c b/auth2-kbdint.c index c39bdc62d..bf75c6059 100644 --- a/auth2-kbdint.c +++ b/auth2-kbdint.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-kbdint.c,v 1.6 2013/05/17 00:13:13 djm Exp $ */ +/* $OpenBSD: auth2-kbdint.c,v 1.7 2014/07/15 15:54:14 millert Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -36,6 +36,7 @@ #include "auth.h" #include "log.h" #include "buffer.h" +#include "misc.h" #include "servconf.h" /* import */ diff --git a/auth2-none.c b/auth2-none.c index 5501b9d64..e71e2219c 100644 --- a/auth2-none.c +++ b/auth2-none.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-none.c,v 1.17 2014/06/24 01:13:21 djm Exp $ */ +/* $OpenBSD: auth2-none.c,v 1.18 2014/07/15 15:54:14 millert Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -43,6 +43,7 @@ #include "packet.h" #include "log.h" #include "buffer.h" +#include "misc.h" #include "servconf.h" #include "compat.h" #include "ssh2.h" diff --git a/auth2-passwd.c b/auth2-passwd.c index 707680cd0..b638e8715 100644 --- a/auth2-passwd.c +++ b/auth2-passwd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-passwd.c,v 1.11 2014/02/02 03:44:31 djm Exp $ */ +/* $OpenBSD: auth2-passwd.c,v 1.12 2014/07/15 15:54:14 millert Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -41,6 +41,7 @@ #include "ssh-gss.h" #endif #include "monitor_wrap.h" +#include "misc.h" #include "servconf.h" /* import */ diff --git a/auth2-pubkey.c b/auth2-pubkey.c index b2fd07a61..f3ca96592 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-pubkey.c,v 1.40 2014/06/24 01:13:21 djm Exp $ */ +/* $OpenBSD: auth2-pubkey.c,v 1.41 2014/07/15 15:54:14 millert Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -48,6 +48,7 @@ #include "packet.h" #include "buffer.h" #include "log.h" +#include "misc.h" #include "servconf.h" #include "compat.h" #include "key.h" @@ -61,7 +62,6 @@ #include "ssh-gss.h" #endif #include "monitor_wrap.h" -#include "misc.h" #include "authfile.h" #include "match.h" diff --git a/auth2.c b/auth2.c index 6572381cb..d9b440ae3 100644 --- a/auth2.c +++ b/auth2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2.c,v 1.131 2014/07/03 11:16:55 djm Exp $ */ +/* $OpenBSD: auth2.c,v 1.132 2014/07/15 15:54:14 millert Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -41,6 +41,7 @@ #include "packet.h" #include "log.h" #include "buffer.h" +#include "misc.h" #include "servconf.h" #include "compat.h" #include "key.h" diff --git a/canohost.c b/canohost.c index a61a8c94d..a3e3bbff8 100644 --- a/canohost.c +++ b/canohost.c @@ -1,4 +1,4 @@ -/* $OpenBSD: canohost.c,v 1.70 2014/01/19 04:17:29 dtucker Exp $ */ +/* $OpenBSD: canohost.c,v 1.71 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -262,6 +263,11 @@ get_socket_address(int sock, int remote, int flags) if (addr.ss_family == AF_INET6) addrlen = sizeof(struct sockaddr_in6); + if (addr.ss_family == AF_UNIX) { + /* Get the Unix domain socket path. */ + return xstrdup(((struct sockaddr_un *)&addr)->sun_path); + } + ipv64_normalise_mapped(&addr, &addrlen); /* Get the address in ascii. */ @@ -384,6 +390,10 @@ get_sock_port(int sock, int local) if (from.ss_family == AF_INET6) fromlen = sizeof(struct sockaddr_in6); + /* Unix domain sockets don't have a port number. */ + if (from.ss_family == AF_UNIX) + return 0; + /* Return port number. */ if ((r = getnameinfo((struct sockaddr *)&from, fromlen, NULL, 0, strport, sizeof(strport), NI_NUMERICSERV)) != 0) diff --git a/channels.c b/channels.c index dcd75346b..d67fdf48b 100644 --- a/channels.c +++ b/channels.c @@ -1,4 +1,4 @@ -/* $OpenBSD: channels.c,v 1.335 2014/07/05 23:11:48 djm Exp $ */ +/* $OpenBSD: channels.c,v 1.336 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -42,6 +42,7 @@ #include "includes.h" #include +#include #include #include #include @@ -107,11 +108,15 @@ static int channel_max_fd = 0; * a corrupt remote server from accessing arbitrary TCP/IP ports on our local * network (which might be behind a firewall). */ +/* XXX: streamlocal wants a path instead of host:port */ +/* Overload host_to_connect; we could just make this match Forward */ +/* XXX - can we use listen_host instead of listen_path? */ typedef struct { char *host_to_connect; /* Connect to 'host'. */ - u_short port_to_connect; /* Connect to 'port'. */ + int port_to_connect; /* Connect to 'port'. */ char *listen_host; /* Remote side should listen address. */ - u_short listen_port; /* Remote side should listen port. */ + char *listen_path; /* Remote side should listen path. */ + int listen_port; /* Remote side should listen port. */ } ForwardPermission; /* List of all permitted host/port pairs to connect by the user. */ @@ -474,6 +479,8 @@ channel_stop_listening(void) case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: case SSH_CHANNEL_X11_LISTENER: + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: channel_close_fd(&c->sock); channel_free(c); break; @@ -536,6 +543,8 @@ channel_still_open(void) case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_ZOMBIE: case SSH_CHANNEL_ABANDONED: + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: continue; case SSH_CHANNEL_LARVAL: if (!compat20) @@ -582,6 +591,8 @@ channel_find_open(void) case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_ZOMBIE: case SSH_CHANNEL_ABANDONED: + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: continue; case SSH_CHANNEL_LARVAL: case SSH_CHANNEL_AUTH_SOCKET: @@ -632,6 +643,8 @@ channel_open_message(void) case SSH_CHANNEL_ABANDONED: case SSH_CHANNEL_MUX_CLIENT: case SSH_CHANNEL_MUX_LISTENER: + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: continue; case SSH_CHANNEL_LARVAL: case SSH_CHANNEL_OPENING: @@ -1387,7 +1400,6 @@ channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset) static void port_open_helper(Channel *c, char *rtype) { - int direct; char buf[1024]; char *local_ipaddr = get_local_ipaddr(c->sock); int local_port = c->sock == -1 ? 65536 : get_sock_port(c->sock, 1); @@ -1401,8 +1413,6 @@ port_open_helper(Channel *c, char *rtype) remote_port = 65535; } - direct = (strcmp(rtype, "direct-tcpip") == 0); - snprintf(buf, sizeof buf, "%s: listening port %d for %.100s port %d, " "connect from %.200s port %d to %.100s port %d", @@ -1418,18 +1428,29 @@ port_open_helper(Channel *c, char *rtype) packet_put_int(c->self); packet_put_int(c->local_window_max); packet_put_int(c->local_maxpacket); - if (direct) { + if (strcmp(rtype, "direct-tcpip") == 0) { /* target host, port */ packet_put_cstring(c->path); packet_put_int(c->host_port); + } else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) { + /* target path */ + packet_put_cstring(c->path); + } else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { + /* listen path */ + packet_put_cstring(c->path); } else { /* listen address, port */ packet_put_cstring(c->path); packet_put_int(local_port); } - /* originator host and port */ - packet_put_cstring(remote_ipaddr); - packet_put_int((u_int)remote_port); + if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { + /* reserved for future owner/mode info */ + packet_put_cstring(""); + } else { + /* originator host and port */ + packet_put_cstring(remote_ipaddr); + packet_put_int((u_int)remote_port); + } packet_send(); } else { packet_start(SSH_MSG_PORT_OPEN); @@ -1479,14 +1500,18 @@ channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) if (c->type == SSH_CHANNEL_RPORT_LISTENER) { nextstate = SSH_CHANNEL_OPENING; rtype = "forwarded-tcpip"; + } else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) { + nextstate = SSH_CHANNEL_OPENING; + rtype = "forwarded-streamlocal@openssh.com"; + } else if (c->host_port == PORT_STREAMLOCAL) { + nextstate = SSH_CHANNEL_OPENING; + rtype = "direct-streamlocal@openssh.com"; + } else if (c->host_port == 0) { + nextstate = SSH_CHANNEL_DYNAMIC; + rtype = "dynamic-tcpip"; } else { - if (c->host_port == 0) { - nextstate = SSH_CHANNEL_DYNAMIC; - rtype = "dynamic-tcpip"; - } else { - nextstate = SSH_CHANNEL_OPENING; - rtype = "direct-tcpip"; - } + nextstate = SSH_CHANNEL_OPENING; + rtype = "direct-tcpip"; } addrlen = sizeof(addr); @@ -1499,7 +1524,8 @@ channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) c->notbefore = monotime() + 1; return; } - set_nodelay(newsock); + if (c->host_port != PORT_STREAMLOCAL) + set_nodelay(newsock); nc = channel_new(rtype, nextstate, newsock, newsock, -1, c->local_window_max, c->local_maxpacket, 0, rtype, 1); nc->listening_port = c->listening_port; @@ -1988,6 +2014,8 @@ channel_handler_init_20(void) channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_UNIX_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_RUNIX_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; @@ -1998,6 +2026,8 @@ channel_handler_init_20(void) channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; + channel_post[SSH_CHANNEL_UNIX_LISTENER] = &channel_post_port_listener; + channel_post[SSH_CHANNEL_RUNIX_LISTENER] = &channel_post_port_listener; channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; @@ -2638,7 +2668,7 @@ channel_input_port_open(int type, u_int32_t seq, void *ctxt) originator_string = xstrdup("unknown (remote did not supply name)"); } packet_check_eom(); - c = channel_connect_to(host, host_port, + c = channel_connect_to_port(host, host_port, "connected socket", originator_string); free(originator_string); free(host); @@ -2705,20 +2735,20 @@ channel_set_af(int af) */ static const char * channel_fwd_bind_addr(const char *listen_addr, int *wildcardp, - int is_client, int gateway_ports) + int is_client, struct ForwardOptions *fwd_opts) { const char *addr = NULL; int wildcard = 0; if (listen_addr == NULL) { /* No address specified: default to gateway_ports setting */ - if (gateway_ports) + if (fwd_opts->gateway_ports) wildcard = 1; - } else if (gateway_ports || is_client) { + } else if (fwd_opts->gateway_ports || is_client) { if (((datafellows & SSH_OLD_FORWARD_ADDR) && strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || - (!is_client && gateway_ports == 1)) { + (!is_client && fwd_opts->gateway_ports == 1)) { wildcard = 1; /* * Notify client if they requested a specific listen @@ -2752,9 +2782,8 @@ channel_fwd_bind_addr(const char *listen_addr, int *wildcardp, } static int -channel_setup_fwd_listener(int type, const char *listen_addr, - u_short listen_port, int *allocated_listen_port, - const char *host_to_connect, u_short port_to_connect, int gateway_ports) +channel_setup_fwd_listener_tcpip(int type, struct Forward *fwd, + int *allocated_listen_port, struct ForwardOptions *fwd_opts) { Channel *c; int sock, r, success = 0, wildcard = 0, is_client; @@ -2764,7 +2793,7 @@ channel_setup_fwd_listener(int type, const char *listen_addr, in_port_t *lport_p; host = (type == SSH_CHANNEL_RPORT_LISTENER) ? - listen_addr : host_to_connect; + fwd->listen_host : fwd->connect_host; is_client = (type == SSH_CHANNEL_PORT_LISTENER); if (host == NULL) { @@ -2777,9 +2806,9 @@ channel_setup_fwd_listener(int type, const char *listen_addr, } /* Determine the bind address, cf. channel_fwd_bind_addr() comment */ - addr = channel_fwd_bind_addr(listen_addr, &wildcard, - is_client, gateway_ports); - debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s", + addr = channel_fwd_bind_addr(fwd->listen_host, &wildcard, + is_client, fwd_opts); + debug3("%s: type %d wildcard %d addr %s", __func__, type, wildcard, (addr == NULL) ? "NULL" : addr); /* @@ -2790,15 +2819,14 @@ channel_setup_fwd_listener(int type, const char *listen_addr, hints.ai_family = IPv4or6; hints.ai_flags = wildcard ? AI_PASSIVE : 0; hints.ai_socktype = SOCK_STREAM; - snprintf(strport, sizeof strport, "%d", listen_port); + snprintf(strport, sizeof strport, "%d", fwd->listen_port); if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { if (addr == NULL) { /* This really shouldn't happen */ packet_disconnect("getaddrinfo: fatal error: %s", ssh_gai_strerror(r)); } else { - error("channel_setup_fwd_listener: " - "getaddrinfo(%.64s): %s", addr, + error("%s: getaddrinfo(%.64s): %s", __func__, addr, ssh_gai_strerror(r)); } return 0; @@ -2822,13 +2850,13 @@ channel_setup_fwd_listener(int type, const char *listen_addr, * If allocating a port for -R forwards, then use the * same port for all address families. */ - if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 && + if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 && allocated_listen_port != NULL && *allocated_listen_port > 0) *lport_p = htons(*allocated_listen_port); if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { - error("channel_setup_fwd_listener: getnameinfo failed"); + error("%s: getnameinfo failed", __func__); continue; } /* Create a port to listen for the host. */ @@ -2865,10 +2893,10 @@ channel_setup_fwd_listener(int type, const char *listen_addr, } /* - * listen_port == 0 requests a dynamically allocated port - + * fwd->listen_port == 0 requests a dynamically allocated port - * record what we got. */ - if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 && + if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 && allocated_listen_port != NULL && *allocated_listen_port == 0) { *allocated_listen_port = get_sock_port(sock, 1); @@ -2881,24 +2909,98 @@ channel_setup_fwd_listener(int type, const char *listen_addr, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "port listener", 1); c->path = xstrdup(host); - c->host_port = port_to_connect; + c->host_port = fwd->connect_port; c->listening_addr = addr == NULL ? NULL : xstrdup(addr); - if (listen_port == 0 && allocated_listen_port != NULL && + if (fwd->listen_port == 0 && allocated_listen_port != NULL && !(datafellows & SSH_BUG_DYNAMIC_RPORT)) c->listening_port = *allocated_listen_port; else - c->listening_port = listen_port; + c->listening_port = fwd->listen_port; success = 1; } if (success == 0) - error("channel_setup_fwd_listener: cannot listen to port: %d", - listen_port); + error("%s: cannot listen to port: %d", __func__, + fwd->listen_port); freeaddrinfo(aitop); return success; } -int -channel_cancel_rport_listener(const char *host, u_short port) +static int +channel_setup_fwd_listener_streamlocal(int type, struct Forward *fwd, + struct ForwardOptions *fwd_opts) +{ + struct sockaddr_un sunaddr; + const char *path; + Channel *c; + int port, sock; + mode_t omask; + + switch (type) { + case SSH_CHANNEL_UNIX_LISTENER: + if (fwd->connect_path != NULL) { + if (strlen(fwd->connect_path) > sizeof(sunaddr.sun_path)) { + error("Local connecting path too long: %s", + fwd->connect_path); + return 0; + } + path = fwd->connect_path; + port = PORT_STREAMLOCAL; + } else { + if (fwd->connect_host == NULL) { + error("No forward host name."); + return 0; + } + if (strlen(fwd->connect_host) >= NI_MAXHOST) { + error("Forward host name too long."); + return 0; + } + path = fwd->connect_host; + port = fwd->connect_port; + } + break; + case SSH_CHANNEL_RUNIX_LISTENER: + path = fwd->listen_path; + port = PORT_STREAMLOCAL; + break; + default: + error("%s: unexpected channel type %d", __func__, type); + return 0; + } + + if (fwd->listen_path == NULL) { + error("No forward path name."); + return 0; + } + if (strlen(fwd->listen_path) > sizeof(sunaddr.sun_path)) { + error("Local listening path too long: %s", fwd->listen_path); + return 0; + } + + debug3("%s: type %d path %s", __func__, type, fwd->listen_path); + + /* Start a Unix domain listener. */ + omask = umask(fwd_opts->streamlocal_bind_mask); + sock = unix_listener(fwd->listen_path, SSH_LISTEN_BACKLOG, + fwd_opts->streamlocal_bind_unlink); + umask(omask); + if (sock < 0) + return 0; + + debug("Local forwarding listening on path %s.", fwd->listen_path); + + /* Allocate a channel number for the socket. */ + c = channel_new("unix listener", type, sock, sock, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, + 0, "unix listener", 1); + c->path = xstrdup(path); + c->host_port = port; + c->listening_port = PORT_STREAMLOCAL; + c->listening_addr = xstrdup(fwd->listen_path); + return 1; +} + +static int +channel_cancel_rport_listener_tcpip(const char *host, u_short port) { u_int i; int found = 0; @@ -2917,13 +3019,44 @@ channel_cancel_rport_listener(const char *host, u_short port) return (found); } +static int +channel_cancel_rport_listener_streamlocal(const char *path) +{ + u_int i; + int found = 0; + + for (i = 0; i < channels_alloc; i++) { + Channel *c = channels[i]; + if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER) + continue; + if (c->path == NULL) + continue; + if (strcmp(c->path, path) == 0) { + debug2("%s: close channel %d", __func__, i); + channel_free(c); + found = 1; + } + } + + return (found); +} + int -channel_cancel_lport_listener(const char *lhost, u_short lport, - int cport, int gateway_ports) +channel_cancel_rport_listener(struct Forward *fwd) +{ + if (fwd->listen_path != NULL) + return channel_cancel_rport_listener_streamlocal(fwd->listen_path); + else + return channel_cancel_rport_listener_tcpip(fwd->listen_host, fwd->listen_port); +} + +static int +channel_cancel_lport_listener_tcpip(const char *lhost, u_short lport, + int cport, struct ForwardOptions *fwd_opts) { u_int i; int found = 0; - const char *addr = channel_fwd_bind_addr(lhost, NULL, 1, gateway_ports); + const char *addr = channel_fwd_bind_addr(lhost, NULL, 1, fwd_opts); for (i = 0; i < channels_alloc; i++) { Channel *c = channels[i]; @@ -2952,24 +3085,68 @@ channel_cancel_lport_listener(const char *lhost, u_short lport, return (found); } +static int +channel_cancel_lport_listener_streamlocal(const char *path) +{ + u_int i; + int found = 0; + + if (path == NULL) { + error("%s: no path specified.", __func__); + return 0; + } + + for (i = 0; i < channels_alloc; i++) { + Channel *c = channels[i]; + if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER) + continue; + if (c->listening_addr == NULL) + continue; + if (strcmp(c->listening_addr, path) == 0) { + debug2("%s: close channel %d", __func__, i); + channel_free(c); + found = 1; + } + } + + return (found); +} + +int +channel_cancel_lport_listener(struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts) +{ + if (fwd->listen_path != NULL) + return channel_cancel_lport_listener_streamlocal(fwd->listen_path); + else + return channel_cancel_lport_listener_tcpip(fwd->listen_host, fwd->listen_port, cport, fwd_opts); +} + /* protocol local port fwd, used by ssh (and sshd in v1) */ int -channel_setup_local_fwd_listener(const char *listen_host, u_short listen_port, - const char *host_to_connect, u_short port_to_connect, int gateway_ports) +channel_setup_local_fwd_listener(struct Forward *fwd, struct ForwardOptions *fwd_opts) { - return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER, - listen_host, listen_port, NULL, host_to_connect, port_to_connect, - gateway_ports); + if (fwd->listen_path != NULL) { + return channel_setup_fwd_listener_streamlocal( + SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts); + } else { + return channel_setup_fwd_listener_tcpip(SSH_CHANNEL_PORT_LISTENER, + fwd, NULL, fwd_opts); + } } /* protocol v2 remote port fwd, used by sshd */ int -channel_setup_remote_fwd_listener(const char *listen_address, - u_short listen_port, int *allocated_listen_port, int gateway_ports) +channel_setup_remote_fwd_listener(struct Forward *fwd, + int *allocated_listen_port, struct ForwardOptions *fwd_opts) { - return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER, - listen_address, listen_port, allocated_listen_port, - NULL, 0, gateway_ports); + if (fwd->listen_path != NULL) { + return channel_setup_fwd_listener_streamlocal( + SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts); + } else { + return channel_setup_fwd_listener_tcpip( + SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port, + fwd_opts); + } } /* @@ -3000,27 +3177,32 @@ channel_rfwd_bind_host(const char *listen_host) * channel_update_permitted_opens(). */ int -channel_request_remote_forwarding(const char *listen_host, u_short listen_port, - const char *host_to_connect, u_short port_to_connect) +channel_request_remote_forwarding(struct Forward *fwd) { int type, success = 0, idx = -1; /* Send the forward request to the remote side. */ if (compat20) { packet_start(SSH2_MSG_GLOBAL_REQUEST); - packet_put_cstring("tcpip-forward"); - packet_put_char(1); /* boolean: want reply */ - packet_put_cstring(channel_rfwd_bind_host(listen_host)); - packet_put_int(listen_port); + if (fwd->listen_path != NULL) { + packet_put_cstring("streamlocal-forward@openssh.com"); + packet_put_char(1); /* boolean: want reply */ + packet_put_cstring(fwd->listen_path); + } else { + packet_put_cstring("tcpip-forward"); + packet_put_char(1); /* boolean: want reply */ + packet_put_cstring(channel_rfwd_bind_host(fwd->listen_host)); + packet_put_int(fwd->listen_port); + } packet_send(); packet_write_wait(); /* Assume that server accepts the request */ success = 1; - } else { + } else if (fwd->listen_path == NULL) { packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); - packet_put_int(listen_port); - packet_put_cstring(host_to_connect); - packet_put_int(port_to_connect); + packet_put_int(fwd->listen_port); + packet_put_cstring(fwd->connect_host); + packet_put_int(fwd->connect_port); packet_send(); packet_write_wait(); @@ -3037,24 +3219,43 @@ channel_request_remote_forwarding(const char *listen_host, u_short listen_port, packet_disconnect("Protocol error for port forward request:" "received packet type %d.", type); } + } else { + logit("Warning: Server does not support remote stream local forwarding."); } if (success) { /* Record that connection to this host/port is permitted. */ permitted_opens = xrealloc(permitted_opens, num_permitted_opens + 1, sizeof(*permitted_opens)); idx = num_permitted_opens++; - permitted_opens[idx].host_to_connect = xstrdup(host_to_connect); - permitted_opens[idx].port_to_connect = port_to_connect; - permitted_opens[idx].listen_host = listen_host ? - xstrdup(listen_host) : NULL; - permitted_opens[idx].listen_port = listen_port; + if (fwd->connect_path != NULL) { + permitted_opens[idx].host_to_connect = + xstrdup(fwd->connect_path); + permitted_opens[idx].port_to_connect = + PORT_STREAMLOCAL; + } else { + permitted_opens[idx].host_to_connect = + xstrdup(fwd->connect_host); + permitted_opens[idx].port_to_connect = + fwd->connect_port; + } + if (fwd->listen_path != NULL) { + permitted_opens[idx].listen_host = NULL; + permitted_opens[idx].listen_path = + xstrdup(fwd->listen_path); + permitted_opens[idx].listen_port = PORT_STREAMLOCAL; + } else { + permitted_opens[idx].listen_host = + fwd->listen_host ? xstrdup(fwd->listen_host) : NULL; + permitted_opens[idx].listen_path = NULL; + permitted_opens[idx].listen_port = fwd->listen_port; + } } return (idx); } static int open_match(ForwardPermission *allowed_open, const char *requestedhost, - u_short requestedport) + int requestedport) { if (allowed_open->host_to_connect == NULL) return 0; @@ -3067,14 +3268,14 @@ open_match(ForwardPermission *allowed_open, const char *requestedhost, } /* - * Note that in he listen host/port case + * Note that in the listen host/port case * we don't support FWD_PERMIT_ANY_PORT and * need to translate between the configured-host (listen_host) * and what we've sent to the remote server (channel_rfwd_bind_host) */ static int -open_listen_match(ForwardPermission *allowed_open, const char *requestedhost, - u_short requestedport, int translate) +open_listen_match_tcpip(ForwardPermission *allowed_open, + const char *requestedhost, u_short requestedport, int translate) { const char *allowed_host; @@ -3094,12 +3295,26 @@ open_listen_match(ForwardPermission *allowed_open, const char *requestedhost, return 1; } +static int +open_listen_match_streamlocal(ForwardPermission *allowed_open, + const char *requestedpath) +{ + if (allowed_open->host_to_connect == NULL) + return 0; + if (allowed_open->listen_port != PORT_STREAMLOCAL) + return 0; + if (allowed_open->listen_path == NULL || + strcmp(allowed_open->listen_path, requestedpath) != 0) + return 0; + return 1; +} + /* * Request cancellation of remote forwarding of connection host:port from * local side. */ -int -channel_request_rforward_cancel(const char *host, u_short port) +static int +channel_request_rforward_cancel_tcpip(const char *host, u_short port) { int i; @@ -3107,7 +3322,7 @@ channel_request_rforward_cancel(const char *host, u_short port) return -1; for (i = 0; i < num_permitted_opens; i++) { - if (open_listen_match(&permitted_opens[i], host, port, 0)) + if (open_listen_match_tcpip(&permitted_opens[i], host, port, 0)) break; } if (i >= num_permitted_opens) { @@ -3121,15 +3336,68 @@ channel_request_rforward_cancel(const char *host, u_short port) packet_put_int(port); packet_send(); - permitted_opens[i].port_to_connect = 0; permitted_opens[i].listen_port = 0; + permitted_opens[i].port_to_connect = 0; free(permitted_opens[i].host_to_connect); permitted_opens[i].host_to_connect = NULL; free(permitted_opens[i].listen_host); permitted_opens[i].listen_host = NULL; + permitted_opens[i].listen_path = NULL; + + return 0; +} + +/* + * Request cancellation of remote forwarding of Unix domain socket + * path from local side. + */ +static int +channel_request_rforward_cancel_streamlocal(const char *path) +{ + int i; + + if (!compat20) + return -1; + + for (i = 0; i < num_permitted_opens; i++) { + if (open_listen_match_streamlocal(&permitted_opens[i], path)) + break; + } + if (i >= num_permitted_opens) { + debug("%s: requested forward not found", __func__); + return -1; + } + packet_start(SSH2_MSG_GLOBAL_REQUEST); + packet_put_cstring("cancel-streamlocal-forward@openssh.com"); + packet_put_char(0); + packet_put_cstring(path); + packet_send(); + + permitted_opens[i].listen_port = 0; + permitted_opens[i].port_to_connect = 0; + free(permitted_opens[i].host_to_connect); + permitted_opens[i].host_to_connect = NULL; + permitted_opens[i].listen_host = NULL; + free(permitted_opens[i].listen_path); + permitted_opens[i].listen_path = NULL; return 0; } + +/* + * Request cancellation of remote forwarding of a connection from local side. + */ +int +channel_request_rforward_cancel(struct Forward *fwd) +{ + if (fwd->listen_path != NULL) { + return (channel_request_rforward_cancel_streamlocal( + fwd->listen_path)); + } else { + return (channel_request_rforward_cancel_tcpip(fwd->listen_host, + fwd->listen_port ? fwd->listen_port : fwd->allocated_port)); + } +} /* * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates @@ -3137,36 +3405,35 @@ channel_request_rforward_cancel(const char *host, u_short port) * message if there was an error). */ int -channel_input_port_forward_request(int is_root, int gateway_ports) +channel_input_port_forward_request(int is_root, struct ForwardOptions *fwd_opts) { - u_short port, host_port; int success = 0; - char *hostname; + struct Forward fwd; /* Get arguments from the packet. */ - port = packet_get_int(); - hostname = packet_get_string(NULL); - host_port = packet_get_int(); + memset(&fwd, 0, sizeof(fwd)); + fwd.listen_port = packet_get_int(); + fwd.connect_host = packet_get_string(NULL); + fwd.connect_port = packet_get_int(); #ifndef HAVE_CYGWIN /* * Check that an unprivileged user is not trying to forward a * privileged port. */ - if (port < IPPORT_RESERVED && !is_root) + if (fwd.listen_port < IPPORT_RESERVED && !is_root) packet_disconnect( "Requested forwarding of port %d but user is not root.", - port); - if (host_port == 0) + fwd.listen_port); + if (fwd.connect_port == 0) packet_disconnect("Dynamic forwarding denied."); #endif /* Initiate forwarding */ - success = channel_setup_local_fwd_listener(NULL, port, hostname, - host_port, gateway_ports); + success = channel_setup_local_fwd_listener(&fwd, fwd_opts); /* Free the argument string. */ - free(hostname); + free(fwd.connect_host); return (success ? 0 : -1); } @@ -3193,6 +3460,7 @@ channel_add_permitted_opens(char *host, int port) permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); permitted_opens[num_permitted_opens].port_to_connect = port; permitted_opens[num_permitted_opens].listen_host = NULL; + permitted_opens[num_permitted_opens].listen_path = NULL; permitted_opens[num_permitted_opens].listen_port = 0; num_permitted_opens++; @@ -3227,6 +3495,8 @@ channel_update_permitted_opens(int idx, int newport) permitted_opens[idx].host_to_connect = NULL; free(permitted_opens[idx].listen_host); permitted_opens[idx].listen_host = NULL; + free(permitted_opens[idx].listen_path); + permitted_opens[idx].listen_path = NULL; } } @@ -3241,6 +3511,7 @@ channel_add_adm_permitted_opens(char *host, int port) = xstrdup(host); permitted_adm_opens[num_adm_permitted_opens].port_to_connect = port; permitted_adm_opens[num_adm_permitted_opens].listen_host = NULL; + permitted_adm_opens[num_adm_permitted_opens].listen_path = NULL; permitted_adm_opens[num_adm_permitted_opens].listen_port = 0; return ++num_adm_permitted_opens; } @@ -3262,6 +3533,7 @@ channel_clear_permitted_opens(void) for (i = 0; i < num_permitted_opens; i++) { free(permitted_opens[i].host_to_connect); free(permitted_opens[i].listen_host); + free(permitted_opens[i].listen_path); } free(permitted_opens); permitted_opens = NULL; @@ -3276,6 +3548,7 @@ channel_clear_adm_permitted_opens(void) for (i = 0; i < num_adm_permitted_opens; i++) { free(permitted_adm_opens[i].host_to_connect); free(permitted_adm_opens[i].listen_host); + free(permitted_adm_opens[i].listen_path); } free(permitted_adm_opens); permitted_adm_opens = NULL; @@ -3319,16 +3592,27 @@ static int connect_next(struct channel_connect *cctx) { int sock, saved_errno; - char ntop[NI_MAXHOST], strport[NI_MAXSERV]; + struct sockaddr_un *sunaddr; + char ntop[NI_MAXHOST], strport[MAX(NI_MAXSERV,sizeof(sunaddr->sun_path))]; for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { - if (cctx->ai->ai_family != AF_INET && - cctx->ai->ai_family != AF_INET6) - continue; - if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, - ntop, sizeof(ntop), strport, sizeof(strport), - NI_NUMERICHOST|NI_NUMERICSERV) != 0) { - error("connect_next: getnameinfo failed"); + switch (cctx->ai->ai_family) { + case AF_UNIX: + /* unix:pathname instead of host:port */ + sunaddr = (struct sockaddr_un *)cctx->ai->ai_addr; + strlcpy(ntop, "unix", sizeof(ntop)); + strlcpy(strport, sunaddr->sun_path, sizeof(strport)); + break; + case AF_INET: + case AF_INET6: + if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, + ntop, sizeof(ntop), strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV) != 0) { + error("connect_next: getnameinfo failed"); + continue; + } + break; + default: continue; } if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype, @@ -3351,10 +3635,11 @@ connect_next(struct channel_connect *cctx) errno = saved_errno; continue; /* fail -- try next */ } + if (cctx->ai->ai_family != AF_UNIX) + set_nodelay(sock); debug("connect_next: host %.100s ([%.100s]:%s) " "in progress, fd=%d", cctx->host, ntop, strport, sock); cctx->ai = cctx->ai->ai_next; - set_nodelay(sock); return sock; } return -1; @@ -3364,14 +3649,18 @@ static void channel_connect_ctx_free(struct channel_connect *cctx) { free(cctx->host); - if (cctx->aitop) - freeaddrinfo(cctx->aitop); + if (cctx->aitop) { + if (cctx->aitop->ai_family == AF_UNIX) + free(cctx->aitop); + else + freeaddrinfo(cctx->aitop); + } memset(cctx, 0, sizeof(*cctx)); } -/* Return CONNECTING channel to remote host, port */ +/* Return CONNECTING channel to remote host:port or local socket path */ static Channel * -connect_to(const char *host, u_short port, char *ctype, char *rname) +connect_to(const char *name, int port, char *ctype, char *rname) { struct addrinfo hints; int gaierr; @@ -3381,23 +3670,51 @@ connect_to(const char *host, u_short port, char *ctype, char *rname) Channel *c; memset(&cctx, 0, sizeof(cctx)); - memset(&hints, 0, sizeof(hints)); - hints.ai_family = IPv4or6; - hints.ai_socktype = SOCK_STREAM; - snprintf(strport, sizeof strport, "%d", port); - if ((gaierr = getaddrinfo(host, strport, &hints, &cctx.aitop)) != 0) { - error("connect_to %.100s: unknown host (%s)", host, - ssh_gai_strerror(gaierr)); - return NULL; + + if (port == PORT_STREAMLOCAL) { + struct sockaddr_un *sunaddr; + struct addrinfo *ai; + + if (strlen(name) > sizeof(sunaddr->sun_path)) { + error("%.100s: %.100s", name, strerror(ENAMETOOLONG)); + return (NULL); + } + + /* + * Fake up a struct addrinfo for AF_UNIX connections. + * channel_connect_ctx_free() must check ai_family + * and use free() not freeaddirinfo() for AF_UNIX. + */ + ai = xmalloc(sizeof(*ai) + sizeof(*sunaddr)); + memset(ai, 0, sizeof(*ai) + sizeof(*sunaddr)); + ai->ai_addr = (struct sockaddr *)(ai + 1); + ai->ai_addrlen = sizeof(*sunaddr); + ai->ai_family = AF_UNIX; + ai->ai_socktype = SOCK_STREAM; + ai->ai_protocol = PF_UNSPEC; + sunaddr = (struct sockaddr_un *)ai->ai_addr; + sunaddr->sun_family = AF_UNIX; + strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path)); + cctx.aitop = ai; + } else { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = IPv4or6; + hints.ai_socktype = SOCK_STREAM; + snprintf(strport, sizeof strport, "%d", port); + if ((gaierr = getaddrinfo(name, strport, &hints, &cctx.aitop)) != 0) { + error("connect_to %.100s: unknown host (%s)", name, + ssh_gai_strerror(gaierr)); + return NULL; + } } - cctx.host = xstrdup(host); + cctx.host = xstrdup(name); cctx.port = port; cctx.ai = cctx.aitop; if ((sock = connect_next(&cctx)) == -1) { error("connect to %.100s port %d failed: %s", - host, port, strerror(errno)); + name, port, strerror(errno)); channel_connect_ctx_free(&cctx); return NULL; } @@ -3414,7 +3731,7 @@ channel_connect_by_listen_address(const char *listen_host, int i; for (i = 0; i < num_permitted_opens; i++) { - if (open_listen_match(&permitted_opens[i], listen_host, + if (open_listen_match_tcpip(&permitted_opens[i], listen_host, listen_port, 1)) { return connect_to( permitted_opens[i].host_to_connect, @@ -3426,9 +3743,26 @@ channel_connect_by_listen_address(const char *listen_host, return NULL; } +Channel * +channel_connect_by_listen_path(const char *path, char *ctype, char *rname) +{ + int i; + + for (i = 0; i < num_permitted_opens; i++) { + if (open_listen_match_streamlocal(&permitted_opens[i], path)) { + return connect_to( + permitted_opens[i].host_to_connect, + permitted_opens[i].port_to_connect, ctype, rname); + } + } + error("WARNING: Server requests forwarding for unknown path %.100s", + path); + return NULL; +} + /* Check if connecting to that port is permitted and connect. */ Channel * -channel_connect_to(const char *host, u_short port, char *ctype, char *rname) +channel_connect_to_port(const char *host, u_short port, char *ctype, char *rname) { int i, permit, permit_adm = 1; @@ -3458,6 +3792,38 @@ channel_connect_to(const char *host, u_short port, char *ctype, char *rname) return connect_to(host, port, ctype, rname); } +/* Check if connecting to that path is permitted and connect. */ +Channel * +channel_connect_to_path(const char *path, char *ctype, char *rname) +{ + int i, permit, permit_adm = 1; + + permit = all_opens_permitted; + if (!permit) { + for (i = 0; i < num_permitted_opens; i++) + if (open_match(&permitted_opens[i], path, PORT_STREAMLOCAL)) { + permit = 1; + break; + } + } + + if (num_adm_permitted_opens > 0) { + permit_adm = 0; + for (i = 0; i < num_adm_permitted_opens; i++) + if (open_match(&permitted_adm_opens[i], path, PORT_STREAMLOCAL)) { + permit_adm = 1; + break; + } + } + + if (!permit || !permit_adm) { + logit("Received request to connect to path %.100s, " + "but the request was denied.", path); + return NULL; + } + return connect_to(path, PORT_STREAMLOCAL, ctype, rname); +} + void channel_send_window_changes(void) { diff --git a/channels.h b/channels.h index 4745b9a7d..a000c98e5 100644 --- a/channels.h +++ b/channels.h @@ -1,4 +1,4 @@ -/* $OpenBSD: channels.h,v 1.114 2014/06/27 16:41:56 markus Exp $ */ +/* $OpenBSD: channels.h,v 1.115 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen @@ -56,7 +56,9 @@ #define SSH_CHANNEL_MUX_LISTENER 15 /* Listener for mux conn. */ #define SSH_CHANNEL_MUX_CLIENT 16 /* Conn. to mux slave */ #define SSH_CHANNEL_ABANDONED 17 /* Abandoned session, eg mux */ -#define SSH_CHANNEL_MAX_TYPE 18 +#define SSH_CHANNEL_UNIX_LISTENER 18 /* Listening on a domain socket. */ +#define SSH_CHANNEL_RUNIX_LISTENER 19 /* Listening to a R-style domain socket. */ +#define SSH_CHANNEL_MAX_TYPE 20 #define CHANNEL_CANCEL_PORT_STATIC -1 @@ -254,6 +256,8 @@ char *channel_open_message(void); int channel_find_open(void); /* tcp forwarding */ +struct Forward; +struct ForwardOptions; void channel_set_af(int af); void channel_permit_all_opens(void); void channel_add_permitted_opens(char *, int); @@ -263,19 +267,19 @@ void channel_update_permitted_opens(int, int); void channel_clear_permitted_opens(void); void channel_clear_adm_permitted_opens(void); void channel_print_adm_permitted_opens(void); -int channel_input_port_forward_request(int, int); -Channel *channel_connect_to(const char *, u_short, char *, char *); +int channel_input_port_forward_request(int, struct ForwardOptions *); +Channel *channel_connect_to_port(const char *, u_short, char *, char *); +Channel *channel_connect_to_path(const char *, char *, char *); Channel *channel_connect_stdio_fwd(const char*, u_short, int, int); Channel *channel_connect_by_listen_address(const char *, u_short, char *, char *); -int channel_request_remote_forwarding(const char *, u_short, - const char *, u_short); -int channel_setup_local_fwd_listener(const char *, u_short, - const char *, u_short, int); -int channel_request_rforward_cancel(const char *host, u_short port); -int channel_setup_remote_fwd_listener(const char *, u_short, int *, int); -int channel_cancel_rport_listener(const char *, u_short); -int channel_cancel_lport_listener(const char *, u_short, int, int); +Channel *channel_connect_by_listen_path(const char *, char *, char *); +int channel_request_remote_forwarding(struct Forward *); +int channel_setup_local_fwd_listener(struct Forward *, struct ForwardOptions *); +int channel_request_rforward_cancel(struct Forward *); +int channel_setup_remote_fwd_listener(struct Forward *, int *, struct ForwardOptions *); +int channel_cancel_rport_listener(struct Forward *); +int channel_cancel_lport_listener(struct Forward *, int, struct ForwardOptions *); int permitopen_port(const char *); /* x11 forwarding */ diff --git a/clientloop.c b/clientloop.c index 02510e26d..397c96532 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.260 2014/06/27 16:41:56 markus Exp $ */ +/* $OpenBSD: clientloop.c,v 1.261 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -100,13 +100,13 @@ #include "cipher.h" #include "kex.h" #include "log.h" +#include "misc.h" #include "readconf.h" #include "clientloop.h" #include "sshconnect.h" #include "authfd.h" #include "atomicio.h" #include "sshpty.h" -#include "misc.h" #include "match.h" #include "msg.h" #include "roaming.h" @@ -871,13 +871,11 @@ static void process_cmdline(void) { void (*handler)(int); - char *s, *cmd, *cancel_host; - int delete = 0, local = 0, remote = 0, dynamic = 0; - int cancel_port, ok; - Forward fwd; + char *s, *cmd; + int ok, delete = 0, local = 0, remote = 0, dynamic = 0; + struct Forward fwd; memset(&fwd, 0, sizeof(fwd)); - fwd.listen_host = fwd.connect_host = NULL; leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); handler = signal(SIGINT, SIG_IGN); @@ -943,29 +941,20 @@ process_cmdline(void) /* XXX update list of forwards in options */ if (delete) { - cancel_port = 0; - cancel_host = hpdelim(&s); /* may be NULL */ - if (s != NULL) { - cancel_port = a2port(s); - cancel_host = cleanhostname(cancel_host); - } else { - cancel_port = a2port(cancel_host); - cancel_host = NULL; - } - if (cancel_port <= 0) { - logit("Bad forwarding close port"); + /* We pass 1 for dynamicfwd to restrict to 1 or 2 fields. */ + if (!parse_forward(&fwd, s, 1, 0)) { + logit("Bad forwarding close specification."); goto out; } if (remote) - ok = channel_request_rforward_cancel(cancel_host, - cancel_port) == 0; + ok = channel_request_rforward_cancel(&fwd) == 0; else if (dynamic) - ok = channel_cancel_lport_listener(cancel_host, - cancel_port, 0, options.gateway_ports) > 0; + ok = channel_cancel_lport_listener(&fwd, + 0, &options.fwd_opts) > 0; else - ok = channel_cancel_lport_listener(cancel_host, - cancel_port, CHANNEL_CANCEL_PORT_STATIC, - options.gateway_ports) > 0; + ok = channel_cancel_lport_listener(&fwd, + CHANNEL_CANCEL_PORT_STATIC, + &options.fwd_opts) > 0; if (!ok) { logit("Unkown port forwarding."); goto out; @@ -977,16 +966,13 @@ process_cmdline(void) goto out; } if (local || dynamic) { - if (!channel_setup_local_fwd_listener(fwd.listen_host, - fwd.listen_port, fwd.connect_host, - fwd.connect_port, options.gateway_ports)) { + if (!channel_setup_local_fwd_listener(&fwd, + &options.fwd_opts)) { logit("Port forwarding failed."); goto out; } } else { - if (channel_request_remote_forwarding(fwd.listen_host, - fwd.listen_port, fwd.connect_host, - fwd.connect_port) < 0) { + if (channel_request_remote_forwarding(&fwd) < 0) { logit("Port forwarding failed."); goto out; } @@ -999,7 +985,9 @@ out: enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); free(cmd); free(fwd.listen_host); + free(fwd.listen_path); free(fwd.connect_host); + free(fwd.connect_path); } /* reasons to suppress output of an escape command in help output */ @@ -1845,9 +1833,8 @@ client_request_forwarded_tcpip(const char *request_type, int rchan) originator_port = packet_get_int(); packet_check_eom(); - debug("client_request_forwarded_tcpip: listen %s port %d, " - "originator %s port %d", listen_address, listen_port, - originator_address, originator_port); + debug("%s: listen %s port %d, originator %s port %d", __func__, + listen_address, listen_port, originator_address, originator_port); c = channel_connect_by_listen_address(listen_address, listen_port, "forwarded-tcpip", originator_address); @@ -1857,6 +1844,27 @@ client_request_forwarded_tcpip(const char *request_type, int rchan) return c; } +static Channel * +client_request_forwarded_streamlocal(const char *request_type, int rchan) +{ + Channel *c = NULL; + char *listen_path; + + /* Get the remote path. */ + listen_path = packet_get_string(NULL); + /* XXX: Skip reserved field for now. */ + if (packet_get_string_ptr(NULL) == NULL) + fatal("%s: packet_get_string_ptr failed", __func__); + packet_check_eom(); + + debug("%s: %s", __func__, listen_path); + + c = channel_connect_by_listen_path(listen_path, + "forwarded-streamlocal@openssh.com", "forwarded-streamlocal"); + free(listen_path); + return c; +} + static Channel * client_request_x11(const char *request_type, int rchan) { @@ -1984,6 +1992,8 @@ client_input_channel_open(int type, u_int32_t seq, void *ctxt) if (strcmp(ctype, "forwarded-tcpip") == 0) { c = client_request_forwarded_tcpip(ctype, rchan); + } else if (strcmp(ctype, "forwarded-streamlocal@openssh.com") == 0) { + c = client_request_forwarded_streamlocal(ctype, rchan); } else if (strcmp(ctype, "x11") == 0) { c = client_request_x11(ctype, rchan); } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { diff --git a/misc.c b/misc.c index 099c4ef80..739916ba4 100644 --- a/misc.c +++ b/misc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.c,v 1.93 2014/04/20 02:30:25 djm Exp $ */ +/* $OpenBSD: misc.c,v 1.94 2014/07/15 15:54:14 millert Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2005,2006 Damien Miller. All rights reserved. @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -1056,6 +1057,52 @@ lowercase(char *s) for (; *s; s++) *s = tolower((u_char)*s); } + +int +unix_listener(const char *path, int backlog, int unlink_first) +{ + struct sockaddr_un sunaddr; + int saved_errno, sock; + + memset(&sunaddr, 0, sizeof(sunaddr)); + sunaddr.sun_family = AF_UNIX; + if (strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path)) >= sizeof(sunaddr.sun_path)) { + error("%s: \"%s\" too long for Unix domain socket", __func__, + path); + errno = ENAMETOOLONG; + return -1; + } + + sock = socket(PF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + saved_errno = errno; + error("socket: %.100s", strerror(errno)); + errno = saved_errno; + return -1; + } + if (unlink_first == 1) { + if (unlink(path) != 0 && errno != ENOENT) + error("unlink(%s): %.100s", path, strerror(errno)); + } + if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) { + saved_errno = errno; + error("bind: %.100s", strerror(errno)); + close(sock); + error("%s: cannot bind to path: %s", __func__, path); + errno = saved_errno; + return -1; + } + if (listen(sock, backlog) < 0) { + saved_errno = errno; + error("listen: %.100s", strerror(errno)); + close(sock); + unlink(path); + error("%s: cannot listen on path: %s", __func__, path); + errno = saved_errno; + return -1; + } + return sock; +} void sock_set_v6only(int s) { diff --git a/misc.h b/misc.h index 7b0c503a3..374c33ce1 100644 --- a/misc.h +++ b/misc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.h,v 1.53 2014/05/02 03:27:54 djm Exp $ */ +/* $OpenBSD: misc.h,v 1.54 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen @@ -15,6 +15,25 @@ #ifndef _MISC_H #define _MISC_H +/* Data structure for representing a forwarding request. */ +struct Forward { + char *listen_host; /* Host (address) to listen on. */ + int listen_port; /* Port to forward. */ + char *listen_path; /* Path to bind domain socket. */ + char *connect_host; /* Host to connect. */ + int connect_port; /* Port to connect on connect_host. */ + char *connect_path; /* Path to connect domain socket. */ + int allocated_port; /* Dynamically allocated listen port */ + int handle; /* Handle for dynamic listen ports */ +}; + +/* Common server and client forwarding options. */ +struct ForwardOptions { + int gateway_ports; /* Allow remote connects to forwarded ports. */ + mode_t streamlocal_bind_mask; /* umask for streamlocal binds */ + int streamlocal_bind_unlink; /* unlink socket before bind */ +}; + /* misc.c */ char *chop(char *); @@ -37,6 +56,7 @@ void ms_subtract_diff(struct timeval *, int *); void ms_to_timeval(struct timeval *, int); time_t monotime(void); void lowercase(char *s); +int unix_listener(const char *, int, int); void sock_set_v6only(int); @@ -68,6 +88,9 @@ int tun_open(int, int); #define SSH_TUNID_ERR (SSH_TUNID_ANY - 1) #define SSH_TUNID_MAX (SSH_TUNID_ANY - 2) +/* Fake port to indicate that host field is really a path. */ +#define PORT_STREAMLOCAL -2 + /* Functions to extract or store big-endian words of various sizes */ u_int64_t get_u64(const void *) __attribute__((__bounded__( __minbytes__, 1, 8))); diff --git a/monitor.c b/monitor.c index 68f4dbc71..72d71c4d3 100644 --- a/monitor.c +++ b/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.134 2014/06/24 01:13:21 djm Exp $ */ +/* $OpenBSD: monitor.c,v 1.135 2014/07/15 15:54:14 millert Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -87,6 +87,7 @@ #include "sshlogin.h" #include "canohost.h" #include "log.h" +#include "misc.h" #include "servconf.h" #include "monitor.h" #include "monitor_mm.h" @@ -95,7 +96,6 @@ #endif #include "monitor_wrap.h" #include "monitor_fdpass.h" -#include "misc.h" #include "compat.h" #include "ssh2.h" #include "roaming.h" diff --git a/mux.c b/mux.c index 784e942ba..5278ce4b6 100644 --- a/mux.c +++ b/mux.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mux.c,v 1.45 2014/04/28 03:09:18 djm Exp $ */ +/* $OpenBSD: mux.c,v 1.46 2014/07/15 15:54:14 millert Exp $ */ /* * Copyright (c) 2002-2008 Damien Miller * @@ -509,29 +509,33 @@ process_mux_terminate(u_int rid, Channel *c, Buffer *m, Buffer *r) } static char * -format_forward(u_int ftype, Forward *fwd) +format_forward(u_int ftype, struct Forward *fwd) { char *ret; switch (ftype) { case MUX_FWD_LOCAL: xasprintf(&ret, "local forward %.200s:%d -> %.200s:%d", + (fwd->listen_path != NULL) ? fwd->listen_path : (fwd->listen_host == NULL) ? - (options.gateway_ports ? "*" : "LOCALHOST") : + (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : fwd->listen_host, fwd->listen_port, + (fwd->connect_path != NULL) ? fwd->connect_path : fwd->connect_host, fwd->connect_port); break; case MUX_FWD_DYNAMIC: xasprintf(&ret, "dynamic forward %.200s:%d -> *", (fwd->listen_host == NULL) ? - (options.gateway_ports ? "*" : "LOCALHOST") : + (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : fwd->listen_host, fwd->listen_port); break; case MUX_FWD_REMOTE: xasprintf(&ret, "remote forward %.200s:%d -> %.200s:%d", + (fwd->listen_path != NULL) ? fwd->listen_path : (fwd->listen_host == NULL) ? "LOCALHOST" : fwd->listen_host, fwd->listen_port, + (fwd->connect_path != NULL) ? fwd->connect_path : fwd->connect_host, fwd->connect_port); break; default: @@ -551,14 +555,18 @@ compare_host(const char *a, const char *b) } static int -compare_forward(Forward *a, Forward *b) +compare_forward(struct Forward *a, struct Forward *b) { if (!compare_host(a->listen_host, b->listen_host)) return 0; + if (!compare_host(a->listen_path, b->listen_path)) + return 0; if (a->listen_port != b->listen_port) return 0; if (!compare_host(a->connect_host, b->connect_host)) return 0; + if (!compare_host(a->connect_path, b->connect_path)) + return 0; if (a->connect_port != b->connect_port) return 0; @@ -570,7 +578,7 @@ mux_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) { struct mux_channel_confirm_ctx *fctx = ctxt; char *failmsg = NULL; - Forward *rfwd; + struct Forward *rfwd; Channel *c; Buffer out; @@ -587,7 +595,8 @@ mux_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) rfwd = &options.remote_forwards[fctx->fid]; debug("%s: %s for: listen %d, connect %s:%d", __func__, type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", - rfwd->listen_port, rfwd->connect_host, rfwd->connect_port); + rfwd->listen_port, rfwd->connect_path ? rfwd->connect_path : + rfwd->connect_host, rfwd->connect_port); if (type == SSH2_MSG_REQUEST_SUCCESS) { if (rfwd->listen_port == 0) { rfwd->allocated_port = packet_get_int(); @@ -607,8 +616,12 @@ mux_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) } else { if (rfwd->listen_port == 0) channel_update_permitted_opens(rfwd->handle, -1); - xasprintf(&failmsg, "remote port forwarding failed for " - "listen port %d", rfwd->listen_port); + if (rfwd->listen_path != NULL) + xasprintf(&failmsg, "remote port forwarding failed for " + "listen path %s", rfwd->listen_path); + else + xasprintf(&failmsg, "remote port forwarding failed for " + "listen port %d", rfwd->listen_port); } fail: error("%s: %s", __func__, failmsg); @@ -627,34 +640,46 @@ mux_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) static int process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) { - Forward fwd; + struct Forward fwd; char *fwd_desc = NULL; + char *listen_addr, *connect_addr; u_int ftype; u_int lport, cport; int i, ret = 0, freefwd = 1; - fwd.listen_host = fwd.connect_host = NULL; + /* XXX - lport/cport check redundant */ if (buffer_get_int_ret(&ftype, m) != 0 || - (fwd.listen_host = buffer_get_string_ret(m, NULL)) == NULL || + (listen_addr = buffer_get_string_ret(m, NULL)) == NULL || buffer_get_int_ret(&lport, m) != 0 || - (fwd.connect_host = buffer_get_string_ret(m, NULL)) == NULL || + (connect_addr = buffer_get_string_ret(m, NULL)) == NULL || buffer_get_int_ret(&cport, m) != 0 || - lport > 65535 || cport > 65535) { + (lport != (u_int)PORT_STREAMLOCAL && lport > 65535) || + (cport != (u_int)PORT_STREAMLOCAL && cport > 65535)) { error("%s: malformed message", __func__); ret = -1; goto out; } - fwd.listen_port = lport; - fwd.connect_port = cport; - if (*fwd.listen_host == '\0') { - free(fwd.listen_host); - fwd.listen_host = NULL; + if (*listen_addr == '\0') { + free(listen_addr); + listen_addr = NULL; } - if (*fwd.connect_host == '\0') { - free(fwd.connect_host); - fwd.connect_host = NULL; + if (*connect_addr == '\0') { + free(connect_addr); + connect_addr = NULL; } + memset(&fwd, 0, sizeof(fwd)); + fwd.listen_port = lport; + if (fwd.listen_port == PORT_STREAMLOCAL) + fwd.listen_path = listen_addr; + else + fwd.listen_host = listen_addr; + fwd.connect_port = cport; + if (fwd.connect_port == PORT_STREAMLOCAL) + fwd.connect_path = connect_addr; + else + fwd.connect_host = connect_addr; + debug2("%s: channel %d: request %s", __func__, c->self, (fwd_desc = format_forward(ftype, &fwd))); @@ -662,25 +687,30 @@ process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) ftype != MUX_FWD_DYNAMIC) { logit("%s: invalid forwarding type %u", __func__, ftype); invalid: - free(fwd.listen_host); - free(fwd.connect_host); + free(listen_addr); + free(connect_addr); buffer_put_int(r, MUX_S_FAILURE); buffer_put_int(r, rid); buffer_put_cstring(r, "Invalid forwarding request"); return 0; } - if (fwd.listen_port >= 65536) { + if (ftype == MUX_FWD_DYNAMIC && fwd.listen_path) { + logit("%s: streamlocal and dynamic forwards " + "are mutually exclusive", __func__); + goto invalid; + } + if (fwd.listen_port != PORT_STREAMLOCAL && fwd.listen_port >= 65536) { logit("%s: invalid listen port %u", __func__, fwd.listen_port); goto invalid; } - if (fwd.connect_port >= 65536 || (ftype != MUX_FWD_DYNAMIC && - ftype != MUX_FWD_REMOTE && fwd.connect_port == 0)) { + if ((fwd.connect_port != PORT_STREAMLOCAL && fwd.connect_port >= 65536) + || (ftype != MUX_FWD_DYNAMIC && ftype != MUX_FWD_REMOTE && fwd.connect_port == 0)) { logit("%s: invalid connect port %u", __func__, fwd.connect_port); goto invalid; } - if (ftype != MUX_FWD_DYNAMIC && fwd.connect_host == NULL) { + if (ftype != MUX_FWD_DYNAMIC && fwd.connect_host == NULL && fwd.connect_path == NULL) { logit("%s: missing connect host", __func__); goto invalid; } @@ -731,9 +761,8 @@ process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) } if (ftype == MUX_FWD_LOCAL || ftype == MUX_FWD_DYNAMIC) { - if (!channel_setup_local_fwd_listener(fwd.listen_host, - fwd.listen_port, fwd.connect_host, fwd.connect_port, - options.gateway_ports)) { + if (!channel_setup_local_fwd_listener(&fwd, + &options.fwd_opts)) { fail: logit("slave-requested %s failed", fwd_desc); buffer_put_int(r, MUX_S_FAILURE); @@ -746,8 +775,7 @@ process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) } else { struct mux_channel_confirm_ctx *fctx; - fwd.handle = channel_request_remote_forwarding(fwd.listen_host, - fwd.listen_port, fwd.connect_host, fwd.connect_port); + fwd.handle = channel_request_remote_forwarding(&fwd); if (fwd.handle < 0) goto fail; add_remote_forward(&options, &fwd); @@ -768,7 +796,9 @@ process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) free(fwd_desc); if (freefwd) { free(fwd.listen_host); + free(fwd.listen_path); free(fwd.connect_host); + free(fwd.connect_path); } return ret; } @@ -776,36 +806,47 @@ process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) static int process_mux_close_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) { - Forward fwd, *found_fwd; + struct Forward fwd, *found_fwd; char *fwd_desc = NULL; const char *error_reason = NULL; + char *listen_addr = NULL, *connect_addr = NULL; u_int ftype; - int i, listen_port, ret = 0; + int i, ret = 0; u_int lport, cport; - fwd.listen_host = fwd.connect_host = NULL; if (buffer_get_int_ret(&ftype, m) != 0 || - (fwd.listen_host = buffer_get_string_ret(m, NULL)) == NULL || + (listen_addr = buffer_get_string_ret(m, NULL)) == NULL || buffer_get_int_ret(&lport, m) != 0 || - (fwd.connect_host = buffer_get_string_ret(m, NULL)) == NULL || + (connect_addr = buffer_get_string_ret(m, NULL)) == NULL || buffer_get_int_ret(&cport, m) != 0 || - lport > 65535 || cport > 65535) { + (lport != (u_int)PORT_STREAMLOCAL && lport > 65535) || + (cport != (u_int)PORT_STREAMLOCAL && cport > 65535)) { error("%s: malformed message", __func__); ret = -1; goto out; } - fwd.listen_port = lport; - fwd.connect_port = cport; - if (*fwd.listen_host == '\0') { - free(fwd.listen_host); - fwd.listen_host = NULL; + if (*listen_addr == '\0') { + free(listen_addr); + listen_addr = NULL; } - if (*fwd.connect_host == '\0') { - free(fwd.connect_host); - fwd.connect_host = NULL; + if (*connect_addr == '\0') { + free(connect_addr); + connect_addr = NULL; } + memset(&fwd, 0, sizeof(fwd)); + fwd.listen_port = lport; + if (fwd.listen_port == PORT_STREAMLOCAL) + fwd.listen_path = listen_addr; + else + fwd.listen_host = listen_addr; + fwd.connect_port = cport; + if (fwd.connect_port == PORT_STREAMLOCAL) + fwd.connect_path = connect_addr; + else + fwd.connect_host = connect_addr; + debug2("%s: channel %d: request cancel %s", __func__, c->self, (fwd_desc = format_forward(ftype, &fwd))); @@ -840,18 +881,14 @@ process_mux_close_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) * This shouldn't fail unless we confused the host/port * between options.remote_forwards and permitted_opens. * However, for dynamic allocated listen ports we need - * to lookup the actual listen port. + * to use the actual listen port. */ - listen_port = (fwd.listen_port == 0) ? - found_fwd->allocated_port : fwd.listen_port; - if (channel_request_rforward_cancel(fwd.listen_host, - listen_port) == -1) + if (channel_request_rforward_cancel(found_fwd) == -1) error_reason = "port not in permitted opens"; } else { /* local and dynamic forwards */ /* Ditto */ - if (channel_cancel_lport_listener(fwd.listen_host, - fwd.listen_port, fwd.connect_port, - options.gateway_ports) == -1) + if (channel_cancel_lport_listener(&fwd, fwd.connect_port, + &options.fwd_opts) == -1) error_reason = "port not found"; } @@ -860,8 +897,11 @@ process_mux_close_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) buffer_put_int(r, rid); free(found_fwd->listen_host); + free(found_fwd->listen_path); free(found_fwd->connect_host); + free(found_fwd->connect_path); found_fwd->listen_host = found_fwd->connect_host = NULL; + found_fwd->listen_path = found_fwd->connect_path = NULL; found_fwd->listen_port = found_fwd->connect_port = 0; } else { buffer_put_int(r, MUX_S_FAILURE); @@ -870,8 +910,8 @@ process_mux_close_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) } out: free(fwd_desc); - free(fwd.listen_host); - free(fwd.connect_host); + free(listen_addr); + free(connect_addr); return ret; } @@ -1133,8 +1173,6 @@ mux_tty_alloc_failed(Channel *c) void muxserver_listen(void) { - struct sockaddr_un addr; - socklen_t sun_len; mode_t old_umask; char *orig_control_path = options.control_path; char rbuf[16+1]; @@ -1163,23 +1201,10 @@ muxserver_listen(void) xasprintf(&options.control_path, "%s.%s", orig_control_path, rbuf); debug3("%s: temporary control path %s", __func__, options.control_path); - memset(&addr, '\0', sizeof(addr)); - addr.sun_family = AF_UNIX; - sun_len = offsetof(struct sockaddr_un, sun_path) + - strlen(options.control_path) + 1; - - if (strlcpy(addr.sun_path, options.control_path, - sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) { - error("ControlPath \"%s\" too long for Unix domain socket", - options.control_path); - goto disable_mux_master; - } - - if ((muxserver_sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) - fatal("%s socket(): %s", __func__, strerror(errno)); - old_umask = umask(0177); - if (bind(muxserver_sock, (struct sockaddr *)&addr, sun_len) == -1) { + muxserver_sock = unix_listener(options.control_path, 64, 0); + umask(old_umask); + if (muxserver_sock < 0) { if (errno == EINVAL || errno == EADDRINUSE) { error("ControlSocket %s already exists, " "disabling multiplexing", options.control_path); @@ -1193,13 +1218,11 @@ muxserver_listen(void) options.control_path = NULL; options.control_master = SSHCTL_MASTER_NO; return; - } else - fatal("%s bind(): %s", __func__, strerror(errno)); + } else { + /* unix_listener() logs the error */ + cleanup_exit(255); + } } - umask(old_umask); - - if (listen(muxserver_sock, 64) == -1) - fatal("%s listen(): %s", __func__, strerror(errno)); /* Now atomically "move" the mux socket into position */ if (link(options.control_path, orig_control_path) != 0) { @@ -1593,7 +1616,7 @@ mux_client_request_terminate(int fd) } static int -mux_client_forward(int fd, int cancel_flag, u_int ftype, Forward *fwd) +mux_client_forward(int fd, int cancel_flag, u_int ftype, struct Forward *fwd) { Buffer m; char *e, *fwd_desc; @@ -1608,11 +1631,19 @@ mux_client_forward(int fd, int cancel_flag, u_int ftype, Forward *fwd) buffer_put_int(&m, cancel_flag ? MUX_C_CLOSE_FWD : MUX_C_OPEN_FWD); buffer_put_int(&m, muxclient_request_id); buffer_put_int(&m, ftype); - buffer_put_cstring(&m, - fwd->listen_host == NULL ? "" : fwd->listen_host); + if (fwd->listen_path != NULL) { + buffer_put_cstring(&m, fwd->listen_path); + } else { + buffer_put_cstring(&m, + fwd->listen_host == NULL ? "" : fwd->listen_host); + } buffer_put_int(&m, fwd->listen_port); - buffer_put_cstring(&m, - fwd->connect_host == NULL ? "" : fwd->connect_host); + if (fwd->connect_path != NULL) { + buffer_put_cstring(&m, fwd->connect_path); + } else { + buffer_put_cstring(&m, + fwd->connect_host == NULL ? "" : fwd->connect_host); + } buffer_put_int(&m, fwd->connect_port); if (mux_client_write_packet(fd, &m) != 0) diff --git a/packet.c b/packet.c index b97257986..6e7b87757 100644 --- a/packet.c +++ b/packet.c @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.c,v 1.197 2014/06/24 01:13:21 djm Exp $ */ +/* $OpenBSD: packet.c,v 1.198 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -66,7 +66,6 @@ #include "crc32.h" #include "compress.h" #include "deattack.h" -#include "channels.h" #include "compat.h" #include "ssh1.h" #include "ssh2.h" @@ -77,6 +76,7 @@ #include "log.h" #include "canohost.h" #include "misc.h" +#include "channels.h" #include "ssh.h" #include "ssherr.h" #include "roaming.h" diff --git a/platform.c b/platform.c index 30fc60909..ee313da55 100644 --- a/platform.c +++ b/platform.c @@ -1,4 +1,4 @@ -/* $Id: platform.c,v 1.21 2014/01/21 01:59:29 tim Exp $ */ +/* $Id: platform.c,v 1.22 2014/07/18 04:11:26 djm Exp $ */ /* * Copyright (c) 2006 Darren Tucker. All rights reserved. @@ -25,6 +25,7 @@ #include "log.h" #include "buffer.h" +#include "misc.h" #include "servconf.h" #include "key.h" #include "hostfile.h" diff --git a/readconf.c b/readconf.c index a4ecf7a0b..7948ce1cd 100644 --- a/readconf.c +++ b/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.219 2014/04/23 12:42:34 djm Exp $ */ +/* $OpenBSD: readconf.c,v 1.220 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -48,9 +49,9 @@ #include "pathnames.h" #include "log.h" #include "key.h" +#include "misc.h" #include "readconf.h" #include "match.h" -#include "misc.h" #include "buffer.h" #include "kex.h" #include "mac.h" @@ -149,6 +150,7 @@ typedef enum { oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots, oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs, + oStreamLocalBindMask, oStreamLocalBindUnlink, oIgnoredUnknownOption, oDeprecated, oUnsupported } OpCodes; @@ -261,6 +263,8 @@ static struct { { "canonicalizehostname", oCanonicalizeHostname }, { "canonicalizemaxdots", oCanonicalizeMaxDots }, { "canonicalizepermittedcnames", oCanonicalizePermittedCNAMEs }, + { "streamlocalbindmask", oStreamLocalBindMask }, + { "streamlocalbindunlink", oStreamLocalBindUnlink }, { "ignoreunknown", oIgnoreUnknown }, { NULL, oBadOption } @@ -272,12 +276,13 @@ static struct { */ void -add_local_forward(Options *options, const Forward *newfwd) +add_local_forward(Options *options, const struct Forward *newfwd) { - Forward *fwd; + struct Forward *fwd; #ifndef NO_IPPORT_RESERVED_CONCEPT extern uid_t original_real_uid; - if (newfwd->listen_port < IPPORT_RESERVED && original_real_uid != 0) + if (newfwd->listen_port < IPPORT_RESERVED && original_real_uid != 0 && + newfwd->listen_path == NULL) fatal("Privileged ports can only be forwarded by root."); #endif options->local_forwards = xrealloc(options->local_forwards, @@ -287,8 +292,10 @@ add_local_forward(Options *options, const Forward *newfwd) fwd->listen_host = newfwd->listen_host; fwd->listen_port = newfwd->listen_port; + fwd->listen_path = newfwd->listen_path; fwd->connect_host = newfwd->connect_host; fwd->connect_port = newfwd->connect_port; + fwd->connect_path = newfwd->connect_path; } /* @@ -297,9 +304,9 @@ add_local_forward(Options *options, const Forward *newfwd) */ void -add_remote_forward(Options *options, const Forward *newfwd) +add_remote_forward(Options *options, const struct Forward *newfwd) { - Forward *fwd; + struct Forward *fwd; options->remote_forwards = xrealloc(options->remote_forwards, options->num_remote_forwards + 1, @@ -308,8 +315,10 @@ add_remote_forward(Options *options, const Forward *newfwd) fwd->listen_host = newfwd->listen_host; fwd->listen_port = newfwd->listen_port; + fwd->listen_path = newfwd->listen_path; fwd->connect_host = newfwd->connect_host; fwd->connect_port = newfwd->connect_port; + fwd->connect_path = newfwd->connect_path; fwd->handle = newfwd->handle; fwd->allocated_port = 0; } @@ -321,7 +330,9 @@ clear_forwardings(Options *options) for (i = 0; i < options->num_local_forwards; i++) { free(options->local_forwards[i].listen_host); + free(options->local_forwards[i].listen_path); free(options->local_forwards[i].connect_host); + free(options->local_forwards[i].connect_path); } if (options->num_local_forwards > 0) { free(options->local_forwards); @@ -330,7 +341,9 @@ clear_forwardings(Options *options) options->num_local_forwards = 0; for (i = 0; i < options->num_remote_forwards; i++) { free(options->remote_forwards[i].listen_host); + free(options->remote_forwards[i].listen_path); free(options->remote_forwards[i].connect_host); + free(options->remote_forwards[i].connect_path); } if (options->num_remote_forwards > 0) { free(options->remote_forwards); @@ -715,7 +728,7 @@ process_config_line(Options *options, struct passwd *pw, const char *host, LogLevel *log_level_ptr; long long val64; size_t len; - Forward fwd; + struct Forward fwd; const struct multistate *multistate_ptr; struct allowed_cname *cname; @@ -805,7 +818,7 @@ parse_time: goto parse_time; case oGatewayPorts: - intptr = &options->gateway_ports; + intptr = &options->fwd_opts.gateway_ports; goto parse_flag; case oExitOnForwardFailure: @@ -1405,6 +1418,21 @@ parse_int: intptr = &options->canonicalize_fallback_local; goto parse_flag; + case oStreamLocalBindMask: + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing StreamLocalBindMask argument.", filename, linenum); + /* Parse mode in octal format */ + value = strtol(arg, &endofnumber, 8); + if (arg == endofnumber || value < 0 || value > 0777) + fatal("%.200s line %d: Bad mask.", filename, linenum); + options->fwd_opts.streamlocal_bind_mask = (mode_t)value; + break; + + case oStreamLocalBindUnlink: + intptr = &options->fwd_opts.streamlocal_bind_unlink; + goto parse_flag; + case oDeprecated: debug("%s line %d: Deprecated option \"%s\"", filename, linenum, keyword); @@ -1502,7 +1530,9 @@ initialize_options(Options * options) options->forward_x11_timeout = -1; options->exit_on_forward_failure = -1; options->xauth_location = NULL; - options->gateway_ports = -1; + options->fwd_opts.gateway_ports = -1; + options->fwd_opts.streamlocal_bind_mask = (mode_t)-1; + options->fwd_opts.streamlocal_bind_unlink = -1; options->use_privileged_port = -1; options->rsa_authentication = -1; options->pubkey_authentication = -1; @@ -1615,8 +1645,12 @@ fill_default_options(Options * options) options->exit_on_forward_failure = 0; if (options->xauth_location == NULL) options->xauth_location = _PATH_XAUTH; - if (options->gateway_ports == -1) - options->gateway_ports = 0; + if (options->fwd_opts.gateway_ports == -1) + options->fwd_opts.gateway_ports = 0; + if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1) + options->fwd_opts.streamlocal_bind_mask = 0177; + if (options->fwd_opts.streamlocal_bind_unlink == -1) + options->fwd_opts.streamlocal_bind_unlink = 0; if (options->use_privileged_port == -1) options->use_privileged_port = 0; if (options->rsa_authentication == -1) @@ -1768,22 +1802,92 @@ fill_default_options(Options * options) /* options->preferred_authentications will be set in ssh */ } +struct fwdarg { + char *arg; + int ispath; +}; + +/* + * parse_fwd_field + * parses the next field in a port forwarding specification. + * sets fwd to the parsed field and advances p past the colon + * or sets it to NULL at end of string. + * returns 0 on success, else non-zero. + */ +static int +parse_fwd_field(char **p, struct fwdarg *fwd) +{ + char *ep, *cp = *p; + int ispath = 0; + + if (*cp == '\0') { + *p = NULL; + return -1; /* end of string */ + } + + /* + * A field escaped with square brackets is used literally. + * XXX - allow ']' to be escaped via backslash? + */ + if (*cp == '[') { + /* find matching ']' */ + for (ep = cp + 1; *ep != ']' && *ep != '\0'; ep++) { + if (*ep == '/') + ispath = 1; + } + /* no matching ']' or not at end of field. */ + if (ep[0] != ']' || (ep[1] != ':' && ep[1] != '\0')) + return -1; + /* NUL terminate the field and advance p past the colon */ + *ep++ = '\0'; + if (*ep != '\0') + *ep++ = '\0'; + fwd->arg = cp + 1; + fwd->ispath = ispath; + *p = ep; + return 0; + } + + for (cp = *p; *cp != '\0'; cp++) { + switch (*cp) { + case '\\': + memmove(cp, cp + 1, strlen(cp + 1) + 1); + cp++; + break; + case '/': + ispath = 1; + break; + case ':': + *cp++ = '\0'; + goto done; + } + } +done: + fwd->arg = *p; + fwd->ispath = ispath; + *p = cp; + return 0; +} + /* * parse_forward * parses a string containing a port forwarding specification of the form: * dynamicfwd == 0 - * [listenhost:]listenport:connecthost:connectport + * [listenhost:]listenport|listenpath:connecthost:connectport|connectpath + * listenpath:connectpath * dynamicfwd == 1 * [listenhost:]listenport * returns number of arguments parsed or zero on error */ int -parse_forward(Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd) +parse_forward(struct Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd) { + struct fwdarg fwdargs[4]; + char *p, *cp; int i; - char *p, *cp, *fwdarg[4]; - memset(fwd, '\0', sizeof(*fwd)); + memset(fwd, 0, sizeof(*fwd)); + memset(fwdargs, 0, sizeof(fwdargs)); cp = p = xstrdup(fwdspec); @@ -1791,39 +1895,70 @@ parse_forward(Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd) while (isspace((u_char)*cp)) cp++; - for (i = 0; i < 4; ++i) - if ((fwdarg[i] = hpdelim(&cp)) == NULL) + for (i = 0; i < 4; ++i) { + if (parse_fwd_field(&cp, &fwdargs[i]) != 0) break; + } /* Check for trailing garbage */ - if (cp != NULL) + if (cp != NULL && *cp != '\0') { i = 0; /* failure */ + } switch (i) { case 1: - fwd->listen_host = NULL; - fwd->listen_port = a2port(fwdarg[0]); + if (fwdargs[0].ispath) { + fwd->listen_path = xstrdup(fwdargs[0].arg); + fwd->listen_port = PORT_STREAMLOCAL; + } else { + fwd->listen_host = NULL; + fwd->listen_port = a2port(fwdargs[0].arg); + } fwd->connect_host = xstrdup("socks"); break; case 2: - fwd->listen_host = xstrdup(cleanhostname(fwdarg[0])); - fwd->listen_port = a2port(fwdarg[1]); - fwd->connect_host = xstrdup("socks"); + if (fwdargs[0].ispath && fwdargs[1].ispath) { + fwd->listen_path = xstrdup(fwdargs[0].arg); + fwd->listen_port = PORT_STREAMLOCAL; + fwd->connect_path = xstrdup(fwdargs[1].arg); + fwd->connect_port = PORT_STREAMLOCAL; + } else if (fwdargs[1].ispath) { + fwd->listen_host = NULL; + fwd->listen_port = a2port(fwdargs[0].arg); + fwd->connect_path = xstrdup(fwdargs[1].arg); + fwd->connect_port = PORT_STREAMLOCAL; + } else { + fwd->listen_host = xstrdup(fwdargs[0].arg); + fwd->listen_port = a2port(fwdargs[1].arg); + fwd->connect_host = xstrdup("socks"); + } break; case 3: - fwd->listen_host = NULL; - fwd->listen_port = a2port(fwdarg[0]); - fwd->connect_host = xstrdup(cleanhostname(fwdarg[1])); - fwd->connect_port = a2port(fwdarg[2]); + if (fwdargs[0].ispath) { + fwd->listen_path = xstrdup(fwdargs[0].arg); + fwd->listen_port = PORT_STREAMLOCAL; + fwd->connect_host = xstrdup(fwdargs[1].arg); + fwd->connect_port = a2port(fwdargs[2].arg); + } else if (fwdargs[2].ispath) { + fwd->listen_host = xstrdup(fwdargs[0].arg); + fwd->listen_port = a2port(fwdargs[1].arg); + fwd->connect_path = xstrdup(fwdargs[2].arg); + fwd->connect_port = PORT_STREAMLOCAL; + } else { + fwd->listen_host = NULL; + fwd->listen_port = a2port(fwdargs[0].arg); + fwd->connect_host = xstrdup(fwdargs[1].arg); + fwd->connect_port = a2port(fwdargs[2].arg); + } break; case 4: - fwd->listen_host = xstrdup(cleanhostname(fwdarg[0])); - fwd->listen_port = a2port(fwdarg[1]); - fwd->connect_host = xstrdup(cleanhostname(fwdarg[2])); - fwd->connect_port = a2port(fwdarg[3]); + fwd->listen_host = xstrdup(fwdargs[0].arg); + fwd->listen_port = a2port(fwdargs[1].arg); + fwd->connect_host = xstrdup(fwdargs[2].arg); + fwd->connect_port = a2port(fwdargs[3].arg); break; default: i = 0; /* failure */ @@ -1835,29 +1970,42 @@ parse_forward(Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd) if (!(i == 1 || i == 2)) goto fail_free; } else { - if (!(i == 3 || i == 4)) - goto fail_free; - if (fwd->connect_port <= 0) + if (!(i == 3 || i == 4)) { + if (fwd->connect_path == NULL && + fwd->listen_path == NULL) + goto fail_free; + } + if (fwd->connect_port <= 0 && fwd->connect_path == NULL) goto fail_free; } - if (fwd->listen_port < 0 || (!remotefwd && fwd->listen_port == 0)) + if ((fwd->listen_port < 0 && fwd->listen_path == NULL) || + (!remotefwd && fwd->listen_port == 0)) goto fail_free; - if (fwd->connect_host != NULL && strlen(fwd->connect_host) >= NI_MAXHOST) goto fail_free; + /* XXX - if connecting to a remote socket, max sun len may not match this host */ + if (fwd->connect_path != NULL && + strlen(fwd->connect_path) >= PATH_MAX_SUN) + goto fail_free; if (fwd->listen_host != NULL && strlen(fwd->listen_host) >= NI_MAXHOST) goto fail_free; - + if (fwd->listen_path != NULL && + strlen(fwd->listen_path) >= PATH_MAX_SUN) + goto fail_free; return (i); fail_free: free(fwd->connect_host); fwd->connect_host = NULL; + free(fwd->connect_path); + fwd->connect_path = NULL; free(fwd->listen_host); fwd->listen_host = NULL; + free(fwd->listen_path); + fwd->listen_path = NULL; return (0); } diff --git a/readconf.h b/readconf.h index 75e3f8f7a..0b9cb777a 100644 --- a/readconf.h +++ b/readconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.h,v 1.101 2014/02/23 20:11:36 djm Exp $ */ +/* $OpenBSD: readconf.h,v 1.102 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen @@ -16,21 +16,12 @@ #ifndef READCONF_H #define READCONF_H -/* Data structure for representing a forwarding request. */ - -typedef struct { - char *listen_host; /* Host (address) to listen on. */ - int listen_port; /* Port to forward. */ - char *connect_host; /* Host to connect. */ - int connect_port; /* Port to connect on connect_host. */ - int allocated_port; /* Dynamically allocated listen port */ - int handle; /* Handle for dynamic listen ports */ -} Forward; /* Data structure for representing option data. */ #define MAX_SEND_ENV 256 #define SSH_MAX_HOSTS_FILES 32 #define MAX_CANON_DOMAINS 32 +#define PATH_MAX_SUN (sizeof((struct sockaddr_un *)0)->sun_path) struct allowed_cname { char *source_list; @@ -44,7 +35,7 @@ typedef struct { int forward_x11_trusted; /* Trust Forward X11 display. */ int exit_on_forward_failure; /* Exit if bind(2) fails for -L/-R */ char *xauth_location; /* Location for xauth program */ - int gateway_ports; /* Allow remote connects to forwarded ports. */ + struct ForwardOptions fwd_opts; /* forwarding options */ int use_privileged_port; /* Don't use privileged port if false. */ int rhosts_rsa_authentication; /* Try rhosts with RSA * authentication. */ @@ -106,11 +97,11 @@ typedef struct { /* Local TCP/IP forward requests. */ int num_local_forwards; - Forward *local_forwards; + struct Forward *local_forwards; /* Remote TCP/IP forward requests. */ int num_remote_forwards; - Forward *remote_forwards; + struct Forward *remote_forwards; int clear_forwardings; int enable_ssh_keysign; @@ -181,12 +172,12 @@ int process_config_line(Options *, struct passwd *, const char *, char *, const char *, int, int *, int); int read_config_file(const char *, struct passwd *, const char *, Options *, int); -int parse_forward(Forward *, const char *, int, int); +int parse_forward(struct Forward *, const char *, int, int); int default_ssh_port(void); int option_clear_or_none(const char *); -void add_local_forward(Options *, const Forward *); -void add_remote_forward(Options *, const Forward *); +void add_local_forward(Options *, const struct Forward *); +void add_remote_forward(Options *, const struct Forward *); void add_identity_file(Options *, const char *, const char *, int); #endif /* READCONF_H */ diff --git a/sandbox-systrace.c b/sandbox-systrace.c index 08cb650bd..a74dc4f9d 100644 --- a/sandbox-systrace.c +++ b/sandbox-systrace.c @@ -60,7 +60,7 @@ static const struct sandbox_policy preauth_policy[] = { { SYS___sysctl, SYSTR_POLICY_PERMIT }, #endif - { SYS_sendsyslog, SYSTR_POLICY_PERMIT }, +// { SYS_sendsyslog, SYSTR_POLICY_PERMIT }, { SYS_close, SYSTR_POLICY_PERMIT }, { SYS_exit, SYSTR_POLICY_PERMIT }, { SYS_getpid, SYSTR_POLICY_PERMIT }, diff --git a/servconf.c b/servconf.c index 331716c8f..b7f329447 100644 --- a/servconf.c +++ b/servconf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: servconf.c,v 1.250 2014/07/03 22:40:43 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.251 2014/07/15 15:54:14 millert Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -39,10 +39,10 @@ #include "ssh.h" #include "log.h" #include "buffer.h" +#include "misc.h" #include "servconf.h" #include "compat.h" #include "pathnames.h" -#include "misc.h" #include "cipher.h" #include "key.h" #include "kex.h" @@ -120,6 +120,7 @@ initialize_server_options(ServerOptions *options) options->rekey_limit = -1; options->rekey_interval = -1; options->allow_tcp_forwarding = -1; + options->allow_streamlocal_forwarding = -1; options->allow_agent_forwarding = -1; options->num_allow_users = 0; options->num_deny_users = 0; @@ -129,7 +130,9 @@ initialize_server_options(ServerOptions *options) options->macs = NULL; options->kex_algorithms = NULL; options->protocol = SSH_PROTO_UNKNOWN; - options->gateway_ports = -1; + options->fwd_opts.gateway_ports = -1; + options->fwd_opts.streamlocal_bind_mask = (mode_t)-1; + options->fwd_opts.streamlocal_bind_unlink = -1; options->num_subsystems = 0; options->max_startups_begin = -1; options->max_startups_rate = -1; @@ -269,10 +272,12 @@ fill_default_server_options(ServerOptions *options) options->rekey_interval = 0; if (options->allow_tcp_forwarding == -1) options->allow_tcp_forwarding = FORWARD_ALLOW; + if (options->allow_streamlocal_forwarding == -1) + options->allow_streamlocal_forwarding = FORWARD_ALLOW; if (options->allow_agent_forwarding == -1) options->allow_agent_forwarding = 1; - if (options->gateway_ports == -1) - options->gateway_ports = 0; + if (options->fwd_opts.gateway_ports == -1) + options->fwd_opts.gateway_ports = 0; if (options->max_startups == -1) options->max_startups = 100; if (options->max_startups_rate == -1) @@ -303,6 +308,10 @@ fill_default_server_options(ServerOptions *options) options->ip_qos_bulk = IPTOS_THROUGHPUT; if (options->version_addendum == NULL) options->version_addendum = xstrdup(""); + if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1) + options->fwd_opts.streamlocal_bind_mask = 0177; + if (options->fwd_opts.streamlocal_bind_unlink == -1) + options->fwd_opts.streamlocal_bind_unlink = 0; /* Turn privilege separation on by default */ if (use_privsep == -1) use_privsep = PRIVSEP_NOSANDBOX; @@ -351,6 +360,8 @@ typedef enum { sKexAlgorithms, sIPQoS, sVersionAddendum, sAuthorizedKeysCommand, sAuthorizedKeysCommandUser, sAuthenticationMethods, sHostKeyAgent, sPermitUserRC, + sStreamLocalBindMask, sStreamLocalBindUnlink, + sAllowStreamLocalForwarding, sDeprecated, sUnsupported } ServerOpCodes; @@ -478,6 +489,9 @@ static struct { { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL }, { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL }, { "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL }, + { "streamlocalbindmask", sStreamLocalBindMask, SSHCFG_ALL }, + { "streamlocalbindunlink", sStreamLocalBindUnlink, SSHCFG_ALL }, + { "allowstreamlocalforwarding", sAllowStreamLocalForwarding, SSHCFG_ALL }, { NULL, sBadOption, 0 } }; @@ -1195,7 +1209,7 @@ process_server_config_line(ServerOptions *options, char *line, break; case sGatewayPorts: - intptr = &options->gateway_ports; + intptr = &options->fwd_opts.gateway_ports; multistate_ptr = multistate_gatewayports; goto parse_multistate; @@ -1230,6 +1244,11 @@ process_server_config_line(ServerOptions *options, char *line, multistate_ptr = multistate_tcpfwd; goto parse_multistate; + case sAllowStreamLocalForwarding: + intptr = &options->allow_streamlocal_forwarding; + multistate_ptr = multistate_tcpfwd; + goto parse_multistate; + case sAllowAgentForwarding: intptr = &options->allow_agent_forwarding; goto parse_flag; @@ -1628,6 +1647,22 @@ process_server_config_line(ServerOptions *options, char *line, } return 0; + case sStreamLocalBindMask: + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: missing StreamLocalBindMask argument.", + filename, linenum); + /* Parse mode in octal format */ + value = strtol(arg, &p, 8); + if (arg == p || value < 0 || value > 0777) + fatal("%s line %d: Bad mask.", filename, linenum); + options->fwd_opts.streamlocal_bind_mask = (mode_t)value; + break; + + case sStreamLocalBindUnlink: + intptr = &options->fwd_opts.streamlocal_bind_unlink; + goto parse_flag; + case sDeprecated: logit("%s line %d: Deprecated option %s", filename, linenum, arg); @@ -1767,9 +1802,10 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) M_CP_INTOPT(permit_empty_passwd); M_CP_INTOPT(allow_tcp_forwarding); + M_CP_INTOPT(allow_streamlocal_forwarding); M_CP_INTOPT(allow_agent_forwarding); M_CP_INTOPT(permit_tun); - M_CP_INTOPT(gateway_ports); + M_CP_INTOPT(fwd_opts.gateway_ports); M_CP_INTOPT(x11_display_offset); M_CP_INTOPT(x11_forwarding); M_CP_INTOPT(x11_use_localhost); @@ -1867,6 +1903,8 @@ fmt_intarg(ServerOpCodes code, int val) return fmt_multistate_int(val, multistate_privsep); case sAllowTcpForwarding: return fmt_multistate_int(val, multistate_tcpfwd); + case sAllowStreamLocalForwarding: + return fmt_multistate_int(val, multistate_tcpfwd); case sProtocol: switch (val) { case SSH_PROTO_1: @@ -2023,9 +2061,10 @@ dump_config(ServerOptions *o) dump_cfg_fmtint(sPermitUserEnvironment, o->permit_user_env); dump_cfg_fmtint(sUseLogin, o->use_login); dump_cfg_fmtint(sCompression, o->compression); - dump_cfg_fmtint(sGatewayPorts, o->gateway_ports); + dump_cfg_fmtint(sGatewayPorts, o->fwd_opts.gateway_ports); dump_cfg_fmtint(sUseDNS, o->use_dns); dump_cfg_fmtint(sAllowTcpForwarding, o->allow_tcp_forwarding); + dump_cfg_fmtint(sAllowStreamLocalForwarding, o->allow_streamlocal_forwarding); dump_cfg_fmtint(sUsePrivilegeSeparation, use_privsep); /* string arguments */ diff --git a/servconf.h b/servconf.h index f2a177649..766db3a3d 100644 --- a/servconf.h +++ b/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.113 2014/07/03 22:40:43 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.114 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen @@ -92,7 +92,7 @@ typedef struct { char *macs; /* Supported SSH2 macs. */ char *kex_algorithms; /* SSH2 kex methods in order of preference. */ int protocol; /* Supported protocol versions. */ - int gateway_ports; /* If true, allow remote connects to forwarded ports. */ + struct ForwardOptions fwd_opts; /* forwarding options */ SyslogFacility log_facility; /* Facility for system logging. */ LogLevel log_level; /* Level for system logging. */ int rhosts_rsa_authentication; /* If true, permit rhosts RSA @@ -124,6 +124,7 @@ typedef struct { int use_login; /* If true, login(1) is used */ int compression; /* If true, compression is allowed */ int allow_tcp_forwarding; /* One of FORWARD_* */ + int allow_streamlocal_forwarding; /* One of FORWARD_* */ int allow_agent_forwarding; u_int num_allow_users; char *allow_users[MAX_ALLOW_USERS]; diff --git a/serverloop.c b/serverloop.c index 6c4b2b512..7a80da55a 100644 --- a/serverloop.c +++ b/serverloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: serverloop.c,v 1.171 2014/04/29 13:10:30 djm Exp $ */ +/* $OpenBSD: serverloop.c,v 1.172 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -61,6 +61,7 @@ #include "packet.h" #include "buffer.h" #include "log.h" +#include "misc.h" #include "servconf.h" #include "canohost.h" #include "sshpty.h" @@ -77,7 +78,6 @@ #include "dispatch.h" #include "auth-options.h" #include "serverloop.h" -#include "misc.h" #include "roaming.h" extern ServerOptions options; @@ -970,7 +970,7 @@ server_request_direct_tcpip(void) /* XXX fine grained permissions */ if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0 && !no_port_forwarding_flag) { - c = channel_connect_to(target, target_port, + c = channel_connect_to_port(target, target_port, "direct-tcpip", "direct-tcpip"); } else { logit("refused local port forward: " @@ -984,6 +984,38 @@ server_request_direct_tcpip(void) return c; } +static Channel * +server_request_direct_streamlocal(void) +{ + Channel *c = NULL; + char *target, *originator; + u_short originator_port; + + 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", + originator, originator_port, target); + + /* XXX fine grained permissions */ + if ((options.allow_streamlocal_forwarding & FORWARD_LOCAL) != 0 && + !no_port_forwarding_flag) { + c = channel_connect_to_path(target, + "direct-streamlocal@openssh.com", "direct-streamlocal"); + } else { + logit("refused streamlocal port forward: " + "originator %s port %d, target %s", + originator, originator_port, target); + } + + free(originator); + free(target); + + return c; +} + static Channel * server_request_tun(void) { @@ -1081,6 +1113,8 @@ server_input_channel_open(int type, u_int32_t seq, void *ctxt) c = server_request_session(); } else if (strcmp(ctype, "direct-tcpip") == 0) { c = server_request_direct_tcpip(); + } else if (strcmp(ctype, "direct-streamlocal@openssh.com") == 0) { + c = server_request_direct_streamlocal(); } else if (strcmp(ctype, "tun@openssh.com") == 0) { c = server_request_tun(); } @@ -1125,47 +1159,74 @@ server_input_global_request(int type, u_int32_t seq, void *ctxt) /* -R style forwarding */ if (strcmp(rtype, "tcpip-forward") == 0) { struct passwd *pw; - char *listen_address; - u_short listen_port; + struct Forward fwd; pw = the_authctxt->pw; if (pw == NULL || !the_authctxt->valid) fatal("server_input_global_request: no/invalid user"); - listen_address = packet_get_string(NULL); - listen_port = (u_short)packet_get_int(); + memset(&fwd, 0, sizeof(fwd)); + fwd.listen_host = packet_get_string(NULL); + fwd.listen_port = (u_short)packet_get_int(); debug("server_input_global_request: tcpip-forward listen %s port %d", - listen_address, listen_port); + fwd.listen_host, fwd.listen_port); /* check permissions */ if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0 || no_port_forwarding_flag || - (!want_reply && listen_port == 0) + (!want_reply && fwd.listen_port == 0) || #ifndef NO_IPPORT_RESERVED_CONCEPT - || (listen_port != 0 && listen_port < IPPORT_RESERVED && - pw->pw_uid != 0) + (fwd.listen_port != 0 && fwd.listen_port < IPPORT_RESERVED && + pw->pw_uid != 0) #endif ) { success = 0; packet_send_debug("Server has disabled port forwarding."); } else { /* Start listening on the port */ - success = channel_setup_remote_fwd_listener( - listen_address, listen_port, - &allocated_listen_port, options.gateway_ports); + success = channel_setup_remote_fwd_listener(&fwd, + &allocated_listen_port, &options.fwd_opts); } - free(listen_address); + free(fwd.listen_host); } else if (strcmp(rtype, "cancel-tcpip-forward") == 0) { - char *cancel_address; - u_short cancel_port; + struct Forward fwd; - cancel_address = packet_get_string(NULL); - cancel_port = (u_short)packet_get_int(); + memset(&fwd, 0, sizeof(fwd)); + fwd.listen_host = packet_get_string(NULL); + fwd.listen_port = (u_short)packet_get_int(); debug("%s: cancel-tcpip-forward addr %s port %d", __func__, - cancel_address, cancel_port); + fwd.listen_host, fwd.listen_port); + + success = channel_cancel_rport_listener(&fwd); + free(fwd.listen_host); + } else if (strcmp(rtype, "streamlocal-forward@openssh.com") == 0) { + struct Forward fwd; + + memset(&fwd, 0, sizeof(fwd)); + fwd.listen_path = packet_get_string(NULL); + debug("server_input_global_request: streamlocal-forward listen path %s", + fwd.listen_path); + + /* check permissions */ + if ((options.allow_streamlocal_forwarding & FORWARD_REMOTE) == 0 + || no_port_forwarding_flag) { + success = 0; + packet_send_debug("Server has disabled port forwarding."); + } else { + /* Start listening on the socket */ + success = channel_setup_remote_fwd_listener( + &fwd, NULL, &options.fwd_opts); + } + free(fwd.listen_path); + } else if (strcmp(rtype, "cancel-streamlocal-forward@openssh.com") == 0) { + struct Forward fwd; + + memset(&fwd, 0, sizeof(fwd)); + fwd.listen_path = packet_get_string(NULL); + debug("%s: cancel-streamlocal-forward path %s", __func__, + fwd.listen_path); - success = channel_cancel_rport_listener(cancel_address, - cancel_port); - free(cancel_address); + success = channel_cancel_rport_listener(&fwd); + free(fwd.listen_path); } else if (strcmp(rtype, "no-more-sessions@openssh.com") == 0) { no_more_sessions = 1; success = 1; diff --git a/session.c b/session.c index b5979dd91..3e96557b8 100644 --- a/session.c +++ b/session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.273 2014/07/03 22:40:43 djm Exp $ */ +/* $OpenBSD: session.c,v 1.274 2014/07/15 15:54:14 millert Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -84,11 +84,11 @@ #include "authfd.h" #include "pathnames.h" #include "log.h" +#include "misc.h" #include "servconf.h" #include "sshlogin.h" #include "serverloop.h" #include "canohost.h" -#include "misc.h" #include "session.h" #include "kex.h" #include "monitor_wrap.h" @@ -183,7 +183,6 @@ auth_input_request_forwarding(struct passwd * pw) { Channel *nc; int sock = -1; - struct sockaddr_un sunaddr; if (auth_sock_name != NULL) { error("authentication forwarding requested twice."); @@ -209,33 +208,15 @@ auth_input_request_forwarding(struct passwd * pw) xasprintf(&auth_sock_name, "%s/agent.%ld", auth_sock_dir, (long) getpid()); - /* Create the socket. */ - sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock < 0) { - error("socket: %.100s", strerror(errno)); - restore_uid(); - goto authsock_err; - } - - /* Bind it to the name. */ - memset(&sunaddr, 0, sizeof(sunaddr)); - sunaddr.sun_family = AF_UNIX; - strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path)); - - if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) { - error("bind: %.100s", strerror(errno)); - restore_uid(); - goto authsock_err; - } + /* Start a Unix listener on auth_sock_name. */ + sock = unix_listener(auth_sock_name, SSH_LISTEN_BACKLOG, 0); /* Restore the privileged uid. */ restore_uid(); - /* Start listening on the socket. */ - if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { - error("listen: %.100s", strerror(errno)); + /* Check for socket/bind/listen failure. */ + if (sock < 0) goto authsock_err; - } /* Allocate a channel for the authentication agent socket. */ nc = channel_new("auth socket", @@ -274,6 +255,7 @@ do_authenticated(Authctxt *authctxt) setproctitle("%s", authctxt->pw->pw_name); /* setup the channel layer */ + /* XXX - streamlocal? */ if (no_port_forwarding_flag || (options.allow_tcp_forwarding & FORWARD_LOCAL) == 0) channel_disable_adm_local_opens(); @@ -393,7 +375,7 @@ do_authenticated1(Authctxt *authctxt) } debug("Received TCP/IP port forwarding request."); if (channel_input_port_forward_request(s->pw->pw_uid == 0, - options.gateway_ports) < 0) { + &options.fwd_opts) < 0) { debug("Port forwarding failed."); break; } diff --git a/ssh-agent.c b/ssh-agent.c index f7a021364..26c1bd37e 100644 --- a/ssh-agent.c +++ b/ssh-agent.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-agent.c,v 1.187 2014/07/03 03:11:03 djm Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.188 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1038,11 +1038,9 @@ main(int ac, char **av) u_int nalloc; char *shell, *format, *pidstr, *agentsocket = NULL; fd_set *readsetp = NULL, *writesetp = NULL; - struct sockaddr_un sunaddr; #ifdef HAVE_SETRLIMIT struct rlimit rlim; #endif - int prev_mask; extern int optind; extern char *optarg; pid_t pid; @@ -1161,25 +1159,10 @@ main(int ac, char **av) * Create socket early so it will exist before command gets run from * the parent. */ - sock = socket(AF_UNIX, SOCK_STREAM, 0); + sock = unix_listener(socket_name, SSH_LISTEN_BACKLOG, 0); if (sock < 0) { - perror("socket"); - *socket_name = '\0'; /* Don't unlink any existing file */ - cleanup_exit(1); - } - memset(&sunaddr, 0, sizeof(sunaddr)); - sunaddr.sun_family = AF_UNIX; - strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path)); - prev_mask = umask(0177); - if (bind(sock, (struct sockaddr *) &sunaddr, sizeof(sunaddr)) < 0) { - perror("bind"); + /* XXX - unix_listener() calls error() not perror() */ *socket_name = '\0'; /* Don't unlink any existing file */ - umask(prev_mask); - cleanup_exit(1); - } - umask(prev_mask); - if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { - perror("listen"); cleanup_exit(1); } diff --git a/ssh.c b/ssh.c index 54f1dbd0a..47375f5ea 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.405 2014/07/03 06:39:19 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.406 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -96,9 +96,9 @@ #include "dispatch.h" #include "clientloop.h" #include "log.h" +#include "misc.h" #include "readconf.h" #include "sshconnect.h" -#include "misc.h" #include "kex.h" #include "mac.h" #include "sshpty.h" @@ -423,7 +423,7 @@ main(int ac, char **av) int timeout_ms; extern int optind, optreset; extern char *optarg; - Forward fwd; + struct Forward fwd; struct addrinfo *addrs = NULL; struct ssh_digest_ctx *md; u_char conn_hash[SSH_DIGEST_MAX_LENGTH]; @@ -545,7 +545,7 @@ main(int ac, char **av) options.forward_x11_trusted = 1; break; case 'g': - options.gateway_ports = 1; + options.fwd_opts.gateway_ports = 1; break; case 'O': if (stdio_forward_host != NULL) @@ -1305,15 +1305,17 @@ fork_postauth(void) static void ssh_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) { - Forward *rfwd = (Forward *)ctxt; + struct Forward *rfwd = (struct Forward *)ctxt; /* XXX verbose() on failure? */ debug("remote forward %s for: listen %s%s%d, connect %s:%d", type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", - rfwd->listen_host == NULL ? "" : rfwd->listen_host, - rfwd->listen_host == NULL ? "" : ":", - rfwd->listen_port, rfwd->connect_host, rfwd->connect_port); - if (rfwd->listen_port == 0) { + rfwd->listen_path ? rfwd->listen_path : + rfwd->listen_host ? rfwd->listen_host : "", + (rfwd->listen_path || rfwd->listen_host) ? ":" : "", + rfwd->listen_port, rfwd->connect_path ? rfwd->connect_path : + rfwd->connect_host, rfwd->connect_port); + if (rfwd->listen_path == NULL && rfwd->listen_port == 0) { if (type == SSH2_MSG_REQUEST_SUCCESS) { rfwd->allocated_port = packet_get_int(); logit("Allocated port %u for remote forward to %s:%d", @@ -1327,12 +1329,21 @@ ssh_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) } if (type == SSH2_MSG_REQUEST_FAILURE) { - if (options.exit_on_forward_failure) - fatal("Error: remote port forwarding failed for " - "listen port %d", rfwd->listen_port); - else - logit("Warning: remote port forwarding failed for " - "listen port %d", rfwd->listen_port); + if (options.exit_on_forward_failure) { + if (rfwd->listen_path != NULL) + fatal("Error: remote port forwarding failed " + "for listen path %s", rfwd->listen_path); + else + fatal("Error: remote port forwarding failed " + "for listen port %d", rfwd->listen_port); + } else { + if (rfwd->listen_path != NULL) + logit("Warning: remote port forwarding failed " + "for listen path %s", rfwd->listen_path); + else + logit("Warning: remote port forwarding failed " + "for listen port %d", rfwd->listen_port); + } } if (++remote_forward_confirms_received == options.num_remote_forwards) { debug("All remote forwarding requests processed"); @@ -1380,18 +1391,18 @@ ssh_init_forwarding(void) for (i = 0; i < options.num_local_forwards; i++) { debug("Local connections to %.200s:%d forwarded to remote " "address %.200s:%d", + (options.local_forwards[i].listen_path != NULL) ? + options.local_forwards[i].listen_path : (options.local_forwards[i].listen_host == NULL) ? - (options.gateway_ports ? "*" : "LOCALHOST") : + (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : options.local_forwards[i].listen_host, options.local_forwards[i].listen_port, + (options.local_forwards[i].connect_path != NULL) ? + options.local_forwards[i].connect_path : options.local_forwards[i].connect_host, options.local_forwards[i].connect_port); success += channel_setup_local_fwd_listener( - options.local_forwards[i].listen_host, - options.local_forwards[i].listen_port, - options.local_forwards[i].connect_host, - options.local_forwards[i].connect_port, - options.gateway_ports); + &options.local_forwards[i], &options.fwd_opts); } if (i > 0 && success != i && options.exit_on_forward_failure) fatal("Could not request local forwarding."); @@ -1402,17 +1413,18 @@ ssh_init_forwarding(void) for (i = 0; i < options.num_remote_forwards; i++) { debug("Remote connections from %.200s:%d forwarded to " "local address %.200s:%d", + (options.remote_forwards[i].listen_path != NULL) ? + options.remote_forwards[i].listen_path : (options.remote_forwards[i].listen_host == NULL) ? "LOCALHOST" : options.remote_forwards[i].listen_host, options.remote_forwards[i].listen_port, + (options.remote_forwards[i].connect_path != NULL) ? + options.remote_forwards[i].connect_path : options.remote_forwards[i].connect_host, options.remote_forwards[i].connect_port); options.remote_forwards[i].handle = channel_request_remote_forwarding( - options.remote_forwards[i].listen_host, - options.remote_forwards[i].listen_port, - options.remote_forwards[i].connect_host, - options.remote_forwards[i].connect_port); + &options.remote_forwards[i]); if (options.remote_forwards[i].handle < 0) { if (options.exit_on_forward_failure) fatal("Could not request remote forwarding."); diff --git a/ssh_config.5 b/ssh_config.5 index 71b9bdcc5..f9ede7a31 100644 --- a/ssh_config.5 +++ b/ssh_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: ssh_config.5,v 1.190 2014/07/07 08:19:12 djm Exp $ -.Dd $Mdocdate: July 7 2014 $ +.\" $OpenBSD: ssh_config.5,v 1.191 2014/07/15 15:54:14 millert Exp $ +.Dd $Mdocdate: July 15 2014 $ .Dt SSH_CONFIG 5 .Os .Sh NAME @@ -1303,6 +1303,33 @@ channel to request a response from the server. The default is 0, indicating that these messages will not be sent to the server. This option applies to protocol version 2 only. +.It Cm StreamLocalBindMask +Sets the octal file creation mode mask +.Pq umask +used when creating a Unix-domain socket file for local or remote +port forwarding. +This option is only used for port forwarding to a Unix-domain socket file. +.Pp +The default value is 0177, which creates a Unix-domain socket file that is +readable and writable only by the owner. +Note that not all operating systems honor the file mode on Unix-domain +socket files. +.It Cm StreamLocalBindUnlink +Specifies whether to remove an existing Unix-domain socket file for local +or remote port forwarding before creating a new one. +If the socket file already exists and +.Cm StreamLocalBindUnlink +is not enabled, +.Nm ssh +will be unable to forward the port to the Unix-domain socket file. +This option is only used for port forwarding to a Unix-domain socket file. +.Pp +The argument must be +.Dq yes +or +.Dq no . +The default is +.Dq no . .It Cm StrictHostKeyChecking If this flag is set to .Dq yes , diff --git a/sshconnect.c b/sshconnect.c index 799c8d00c..ac09eae67 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.250 2014/07/03 22:23:46 djm Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.251 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -54,9 +54,9 @@ #include "sshconnect.h" #include "hostfile.h" #include "log.h" +#include "misc.h" #include "readconf.h" #include "atomicio.h" -#include "misc.h" #include "dns.h" #include "roaming.h" #include "monitor_fdpass.h" diff --git a/sshconnect1.c b/sshconnect1.c index 62a7bd17f..dd12a3af2 100644 --- a/sshconnect1.c +++ b/sshconnect1.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect1.c,v 1.75 2014/06/24 01:13:21 djm Exp $ */ +/* $OpenBSD: sshconnect1.c,v 1.76 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -38,11 +38,11 @@ #include "kex.h" #include "uidswap.h" #include "log.h" +#include "misc.h" #include "readconf.h" #include "authfd.h" #include "sshconnect.h" #include "authfile.h" -#include "misc.h" #include "canohost.h" #include "hostfile.h" #include "auth.h" diff --git a/sshconnect2.c b/sshconnect2.c index eb1b0ae3c..68f7f4fdd 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect2.c,v 1.209 2014/06/24 01:13:21 djm Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.210 2014/07/15 15:54:14 millert Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. @@ -61,8 +61,8 @@ #include "dh.h" #include "authfd.h" #include "log.h" -#include "readconf.h" #include "misc.h" +#include "readconf.h" #include "match.h" #include "dispatch.h" #include "canohost.h" diff --git a/sshd.c b/sshd.c index 67eb66a11..481d00155 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.427 2014/06/24 01:13:21 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.428 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -93,6 +93,7 @@ #include "packet.h" #include "log.h" #include "buffer.h" +#include "misc.h" #include "servconf.h" #include "uidswap.h" #include "compat.h" @@ -108,7 +109,6 @@ #include "hostfile.h" #include "auth.h" #include "authfd.h" -#include "misc.h" #include "msg.h" #include "dispatch.h" #include "channels.h" diff --git a/sshd_config.5 b/sshd_config.5 index 06fd62de7..f92084857 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.174 2014/07/03 22:40:43 djm Exp $ -.Dd $Mdocdate: July 3 2014 $ +.\" $OpenBSD: sshd_config.5,v 1.175 2014/07/15 15:54:14 millert Exp $ +.Dd $Mdocdate: July 15 2014 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -140,6 +140,26 @@ The default is Note that disabling TCP forwarding does not improve security unless users are also denied shell access, as they can always install their own forwarders. +.It Cm AllowStreamLocalForwarding +Specifies whether StreamLocal (Unix-domain socket) forwarding is permitted. +The available options are +.Dq yes +or +.Dq all +to allow StreamLocal forwarding, +.Dq no +to prevent all StreamLocal forwarding, +.Dq local +to allow local (from the perspective of +.Xr ssh 1 ) +forwarding only or +.Dq remote +to allow remote forwarding only. +The default is +.Dq yes . +Note that disabling StreamLocal forwarding does not improve security unless +users are also denied shell access, as they can always install their +own forwarders. .It Cm AllowUsers This keyword can be followed by a list of user name patterns, separated by spaces. @@ -1171,6 +1191,33 @@ This option applies to protocol version 1 only. .It Cm ServerKeyBits Defines the number of bits in the ephemeral protocol version 1 server key. The minimum value is 512, and the default is 1024. +.It Cm StreamLocalBindMask +Sets the octal file creation mode mask +.Pq umask +used when creating a Unix-domain socket file for local or remote +port forwarding. +This option is only used for port forwarding to a Unix-domain socket file. +.Pp +The default value is 0177, which creates a Unix-domain socket file that is +readable and writable only by the owner. +Note that not all operating systems honor the file mode on Unix-domain +socket files. +.It Cm StreamLocalBindUnlink +Specifies whether to remove an existing Unix-domain socket file for local +or remote port forwarding before creating a new one. +If the socket file already exists and +.Cm StreamLocalBindUnlink +is not enabled, +.Nm sshd +will be unable to forward the port to the Unix-domain socket file. +This option is only used for port forwarding to a Unix-domain socket file. +.Pp +The argument must be +.Dq yes +or +.Dq no . +The default is +.Dq no . .It Cm StrictModes Specifies whether .Xr sshd 8 diff --git a/sshlogin.c b/sshlogin.c index e79ca9b47..7b951c844 100644 --- a/sshlogin.c +++ b/sshlogin.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshlogin.c,v 1.28 2014/01/31 16:39:19 tedu Exp $ */ +/* $OpenBSD: sshlogin.c,v 1.29 2014/07/15 15:54:14 millert Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -58,6 +58,7 @@ #include "loginrec.h" #include "log.h" #include "buffer.h" +#include "misc.h" #include "servconf.h" extern Buffer loginmsg; -- cgit v1.2.3 From 28ea747089f695e58a476a2849133402d4f86b92 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Sun, 9 Feb 2014 16:09:58 +0000 Subject: Allow harmless group-writability Allow secure files (~/.ssh/config, ~/.ssh/authorized_keys, etc.) to be group-writable, provided that the group in question contains only the file's owner. Rejected upstream for IMO incorrect reasons (e.g. a misunderstanding about the contents of gr->gr_mem). Given that per-user groups and umask 002 are the default setup in Debian (for good reasons - this makes operating in setgid directories with other groups much easier), we need to permit this by default. Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1060 Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=314347 Last-Update: 2013-09-14 Patch-Name: user-group-modes.patch --- auth-rhosts.c | 6 ++---- auth.c | 9 +++----- misc.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- misc.h | 2 ++ platform.c | 16 -------------- readconf.c | 5 +++-- ssh.1 | 2 ++ ssh_config.5 | 2 ++ 8 files changed, 82 insertions(+), 29 deletions(-) (limited to 'auth.c') diff --git a/auth-rhosts.c b/auth-rhosts.c index b5bedee8d..11fcca643 100644 --- a/auth-rhosts.c +++ b/auth-rhosts.c @@ -256,8 +256,7 @@ auth_rhosts2_raw(struct passwd *pw, const char *client_user, const char *hostnam return 0; } if (options.strict_modes && - ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || - (st.st_mode & 022) != 0)) { + !secure_permissions(&st, pw->pw_uid)) { logit("Rhosts authentication refused for %.100s: " "bad ownership or modes for home directory.", pw->pw_name); auth_debug_add("Rhosts authentication refused for %.100s: " @@ -283,8 +282,7 @@ auth_rhosts2_raw(struct passwd *pw, const char *client_user, const char *hostnam * allowing access to their account by anyone. */ if (options.strict_modes && - ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || - (st.st_mode & 022) != 0)) { + !secure_permissions(&st, pw->pw_uid)) { logit("Rhosts authentication refused for %.100s: bad modes for %.200s", pw->pw_name, buf); auth_debug_add("Bad file modes for %.200s", buf); diff --git a/auth.c b/auth.c index 5e60682ce..18de51a29 100644 --- a/auth.c +++ b/auth.c @@ -421,8 +421,7 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host, user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); if (options.strict_modes && (stat(user_hostfile, &st) == 0) && - ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || - (st.st_mode & 022) != 0)) { + !secure_permissions(&st, pw->pw_uid)) { logit("Authentication refused for %.100s: " "bad owner or modes for %.200s", pw->pw_name, user_hostfile); @@ -484,8 +483,7 @@ auth_secure_path(const char *name, struct stat *stp, const char *pw_dir, snprintf(err, errlen, "%s is not a regular file", buf); return -1; } - if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || - (stp->st_mode & 022) != 0) { + if (!secure_permissions(stp, uid)) { snprintf(err, errlen, "bad ownership or modes for file %s", buf); return -1; @@ -500,8 +498,7 @@ auth_secure_path(const char *name, struct stat *stp, const char *pw_dir, strlcpy(buf, cp, sizeof(buf)); if (stat(buf, &st) < 0 || - (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || - (st.st_mode & 022) != 0) { + !secure_permissions(&st, uid)) { snprintf(err, errlen, "bad ownership or modes for directory %s", buf); return -1; diff --git a/misc.c b/misc.c index 94b05b08e..c25ccd80e 100644 --- a/misc.c +++ b/misc.c @@ -50,8 +50,9 @@ #include #ifdef HAVE_PATHS_H # include -#include #endif +#include +#include #ifdef SSH_TUN_OPENBSD #include #endif @@ -60,6 +61,7 @@ #include "misc.h" #include "log.h" #include "ssh.h" +#include "platform.h" /* remove newline at end of string */ char * @@ -644,6 +646,71 @@ read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz, return -1; } +/* + * return 1 if the specified uid is a uid that may own a system directory + * otherwise 0. + */ +int +platform_sys_dir_uid(uid_t uid) +{ + if (uid == 0) + return 1; +#ifdef PLATFORM_SYS_DIR_UID + if (uid == PLATFORM_SYS_DIR_UID) + return 1; +#endif + return 0; +} + +int +secure_permissions(struct stat *st, uid_t uid) +{ + if (!platform_sys_dir_uid(st->st_uid) && st->st_uid != uid) + return 0; + if ((st->st_mode & 002) != 0) + return 0; + if ((st->st_mode & 020) != 0) { + /* If the file is group-writable, the group in question must + * have exactly one member, namely the file's owner. + * (Zero-member groups are typically used by setgid + * binaries, and are unlikely to be suitable.) + */ + struct passwd *pw; + struct group *gr; + int members = 0; + + gr = getgrgid(st->st_gid); + if (!gr) + return 0; + + /* Check primary group memberships. */ + while ((pw = getpwent()) != NULL) { + if (pw->pw_gid == gr->gr_gid) { + ++members; + if (pw->pw_uid != uid) + return 0; + } + } + endpwent(); + + pw = getpwuid(st->st_uid); + if (!pw) + return 0; + + /* Check supplementary group memberships. */ + if (gr->gr_mem[0]) { + ++members; + if (strcmp(pw->pw_name, gr->gr_mem[0]) || + gr->gr_mem[1]) + return 0; + } + + if (!members) + return 0; + } + return 1; +} + int tun_open(int tun, int mode) { diff --git a/misc.h b/misc.h index 374c33ce1..89e1f75d3 100644 --- a/misc.h +++ b/misc.h @@ -135,4 +135,6 @@ char *read_passphrase(const char *, int); int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2))); int read_keyfile_line(FILE *, const char *, char *, size_t, u_long *); +int secure_permissions(struct stat *st, uid_t uid); + #endif /* _MISC_H */ diff --git a/platform.c b/platform.c index f35ec39a8..9a23e6e3e 100644 --- a/platform.c +++ b/platform.c @@ -197,19 +197,3 @@ platform_krb5_get_principal_name(const char *pw_name) return NULL; #endif } - -/* - * return 1 if the specified uid is a uid that may own a system directory - * otherwise 0. - */ -int -platform_sys_dir_uid(uid_t uid) -{ - if (uid == 0) - return 1; -#ifdef PLATFORM_SYS_DIR_UID - if (uid == PLATFORM_SYS_DIR_UID) - return 1; -#endif - return 0; -} diff --git a/readconf.c b/readconf.c index 337818c63..0648867e8 100644 --- a/readconf.c +++ b/readconf.c @@ -38,6 +38,8 @@ #include #include #include +#include +#include #ifdef HAVE_UTIL_H #include #endif @@ -1516,8 +1518,7 @@ read_config_file(const char *filename, struct passwd *pw, const char *host, if (fstat(fileno(f), &sb) == -1) fatal("fstat %s: %s", filename, strerror(errno)); - if (((sb.st_uid != 0 && sb.st_uid != getuid()) || - (sb.st_mode & 022) != 0)) + if (!secure_permissions(&sb, getuid())) fatal("Bad owner or permissions on %s", filename); } diff --git a/ssh.1 b/ssh.1 index fa5cfb2c2..7f6ab77eb 100644 --- a/ssh.1 +++ b/ssh.1 @@ -1342,6 +1342,8 @@ The file format and configuration options are described in .Xr ssh_config 5 . Because of the potential for abuse, this file must have strict permissions: read/write for the user, and not writable by others. +It may be group-writable provided that the group in question contains only +the user. .Pp .It Pa ~/.ssh/environment Contains additional definitions for environment variables; see diff --git a/ssh_config.5 b/ssh_config.5 index ea92ea80d..d68b45a79 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -1587,6 +1587,8 @@ The format of this file is described above. This file is used by the SSH client. Because of the potential for abuse, this file must have strict permissions: read/write for the user, and not accessible by others. +It may be group-writable provided that the group in question contains only +the user. .It Pa /etc/ssh/ssh_config Systemwide configuration file. This file provides defaults for those -- cgit v1.2.3