Import OpenSSH-5.1p1.
authorPeter Avalos <pavalos@dragonflybsd.org>
Sat, 27 Sep 2008 22:32:45 +0000 (22:32 +0000)
committerPeter Avalos <pavalos@dragonflybsd.org>
Sat, 27 Sep 2008 22:32:45 +0000 (22:32 +0000)
89 files changed:
crypto/openssh-5/PROTOCOL [new file with mode: 0644]
crypto/openssh-5/PROTOCOL.agent [new file with mode: 0644]
crypto/openssh-5/README
crypto/openssh-5/addrmatch.c [new file with mode: 0644]
crypto/openssh-5/atomicio.c
crypto/openssh-5/auth-options.c
crypto/openssh-5/auth-rhosts.c
crypto/openssh-5/auth-rsa.c
crypto/openssh-5/auth.c
crypto/openssh-5/auth.h
crypto/openssh-5/auth1.c
crypto/openssh-5/auth2-hostbased.c
crypto/openssh-5/auth2-none.c
crypto/openssh-5/auth2-pubkey.c
crypto/openssh-5/auth2.c
crypto/openssh-5/bufaux.c
crypto/openssh-5/buffer.h
crypto/openssh-5/canohost.c
crypto/openssh-5/channels.c
crypto/openssh-5/channels.h
crypto/openssh-5/clientloop.c
crypto/openssh-5/clientloop.h
crypto/openssh-5/defines.h
crypto/openssh-5/dh.c
crypto/openssh-5/dh.h
crypto/openssh-5/dns.c
crypto/openssh-5/groupaccess.c
crypto/openssh-5/groupaccess.h
crypto/openssh-5/gss-serv.c
crypto/openssh-5/includes.h
crypto/openssh-5/key.c
crypto/openssh-5/key.h
crypto/openssh-5/log.c
crypto/openssh-5/log.h
crypto/openssh-5/mac.c
crypto/openssh-5/match.c
crypto/openssh-5/match.h
crypto/openssh-5/misc.c
crypto/openssh-5/misc.h
crypto/openssh-5/moduli.5 [new file with mode: 0644]
crypto/openssh-5/moduli.c
crypto/openssh-5/monitor.c
crypto/openssh-5/monitor_mm.h
crypto/openssh-5/monitor_wrap.c
crypto/openssh-5/mux.c [new file with mode: 0644]
crypto/openssh-5/nchan.c
crypto/openssh-5/openbsd-compat/bsd-statvfs.h [new file with mode: 0644]
crypto/openssh-5/openbsd-compat/fake-rfc2553.h
crypto/openssh-5/openbsd-compat/fmt_scaled.c [new file with mode: 0644]
crypto/openssh-5/openbsd-compat/openbsd-compat.h
crypto/openssh-5/openbsd-compat/port-tun.c
crypto/openssh-5/packet.c
crypto/openssh-5/packet.h
crypto/openssh-5/readconf.c
crypto/openssh-5/readconf.h
crypto/openssh-5/scp.1
crypto/openssh-5/scp.c
crypto/openssh-5/servconf.c
crypto/openssh-5/servconf.h
crypto/openssh-5/serverloop.c
crypto/openssh-5/session.c
crypto/openssh-5/session.h
crypto/openssh-5/sftp-client.c
crypto/openssh-5/sftp-client.h
crypto/openssh-5/sftp-server.8
crypto/openssh-5/sftp-server.c
crypto/openssh-5/sftp.1
crypto/openssh-5/sftp.c
crypto/openssh-5/sftp.h
crypto/openssh-5/ssh-agent.1
crypto/openssh-5/ssh-agent.c
crypto/openssh-5/ssh-keygen.1
crypto/openssh-5/ssh-keygen.c
crypto/openssh-5/ssh-keyscan.1
crypto/openssh-5/ssh-keyscan.c
crypto/openssh-5/ssh-keysign.8
crypto/openssh-5/ssh.1
crypto/openssh-5/ssh.c
crypto/openssh-5/ssh_config.5
crypto/openssh-5/sshconnect.c
crypto/openssh-5/sshconnect2.c
crypto/openssh-5/sshd.8
crypto/openssh-5/sshd.c
crypto/openssh-5/sshd_config
crypto/openssh-5/sshd_config.5
crypto/openssh-5/sshpty.h
crypto/openssh-5/sshtty.c
crypto/openssh-5/ttymodes.c
crypto/openssh-5/umac.c

diff --git a/crypto/openssh-5/PROTOCOL b/crypto/openssh-5/PROTOCOL
new file mode 100644 (file)
index 0000000..37fd536
--- /dev/null
@@ -0,0 +1,243 @@
+This documents OpenSSH's deviations and extensions to the published SSH
+protocol.
+
+Note that OpenSSH's sftp and sftp-server implement revision 3 of the SSH
+filexfer protocol described in:
+
+http://www.openssh.com/txt/draft-ietf-secsh-filexfer-02.txt
+
+Features from newer versions of the draft are not supported, unless
+explicitly implemented as extensions described below.
+
+The protocol used by OpenSSH's ssh-agent is described in the file
+PROTOCOL.agent
+
+1. transport: Protocol 2 MAC algorithm "umac-64@openssh.com"
+
+This is a new transport-layer MAC method using the UMAC algorithm
+(rfc4418). This method is identical to the "umac-64" method documented
+in:
+
+http://www.openssh.com/txt/draft-miller-secsh-umac-01.txt
+
+2. transport: Protocol 2 compression algorithm "zlib@openssh.com"
+
+This transport-layer compression method uses the zlib compression
+algorithm (identical to the "zlib" method in rfc4253), but delays the
+start of compression until after authentication has completed. This
+avoids exposing compression code to attacks from unauthenticated users.
+
+The method is documented in:
+
+http://www.openssh.com/txt/draft-miller-secsh-compression-delayed-00.txt
+
+3. connection: Channel write close extension "eow@openssh.com"
+
+The SSH connection protocol (rfc4254) provides the SSH_MSG_CHANNEL_EOF
+message to allow an endpoint to signal its peer that it will send no
+more data over a channel. Unfortunately, there is no symmetric way for
+an endpoint to request that its peer should cease sending data to it
+while still keeping the channel open for the endpoint to send data to
+the peer.
+
+This is desirable, since it saves the transmission of data that would
+otherwise need to be discarded and it allows an endpoint to signal local
+processes of the condition, e.g. by closing the corresponding file
+descriptor.
+
+OpenSSH implements a channel extension message to perform this
+signalling: "eow@openssh.com" (End Of Write). This message is sent by
+an endpoint when the local output of a session channel is closed or
+experiences a write error. The message is formatted as follows:
+
+       byte            SSH_MSG_CHANNEL_REQUEST
+       uint32          recipient channel
+       string          "eow@openssh.com"
+       boolean         FALSE
+
+On receiving this message, the peer SHOULD cease sending data of
+the channel and MAY signal the process from which the channel data
+originates (e.g. by closing its read file descriptor).
+
+As with the symmetric SSH_MSG_CHANNEL_EOF message, the channel does
+remain open after a "eow@openssh.com" has been sent and more data may
+still be sent in the other direction. This message does not consume
+window space and may be sent even if no window space is available.
+
+4. connection: disallow additional sessions extension
+   "no-more-sessions@openssh.com"
+
+Most SSH connections will only ever request a single session, but a
+attacker may abuse a running ssh client to surreptitiously open
+additional sessions under their control. OpenSSH provides a global
+request "no-more-sessions@openssh.com" to mitigate this attack.
+
+When an OpenSSH client expects that it will never open another session
+(i.e. it has been started with connection multiplexing disabled), it
+will send the following global request:
+
+       byte            SSH_MSG_GLOBAL_REQUEST
+       string          "no-more-sessions@openssh.com"
+       char            want-reply
+
+On receipt of such a message, an OpenSSH server will refuse to open
+future channels of type "session" and instead immediately abort the
+connection.
+
+Note that this is not a general defence against compromised clients
+(that is impossible), but it thwarts a simple attack.
+
+5. connection: Tunnel forward extension "tun@openssh.com"
+
+OpenSSH supports layer 2 and layer 3 tunnelling via the "tun@openssh.com"
+channel type. This channel type supports forwarding of network packets
+with datagram boundaries intact between endpoints equipped with 
+interfaces like the BSD tun(4) device. Tunnel forwarding channels are
+requested by the client with the following packet:
+
+       byte            SSH_MSG_CHANNEL_OPEN
+       string          "tun@openssh.com"
+       uint32          sender channel
+       uint32          initial window size
+       uint32          maximum packet size
+       uint32          tunnel mode
+       uint32          remote unit number
+
+The "tunnel mode" parameter specifies whether the tunnel should forward
+layer 2 frames or layer 3 packets. It may take one of the following values:
+
+       SSH_TUNMODE_POINTOPOINT  1              /* layer 3 packets */
+       SSH_TUNMODE_ETHERNET     2              /* layer 2 frames */
+
+The "tunnel unit number" specifies the remote interface number, or may
+be zero to allow the server to automatically chose an interface. A server
+that is not willing to open a client-specified unit should refuse the
+request with a SSH_MSG_CHANNEL_OPEN_FAILURE error. On successful open,
+the server should reply with SSH_MSG_CHANNEL_OPEN_SUCCESS.
+
+Once established the client and server may exchange packet or frames
+over the tunnel channel by encapsulating them in SSH protocol strings
+and sending them as channel data. This ensures that packet boundaries
+are kept intact. Specifically, packets are transmitted using normal
+SSH_MSG_CHANNEL_DATA packets:
+
+       byte            SSH_MSG_CHANNEL_DATA
+       uint32          recipient channel
+       string          data
+
+The contents of the "data" field for layer 3 packets is:
+
+       uint32                  packet length
+       uint32                  address family
+       byte[packet length - 4] packet data
+
+The "address family" field identifies the type of packet in the message.
+It may be one of:
+
+       SSH_TUN_AF_INET         2               /* IPv4 */
+       SSH_TUN_AF_INET6        24              /* IPv6 */
+
+The "packet data" field consists of the IPv4/IPv6 datagram itself
+without any link layer header.
+
+The contents of the "data" field for layer 3 packets is:
+
+       uint32                  packet length
+       byte[packet length]     frame
+
+The "frame" field contains an IEEE 802.3 Ethernet frame, including
+header.
+
+6. sftp: Reversal of arguments to SSH_FXP_SYMLINK
+
+When OpenSSH's sftp-server was implemented, the order of the arguments
+to the SSH_FXP_SYMLINK method was inadvertently reversed. Unfortunately,
+the reversal was not noticed until the server was widely deployed. Since
+fixing this to follow the specification would cause incompatibility, the
+current order was retained. For correct operation, clients should send
+SSH_FXP_SYMLINK as follows:
+
+       uint32          id
+       string          targetpath
+       string          linkpath
+
+7. sftp: Server extension announcement in SSH_FXP_VERSION
+
+OpenSSH's sftp-server lists the extensions it supports using the
+standard extension announcement mechanism in the SSH_FXP_VERSION server
+hello packet:
+
+       uint32          3               /* protocol version */
+       string          ext1-name
+       string          ext1-version
+       string          ext2-name
+       string          ext2-version
+       ...
+       string          extN-name
+       string          extN-version
+
+Each extension reports its integer version number as an ASCII encoded
+string, e.g. "1". The version will be incremented if the extension is
+ever changed in an incompatible way. The server MAY advertise the same
+extension with multiple versions (though this is unlikely). Clients MUST
+check the version number before attempting to use the extension.
+
+8. sftp: Extension request "posix-rename@openssh.com"
+
+This operation provides a rename operation with POSIX semantics, which
+are different to those provided by the standard SSH_FXP_RENAME in
+draft-ietf-secsh-filexfer-02.txt. This request is implemented as a
+SSH_FXP_EXTENDED request with the following format:
+
+       uint32          id
+       string          "posix-rename@openssh.com"
+       string          oldpath
+       string          newpath
+
+On receiving this request the server will perform the POSIX operation
+rename(oldpath, newpath) and will respond with a SSH_FXP_STATUS message.
+This extension is advertised in the SSH_FXP_VERSION hello with version
+"1".
+
+9. sftp: Extension requests "statvfs@openssh.com" and
+         "fstatvfs@openssh.com"
+
+These requests correspond to the statvfs and fstatvfs POSIX system
+interfaces. The "statvfs@openssh.com" request operates on an explicit
+pathname, and is formatted as follows:
+
+       uint32          id
+       string          "statvfs@openssh.com"
+       string          path
+
+The "fstatvfs@openssh.com" operates on an open file handle:
+
+       uint32          id
+       string          "fstatvfs@openssh.com"
+       string          handle
+
+These requests return a SSH_FXP_STATUS reply on failure. On success they
+return the following SSH_FXP_EXTENDED_REPLY reply:
+
+       uint32          id
+       uint64          f_bsize         /* file system block size */
+       uint64          f_frsize        /* fundamental fs block size */
+       uint64          f_blocks        /* number of blocks (unit f_frsize) */
+       uint64          f_bfree         /* free blocks in file system */
+       uint64          f_bavail        /* free blocks for non-root */
+       uint64          f_files         /* total file inodes */
+       uint64          f_ffree         /* free file inodes */
+       uint64          f_favail        /* free file inodes for to non-root */
+       uint64          f_fsid          /* file system id */
+       uint64          f_flag          /* bit mask of f_flag values */
+       uint64          f_namemax       /* maximum filename length */
+
+The values of the f_flag bitmask are as follows:
+
+       #define SSH_FXE_STATVFS_ST_RDONLY       0x1     /* read-only */
+       #define SSH_FXE_STATVFS_ST_NOSUID       0x2     /* no setuid */
+
+Both the "statvfs@openssh.com" and "fstatvfs@openssh.com" extensions are
+advertised in the SSH_FXP_VERSION hello with version "2".
+
+$OpenBSD: PROTOCOL,v 1.11 2008/07/05 05:16:01 djm Exp $
diff --git a/crypto/openssh-5/PROTOCOL.agent b/crypto/openssh-5/PROTOCOL.agent
new file mode 100644 (file)
index 0000000..49adbdd
--- /dev/null
@@ -0,0 +1,516 @@
+This describes the protocol used by OpenSSH's ssh-agent.
+
+OpenSSH's agent supports managing keys for the standard SSH protocol
+2 as well as the legacy SSH protocol 1. Support for these key types
+is almost completely disjoint - in all but a few cases, operations on
+protocol 2 keys cannot see or affect protocol 1 keys and vice-versa.
+
+Protocol 1 and protocol 2 keys are separated because of the differing
+cryptographic usage: protocol 1 private RSA keys are used to decrypt
+challenges that were encrypted with the corresponding public key,
+whereas protocol 2 RSA private keys are used to sign challenges with
+a private key for verification with the corresponding public key. It
+is considered unsound practice to use the same key for signing and
+encryption.
+
+With a couple of exceptions, the protocol message names used in this
+document indicate which type of key the message relates to. SSH_*
+messages refer to protocol 1 keys only. SSH2_* messages refer to
+protocol 2 keys. Furthermore, the names also indicate whether the
+message is a request to the agent (*_AGENTC_*) or a reply from the
+agent (*_AGENT_*). Section 3 below contains the mapping of the
+protocol message names to their integer values.
+
+1. Data types
+
+Because of support for legacy SSH protocol 1 keys, OpenSSH's agent
+protocol makes use of some data types not defined in RFC 4251.
+
+1.1 uint16
+
+The "uint16" data type is a simple MSB-first 16 bit unsigned integer
+encoded in two bytes.
+
+1.2 mpint1
+
+The "mpint1" type represents an arbitrary precision integer (bignum).
+Its format is as follows:
+
+       uint16                  bits
+       byte[(bits + 7) / 8]    bignum
+
+"bignum" contains an unsigned arbitrary precision integer encoded as
+eight bits per byte in big-endian (MSB first) format.
+
+Note the difference between the "mpint1" encoding and the "mpint"
+encoding defined in RFC 4251. Also note that the length of the encoded
+integer is specified in bits, not bytes and that the byte length of
+the integer must be calculated by rounding up the number of bits to the
+nearest eight.
+
+2. Protocol Messages
+
+All protocol messages are prefixed with their length in bytes, encoded
+as a 32 bit unsigned integer. Specifically:
+
+       uint32                  message_length
+       byte[message_length]    message
+
+The following message descriptions refer only to the content the
+"message" field.
+
+2.1 Generic server responses
+
+The following generic messages may be sent by the server in response to
+requests from the client. On success the agent may reply either with:
+
+       byte                    SSH_AGENT_SUCCESS
+
+or a request-specific success message.
+
+On failure, the agent may reply with:
+
+       byte                    SSH_AGENT_FAILURE
+
+SSH_AGENT_FAILURE messages are also sent in reply to unknown request
+types.
+
+2.2 Adding keys to the agent
+
+Keys are added to the agent using the SSH_AGENTC_ADD_RSA_IDENTITY and
+SSH2_AGENTC_ADD_IDENTITY requests for protocol 1 and protocol 2 keys
+respectively.
+
+Two variants of these requests are SSH_AGENTC_ADD_RSA_ID_CONSTRAINED
+and SSH2_AGENTC_ADD_ID_CONSTRAINED - these add keys with optional
+"constraints" on their usage.
+
+OpenSSH may be built with support for keys hosted on a smartcard
+or other hardware security module. These keys may be added
+to the agent using the SSH_AGENTC_ADD_SMARTCARD_KEY and
+SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED requests.
+
+2.2.1 Key constraints
+
+The OpenSSH agent supports some basic optional constraints on key usage.
+At present there are two constraints defined.
+
+The first constraint limits the validity duration of a key. It is
+encoded as:
+
+       byte                    SSH_AGENT_CONSTRAIN_LIFETIME
+       uint32                  seconds
+
+Where "seconds" contains the number of seconds that the key shall remain
+valid measured from the moment that the agent receives it. After the
+validity period has expired, OpenSSH's agent will erase these keys from
+memory.
+
+The second constraint requires the agent to seek explicit user
+confirmation before performing private key operations with the loaded
+key. This constraint is encoded as:
+
+       byte                    SSH_AGENT_CONSTRAIN_CONFIRM
+
+Zero or more constraints may be specified when adding a key with one
+of the *_CONSTRAINED requests. Multiple constraints are appended
+consecutively to the end of the request:
+
+       byte                    constraint1_type
+       ....                    constraint1_data
+       byte                    constraint2_type
+       ....                    constraint2_data
+       ....
+       byte                    constraintN_type
+       ....                    constraintN_data
+
+Such a sequence of zero or more constraints will be referred to below
+as "constraint[]". Agents may determine whether there are constraints
+by checking whether additional data exists in the "add key" request
+after the key data itself. OpenSSH will refuse to add a key if it
+contains unknown constraints.
+
+2.2.2 Add protocol 1 key
+
+A client may add a protocol 1 key to an agent with the following
+request:
+
+       byte                    SSH_AGENTC_ADD_RSA_IDENTITY or
+                               SSH_AGENTC_ADD_RSA_ID_CONSTRAINED
+       uint32                  ignored
+       mpint1                  rsa_n
+       mpint1                  rsa_e
+       mpint1                  rsa_d
+       mpint1                  rsa_iqmp
+       mpint1                  rsa_q
+       mpint1                  rsa_p
+       string                  key_comment
+       constraint[]            key_constraints
+
+Note that there is some redundancy in the key parameters; a key could be
+fully specified using just rsa_q, rsa_p and rsa_e at the cost of extra
+computation.
+
+"key_constraints" may only be present if the request type is
+SSH_AGENTC_ADD_RSA_IDENTITY.
+
+The agent will reply with a SSH_AGENT_SUCCESS if the key has been
+successfully added or a SSH_AGENT_FAILURE if an error occurred.
+
+2.2.3 Add protocol 2 key
+
+The OpenSSH agent supports DSA and RSA keys for protocol 2. DSA keys may
+be added using the following request
+
+       byte                    SSH2_AGENTC_ADD_IDENTITY or
+                               SSH2_AGENTC_ADD_ID_CONSTRAINED
+       string                  "ssh-dss"
+       mpint                   dsa_p
+       mpint                   dsa_q
+       mpint                   dsa_g
+       mpint                   dsa_public_key
+       mpint                   dsa_private_key
+       string                  key_comment
+       constraint[]            key_constraints
+
+RSA keys may be added with this request:
+
+       byte                    SSH2_AGENTC_ADD_IDENTITY or
+                               SSH2_AGENTC_ADD_ID_CONSTRAINED
+       string                  "ssh-rsa"
+       mpint                   rsa_n
+       mpint                   rsa_e
+       mpint                   rsa_d
+       mpint                   rsa_iqmp
+       mpint                   rsa_p
+       mpint                   rsa_q
+       string                  key_comment
+       constraint[]            key_constraints
+
+Note that the 'rsa_p' and 'rsa_q' parameters are sent in the reverse
+order to the protocol 1 add keys message. As with the corresponding
+protocol 1 "add key" request, the private key is overspecified to avoid
+redundant processing.
+
+For both DSA and RSA key add requests, "key_constraints" may only be
+present if the request type is SSH2_AGENTC_ADD_ID_CONSTRAINED.
+
+The agent will reply with a SSH_AGENT_SUCCESS if the key has been
+successfully added or a SSH_AGENT_FAILURE if an error occurred.
+
+2.2.4 Loading keys from a smartcard
+
+The OpenSSH agent may have optional smartcard support built in to it. If
+so, it supports an operation to load keys from a smartcard. Technically,
+only the public components of the keys are loaded into the agent so
+this operation really arranges for future private key operations to be
+delegated to the smartcard.
+
+       byte                    SSH_AGENTC_ADD_SMARTCARD_KEY or
+                               SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED
+       string                  reader_id
+       string                  pin
+       constraint[]            key_constraints
+
+"reader_id" is an identifier to a smartcard reader and "pin"
+is a PIN or passphrase used to unlock the private key(s) on the
+device. "key_constraints" may only be present if the request type is
+SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED.
+
+This operation may load all SSH keys that are unlocked using the
+"pin" on the specified reader. The type of key loaded (protocol 1
+or protocol 2) will be specified by the smartcard itself, it is not
+client-specified.
+
+The agent will reply with a SSH_AGENT_SUCCESS if one or more keys have
+been successfully loaded or a SSH_AGENT_FAILURE if an error occurred.
+The agent will also return SSH_AGENT_FAILURE if it does not support
+smartcards.
+
+2.3 Removing multiple keys
+
+A client may request that an agent delete all protocol 1 keys using the
+following request:
+
+       byte                    SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES
+
+This message requests the deletion of all protocol 2 keys:
+
+       byte                    SSH2_AGENTC_REMOVE_ALL_IDENTITIES
+
+On success, the agent will delete all keys of the requested type and
+reply with a SSH_AGENT_SUCCESS message. If an error occurred, the agent
+will reply with SSH_AGENT_FAILURE.
+
+Note that, to delete all keys (both protocol 1 and 2), a client
+must send both a SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES and a
+SSH2_AGENTC_REMOVE_ALL_IDENTITIES request.
+
+2.4 Removing specific keys
+
+2.4.1 Removing a protocol 1 key
+
+Removal of a protocol 1 key may be requested with the following message:
+
+       byte                    SSH_AGENTC_REMOVE_RSA_IDENTITY
+       uint32                  key_bits
+       mpint1                  rsa_e
+       mpint1                  rsa_n
+
+Note that key_bits is strictly redundant, as it may be inferred by the
+length of rsa_n.
+
+The agent will delete any private key matching the specified public key
+and return SSH_AGENT_SUCCESS. If no such key was found, the agent will
+return SSH_AGENT_FAILURE.
+
+2.4.2 Removing a protocol 2 key
+
+Protocol 2 keys may be removed with the following request:
+
+       byte                    SSH2_AGENTC_REMOVE_IDENTITY
+       string                  key_blob
+
+Where "key_blob" is encoded as per RFC 4253 section 6.6 "Public Key
+Algorithms" for either of the supported key types: "ssh-dss" or
+"ssh-rsa".
+
+The agent will delete any private key matching the specified public key
+and return SSH_AGENT_SUCCESS. If no such key was found, the agent will
+return SSH_AGENT_FAILURE.
+
+2.4.3 Removing keys loaded from a smartcard
+
+A client may request that a server remove one or more smartcard-hosted
+keys using this message:
+
+       byte                    SSH_AGENTC_REMOVE_SMARTCARD_KEY
+       string                  reader_id
+       string                  pin
+
+"reader_id" the an identifier to a smartcard reader and "pin" is a PIN
+or passphrase used to unlock the private key(s) on the device.
+
+When this message is received, and if the agent supports
+smartcard-hosted keys, it will delete all keys that are hosted on the
+specified smartcard that may be accessed with the given "pin".
+
+The agent will reply with a SSH_AGENT_SUCCESS if one or more keys have
+been successfully removed or a SSH_AGENT_FAILURE if an error occurred.
+The agent will also return SSH_AGENT_FAILURE if it does not support
+smartcards.
+
+2.5 Requesting a list of known keys
+
+An agent may be requested to list which keys it holds. Different
+requests exist for protocol 1 and protocol 2 keys.
+
+2.5.1 Requesting a list of protocol 1 keys
+
+To request a list of protocol 1 keys that are held in the agent, a
+client may send the following message:
+
+       byte                    SSH_AGENTC_REQUEST_RSA_IDENTITIES
+
+The agent will reply with the following message:
+
+       byte                    SSH_AGENT_RSA_IDENTITIES_ANSWER
+       uint32                  num_keys
+
+Followed by zero or more consecutive keys, encoded as:
+
+       uint32                  bits
+       mpint1                  rsa_e
+       mpint1                  rsa_n
+       string                  key_comment
+
+2.5.2 Requesting a list of protocol 2 keys
+
+A client may send the following message to request a list of
+protocol 2 keys that are stored in the agent:
+
+       byte                    SSH2_AGENTC_REQUEST_IDENTITIES
+
+The agent will reply with the following message header:
+
+       byte                    SSH2_AGENT_IDENTITIES_ANSWER
+       uint32                  num_keys
+
+Followed by zero or more consecutive keys, encoded as:
+
+       string                  key_blob
+       string                  key_comment
+
+Where "key_blob" is encoded as per RFC 4253 section 6.6 "Public Key
+Algorithms" for either of the supported key types: "ssh-dss" or
+"ssh-rsa".
+
+2.6 Private key operations
+
+The purpose of the agent is to perform private key operations, such as
+signing and encryption without requiring a passphrase to unlock the
+key and without allowing the private key itself to be exposed. There
+are separate requests for the protocol 1 and protocol 2 private key
+operations.
+
+2.6.1 Protocol 1 private key challenge
+
+The private key operation used in version 1 of the SSH protocol is
+decrypting a challenge that has been encrypted with a public key.
+It may be requested using this message:
+
+       byte                    SSH_AGENTC_RSA_CHALLENGE
+       uint32                  ignored
+       mpint1                  rsa_e
+       mpint1                  rsa_n
+       mpint1                  encrypted_challenge
+       byte[16]                session_id
+       uint32                  response_type /* must be 1 */
+
+"rsa_e" and "rsa_n" are used to identify which private key to use.
+"encrypted_challenge" is a challenge blob that has (presumably)
+been encrypted with the public key and must be in the range 
+1 <= encrypted_challenge < 2^256. "session_id" is the SSH protocol 1
+session ID (computed from the server host key, the server semi-ephemeral
+key and the session cookie).
+
+"ignored" and "response_type" exist for compatibility with legacy
+implementations. "response_type" must be equal to 1; other response
+types are not supported.
+
+On receiving this request, the server decrypts the "encrypted_challenge"
+using the private key matching the supplied (rsa_e, rsa_n) values. For
+the response derivation, the decrypted challenge is represented as an
+unsigned, big-endian integer encoded in a 32 byte buffer (i.e. values
+smaller than 2^248 will have leading 0 bytes).
+
+The response value is then calculated as:
+
+       response = MD5(decrypted_challenge || session_id)
+
+and returned in the following message
+
+       byte                    SSH_AGENT_RSA_RESPONSE
+       byte[16]                response
+
+If the agent cannot find the key specified by the supplied (rsa_e,
+rsa_n) then it will return SSH_AGENT_FAILURE.
+
+2.6.2 Protocol 2 private key signature request
+
+A client may use the following message to request signing of data using
+a protocol 2 key:
+
+       byte                    SSH2_AGENTC_SIGN_REQUEST
+       string                  key_blob
+       string                  data
+       uint32                  flags
+
+Where "key_blob" is encoded as per RFC 4253 section 6.6 "Public Key
+Algorithms" for either of the supported key types: "ssh-dss" or
+"ssh-rsa". "flags" is a bit-mask, but at present only one possible value
+is defined (see below for its meaning):
+
+       SSH_AGENT_OLD_SIGNATURE         1
+
+Upon receiving this request, the agent will look up the private key that
+corresponds to the public key contained in key_blob. It will use this
+private key to sign the "data" and produce a signature blob using the
+key type-specific method described in RFC 4253 section 6.6 "Public Key
+Algorithms".
+
+An exception to this is for "ssh-dss" keys where the "flags" word
+contains the value SSH_AGENT_OLD_SIGNATURE. In this case, a legacy
+signature encoding is used in lieu of the standard one. In this case,
+the DSA signature blob is encoded as:
+
+       byte[40]                signature
+
+The signature will be returned in the response message:
+
+       byte                    SSH2_AGENT_SIGN_RESPONSE
+       string                  signature_blob
+
+If the agent cannot find the key specified by the supplied key_blob then
+it will return SSH_AGENT_FAILURE.
+
+2.7 Locking or unlocking an agent
+
+The agent supports temporary locking with a passphrase to suspend
+processing of sensitive operations until it has been unlocked with the
+same passphrase. To lock an agent, a client send the following request:
+
+       byte                    SSH_AGENTC_LOCK
+       string                  passphrase
+
+Upon receipt of this message and if the agent is not already locked,
+it will suspend processing requests and return a SSH_AGENT_SUCCESS
+reply. If the agent is already locked, it will return SSH_AGENT_FAILURE.
+
+While locked, the agent will refuse all requests except
+SSH_AGENTC_UNLOCK, SSH_AGENTC_REQUEST_RSA_IDENTITIES and
+SSH2_AGENTC_REQUEST_IDENTITIES. The "request identities" requests are
+treated specially by a locked agent: it will always return an empty list
+of keys.
+
+To unlock an agent, a client may request:
+
+       byte                    SSH_AGENTC_UNLOCK
+       string                  passphrase
+
+If the passphrase matches and the agent is locked, then it will resume
+processing all requests and return SSH_AGENT_SUCCESS. If the agent
+is not locked or the passphrase does not match then it will return
+SSH_AGENT_FAILURE.
+
+Locking and unlocking affects both protocol 1 and protocol 2 keys.
+
+3. Protocol message numbers
+
+3.1 Requests from client to agent for protocol 1 key operations
+
+       SSH_AGENTC_REQUEST_RSA_IDENTITIES               1
+       SSH_AGENTC_RSA_CHALLENGE                        3
+       SSH_AGENTC_ADD_RSA_IDENTITY                     7
+       SSH_AGENTC_REMOVE_RSA_IDENTITY                  8
+       SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES            9
+       SSH_AGENTC_ADD_RSA_ID_CONSTRAINED               24
+
+3.2 Requests from client to agent for protocol 2 key operations
+
+       SSH2_AGENTC_REQUEST_IDENTITIES                  11
+       SSH2_AGENTC_SIGN_REQUEST                        13
+       SSH2_AGENTC_ADD_IDENTITY                        17
+       SSH2_AGENTC_REMOVE_IDENTITY                     18
+       SSH2_AGENTC_REMOVE_ALL_IDENTITIES               19
+       SSH2_AGENTC_ADD_ID_CONSTRAINED                  25
+
+3.3 Key-type independent requests from client to agent
+
+       SSH_AGENTC_ADD_SMARTCARD_KEY                    20
+       SSH_AGENTC_REMOVE_SMARTCARD_KEY                 21
+       SSH_AGENTC_LOCK                                 22
+       SSH_AGENTC_UNLOCK                               23
+       SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED        26
+
+3.4 Generic replies from agent to client
+
+       SSH_AGENT_FAILURE                               5
+       SSH_AGENT_SUCCESS                               6
+
+3.5 Replies from agent to client for protocol 1 key operations
+
+       SSH_AGENT_RSA_IDENTITIES_ANSWER                 2
+       SSH_AGENT_RSA_RESPONSE                          4
+
+3.6 Replies from agent to client for protocol 2 key operations
+
+       SSH2_AGENT_IDENTITIES_ANSWER                    12
+       SSH2_AGENT_SIGN_RESPONSE                        14
+
+3.7 Key constraint identifiers
+
+       SSH_AGENT_CONSTRAIN_LIFETIME                    1
+       SSH_AGENT_CONSTRAIN_CONFIRM                     2
+
+$OpenBSD: PROTOCOL.agent,v 1.4 2008/07/01 23:12:47 stevesk Exp $
index d99ceb6..183d92f 100644 (file)
@@ -1,4 +1,4 @@
-See http://www.openssh.com/txt/release-5.0 for the release notes.
+See http://www.openssh.com/txt/release-5.1 for the release notes.
 
 - A Japanese translation of this document and of the OpenSSH FAQ is
 - available at http://www.unixuser.org/~haruyama/security/openssh/index.html
@@ -62,4 +62,4 @@ References -
 [6] http://www.openbsd.org/cgi-bin/man.cgi?query=style&sektion=9
 [7] http://www.openssh.com/faq.html
 
-$Id: README,v 1.68 2008/04/03 09:56:38 djm Exp $
+$Id: README,v 1.69 2008/07/21 08:21:52 djm Exp $
diff --git a/crypto/openssh-5/addrmatch.c b/crypto/openssh-5/addrmatch.c
new file mode 100644 (file)
index 0000000..2086afe
--- /dev/null
@@ -0,0 +1,421 @@
+/*     $OpenBSD: addrmatch.c,v 1.3 2008/06/10 23:06:19 djm Exp $ */
+
+/*
+ * Copyright (c) 2004-2008 Damien Miller <djm@mindrot.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "match.h"
+#include "log.h"
+
+struct xaddr {
+       sa_family_t     af;
+       union {
+               struct in_addr          v4;
+               struct in6_addr         v6;
+               u_int8_t                addr8[16];
+               u_int32_t               addr32[4];
+       } xa;               /* 128-bit address */
+       u_int32_t       scope_id;       /* iface scope id for v6 */
+#define v4     xa.v4
+#define v6     xa.v6
+#define addr8  xa.addr8
+#define addr32 xa.addr32
+};
+
+static int
+addr_unicast_masklen(int af)
+{
+       switch (af) {
+       case AF_INET:
+               return 32;
+       case AF_INET6:
+               return 128;
+       default:
+               return -1;
+       }
+}
+
+static inline int
+masklen_valid(int af, u_int masklen)
+{
+       switch (af) {
+       case AF_INET:
+               return masklen <= 32 ? 0 : -1;
+       case AF_INET6:
+               return masklen <= 128 ? 0 : -1;
+       default:
+               return -1;
+       }
+}
+
+/*
+ * Convert struct sockaddr to struct xaddr
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+addr_sa_to_xaddr(struct sockaddr *sa, socklen_t slen, struct xaddr *xa)
+{
+       struct sockaddr_in *in4 = (struct sockaddr_in *)sa;
+       struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)sa;
+
+       memset(xa, '\0', sizeof(*xa));
+
+       switch (sa->sa_family) {
+       case AF_INET:
+               if (slen < sizeof(*in4))
+                       return -1;
+               xa->af = AF_INET;
+               memcpy(&xa->v4, &in4->sin_addr, sizeof(xa->v4));
+               break;
+       case AF_INET6:
+               if (slen < sizeof(*in6))
+                       return -1;
+               xa->af = AF_INET6;
+               memcpy(&xa->v6, &in6->sin6_addr, sizeof(xa->v6));
+               xa->scope_id = in6->sin6_scope_id;
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
+/*
+ * Calculate a netmask of length 'l' for address family 'af' and
+ * store it in 'n'.
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+addr_netmask(int af, u_int l, struct xaddr *n)
+{
+       int i;
+
+       if (masklen_valid(af, l) != 0 || n == NULL)
+               return -1;
+
+       memset(n, '\0', sizeof(*n));
+       switch (af) {
+       case AF_INET:
+               n->af = AF_INET;
+               n->v4.s_addr = htonl((0xffffffff << (32 - l)) & 0xffffffff);
+               return 0;
+       case AF_INET6:
+               n->af = AF_INET6;
+               for (i = 0; i < 4 && l >= 32; i++, l -= 32)
+                       n->addr32[i] = 0xffffffffU;
+               if (i < 4 && l != 0)
+                       n->addr32[i] = htonl((0xffffffff << (32 - l)) &
+                           0xffffffff);
+               return 0;
+       default:
+               return -1;
+       }
+}
+
+/*
+ * Perform logical AND of addresses 'a' and 'b', storing result in 'dst'.
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+addr_and(struct xaddr *dst, const struct xaddr *a, const struct xaddr *b)
+{
+       int i;
+
+       if (dst == NULL || a == NULL || b == NULL || a->af != b->af)
+               return -1;
+
+       memcpy(dst, a, sizeof(*dst));
+       switch (a->af) {
+       case AF_INET:
+               dst->v4.s_addr &= b->v4.s_addr;
+               return 0;
+       case AF_INET6:
+               dst->scope_id = a->scope_id;
+               for (i = 0; i < 4; i++)
+                       dst->addr32[i] &= b->addr32[i];
+               return 0;
+       default:
+               return -1;
+       }
+}
+
+/*
+ * Compare addresses 'a' and 'b'
+ * Return 0 if addresses are identical, -1 if (a < b) or 1 if (a > b)
+ */
+static int
+addr_cmp(const struct xaddr *a, const struct xaddr *b)
+{
+       int i;
+
+       if (a->af != b->af)
+               return a->af == AF_INET6 ? 1 : -1;
+
+       switch (a->af) {
+       case AF_INET:
+               if (a->v4.s_addr == b->v4.s_addr)
+                       return 0;
+               return ntohl(a->v4.s_addr) > ntohl(b->v4.s_addr) ? 1 : -1;
+       case AF_INET6:
+               for (i = 0; i < 16; i++)
+                       if (a->addr8[i] - b->addr8[i] != 0)
+                               return a->addr8[i] > b->addr8[i] ? 1 : -1;
+               if (a->scope_id == b->scope_id)
+                       return 0;
+               return a->scope_id > b->scope_id ? 1 : -1;
+       default:
+               return -1;
+       }
+}
+
+/*
+ * Parse string address 'p' into 'n'
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+addr_pton(const char *p, struct xaddr *n)
+{
+       struct addrinfo hints, *ai;
+
+       memset(&hints, '\0', sizeof(hints));
+       hints.ai_flags = AI_NUMERICHOST;
+
+       if (p == NULL || getaddrinfo(p, NULL, &hints, &ai) != 0)
+               return -1;
+
+       if (ai == NULL || ai->ai_addr == NULL)
+               return -1;
+
+       if (n != NULL &&
+           addr_sa_to_xaddr(ai->ai_addr, ai->ai_addrlen, n) == -1) {
+               freeaddrinfo(ai);
+               return -1;
+       }
+
+       freeaddrinfo(ai);
+       return 0;
+}
+
+/*
+ * Perform bitwise negation of address
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+addr_invert(struct xaddr *n)
+{
+       int i;
+
+       if (n == NULL)
+               return (-1);
+
+       switch (n->af) {
+       case AF_INET:
+               n->v4.s_addr = ~n->v4.s_addr;
+               return (0);
+       case AF_INET6:
+               for (i = 0; i < 4; i++)
+                       n->addr32[i] = ~n->addr32[i];
+               return (0);
+       default:
+               return (-1);
+       }
+}
+
+/*
+ * Calculate a netmask of length 'l' for address family 'af' and
+ * store it in 'n'.
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+addr_hostmask(int af, u_int l, struct xaddr *n)
+{
+       if (addr_netmask(af, l, n) == -1 || addr_invert(n) == -1)
+               return (-1);
+       return (0);
+}
+
+/*
+ * Test whether address 'a' is all zeros (i.e. 0.0.0.0 or ::)
+ * Returns 0 on if address is all-zeros, -1 if not all zeros or on failure.
+ */
+static int
+addr_is_all0s(const struct xaddr *a)
+{
+       int i;
+
+       switch (a->af) {
+       case AF_INET:
+               return (a->v4.s_addr == 0 ? 0 : -1);
+       case AF_INET6:;
+               for (i = 0; i < 4; i++)
+                       if (a->addr32[i] != 0)
+                               return (-1);
+               return (0);
+       default:
+               return (-1);
+       }
+}
+
+/*
+ * Test whether host portion of address 'a', as determined by 'masklen'
+ * is all zeros.
+ * Returns 0 on if host portion of address is all-zeros,
+ * -1 if not all zeros or on failure.
+ */
+static int
+addr_host_is_all0s(const struct xaddr *a, u_int masklen)
+{
+       struct xaddr tmp_addr, tmp_mask, tmp_result;
+
+       memcpy(&tmp_addr, a, sizeof(tmp_addr));
+       if (addr_hostmask(a->af, masklen, &tmp_mask) == -1)
+               return (-1);
+       if (addr_and(&tmp_result, &tmp_addr, &tmp_mask) == -1)
+               return (-1);
+       return (addr_is_all0s(&tmp_result));
+}
+
+/*
+ * Parse a CIDR address (x.x.x.x/y or xxxx:yyyy::/z).
+ * Return -1 on parse error, -2 on inconsistency or 0 on success.
+ */
+static int
+addr_pton_cidr(const char *p, struct xaddr *n, u_int *l)
+{
+       struct xaddr tmp;
+       long unsigned int masklen = 999;
+       char addrbuf[64], *mp, *cp;
+
+       /* Don't modify argument */
+       if (p == NULL || strlcpy(addrbuf, p, sizeof(addrbuf)) > sizeof(addrbuf))
+               return -1;
+
+       if ((mp = strchr(addrbuf, '/')) != NULL) {
+               *mp = '\0';
+               mp++;
+               masklen = strtoul(mp, &cp, 10);
+               if (*mp == '\0' || *cp != '\0' || masklen > 128)
+                       return -1;
+       }
+
+       if (addr_pton(addrbuf, &tmp) == -1)
+               return -1;
+
+       if (mp == NULL)
+               masklen = addr_unicast_masklen(tmp.af);
+       if (masklen_valid(tmp.af, masklen) == -1)
+               return -2;
+       if (addr_host_is_all0s(&tmp, masklen) != 0)
+               return -2;
+
+       if (n != NULL)
+               memcpy(n, &tmp, sizeof(*n));
+       if (l != NULL)
+               *l = masklen;
+
+       return 0;
+}
+
+static int
+addr_netmatch(const struct xaddr *host, const struct xaddr *net, u_int masklen)
+{
+       struct xaddr tmp_mask, tmp_result;
+
+       if (host->af != net->af)
+               return -1;
+
+       if (addr_netmask(host->af, masklen, &tmp_mask) == -1)
+               return -1;
+       if (addr_and(&tmp_result, host, &tmp_mask) == -1)
+               return -1;
+       return addr_cmp(&tmp_result, net);
+}
+
+/*
+ * Match "addr" against list pattern list "_list", which may contain a
+ * mix of CIDR addresses and old-school wildcards.
+ *
+ * If addr is NULL, then no matching is performed, but _list is parsed
+ * and checked for well-formedness.
+ *
+ * Returns 1 on match found (never returned when addr == NULL).
+ * Returns 0 on if no match found, or no errors found when addr == NULL.
+ * Returns -1 on negated match found (never returned when addr == NULL).
+ * Returns -2 on invalid list entry.
+ */
+int
+addr_match_list(const char *addr, const char *_list)
+{
+       char *list, *cp, *o;
+       struct xaddr try_addr, match_addr;
+       u_int masklen, neg;
+       int ret = 0, r;
+
+       if (addr != NULL && addr_pton(addr, &try_addr) != 0) {
+               debug2("%s: couldn't parse address %.100s", __func__, addr);
+               return 0;
+       }
+       if ((o = list = strdup(_list)) == NULL)
+               return -1;
+       while ((cp = strsep(&list, ",")) != NULL) {
+               neg = *cp == '!';
+               if (neg)
+                       cp++;
+               if (*cp == '\0') {
+                       ret = -2;
+                       break;
+               }
+               /* Prefer CIDR address matching */
+               r = addr_pton_cidr(cp, &match_addr, &masklen);
+               if (r == -2) {
+                       error("Inconsistent mask length for "
+                           "network \"%.100s\"", cp);
+                       ret = -2;
+                       break;
+               } else if (r == 0) {
+                       if (addr != NULL && addr_netmatch(&try_addr,
+                           &match_addr, masklen) == 0) {
+ foundit:
+                               if (neg) {
+                                       ret = -1;
+                                       break;
+                               }
+                               ret = 1;
+                       }
+                       continue;
+               } else {
+                       /* If CIDR parse failed, try wildcard string match */
+                       if (addr != NULL && match_pattern(addr, cp) == 1)
+                               goto foundit;
+               }
+       }
+       free(o);
+
+       return ret;
+}
index 575bf89..a6b2d12 100644 (file)
@@ -63,11 +63,7 @@ atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n)
                case -1:
                        if (errno == EINTR)
                                continue;
-#ifdef EWOULDBLOCK
                        if (errno == EAGAIN || errno == EWOULDBLOCK) {
-#else
-                       if (errno == EAGAIN) {
-#endif
                                (void)poll(&pfd, 1, -1);
                                continue;
                        }
@@ -101,20 +97,20 @@ atomiciov(ssize_t (*f) (int, const struct iovec *, int), int fd,
        /* Make a copy of the iov array because we may modify it below */
        memcpy(iov, _iov, iovcnt * sizeof(*_iov));
 
+#ifndef BROKEN_READV_COMPARISON
        pfd.fd = fd;
        pfd.events = f == readv ? POLLIN : POLLOUT;
+#endif
        for (; iovcnt > 0 && iov[0].iov_len > 0;) {
                res = (f) (fd, iov, iovcnt);
                switch (res) {
                case -1:
                        if (errno == EINTR)
                                continue;
-#ifdef EWOULDBLOCK
                        if (errno == EAGAIN || errno == EWOULDBLOCK) {
-#else
-                       if (errno == EAGAIN) {
-#endif
+#ifndef BROKEN_READV_COMPARISON
                                (void)poll(&pfd, 1, -1);
+#endif
                                continue;
                        }
                        return 0;
index 6e22569..2536145 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth-options.c,v 1.41 2008/03/26 21:28:14 djm Exp $ */
+/* $OpenBSD: auth-options.c,v 1.43 2008/06/10 23:06:19 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include <stdarg.h>
 
+#include "openbsd-compat/sys-queue.h"
 #include "xmalloc.h"
 #include "match.h"
 #include "log.h"
@@ -225,8 +226,19 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum)
                        }
                        patterns[i] = '\0';
                        opts++;
-                       if (match_host_and_ip(remote_host, remote_ip,
-                           patterns) != 1) {
+                       switch (match_host_and_ip(remote_host, remote_ip,
+                           patterns)) {
+                       case 1:
+                               xfree(patterns);
+                               /* Host name matches. */
+                               goto next_option;
+                       case -1:
+                               debug("%.100s, line %lu: invalid criteria",
+                                   file, linenum);
+                               auth_debug_add("%.100s, line %lu: "
+                                   "invalid criteria", file, linenum);
+                               /* FALLTHROUGH */
+                       case 0:
                                xfree(patterns);
                                logit("Authentication tried for %.100s with "
                                    "correct key but not from a permitted "
@@ -235,12 +247,10 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum)
                                auth_debug_add("Your host '%.200s' is not "
                                    "permitted to use this key for login.",
                                    remote_host);
-                               /* deny access */
-                               return 0;
+                               break;
                        }
-                       xfree(patterns);
-                       /* Host name matches. */
-                       goto next_option;
+                       /* deny access */
+                       return 0;
                }
                cp = "permitopen=\"";
                if (strncasecmp(opts, cp, strlen(cp)) == 0) {
index cd0a796..5c12967 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth-rhosts.c,v 1.41 2006/08/03 03:34:41 deraadt Exp $ */
+/* $OpenBSD: auth-rhosts.c,v 1.43 2008/06/13 14:18:51 dtucker Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -26,6 +26,8 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdarg.h>
+#include <fcntl.h>
+#include <unistd.h>
 
 #include "packet.h"
 #include "buffer.h"
@@ -37,6 +39,7 @@
 #include "key.h"
 #include "hostfile.h"
 #include "auth.h"
+#include "misc.h"
 
 /* import */
 extern ServerOptions options;
@@ -55,12 +58,27 @@ check_rhosts_file(const char *filename, const char *hostname,
 {
        FILE *f;
        char buf[1024]; /* Must not be larger than host, user, dummy below. */
+       int fd;
+       struct stat st;
 
        /* Open the .rhosts file, deny if unreadable */
-       f = fopen(filename, "r");
-       if (!f)
+       if ((fd = open(filename, O_RDONLY|O_NONBLOCK)) == -1)
                return 0;
-
+       if (fstat(fd, &st) == -1) {
+               close(fd);
+               return 0;
+       }
+       if (!S_ISREG(st.st_mode)) {
+               logit("User %s hosts file %s is not a regular file",
+                   server_user, filename);
+               close(fd);
+               return 0;
+       }
+       unset_nonblock(fd);
+       if ((f = fdopen(fd, "r")) == NULL) {
+               close(fd);
+               return 0;
+       }
        while (fgets(buf, sizeof(buf), f)) {
                /* All three must be at least as big as buf to avoid overflows. */
                char hostbuf[1024], userbuf[1024], dummy[1024], *host, *user, *cp;
index 69f9a58..bf54620 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth-rsa.c,v 1.72 2006/11/06 21:25:27 markus Exp $ */
+/* $OpenBSD: auth-rsa.c,v 1.73 2008/07/02 12:03:51 dtucker Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -173,7 +173,6 @@ auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey)
        u_int bits;
        FILE *f;
        u_long linenum = 0;
-       struct stat st;
        Key *key;
 
        /* Temporarily use the user's uid. */
@@ -182,27 +181,9 @@ auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey)
        /* The authorized keys. */
        file = authorized_keys_file(pw);
        debug("trying public RSA key file %s", file);
-
-       /* Fail quietly if file does not exist */
-       if (stat(file, &st) < 0) {
-               /* Restore the privileged uid. */
-               restore_uid();
-               xfree(file);
-               return (0);
-       }
-       /* Open the file containing the authorized keys. */
-       f = fopen(file, "r");
+       f = auth_openkeyfile(file, pw, options.strict_modes);
        if (!f) {
-               /* Restore the privileged uid. */
-               restore_uid();
-               xfree(file);
-               return (0);
-       }
-       if (options.strict_modes &&
-           secure_filename(f, file, pw, line, sizeof(line)) != 0) {
                xfree(file);
-               fclose(f);
-               logit("Authentication refused: %s", line);
                restore_uid();
                return (0);
        }
index f94c7d1..2370e5c 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth.c,v 1.78 2007/09/21 08:15:29 djm Exp $ */
+/* $OpenBSD: auth.c,v 1.79 2008/07/02 12:03:51 dtucker Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -32,6 +32,7 @@
 #include <netinet/in.h>
 
 #include <errno.h>
+#include <fcntl.h>
 #ifdef HAVE_PATHS_H
 # include <paths.h>
 #endif
@@ -48,6 +49,7 @@
 #include <stdarg.h>
 #include <stdio.h>
 #include <string.h>
+#include <unistd.h>
 
 #include "xmalloc.h"
 #include "match.h"
@@ -113,6 +115,7 @@ allowed_user(struct passwd * pw)
 #endif /* USE_SHADOW */
 
        /* grab passwd field for locked account check */
+       passwd = pw->pw_passwd;
 #ifdef USE_SHADOW
        if (spw != NULL)
 #ifdef USE_LIBIAF
@@ -120,8 +123,6 @@ allowed_user(struct passwd * pw)
 #else
                passwd = spw->sp_pwdp;
 #endif /* USE_LIBIAF */
-#else
-       passwd = pw->pw_passwd;
 #endif
 
        /* check for locked account */
@@ -410,7 +411,7 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
  *
  * Returns 0 on success and -1 on failure
  */
-int
+static int
 secure_filename(FILE *f, const char *file, struct passwd *pw,
     char *err, size_t errlen)
 {
@@ -470,6 +471,46 @@ secure_filename(FILE *f, const char *file, struct passwd *pw,
        return 0;
 }
 
+FILE *
+auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes)
+{
+       char line[1024];
+       struct stat st;
+       int fd;
+       FILE *f;
+
+       /*
+        * Open the file containing the authorized keys
+        * Fail quietly if file does not exist
+        */
+       if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1)
+               return NULL;
+
+       if (fstat(fd, &st) < 0) {
+               close(fd);
+               return NULL;
+       }
+       if (!S_ISREG(st.st_mode)) {
+               logit("User %s authorized keys %s is not a regular file",
+                   pw->pw_name, file);
+               close(fd);
+               return NULL;
+       }
+       unset_nonblock(fd);
+       if ((f = fdopen(fd, "r")) == NULL) {
+               close(fd);
+               return NULL;
+       }
+       if (options.strict_modes &&
+           secure_filename(f, file, pw, line, sizeof(line)) != 0) {
+               fclose(f);
+               logit("Authentication refused: %s", line);
+               return NULL;
+       }
+
+       return f;
+}
+
 struct passwd *
 getpwnamallow(const char *user)
 {
index f752c12..6a70f0e 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth.h,v 1.60 2007/09/21 08:15:29 djm Exp $ */
+/* $OpenBSD: auth.h,v 1.61 2008/07/02 12:03:51 dtucker Exp $ */
 
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
@@ -166,8 +166,7 @@ void        abandon_challenge_response(Authctxt *);
 char   *authorized_keys_file(struct passwd *);
 char   *authorized_keys_file2(struct passwd *);
 
-int
-secure_filename(FILE *, const char *, struct passwd *, char *, size_t);
+FILE   *auth_openkeyfile(const char *, struct passwd *, int);
 
 HostStatus
 check_key_in_hostfiles(struct passwd *, Key *, const char *,
index c17cc91..b8a2558 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth1.c,v 1.71 2007/09/21 08:15:29 djm Exp $ */
+/* $OpenBSD: auth1.c,v 1.73 2008/07/04 23:30:16 djm Exp $ */
 /*
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
  *                    All rights reserved
@@ -20,6 +20,7 @@
 #include <unistd.h>
 #include <pwd.h>
 
+#include "openbsd-compat/sys-queue.h"
 #include "xmalloc.h"
 #include "rsa.h"
 #include "ssh1.h"
@@ -283,6 +284,8 @@ do_authloop(Authctxt *authctxt)
                    type != SSH_CMSG_AUTH_TIS_RESPONSE)
                        abandon_challenge_response(authctxt);
 
+               if (authctxt->failures >= options.max_authtries)
+                       goto skip;
                if ((meth = lookup_authmethod1(type)) == NULL) {
                        logit("Unknown message during authentication: "
                            "type %d", type);
@@ -351,7 +354,7 @@ do_authloop(Authctxt *authctxt)
                                        msg[len] = '\0';
                        else
                                msg = "Access denied.";
-                       packet_disconnect(msg);
+                       packet_disconnect("%s", msg);
                }
 #endif
 
@@ -367,7 +370,7 @@ do_authloop(Authctxt *authctxt)
                if (authenticated)
                        return;
 
-               if (authctxt->failures++ > options.max_authtries) {
+               if (++authctxt->failures >= options.max_authtries) {
 #ifdef SSH_AUDIT_EVENTS
                        PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES));
 #endif
index 663dec5..041051c 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-hostbased.c,v 1.11 2006/08/03 03:34:41 deraadt Exp $ */
+/* $OpenBSD: auth2-hostbased.c,v 1.12 2008/07/17 08:51:07 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -151,15 +151,16 @@ hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost,
        debug2("userauth_hostbased: chost %s resolvedname %s ipaddr %s",
            chost, resolvedname, ipaddr);
 
+       if (((len = strlen(chost)) > 0) && chost[len - 1] == '.') {
+               debug2("stripping trailing dot from chost %s", chost);
+               chost[len - 1] = '\0';
+       }
+
        if (options.hostbased_uses_name_from_packet_only) {
                if (auth_rhosts2(pw, cuser, chost, chost) == 0)
                        return 0;
                lookup = chost;
        } else {
-               if (((len = strlen(chost)) > 0) && chost[len - 1] == '.') {
-                       debug2("stripping trailing dot from chost %s", chost);
-                       chost[len - 1] = '\0';
-               }
                if (strcasecmp(resolvedname, chost) != 0)
                        logit("userauth_hostbased mismatch: "
                            "client sends %s, but we resolve %s to %s",
index 28e593e..10accfe 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-none.c,v 1.14 2007/08/23 03:22:16 djm Exp $ */
+/* $OpenBSD: auth2-none.c,v 1.15 2008/07/02 12:36:39 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
 
 #include <fcntl.h>
 #include <stdarg.h>
-#include <unistd.h>
 #include <string.h>
+#include <unistd.h>
 
+#include "atomicio.h"
 #include "xmalloc.h"
 #include "key.h"
 #include "hostfile.h"
@@ -42,7 +43,6 @@
 #include "log.h"
 #include "buffer.h"
 #include "servconf.h"
-#include "atomicio.h"
 #include "compat.h"
 #include "ssh2.h"
 #ifdef GSSAPI
@@ -56,77 +56,11 @@ extern ServerOptions options;
 /* "none" is allowed only one time */
 static int none_enabled = 1;
 
-char *
-auth2_read_banner(void)
-{
-       struct stat st;
-       char *banner = NULL;
-       size_t len, n;
-       int fd;
-
-       if ((fd = open(options.banner, O_RDONLY)) == -1)
-               return (NULL);
-       if (fstat(fd, &st) == -1) {
-               close(fd);
-               return (NULL);
-       }
-       if (st.st_size > 1*1024*1024) {
-               close(fd);
-               return (NULL);
-       }
-
-       len = (size_t)st.st_size;               /* truncate */
-       banner = xmalloc(len + 1);
-       n = atomicio(read, fd, banner, len);
-       close(fd);
-
-       if (n != len) {
-               xfree(banner);
-               return (NULL);
-       }
-       banner[n] = '\0';
-
-       return (banner);
-}
-
-void
-userauth_send_banner(const char *msg)
-{
-       if (datafellows & SSH_BUG_BANNER)
-               return;
-
-       packet_start(SSH2_MSG_USERAUTH_BANNER);
-       packet_put_cstring(msg);
-       packet_put_cstring("");         /* language, unused */
-       packet_send();
-       debug("%s: sent", __func__);
-}
-
-static void
-userauth_banner(void)
-{
-       char *banner = NULL;
-
-       if (options.banner == NULL ||
-           strcasecmp(options.banner, "none") == 0 ||
-           (datafellows & SSH_BUG_BANNER) != 0)
-               return;
-
-       if ((banner = PRIVSEP(auth2_read_banner())) == NULL)
-               goto done;
-       userauth_send_banner(banner);
-
-done:
-       if (banner)
-               xfree(banner);
-}
-
 static int
 userauth_none(Authctxt *authctxt)
 {
        none_enabled = 0;
        packet_check_eom();
-       userauth_banner();
 #ifdef HAVE_CYGWIN
        if (check_nt_auth(1, authctxt->pw) == 0)
                return (0);
index 9863cd9..b1e38e5 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-pubkey.c,v 1.15 2006/08/03 03:34:41 deraadt Exp $ */
+/* $OpenBSD: auth2-pubkey.c,v 1.19 2008/07/03 21:46:58 otto Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
 #include <sys/types.h>
 #include <sys/stat.h>
 
+#include <fcntl.h>
 #include <pwd.h>
 #include <stdio.h>
 #include <stdarg.h>
+#include <unistd.h>
 
 #include "xmalloc.h"
 #include "ssh.h"
@@ -183,7 +185,6 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file)
        int found_key = 0;
        FILE *f;
        u_long linenum = 0;
-       struct stat st;
        Key *found;
        char *fp;
 
@@ -191,24 +192,9 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file)
        temporarily_use_uid(pw);
 
        debug("trying public key file %s", file);
+       f = auth_openkeyfile(file, pw, options.strict_modes);
 
-       /* Fail quietly if file does not exist */
-       if (stat(file, &st) < 0) {
-               /* Restore the privileged uid. */
-               restore_uid();
-               return 0;
-       }
-       /* Open the file containing the authorized keys. */
-       f = fopen(file, "r");
        if (!f) {
-               /* Restore the privileged uid. */
-               restore_uid();
-               return 0;
-       }
-       if (options.strict_modes &&
-           secure_filename(f, file, pw, line, sizeof(line)) != 0) {
-               fclose(f);
-               logit("Authentication refused: %s", line);
                restore_uid();
                return 0;
        }
index 03d7f09..a835abf 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2.c,v 1.116 2007/09/29 00:25:51 dtucker Exp $ */
+/* $OpenBSD: auth2.c,v 1.119 2008/07/04 23:30:16 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
 #include "includes.h"
 
 #include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
 
+#include <fcntl.h>
 #include <pwd.h>
 #include <stdarg.h>
 #include <string.h>
+#include <unistd.h>
 
 #include "xmalloc.h"
+#include "atomicio.h"
 #include "ssh2.h"
 #include "packet.h"
 #include "log.h"
@@ -88,10 +93,74 @@ static void input_userauth_request(int, u_int32_t, void *);
 static Authmethod *authmethod_lookup(const char *);
 static char *authmethods_get(void);
 
+char *
+auth2_read_banner(void)
+{
+       struct stat st;
+       char *banner = NULL;
+       size_t len, n;
+       int fd;
+
+       if ((fd = open(options.banner, O_RDONLY)) == -1)
+               return (NULL);
+       if (fstat(fd, &st) == -1) {
+               close(fd);
+               return (NULL);
+       }
+       if (st.st_size > 1*1024*1024) {
+               close(fd);
+               return (NULL);
+       }
+
+       len = (size_t)st.st_size;               /* truncate */
+       banner = xmalloc(len + 1);
+       n = atomicio(read, fd, banner, len);
+       close(fd);
+
+       if (n != len) {
+               xfree(banner);
+               return (NULL);
+       }
+       banner[n] = '\0';
+
+       return (banner);
+}
+
+void
+userauth_send_banner(const char *msg)
+{
+       if (datafellows & SSH_BUG_BANNER)
+               return;
+
+       packet_start(SSH2_MSG_USERAUTH_BANNER);
+       packet_put_cstring(msg);
+       packet_put_cstring("");         /* language, unused */
+       packet_send();
+       debug("%s: sent", __func__);
+}
+
+static void
+userauth_banner(void)
+{
+       char *banner = NULL;
+
+       if (options.banner == NULL ||
+           strcasecmp(options.banner, "none") == 0 ||
+           (datafellows & SSH_BUG_BANNER) != 0)
+               return;
+
+       if ((banner = PRIVSEP(auth2_read_banner())) == NULL)
+               goto done;
+       userauth_send_banner(banner);
+
+done:
+       if (banner)
+               xfree(banner);
+}
+
 /*
  * loop until authctxt->success == TRUE
  */
-
 void
 do_authentication2(Authctxt *authctxt)
 {
@@ -179,6 +248,7 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
                authctxt->style = style ? xstrdup(style) : NULL;
                if (use_privsep)
                        mm_inform_authserv(service, style);
+               userauth_banner();
        } else if (strcmp(user, authctxt->user) != 0 ||
            strcmp(service, authctxt->service) != 0) {
                packet_disconnect("Change of username or service not allowed: "
@@ -197,7 +267,7 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
 
        /* try to authenticate user */
        m = authmethod_lookup(method);
-       if (m != NULL) {
+       if (m != NULL && authctxt->failures < options.max_authtries) {
                debug2("input_userauth_request: try method %s", method);
                authenticated = m->userauth(authctxt);
        }
@@ -264,7 +334,11 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
                /* now we can break out */
                authctxt->success = 1;
        } else {
-               if (authctxt->failures++ > options.max_authtries) {
+
+               /* Allow initial try of "none" auth without failure penalty */
+               if (authctxt->attempt > 1 || strcmp(method, "none") != 0)
+                       authctxt->failures++;
+               if (authctxt->failures >= options.max_authtries) {
 #ifdef SSH_AUDIT_EVENTS
                        PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES));
 #endif
@@ -320,3 +394,4 @@ authmethod_lookup(const char *name)
            name ? name : "NULL");
        return NULL;
 }
+
index cbdc22c..cd9a35d 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: bufaux.c,v 1.44 2006/08/03 03:34:41 deraadt Exp $ */
+/* $OpenBSD: bufaux.c,v 1.46 2008/06/10 23:21:34 dtucker Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -180,7 +180,7 @@ buffer_get_string_ret(Buffer *buffer, u_int *length_ptr)
                return (NULL);
        }
        /* Append a null character to make processing easier. */
-       value[len] = 0;
+       value[len] = '\0';
        /* Optionally return the length of the string. */
        if (length_ptr)
                *length_ptr = len;
@@ -197,6 +197,22 @@ buffer_get_string(Buffer *buffer, u_int *length_ptr)
        return (ret);
 }
 
+void *
+buffer_get_string_ptr(Buffer *buffer, u_int *length_ptr)
+{
+       void *ptr;
+       u_int len;
+
+       len = buffer_get_int(buffer);
+       if (len > 256 * 1024)
+               fatal("buffer_get_string_ptr: bad string length %u", len);
+       ptr = buffer_ptr(buffer);
+       buffer_consume(buffer, len);
+       if (length_ptr)
+               *length_ptr = len;
+       return (ptr);
+}
+
 /*
  * Stores and arbitrary binary string in the buffer.
  */
index ecc4aea..d0f354e 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: buffer.h,v 1.16 2006/08/03 03:34:41 deraadt Exp $ */
+/* $OpenBSD: buffer.h,v 1.17 2008/05/08 06:59:01 markus Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -66,6 +66,7 @@ int     buffer_get_char(Buffer *);
 void    buffer_put_char(Buffer *, int);
 
 void   *buffer_get_string(Buffer *, u_int *);
+void   *buffer_get_string_ptr(Buffer *, u_int *);
 void    buffer_put_string(Buffer *, const void *, u_int);
 void   buffer_put_cstring(Buffer *, const char *);
 
index 8270500..42011fd 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: canohost.c,v 1.62 2007/12/27 14:22:08 dtucker Exp $ */
+/* $OpenBSD: canohost.c,v 1.63 2008/06/12 00:03:49 dtucker Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -89,7 +89,7 @@ get_remote_hostname(int sock, int use_dns)
        memset(&hints, 0, sizeof(hints));
        hints.ai_socktype = SOCK_DGRAM; /*dummy*/
        hints.ai_flags = AI_NUMERICHOST;
-       if (getaddrinfo(name, "0", &hints, &ai) == 0) {
+       if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
                logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
                    name, ntop);
                freeaddrinfo(ai);
index b6bd901..69c99c9 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.c,v 1.273 2008/04/02 21:36:51 markus Exp $ */
+/* $OpenBSD: channels.c,v 1.286 2008/07/16 11:52:19 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -61,6 +61,7 @@
 #include <unistd.h>
 #include <stdarg.h>
 
+#include "openbsd-compat/sys-queue.h"
 #include "xmalloc.h"
 #include "ssh.h"
 #include "ssh1.h"
@@ -164,6 +165,10 @@ static int IPv4or6 = AF_UNSPEC;
 /* helper */
 static void port_open_helper(Channel *c, char *rtype);
 
+/* non-blocking connect helpers */
+static int connect_next(struct channel_connect *);
+static void channel_connect_ctx_free(struct channel_connect *);
+
 /* -- channel core */
 
 Channel *
@@ -216,7 +221,7 @@ channel_lookup(int id)
  */
 static void
 channel_register_fds(Channel *c, int rfd, int wfd, int efd,
-    int extusage, int nonblock)
+    int extusage, int nonblock, int is_tty)
 {
        /* Update the maximum file descriptor value. */
        channel_max_fd = MAX(channel_max_fd, rfd);
@@ -232,18 +237,9 @@ channel_register_fds(Channel *c, int rfd, int wfd, int efd,
        c->efd = efd;
        c->extended_usage = extusage;
 
-       /* XXX ugly hack: nonblock is only set by the server */
-       if (nonblock && isatty(c->rfd)) {
+       if ((c->isatty = is_tty) != 0)
                debug2("channel %d: rfd %d isatty", c->self, c->rfd);
-               c->isatty = 1;
-               if (!isatty(c->wfd)) {
-                       error("channel %d: wfd %d is not a tty?",
-                           c->self, c->wfd);
-               }
-       } else {
-               c->isatty = 0;
-       }
-       c->wfd_isatty = isatty(c->wfd);
+       c->wfd_isatty = is_tty || isatty(c->wfd);
 
        /* enable nonblocking mode */
        if (nonblock) {
@@ -303,7 +299,7 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd,
        c->ostate = CHAN_OUTPUT_OPEN;
        c->istate = CHAN_INPUT_OPEN;
        c->flags = 0;
-       channel_register_fds(c, rfd, wfd, efd, extusage, nonblock);
+       channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, 0);
        c->self = found;
        c->type = type;
        c->ctype = ctype;
@@ -319,10 +315,13 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd,
        c->single_connection = 0;
        c->detach_user = NULL;
        c->detach_close = 0;
-       c->confirm = NULL;
-       c->confirm_ctx = NULL;
+       c->open_confirm = NULL;
+       c->open_confirm_ctx = NULL;
        c->input_filter = NULL;
        c->output_filter = NULL;
+       c->filter_ctx = NULL;
+       c->filter_cleanup = NULL;
+       TAILQ_INIT(&c->status_confirms);
        debug("channel %d: new [%s]", found, remote_name);
        return c;
 }
@@ -379,6 +378,7 @@ channel_free(Channel *c)
 {
        char *s;
        u_int i, n;
+       struct channel_confirm *cc;
 
        for (n = 0, i = 0; i < channels_alloc; i++)
                if (channels[i])
@@ -402,6 +402,15 @@ channel_free(Channel *c)
                xfree(c->remote_name);
                c->remote_name = NULL;
        }
+       while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) {
+               if (cc->abandon_cb != NULL)
+                       cc->abandon_cb(c, cc->ctx);
+               TAILQ_REMOVE(&c->status_confirms, cc, entry);
+               bzero(cc, sizeof(*cc));
+               xfree(cc);
+       }
+       if (c->filter_cleanup != NULL && c->filter_ctx != NULL)
+               c->filter_cleanup(c->self, c->filter_ctx);
        channels[c->self] = NULL;
        xfree(c);
 }
@@ -660,16 +669,33 @@ channel_request_start(int id, char *service, int wantconfirm)
 }
 
 void
-channel_register_confirm(int id, channel_callback_fn *fn, void *ctx)
+channel_register_status_confirm(int id, channel_confirm_cb *cb,
+    channel_confirm_abandon_cb *abandon_cb, void *ctx)
+{
+       struct channel_confirm *cc;
+       Channel *c;
+
+       if ((c = channel_lookup(id)) == NULL)
+               fatal("channel_register_expect: %d: bad id", id);
+
+       cc = xmalloc(sizeof(*cc));
+       cc->cb = cb;
+       cc->abandon_cb = abandon_cb;
+       cc->ctx = ctx;
+       TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry);
+}
+
+void
+channel_register_open_confirm(int id, channel_callback_fn *fn, void *ctx)
 {
        Channel *c = channel_lookup(id);
 
        if (c == NULL) {
-               logit("channel_register_comfirm: %d: bad id", id);
+               logit("channel_register_open_comfirm: %d: bad id", id);
                return;
        }
-       c->confirm = fn;
-       c->confirm_ctx = ctx;
+       c->open_confirm = fn;
+       c->open_confirm_ctx = ctx;
 }
 
 void
@@ -700,7 +726,7 @@ channel_cancel_cleanup(int id)
 
 void
 channel_register_filter(int id, channel_infilter_fn *ifn,
-    channel_outfilter_fn *ofn)
+    channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx)
 {
        Channel *c = channel_lookup(id);
 
@@ -710,17 +736,19 @@ channel_register_filter(int id, channel_infilter_fn *ifn,
        }
        c->input_filter = ifn;
        c->output_filter = ofn;
+       c->filter_ctx = ctx;
+       c->filter_cleanup = cfn;
 }
 
 void
 channel_set_fds(int id, int rfd, int wfd, int efd,
-    int extusage, int nonblock, u_int window_max)
+    int extusage, int nonblock, int is_tty, u_int window_max)
 {
        Channel *c = channel_lookup(id);
 
        if (c == NULL || c->type != SSH_CHANNEL_LARVAL)
                fatal("channel_activate for non-larval channel %d.", id);
-       channel_register_fds(c, rfd, wfd, efd, extusage, nonblock);
+       channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, is_tty);
        c->type = SSH_CHANNEL_OPEN;
        c->local_window = c->local_window_max = window_max;
        packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
@@ -788,7 +816,8 @@ channel_pre_open(Channel *c, fd_set *readset, fd_set *writeset)
                }
        }
        /** XXX check close conditions, too */
-       if (compat20 && c->efd != -1) {
+       if (compat20 && c->efd != -1 && 
+           !(c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED)) {
                if (c->extended_usage == CHAN_EXTENDED_WRITE &&
                    buffer_len(&c->extended) > 0)
                        FD_SET(c->efd, writeset);
@@ -1181,7 +1210,7 @@ static void
 channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset)
 {
        Channel *nc;
-       struct sockaddr addr;
+       struct sockaddr_storage addr;
        int newsock;
        socklen_t addrlen;
        char buf[16384], *remote_ipaddr;
@@ -1190,7 +1219,7 @@ channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset)
        if (FD_ISSET(c->sock, readset)) {
                debug("X11 connection requested.");
                addrlen = sizeof(addr);
-               newsock = accept(c->sock, &addr, &addrlen);
+               newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
                if (c->single_connection) {
                        debug2("single_connection: closing X11 listener.");
                        channel_close_fd(&c->sock);
@@ -1307,7 +1336,7 @@ static void
 channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset)
 {
        Channel *nc;
-       struct sockaddr addr;
+       struct sockaddr_storage addr;
        int newsock, nextstate;
        socklen_t addrlen;
        char *rtype;
@@ -1331,7 +1360,7 @@ channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset)
                }
 
                addrlen = sizeof(addr);
-               newsock = accept(c->sock, &addr, &addrlen);
+               newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
                if (newsock < 0) {
                        error("accept: %.100s", strerror(errno));
                        return;
@@ -1366,12 +1395,12 @@ channel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset)
 {
        Channel *nc;
        int newsock;
-       struct sockaddr addr;
+       struct sockaddr_storage addr;
        socklen_t addrlen;
 
        if (FD_ISSET(c->sock, readset)) {
                addrlen = sizeof(addr);
-               newsock = accept(c->sock, &addr, &addrlen);
+               newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
                if (newsock < 0) {
                        error("accept from auth socket: %.100s", strerror(errno));
                        return;
@@ -1398,7 +1427,7 @@ channel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset)
 static void
 channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset)
 {
-       int err = 0;
+       int err = 0, sock;
        socklen_t sz = sizeof(err);
 
        if (FD_ISSET(c->sock, writeset)) {
@@ -1407,7 +1436,9 @@ channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset)
                        error("getsockopt SO_ERROR failed");
                }
                if (err == 0) {
-                       debug("channel %d: connected", c->self);
+                       debug("channel %d: connected to %s port %d",
+                           c->self, c->connect_ctx.host, c->connect_ctx.port);
+                       channel_connect_ctx_free(&c->connect_ctx);
                        c->type = SSH_CHANNEL_OPEN;
                        if (compat20) {
                                packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
@@ -1421,8 +1452,19 @@ channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset)
                                packet_put_int(c->self);
                        }
                } else {
-                       debug("channel %d: not connected: %s",
+                       debug("channel %d: connection failed: %s",
                            c->self, strerror(err));
+                       /* Try next address, if any */
+                       if ((sock = connect_next(&c->connect_ctx)) > 0) {
+                               close(c->sock);
+                               c->sock = c->rfd = c->wfd = sock;
+                               channel_max_fd = channel_find_maxfd();
+                               return;
+                       }
+                       /* Exhausted all addresses */
+                       error("connect_to %.100s port %d: failed.",
+                           c->connect_ctx.host, c->connect_ctx.port);
+                       channel_connect_ctx_free(&c->connect_ctx);
                        if (compat20) {
                                packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
                                packet_put_int(c->remote_id);
@@ -1452,7 +1494,8 @@ channel_handle_rfd(Channel *c, fd_set *readset, fd_set *writeset)
        if (c->rfd != -1 && (force || FD_ISSET(c->rfd, readset))) {
                errno = 0;
                len = read(c->rfd, buf, sizeof(buf));
-               if (len < 0 && (errno == EINTR || (errno == EAGAIN && !force)))
+               if (len < 0 && (errno == EINTR ||
+                   ((errno == EAGAIN || errno == EWOULDBLOCK) && !force)))
                        return 1;
 #ifndef PTY_ZEROREAD
                if (len <= 0) {
@@ -1523,7 +1566,8 @@ channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset)
                        c->local_consumed += dlen + 4;
                        len = write(c->wfd, buf, dlen);
                        xfree(data);
-                       if (len < 0 && (errno == EINTR || errno == EAGAIN))
+                       if (len < 0 && (errno == EINTR || errno == EAGAIN ||
+                           errno == EWOULDBLOCK))
                                return 1;
                        if (len <= 0) {
                                if (c->type != SSH_CHANNEL_OPEN)
@@ -1541,7 +1585,8 @@ channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset)
 #endif
 
                len = write(c->wfd, buf, dlen);
-               if (len < 0 && (errno == EINTR || errno == EAGAIN))
+               if (len < 0 &&
+                   (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK))
                        return 1;
                if (len <= 0) {
                        if (c->type != SSH_CHANNEL_OPEN) {
@@ -1593,7 +1638,8 @@ channel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset)
                            buffer_len(&c->extended));
                        debug2("channel %d: written %d to efd %d",
                            c->self, len, c->efd);
-                       if (len < 0 && (errno == EINTR || errno == EAGAIN))
+                       if (len < 0 && (errno == EINTR || errno == EAGAIN ||
+                           errno == EWOULDBLOCK))
                                return 1;
                        if (len <= 0) {
                                debug2("channel %d: closing write-efd %d",
@@ -1608,8 +1654,8 @@ channel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset)
                        len = read(c->efd, buf, sizeof(buf));
                        debug2("channel %d: read %d from efd %d",
                            c->self, len, c->efd);
-                       if (len < 0 && (errno == EINTR ||
-                           (errno == EAGAIN && !c->detach_close)))
+                       if (len < 0 && (errno == EINTR || ((errno == EAGAIN ||
+                           errno == EWOULDBLOCK) && !c->detach_close)))
                                return 1;
                        if (len <= 0) {
                                debug2("channel %d: closing read-efd %d",
@@ -1633,7 +1679,8 @@ channel_handle_ctl(Channel *c, fd_set *readset, fd_set *writeset)
        /* Monitor control fd to detect if the slave client exits */
        if (c->ctl_fd != -1 && FD_ISSET(c->ctl_fd, readset)) {
                len = read(c->ctl_fd, buf, sizeof(buf));
-               if (len < 0 && (errno == EINTR || errno == EAGAIN))
+               if (len < 0 &&
+                   (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK))
                        return 1;
                if (len <= 0) {
                        debug2("channel %d: ctl read<=0", c->self);
@@ -2012,7 +2059,7 @@ channel_input_data(int type, u_int32_t seq, void *ctxt)
                return;
 
        /* Get the data. */
-       data = packet_get_string(&data_len);
+       data = packet_get_string_ptr(&data_len);
 
        /*
         * Ignore data for protocol > 1.3 if output end is no longer open.
@@ -2026,7 +2073,6 @@ channel_input_data(int type, u_int32_t seq, void *ctxt)
                        c->local_window -= data_len;
                        c->local_consumed += data_len;
                }
-               xfree(data);
                return;
        }
 
@@ -2038,17 +2084,15 @@ channel_input_data(int type, u_int32_t seq, void *ctxt)
                if (data_len > c->local_window) {
                        logit("channel %d: rcvd too much data %d, win %d",
                            c->self, data_len, c->local_window);
-                       xfree(data);
                        return;
                }
                c->local_window -= data_len;
        }
-       packet_check_eom();
        if (c->datagram)
                buffer_put_string(&c->output, data, data_len);
        else
                buffer_append(&c->output, data, data_len);
-       xfree(data);
+       packet_check_eom();
 }
 
 /* ARGSUSED */
@@ -2212,9 +2256,9 @@ channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt)
        if (compat20) {
                c->remote_window = packet_get_int();
                c->remote_maxpacket = packet_get_int();
-               if (c->confirm) {
+               if (c->open_confirm) {
                        debug2("callback start");
-                       c->confirm(c->self, c->confirm_ctx);
+                       c->open_confirm(c->self, c->open_confirm_ctx);
                        debug2("callback done");
                }
                debug2("channel %d: open confirm rwindow %u rmax %u", c->self,
@@ -2303,7 +2347,7 @@ channel_input_port_open(int type, u_int32_t seq, void *ctxt)
        Channel *c = NULL;
        u_short host_port;
        char *host, *originator_string;
-       int remote_id, sock = -1;
+       int remote_id;
 
        remote_id = packet_get_int();
        host = packet_get_string(NULL);
@@ -2315,22 +2359,46 @@ channel_input_port_open(int type, u_int32_t seq, void *ctxt)
                originator_string = xstrdup("unknown (remote did not supply name)");
        }
        packet_check_eom();
-       sock = channel_connect_to(host, host_port);
-       if (sock != -1) {
-               c = channel_new("connected socket",
-                   SSH_CHANNEL_CONNECTING, sock, sock, -1, 0, 0, 0,
-                   originator_string, 1);
-               c->remote_id = remote_id;
-       }
+       c = channel_connect_to(host, host_port,
+           "connected socket", originator_string);
        xfree(originator_string);
+       xfree(host);
        if (c == NULL) {
                packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
                packet_put_int(remote_id);
                packet_send();
-       }
-       xfree(host);
+       } else
+               c->remote_id = remote_id;
 }
 
+/* ARGSUSED */
+void
+channel_input_status_confirm(int type, u_int32_t seq, void *ctxt)
+{
+       Channel *c;
+       struct channel_confirm *cc;
+       int remote_id;
+
+       /* Reset keepalive timeout */
+       keep_alive_timeouts = 0;
+
+       remote_id = packet_get_int();
+       packet_check_eom();
+
+       debug2("channel_input_confirm: type %d id %d", type, remote_id);
+
+       if ((c = channel_lookup(remote_id)) == NULL) {
+               logit("channel_input_success_failure: %d: unknown", remote_id);
+               return;
+       }       
+       ;
+       if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL)
+               return;
+       cc->cb(type, c, cc->ctx);
+       TAILQ_REMOVE(&c->status_confirms, cc, entry);
+       bzero(cc, sizeof(*cc));
+       xfree(cc);
+}
 
 /* -- tcp forwarding */
 
@@ -2718,35 +2786,37 @@ channel_clear_adm_permitted_opens(void)
        num_adm_permitted_opens = 0;
 }
 
-/* return socket to remote host, port */
+void
+channel_print_adm_permitted_opens(void)
+{
+       int i;
+
+       for (i = 0; i < num_adm_permitted_opens; i++)
+               if (permitted_adm_opens[i].host_to_connect != NULL)
+                       printf(" %s:%d", permitted_adm_opens[i].host_to_connect,
+                           permitted_adm_opens[i].port_to_connect);
+}
+
+/* Try to start non-blocking connect to next host in cctx list */
 static int
-connect_to(const char *host, u_short port)
+connect_next(struct channel_connect *cctx)
 {
-       struct addrinfo hints, *ai, *aitop;
+       int sock, saved_errno;
        char ntop[NI_MAXHOST], strport[NI_MAXSERV];
-       int gaierr;
-       int sock = -1;
 
-       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, &aitop)) != 0) {
-               error("connect_to %.100s: unknown host (%s)", host,
-                   ssh_gai_strerror(gaierr));
-               return -1;
-       }
-       for (ai = aitop; ai; ai = ai->ai_next) {
-               if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
+       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(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
-                   strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
-                       error("connect_to: getnameinfo failed");
+               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;
                }
-               sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
-               if (sock < 0) {
-                       if (ai->ai_next == NULL)
+               if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype,
+                   cctx->ai->ai_protocol)) == -1) {
+                       if (cctx->ai->ai_next == NULL)
                                error("socket: %.100s", strerror(errno));
                        else
                                verbose("socket: %.100s", strerror(errno));
@@ -2754,45 +2824,95 @@ connect_to(const char *host, u_short port)
                }
                if (set_nonblock(sock) == -1)
                        fatal("%s: set_nonblock(%d)", __func__, sock);
-               if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 &&
-                   errno != EINPROGRESS) {
-                       error("connect_to %.100s port %s: %.100s", ntop, strport,
+               if (connect(sock, cctx->ai->ai_addr,
+                   cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) {
+                       debug("connect_next: host %.100s ([%.100s]:%s): "
+                           "%.100s", cctx->host, ntop, strport,
                            strerror(errno));
+                       saved_errno = errno;
                        close(sock);
+                       errno = saved_errno;
                        continue;       /* fail -- try next */
                }
-               break; /* success */
+               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;
+}
+
+static void
+channel_connect_ctx_free(struct channel_connect *cctx)
+{
+       xfree(cctx->host);
+       if (cctx->aitop)
+               freeaddrinfo(cctx->aitop);
+       bzero(cctx, sizeof(*cctx));
+       cctx->host = NULL;
+       cctx->ai = cctx->aitop = NULL;
+}
+
+/* Return CONNECTING channel to remote host, port */
+static Channel *
+connect_to(const char *host, u_short port, char *ctype, char *rname)
+{
+       struct addrinfo hints;
+       int gaierr;
+       int sock = -1;
+       char strport[NI_MAXSERV];
+       struct channel_connect cctx;
+       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;
        }
-       freeaddrinfo(aitop);
-       if (!ai) {
-               error("connect_to %.100s port %d: failed.", host, port);
-               return -1;
+
+       cctx.host = xstrdup(host);
+       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));
+               channel_connect_ctx_free(&cctx);
+               return NULL;
        }
-       /* success */
-       set_nodelay(sock);
-       return sock;
+       c = channel_new(ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
+           CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
+       c->connect_ctx = cctx;
+       return c;
 }
 
-int
-channel_connect_by_listen_address(u_short listen_port)
+Channel *
+channel_connect_by_listen_address(u_short listen_port, char *ctype, char *rname)
 {
        int i;
 
-       for (i = 0; i < num_permitted_opens; i++)
+       for (i = 0; i < num_permitted_opens; i++) {
                if (permitted_opens[i].host_to_connect != NULL &&
-                   permitted_opens[i].listen_port == listen_port)
+                   permitted_opens[i].listen_port == listen_port) {
                        return connect_to(
                            permitted_opens[i].host_to_connect,
-                           permitted_opens[i].port_to_connect);
+                           permitted_opens[i].port_to_connect, ctype, rname);
+               }
+       }
        error("WARNING: Server requests forwarding for unknown listen_port %d",
            listen_port);
-       return -1;
+       return NULL;
 }
 
 /* Check if connecting to that port is permitted and connect. */
-int
-channel_connect_to(const char *host, u_short port)
+Channel *
+channel_connect_to(const char *host, u_short port, char *ctype, char *rname)
 {
        int i, permit, permit_adm = 1;
 
@@ -2818,9 +2938,9 @@ channel_connect_to(const char *host, u_short port)
        if (!permit || !permit_adm) {
                logit("Received request to connect to host %.100s port %d, "
                    "but the request was denied.", host, port);
-               return -1;
+               return NULL;
        }
-       return connect_to(host, port);
+       return connect_to(host, port, ctype, rname);
 }
 
 void
@@ -2901,7 +3021,8 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost,
                                        error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno));
                        }
 #endif
-                       channel_set_reuseaddr(sock);
+                       if (x11_use_localhost)
+                               channel_set_reuseaddr(sock);
                        if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
                                debug2("bind port %d: %.100s", port, strerror(errno));
                                close(sock);
@@ -2913,17 +3034,8 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost,
                                break;
                        }
                        socks[num_socks++] = sock;
-#ifndef DONT_TRY_OTHER_AF
                        if (num_socks == NUM_SOCKS)
                                break;
-#else
-                       if (x11_use_localhost) {
-                               if (num_socks == NUM_SOCKS)
-                                       break;
-                       } else {
-                               break;
-                       }
-#endif
                }
                freeaddrinfo(aitop);
                if (num_socks > 0)
index b632a86..108b360 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.h,v 1.89 2007/06/11 09:14:00 markus Exp $ */
+/* $OpenBSD: channels.h,v 1.96 2008/06/15 20:06:26 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -62,8 +62,27 @@ typedef struct Channel Channel;
 
 typedef void channel_callback_fn(int, void *);
 typedef int channel_infilter_fn(struct Channel *, char *, int);
+typedef void channel_filter_cleanup_fn(int, void *);
 typedef u_char *channel_outfilter_fn(struct Channel *, u_char **, u_int *);
 
+/* Channel success/failure callbacks */
+typedef void channel_confirm_cb(int, struct Channel *, void *);
+typedef void channel_confirm_abandon_cb(struct Channel *, void *);
+struct channel_confirm {
+       TAILQ_ENTRY(channel_confirm) entry;
+       channel_confirm_cb *cb;
+       channel_confirm_abandon_cb *abandon_cb;
+       void *ctx;
+};
+TAILQ_HEAD(channel_confirms, channel_confirm);
+
+/* Context for non-blocking connects */
+struct channel_connect {
+       char *host;
+       int port;
+       struct addrinfo *ai, *aitop;
+};
+
 struct Channel {
        int     type;           /* channel type/state */
        int     self;           /* my own channel identifier */
@@ -104,16 +123,23 @@ struct Channel {
        char   *ctype;          /* type */
 
        /* callback */
-       channel_callback_fn     *confirm;
-       void                    *confirm_ctx;
+       channel_callback_fn     *open_confirm;
+       void                    *open_confirm_ctx;
        channel_callback_fn     *detach_user;
        int                     detach_close;
+       struct channel_confirms status_confirms;
 
        /* filter */
        channel_infilter_fn     *input_filter;
        channel_outfilter_fn    *output_filter;
+       void                    *filter_ctx;
+       channel_filter_cleanup_fn *filter_cleanup;
+
+       /* keep boundaries */
+       int                     datagram;
 
-       int     datagram;       /* keep boundaries */
+       /* non-blocking connect */
+       struct channel_connect  connect_ctx;
 };
 
 #define CHAN_EXTENDED_IGNORE           0
@@ -162,7 +188,7 @@ struct Channel {
 Channel        *channel_by_id(int);
 Channel        *channel_lookup(int);
 Channel *channel_new(char *, int, int, int, int, u_int, u_int, int, char *, int);
-void    channel_set_fds(int, int, int, int, int, int, u_int);
+void    channel_set_fds(int, int, int, int, int, int, int, u_int);
 void    channel_free(Channel *);
 void    channel_free_all(void);
 void    channel_stop_listening(void);
@@ -170,8 +196,11 @@ void        channel_stop_listening(void);
 void    channel_send_open(int);
 void    channel_request_start(int, char *, int);
 void    channel_register_cleanup(int, channel_callback_fn *, int);
-void    channel_register_confirm(int, channel_callback_fn *, void *);
-void    channel_register_filter(int, channel_infilter_fn *, channel_outfilter_fn *);
+void    channel_register_open_confirm(int, channel_callback_fn *, void *);
+void    channel_register_filter(int, channel_infilter_fn *,
+    channel_outfilter_fn *, channel_filter_cleanup_fn *, void *);
+void    channel_register_status_confirm(int, channel_confirm_cb *,
+    channel_confirm_abandon_cb *, void *);
 void    channel_cancel_cleanup(int);
 int     channel_close_fd(int *);
 void    channel_send_window_changes(void);
@@ -188,6 +217,7 @@ void         channel_input_open_confirmation(int, u_int32_t, void *);
 void    channel_input_open_failure(int, u_int32_t, void *);
 void    channel_input_port_open(int, u_int32_t, void *);
 void    channel_input_window_adjust(int, u_int32_t, void *);
+void    channel_input_status_confirm(int, u_int32_t, void *);
 
 /* file descriptor handling (read/write) */
 
@@ -208,9 +238,10 @@ void        channel_add_permitted_opens(char *, int);
 int     channel_add_adm_permitted_opens(char *, 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);
-int     channel_connect_to(const char *, u_short);
-int     channel_connect_by_listen_address(u_short);
+Channel        *channel_connect_to(const char *, u_short, char *, char *);
+Channel        *channel_connect_by_listen_address(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,
@@ -225,7 +256,7 @@ int  x11_connect_display(void);
 int     x11_create_display_inet(int, int, int, u_int *, int **);
 void     x11_input_open(int, u_int32_t, void *);
 void    x11_request_forwarding_with_spoofing(int, const char *, const char *,
-           const char *);
+            const char *);
 void    deny_input_open(int, u_int32_t, void *);
 
 /* agent forwarding */
@@ -240,6 +271,7 @@ void         chan_mark_dead(Channel *);
 /* channel events */
 
 void    chan_rcvd_oclose(Channel *);
+void    chan_rcvd_eow(Channel *);      /* SSH2-only */
 void    chan_read_failed(Channel *);
 void    chan_ibuf_empty(Channel *);
 
index 8a40bc7..f10fab7 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: clientloop.c,v 1.188 2008/02/22 20:44:02 dtucker Exp $ */
+/* $OpenBSD: clientloop.c,v 1.201 2008/07/16 11:51:14 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -86,6 +86,7 @@
 #include <pwd.h>
 #include <unistd.h>
 
+#include "openbsd-compat/sys-queue.h"
 #include "xmalloc.h"
 #include "ssh.h"
 #include "ssh1.h"
@@ -120,7 +121,7 @@ extern int stdin_null_flag;
 extern int no_shell_flag;
 
 /* Control socket */
-extern int control_fd;
+extern int muxserver_sock;
 
 /*
  * Name of the host we are connecting to.  This is the name given on the
@@ -143,15 +144,14 @@ static int in_non_blocking_mode = 0;
 
 /* Common data for the client loop code. */
 static volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */
-static int escape_char;                /* Escape character. */
-static int escape_pending;     /* Last character was the escape character */
+static int escape_char1;       /* Escape character. (proto1 only) */
+static int escape_pending1;    /* Last character was an escape (proto1 only) */
 static int last_was_cr;                /* Last character was a newline. */
-static int exit_status;                /* Used to store the exit status of the command. */
-static int stdin_eof;          /* EOF has been encountered on standard error. */
+static int exit_status;                /* Used to store the command exit status. */
+static int stdin_eof;          /* EOF has been encountered on stderr. */
 static Buffer stdin_buffer;    /* Buffer for stdin data. */
 static Buffer stdout_buffer;   /* Buffer for stdout data. */
 static Buffer stderr_buffer;   /* Buffer for stderr data. */
-static u_long stdin_bytes, stdout_bytes, stderr_bytes;
 static u_int buffer_high;/* Soft max buffer size. */
 static int connection_in;      /* Connection to server (input). */
 static int connection_out;     /* Connection to server (output). */
@@ -161,17 +161,29 @@ static int session_closed = 0;    /* In SSH2: login session closed. */
 static void client_init_dispatch(void);
 int    session_ident = -1;
 
-struct confirm_ctx {
-       int want_tty;
-       int want_subsys;
-       int want_x_fwd;
-       int want_agent_fwd;
-       Buffer cmd;
-       char *term;
-       struct termios tio;
-       char **env;
+/* Track escape per proto2 channel */
+struct escape_filter_ctx {
+       int escape_pending;
+       int escape_char;
 };
 
+/* Context for channel confirmation replies */
+struct channel_reply_ctx {
+       const char *request_type;
+       int id, do_close;
+};
+
+/* Global request success/failure callbacks */
+struct global_confirm {
+       TAILQ_ENTRY(global_confirm) entry;
+       global_confirm_cb *cb;
+       void *ctx;
+       int ref_count;
+};
+TAILQ_HEAD(global_confirms, global_confirm);
+static struct global_confirms global_confirms =
+    TAILQ_HEAD_INITIALIZER(global_confirms);
+
 /*XXX*/
 extern Kex *xxx_kex;
 
@@ -379,7 +391,10 @@ client_check_initial_eof_on_stdin(void)
                /* Check for immediate EOF on stdin. */
                len = read(fileno(stdin), buf, 1);
                if (len == 0) {
-                       /* EOF.  Record that we have seen it and send EOF to server. */
+                       /*
+                        * EOF.  Record that we have seen it and send
+                        * EOF to server.
+                        */
                        debug("Sending eof.");
                        stdin_eof = 1;
                        packet_start(SSH_CMSG_EOF);
@@ -390,8 +405,8 @@ client_check_initial_eof_on_stdin(void)
                         * and also process it as an escape character if
                         * appropriate.
                         */
-                       if ((u_char) buf[0] == escape_char)
-                               escape_pending = 1;
+                       if ((u_char) buf[0] == escape_char1)
+                               escape_pending1 = 1;
                        else
                                buffer_append(&stdin_buffer, buf, 1);
                }
@@ -421,7 +436,6 @@ client_make_packets_from_stdin_data(void)
                packet_put_string(buffer_ptr(&stdin_buffer), len);
                packet_send();
                buffer_consume(&stdin_buffer, len);
-               stdin_bytes += len;
                /* If we have a pending EOF, send it now. */
                if (stdin_eof && buffer_len(&stdin_buffer) == 0) {
                        packet_start(SSH_CMSG_EOF);
@@ -466,8 +480,19 @@ client_check_window_change(void)
 static void
 client_global_request_reply(int type, u_int32_t seq, void *ctxt)
 {
+       struct global_confirm *gc;
+
+       if ((gc = TAILQ_FIRST(&global_confirms)) == NULL)
+               return;
+       if (gc->cb != NULL)
+               gc->cb(type, seq, gc->ctx);
+       if (--gc->ref_count <= 0) {
+               TAILQ_REMOVE(&global_confirms, gc, entry);
+               bzero(gc, sizeof(*gc));
+               xfree(gc);
+       }
+
        keep_alive_timeouts = 0;
-       client_global_request_reply_fwd(type, seq, ctxt);
 }
 
 static void
@@ -481,6 +506,8 @@ server_alive_check(void)
        packet_put_cstring("keepalive@openssh.com");
        packet_put_char(1);     /* boolean: want reply */
        packet_send();
+       /* Insert an empty placeholder to maintain ordering */
+       client_register_global_confirm(NULL, NULL);
 }
 
 /*
@@ -532,8 +559,8 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
        if (packet_have_data_to_write())
                FD_SET(connection_out, *writesetp);
 
-       if (control_fd != -1)
-               FD_SET(control_fd, *readsetp);
+       if (muxserver_sock != -1)
+               FD_SET(muxserver_sock, *readsetp);
 
        /*
         * Wait for something to happen.  This will suspend the process until
@@ -575,9 +602,11 @@ client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr)
 {
        /* Flush stdout and stderr buffers. */
        if (buffer_len(bout) > 0)
-               atomicio(vwrite, fileno(stdout), buffer_ptr(bout), buffer_len(bout));
+               atomicio(vwrite, fileno(stdout), buffer_ptr(bout),
+                   buffer_len(bout));
        if (buffer_len(berr) > 0)
-               atomicio(vwrite, fileno(stderr), buffer_ptr(berr), buffer_len(berr));
+               atomicio(vwrite, fileno(stderr), buffer_ptr(berr),
+                   buffer_len(berr));
 
        leave_raw_mode();
 
@@ -617,9 +646,13 @@ client_process_net_input(fd_set *readset)
                /* Read as much as possible. */
                len = read(connection_in, buf, sizeof(buf));
                if (len == 0) {
-                       /* Received EOF.  The remote host has closed the connection. */
-                       snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n",
-                                host);
+                       /*
+                        * Received EOF.  The remote host has closed the
+                        * connection.
+                        */
+                       snprintf(buf, sizeof buf,
+                           "Connection to %.300s closed by remote host.\r\n",
+                           host);
                        buffer_append(&stderr_buffer, buf, strlen(buf));
                        quit_pending = 1;
                        return;
@@ -628,13 +661,18 @@ client_process_net_input(fd_set *readset)
                 * There is a kernel bug on Solaris that causes select to
                 * sometimes wake up even though there is no data available.
                 */
-               if (len < 0 && (errno == EAGAIN || errno == EINTR))
+               if (len < 0 &&
+                   (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK))
                        len = 0;
 
                if (len < 0) {
-                       /* An error has encountered.  Perhaps there is a network problem. */
-                       snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n",
-                                host, strerror(errno));
+                       /*
+                        * An error has encountered.  Perhaps there is a
+                        * network problem.
+                        */
+                       snprintf(buf, sizeof buf,
+                           "Read from remote host %.300s: %.100s\r\n",
+                           host, strerror(errno));
                        buffer_append(&stderr_buffer, buf, strlen(buf));
                        quit_pending = 1;
                        return;
@@ -644,303 +682,81 @@ client_process_net_input(fd_set *readset)
 }
 
 static void
-client_subsystem_reply(int type, u_int32_t seq, void *ctxt)
+client_status_confirm(int type, Channel *c, void *ctx)
 {
-       int id;
-       Channel *c;
-
-       id = packet_get_int();
-       packet_check_eom();
-
-       if ((c = channel_lookup(id)) == NULL) {
-               error("%s: no channel for id %d", __func__, id);
-               return;
-       }
-
-       if (type == SSH2_MSG_CHANNEL_SUCCESS)
-               debug2("Request suceeded on channel %d", id);
-       else if (type == SSH2_MSG_CHANNEL_FAILURE) {
-               error("Request failed on channel %d", id);
-               channel_free(c);
+       struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx;
+       char errmsg[256];
+       int tochan;
+
+       /* XXX supress on mux _client_ quietmode */
+       tochan = options.log_level >= SYSLOG_LEVEL_ERROR &&
+           c->ctl_fd != -1 && c->extended_usage == CHAN_EXTENDED_WRITE;
+
+       if (type == SSH2_MSG_CHANNEL_SUCCESS) {
+               debug2("%s request accepted on channel %d",
+                   cr->request_type, c->self);
+       } else if (type == SSH2_MSG_CHANNEL_FAILURE) {
+               if (tochan) {
+                       snprintf(errmsg, sizeof(errmsg),
+                           "%s request failed\r\n", cr->request_type);
+               } else {
+                       snprintf(errmsg, sizeof(errmsg),
+                           "%s request failed on channel %d",
+                           cr->request_type, c->self);
+               }
+               /* If error occurred on primary session channel, then exit */
+               if (cr->do_close && c->self == session_ident)
+                       fatal("%s", errmsg);
+               /* If error occurred on mux client, append to their stderr */
+               if (tochan)
+                       buffer_append(&c->extended, errmsg, strlen(errmsg));
+               else
+                       error("%s", errmsg);
+               if (cr->do_close) {
+                       chan_read_failed(c);
+                       chan_write_failed(c);
+               }
        }
+       xfree(cr);
 }
 
 static void
-client_extra_session2_setup(int id, void *arg)
+client_abandon_status_confirm(Channel *c, void *ctx)
 {
-       struct confirm_ctx *cctx = arg;
-       const char *display;
-       Channel *c;
-       int i;
-
-       if (cctx == NULL)
-               fatal("%s: cctx == NULL", __func__);
-       if ((c = channel_lookup(id)) == NULL)
-               fatal("%s: no channel for id %d", __func__, id);
-
-       display = getenv("DISPLAY");
-       if (cctx->want_x_fwd && options.forward_x11 && display != NULL) {
-               char *proto, *data;
-               /* Get reasonable local authentication information. */
-               client_x11_get_proto(display, options.xauth_location,
-                   options.forward_x11_trusted, &proto, &data);
-               /* Request forwarding with authentication spoofing. */
-               debug("Requesting X11 forwarding with authentication spoofing.");
-               x11_request_forwarding_with_spoofing(id, display, proto, data);
-               /* XXX wait for reply */
-       }
-
-       if (cctx->want_agent_fwd && options.forward_agent) {
-               debug("Requesting authentication agent forwarding.");
-               channel_request_start(id, "auth-agent-req@openssh.com", 0);
-               packet_send();
-       }
-
-       client_session2_setup(id, cctx->want_tty, cctx->want_subsys,
-           cctx->term, &cctx->tio, c->rfd, &cctx->cmd, cctx->env,
-           client_subsystem_reply);
-
-       c->confirm_ctx = NULL;
-       buffer_free(&cctx->cmd);
-       xfree(cctx->term);
-       if (cctx->env != NULL) {
-               for (i = 0; cctx->env[i] != NULL; i++)
-                       xfree(cctx->env[i]);
-               xfree(cctx->env);
-       }
-       xfree(cctx);
+       xfree(ctx);
 }
 
 static void
-client_process_control(fd_set *readset)
+client_expect_confirm(int id, const char *request, int do_close)
 {
-       Buffer m;
-       Channel *c;
-       int client_fd, new_fd[3], ver, allowed, window, packetmax;
-       socklen_t addrlen;
-       struct sockaddr_storage addr;
-       struct confirm_ctx *cctx;
-       char *cmd;
-       u_int i, j, len, env_len, command, flags;
-       uid_t euid;
-       gid_t egid;
+       struct channel_reply_ctx *cr = xmalloc(sizeof(*cr));
 
-       /*
-        * Accept connection on control socket
-        */
-       if (control_fd == -1 || !FD_ISSET(control_fd, readset))
-               return;
+       cr->request_type = request;
+       cr->do_close = do_close;
 
-       memset(&addr, 0, sizeof(addr));
-       addrlen = sizeof(addr);
-       if ((client_fd = accept(control_fd,
-           (struct sockaddr*)&addr, &addrlen)) == -1) {
-               error("%s accept: %s", __func__, strerror(errno));
-               return;
-       }
-
-       if (getpeereid(client_fd, &euid, &egid) < 0) {
-               error("%s getpeereid failed: %s", __func__, strerror(errno));
-               close(client_fd);
-               return;
-       }
-       if ((euid != 0) && (getuid() != euid)) {
-               error("control mode uid mismatch: peer euid %u != uid %u",
-                   (u_int) euid, (u_int) getuid());
-               close(client_fd);
-               return;
-       }
-
-       unset_nonblock(client_fd);
-
-       /* Read command */
-       buffer_init(&m);
-       if (ssh_msg_recv(client_fd, &m) == -1) {
-               error("%s: client msg_recv failed", __func__);
-               close(client_fd);
-               buffer_free(&m);
-               return;
-       }
-       if ((ver = buffer_get_char(&m)) != SSHMUX_VER) {
-               error("%s: wrong client version %d", __func__, ver);
-               buffer_free(&m);
-               close(client_fd);
-               return;
-       }
-
-       allowed = 1;
-       command = buffer_get_int(&m);
-       flags = buffer_get_int(&m);
-
-       buffer_clear(&m);
-
-       switch (command) {
-       case SSHMUX_COMMAND_OPEN:
-               if (options.control_master == SSHCTL_MASTER_ASK ||
-                   options.control_master == SSHCTL_MASTER_AUTO_ASK)
-                       allowed = ask_permission("Allow shared connection "
-                           "to %s? ", host);
-               /* continue below */
-               break;
-       case SSHMUX_COMMAND_TERMINATE:
-               if (options.control_master == SSHCTL_MASTER_ASK ||
-                   options.control_master == SSHCTL_MASTER_AUTO_ASK)
-                       allowed = ask_permission("Terminate shared connection "
-                           "to %s? ", host);
-               if (allowed)
-                       quit_pending = 1;
-               /* FALLTHROUGH */
-       case SSHMUX_COMMAND_ALIVE_CHECK:
-               /* Reply for SSHMUX_COMMAND_TERMINATE and ALIVE_CHECK */
-               buffer_clear(&m);
-               buffer_put_int(&m, allowed);
-               buffer_put_int(&m, getpid());
-               if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
-                       error("%s: client msg_send failed", __func__);
-                       close(client_fd);
-                       buffer_free(&m);
-                       return;
-               }
-               buffer_free(&m);
-               close(client_fd);
-               return;
-       default:
-               error("Unsupported command %d", command);
-               buffer_free(&m);
-               close(client_fd);
-               return;
-       }
-
-       /* Reply for SSHMUX_COMMAND_OPEN */
-       buffer_clear(&m);
-       buffer_put_int(&m, allowed);
-       buffer_put_int(&m, getpid());
-       if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
-               error("%s: client msg_send failed", __func__);
-               close(client_fd);
-               buffer_free(&m);
-               return;
-       }
-
-       if (!allowed) {
-               error("Refused control connection");
-               close(client_fd);
-               buffer_free(&m);
-               return;
-       }
-
-       buffer_clear(&m);
-       if (ssh_msg_recv(client_fd, &m) == -1) {
-               error("%s: client msg_recv failed", __func__);
-               close(client_fd);
-               buffer_free(&m);
-               return;
-       }
-       if ((ver = buffer_get_char(&m)) != SSHMUX_VER) {
-               error("%s: wrong client version %d", __func__, ver);
-               buffer_free(&m);
-               close(client_fd);
-               return;
-       }
-
-       cctx = xcalloc(1, sizeof(*cctx));
-       cctx->want_tty = (flags & SSHMUX_FLAG_TTY) != 0;
-       cctx->want_subsys = (flags & SSHMUX_FLAG_SUBSYS) != 0;
-       cctx->want_x_fwd = (flags & SSHMUX_FLAG_X11_FWD) != 0;
-       cctx->want_agent_fwd = (flags & SSHMUX_FLAG_AGENT_FWD) != 0;
-       cctx->term = buffer_get_string(&m, &len);
-
-       cmd = buffer_get_string(&m, &len);
-       buffer_init(&cctx->cmd);
-       buffer_append(&cctx->cmd, cmd, strlen(cmd));
-
-       env_len = buffer_get_int(&m);
-       env_len = MIN(env_len, 4096);
-       debug3("%s: receiving %d env vars", __func__, env_len);
-       if (env_len != 0) {
-               cctx->env = xcalloc(env_len + 1, sizeof(*cctx->env));
-               for (i = 0; i < env_len; i++)
-                       cctx->env[i] = buffer_get_string(&m, &len);
-               cctx->env[i] = NULL;
-       }
-
-       debug2("%s: accepted tty %d, subsys %d, cmd %s", __func__,
-           cctx->want_tty, cctx->want_subsys, cmd);
-       xfree(cmd);
-
-       /* Gather fds from client */
-       for(i = 0; i < 3; i++) {
-               if ((new_fd[i] = mm_receive_fd(client_fd)) == -1) {
-                       error("%s: failed to receive fd %d from slave",
-                           __func__, i);
-                       for (j = 0; j < i; j++)
-                               close(new_fd[j]);
-                       for (j = 0; j < env_len; j++)
-                               xfree(cctx->env[j]);
-                       if (env_len > 0)
-                               xfree(cctx->env);
-                       xfree(cctx->term);
-                       buffer_free(&cctx->cmd);
-                       close(client_fd);
-                       xfree(cctx);
-                       return;
-               }
-       }
+       channel_register_status_confirm(id, client_status_confirm,
+           client_abandon_status_confirm, cr);
+}
 
-       debug2("%s: got fds stdin %d, stdout %d, stderr %d", __func__,
-           new_fd[0], new_fd[1], new_fd[2]);
-
-       /* Try to pick up ttymodes from client before it goes raw */
-       if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1)
-               error("%s: tcgetattr: %s", __func__, strerror(errno));
-
-       /* This roundtrip is just for synchronisation of ttymodes */
-       buffer_clear(&m);
-       if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
-               error("%s: client msg_send failed", __func__);
-               close(client_fd);
-               close(new_fd[0]);
-               close(new_fd[1]);
-               close(new_fd[2]);
-               buffer_free(&m);
-               xfree(cctx->term);
-               if (env_len != 0) {
-                       for (i = 0; i < env_len; i++)
-                               xfree(cctx->env[i]);
-                       xfree(cctx->env);
-               }
+void
+client_register_global_confirm(global_confirm_cb *cb, void *ctx)
+{
+       struct global_confirm *gc, *last_gc;
+
+       /* Coalesce identical callbacks */
+       last_gc = TAILQ_LAST(&global_confirms, global_confirms);
+       if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) {
+               if (++last_gc->ref_count >= INT_MAX)
+                       fatal("%s: last_gc->ref_count = %d",
+                           __func__, last_gc->ref_count);
                return;
        }
-       buffer_free(&m);
-
-       /* enable nonblocking unless tty */
-       if (!isatty(new_fd[0]))
-               set_nonblock(new_fd[0]);
-       if (!isatty(new_fd[1]))
-               set_nonblock(new_fd[1]);
-       if (!isatty(new_fd[2]))
-               set_nonblock(new_fd[2]);
-
-       set_nonblock(client_fd);
-
-       window = CHAN_SES_WINDOW_DEFAULT;
-       packetmax = CHAN_SES_PACKET_DEFAULT;
-       if (cctx->want_tty) {
-               window >>= 1;
-               packetmax >>= 1;
-       }
-       
-       c = channel_new("session", SSH_CHANNEL_OPENING,
-           new_fd[0], new_fd[1], new_fd[2], window, packetmax,
-           CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0);
-
-       /* XXX */
-       c->ctl_fd = client_fd;
-
-       debug3("%s: channel_new: %d", __func__, c->self);
 
-       channel_send_open(c->self);
-       channel_register_confirm(c->self, client_extra_session2_setup, cctx);
+       gc = xmalloc(sizeof(*gc));
+       gc->cb = cb;
+       gc->ctx = ctx;
+       gc->ref_count = 1;
+       TAILQ_INSERT_TAIL(&global_confirms, gc, entry);
 }
 
 static void
@@ -1061,9 +877,12 @@ out:
                xfree(fwd.connect_host);
 }
 
-/* process the characters one by one */
+/* 
+ * Process the characters one by one, call with c==NULL for proto1 case.
+ */
 static int
-process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len)
+process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr,
+    char *buf, int len)
 {
        char string[1024];
        pid_t pid;
@@ -1071,7 +890,20 @@ process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len)
        u_int i;
        u_char ch;
        char *s;
+       int *escape_pendingp, escape_char;
+       struct escape_filter_ctx *efc;
 
+       if (c == NULL) {
+               escape_pendingp = &escape_pending1;
+               escape_char = escape_char1;
+       } else {
+               if (c->filter_ctx == NULL)
+                       return 0;
+               efc = (struct escape_filter_ctx *)c->filter_ctx;
+               escape_pendingp = &efc->escape_pending;
+               escape_char = efc->escape_char;
+       }
+       
        if (len <= 0)
                return (0);
 
@@ -1079,25 +911,42 @@ process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len)
                /* Get one character at a time. */
                ch = buf[i];
 
-               if (escape_pending) {
+               if (*escape_pendingp) {
                        /* We have previously seen an escape character. */
                        /* Clear the flag now. */
-                       escape_pending = 0;
+                       *escape_pendingp = 0;
 
                        /* Process the escaped character. */
                        switch (ch) {
                        case '.':
                                /* Terminate the connection. */
-                               snprintf(string, sizeof string, "%c.\r\n", escape_char);
+                               snprintf(string, sizeof string, "%c.\r\n",
+                                   escape_char);
                                buffer_append(berr, string, strlen(string));
 
-                               quit_pending = 1;
+                               if (c && c->ctl_fd != -1) {
+                                       chan_read_failed(c);
+                                       chan_write_failed(c);
+                                       return 0;
+                               } else
+                                       quit_pending = 1;
                                return -1;
 
                        case 'Z' - 64:
-                               /* Suspend the program. */
-                               /* Print a message to that effect to the user. */
-                               snprintf(string, sizeof string, "%c^Z [suspend ssh]\r\n", escape_char);
+                               /* XXX support this for mux clients */
+                               if (c && c->ctl_fd != -1) {
+ noescape:
+                                       snprintf(string, sizeof string,
+                                           "%c%c escape not available to "
+                                           "multiplexed sessions\r\n",
+                                           escape_char, ch);
+                                       buffer_append(berr, string,
+                                           strlen(string));
+                                       continue;
+                               }
+                               /* Suspend the program. Inform the user */
+                               snprintf(string, sizeof string,
+                                   "%c^Z [suspend ssh]\r\n", escape_char);
                                buffer_append(berr, string, strlen(string));
 
                                /* Restore terminal modes and suspend. */
@@ -1122,16 +971,20 @@ process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len)
                        case 'R':
                                if (compat20) {
                                        if (datafellows & SSH_BUG_NOREKEY)
-                                               logit("Server does not support re-keying");
+                                               logit("Server does not "
+                                                   "support re-keying");
                                        else
                                                need_rekeying = 1;
                                }
                                continue;
 
                        case '&':
+                               if (c && c->ctl_fd != -1)
+                                       goto noescape;
                                /*
-                                * Detach the program (continue to serve connections,
-                                * but put in background and no more new connections).
+                                * Detach the program (continue to serve
+                                * connections, but put in background and no
+                                * more new connections).
                                 */
                                /* Restore tty modes. */
                                leave_raw_mode();
@@ -1160,9 +1013,9 @@ process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len)
                                        return -1;
                                } else if (!stdin_eof) {
                                        /*
-                                        * Sending SSH_CMSG_EOF alone does not always appear
-                                        * to be enough.  So we try to send an EOF character
-                                        * first.
+                                        * Sending SSH_CMSG_EOF alone does not
+                                        * always appear to be enough.  So we
+                                        * try to send an EOF character first.
                                         */
                                        packet_start(SSH_CMSG_STDIN_DATA);
                                        packet_put_string("\004", 1);
@@ -1177,27 +1030,50 @@ process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len)
                                continue;
 
                        case '?':
-                               snprintf(string, sizeof string,
+                               if (c && c->ctl_fd != -1) {
+                                       snprintf(string, sizeof string,
+"%c?\r\n\
+Supported escape sequences:\r\n\
+  %c.  - terminate session\r\n\
+  %cB  - send a BREAK to the remote system\r\n\
+  %cC  - open a command line\r\n\
+  %cR  - Request rekey (SSH protocol 2 only)\r\n\
+  %c#  - list forwarded connections\r\n\
+  %c?  - this message\r\n\
+  %c%c  - send the escape character by typing it twice\r\n\
+(Note that escapes are only recognized immediately after newline.)\r\n",
+                                           escape_char, escape_char,
+                                           escape_char, escape_char,
+                                           escape_char, escape_char,
+                                           escape_char, escape_char,
+                                           escape_char);
+                               } else {
+                                       snprintf(string, sizeof string,
 "%c?\r\n\
 Supported escape sequences:\r\n\
-%c.  - terminate connection\r\n\
-%cB  - send a BREAK to the remote system\r\n\
-%cC  - open a command line\r\n\
-%cR  - Request rekey (SSH protocol 2 only)\r\n\
-%c^Z - suspend ssh\r\n\
-%c#  - list forwarded connections\r\n\
-%c&  - background ssh (when waiting for connections to terminate)\r\n\
-%c?  - this message\r\n\
-%c%c  - send the escape character by typing it twice\r\n\
+  %c.  - terminate connection (and any multiplexed sessions)\r\n\
+  %cB  - send a BREAK to the remote system\r\n\
+  %cC  - open a command line\r\n\
+  %cR  - Request rekey (SSH protocol 2 only)\r\n\
+  %c^Z - suspend ssh\r\n\
+  %c#  - list forwarded connections\r\n\
+  %c&  - background ssh (when waiting for connections to terminate)\r\n\
+  %c?  - this message\r\n\
+  %c%c  - send the escape character by typing it twice\r\n\
 (Note that escapes are only recognized immediately after newline.)\r\n",
-                                   escape_char, escape_char, escape_char, escape_char,
-                                   escape_char, escape_char, escape_char, escape_char,
-                                   escape_char, escape_char, escape_char);
+                                           escape_char, escape_char,
+                                           escape_char, escape_char,
+                                           escape_char, escape_char,
+                                           escape_char, escape_char,
+                                           escape_char, escape_char,
+                                           escape_char);
+                               }
                                buffer_append(berr, string, strlen(string));
                                continue;
 
                        case '#':
-                               snprintf(string, sizeof string, "%c#\r\n", escape_char);
+                               snprintf(string, sizeof string, "%c#\r\n",
+                                   escape_char);
                                buffer_append(berr, string, strlen(string));
                                s = channel_open_message();
                                buffer_append(berr, s, strlen(s));
@@ -1218,12 +1094,15 @@ Supported escape sequences:\r\n\
                        }
                } else {
                        /*
-                        * The previous character was not an escape char. Check if this
-                        * is an escape.
+                        * The previous character was not an escape char.
+                        * Check if this is an escape.
                         */
                        if (last_was_cr && ch == escape_char) {
-                               /* It is. Set the flag and continue to next character. */
-                               escape_pending = 1;
+                               /*
+                                * It is. Set the flag and continue to
+                                * next character.
+                                */
+                               *escape_pendingp = 1;
                                continue;
                        }
                }
@@ -1249,7 +1128,8 @@ client_process_input(fd_set *readset)
        if (FD_ISSET(fileno(stdin), readset)) {
                /* Read as much as possible. */
                len = read(fileno(stdin), buf, sizeof(buf));
-               if (len < 0 && (errno == EAGAIN || errno == EINTR))
+               if (len < 0 &&
+                   (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK))
                        return;         /* we'll try again later */
                if (len <= 0) {
                        /*
@@ -1258,7 +1138,8 @@ client_process_input(fd_set *readset)
                         * if it was an error condition.
                         */
                        if (len < 0) {
-                               snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno));
+                               snprintf(buf, sizeof buf, "read: %.100s\r\n",
+                                   strerror(errno));
                                buffer_append(&stderr_buffer, buf, strlen(buf));
                        }
                        /* Mark that we have seen EOF. */
@@ -1274,7 +1155,7 @@ client_process_input(fd_set *readset)
                                packet_start(SSH_CMSG_EOF);
                                packet_send();
                        }
-               } else if (escape_char == SSH_ESCAPECHAR_NONE) {
+               } else if (escape_char1 == SSH_ESCAPECHAR_NONE) {
                        /*
                         * Normal successful read, and no escape character.
                         * Just append the data to buffer.
@@ -1282,11 +1163,12 @@ client_process_input(fd_set *readset)
                        buffer_append(&stdin_buffer, buf, len);
                } else {
                        /*
-                        * Normal, successful read.  But we have an escape character
-                        * and have to process the characters one by one.
+                        * Normal, successful read.  But we have an escape
+                        * character and have to process the characters one
+                        * by one.
                         */
-                       if (process_escapes(&stdin_buffer, &stdout_buffer,
-                           &stderr_buffer, buf, len) == -1)
+                       if (process_escapes(NULL, &stdin_buffer,
+                           &stdout_buffer, &stderr_buffer, buf, len) == -1)
                                return;
                }
        }
@@ -1304,14 +1186,16 @@ client_process_output(fd_set *writeset)
                len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
                    buffer_len(&stdout_buffer));
                if (len <= 0) {
-                       if (errno == EINTR || errno == EAGAIN)
+                       if (errno == EINTR || errno == EAGAIN ||
+                           errno == EWOULDBLOCK)
                                len = 0;
                        else {
                                /*
                                 * An error or EOF was encountered.  Put an
                                 * error message to stderr buffer.
                                 */
-                               snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno));
+                               snprintf(buf, sizeof buf,
+                                   "write stdout: %.50s\r\n", strerror(errno));
                                buffer_append(&stderr_buffer, buf, strlen(buf));
                                quit_pending = 1;
                                return;
@@ -1319,7 +1203,6 @@ client_process_output(fd_set *writeset)
                }
                /* Consume printed data from the buffer. */
                buffer_consume(&stdout_buffer, len);
-               stdout_bytes += len;
        }
        /* Write buffered output to stderr. */
        if (FD_ISSET(fileno(stderr), writeset)) {
@@ -1327,17 +1210,20 @@ client_process_output(fd_set *writeset)
                len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
                    buffer_len(&stderr_buffer));
                if (len <= 0) {
-                       if (errno == EINTR || errno == EAGAIN)
+                       if (errno == EINTR || errno == EAGAIN ||
+                           errno == EWOULDBLOCK)
                                len = 0;
                        else {
-                               /* EOF or error, but can't even print error message. */
+                               /*
+                                * EOF or error, but can't even print
+                                * error message.
+                                */
                                quit_pending = 1;
                                return;
                        }
                }
                /* Consume printed characters from the buffer. */
                buffer_consume(&stderr_buffer, len);
-               stderr_bytes += len;
        }
 }
 
@@ -1356,16 +1242,39 @@ client_process_output(fd_set *writeset)
 static void
 client_process_buffered_input_packets(void)
 {
-       dispatch_run(DISPATCH_NONBLOCK, &quit_pending, compat20 ? xxx_kex : NULL);
+       dispatch_run(DISPATCH_NONBLOCK, &quit_pending,
+           compat20 ? xxx_kex : NULL);
 }
 
 /* scan buf[] for '~' before sending data to the peer */
 
-static int
-simple_escape_filter(Channel *c, char *buf, int len)
+/* Helper: allocate a new escape_filter_ctx and fill in its escape char */
+void *
+client_new_escape_filter_ctx(int escape_char)
 {
-       /* XXX we assume c->extended is writeable */
-       return process_escapes(&c->input, &c->output, &c->extended, buf, len);
+       struct escape_filter_ctx *ret;
+
+       ret = xmalloc(sizeof(*ret));
+       ret->escape_pending = 0;
+       ret->escape_char = escape_char;
+       return (void *)ret;
+}
+
+/* Free the escape filter context on channel free */
+void
+client_filter_cleanup(int cid, void *ctx)
+{
+       xfree(ctx);
+}
+
+int
+client_simple_escape_filter(Channel *c, char *buf, int len)
+{
+       if (c->extended_usage != CHAN_EXTENDED_WRITE)
+               return 0;
+
+       return process_escapes(c, &c->input, &c->output, &c->extended,
+           buf, len);
 }
 
 static void
@@ -1389,6 +1298,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
        fd_set *readset = NULL, *writeset = NULL;
        double start_time, total_time;
        int max_fd = 0, max_fd2 = 0, len, rekeying = 0;
+       u_int64_t ibytes, obytes;
        u_int nalloc = 0;
        char buf[100];
 
@@ -1397,7 +1307,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
        start_time = get_current_time();
 
        /* Initialize variables. */
-       escape_pending = 0;
+       escape_pending1 = 0;
        last_was_cr = 1;
        exit_status = -1;
        stdin_eof = 0;
@@ -1405,8 +1315,8 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
        connection_in = packet_get_connection_in();
        connection_out = packet_get_connection_out();
        max_fd = MAX(connection_in, connection_out);
-       if (control_fd != -1)
-               max_fd = MAX(max_fd, control_fd);
+       if (muxserver_sock != -1)
+               max_fd = MAX(max_fd, muxserver_sock);
 
        if (!compat20) {
                /* enable nonblocking unless tty */
@@ -1420,11 +1330,8 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
                max_fd = MAX(max_fd, fileno(stdout));
                max_fd = MAX(max_fd, fileno(stderr));
        }
-       stdin_bytes = 0;
-       stdout_bytes = 0;
-       stderr_bytes = 0;
        quit_pending = 0;
-       escape_char = escape_char_arg;
+       escape_char1 = escape_char_arg;
 
        /* Initialize buffers. */
        buffer_init(&stdin_buffer);
@@ -1452,9 +1359,11 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
 
        if (compat20) {
                session_ident = ssh2_chan_id;
-               if (escape_char != SSH_ESCAPECHAR_NONE)
+               if (escape_char_arg != SSH_ESCAPECHAR_NONE)
                        channel_register_filter(session_ident,
-                           simple_escape_filter, NULL);
+                           client_simple_escape_filter, NULL,
+                           client_filter_cleanup,
+                           client_new_escape_filter_ctx(escape_char_arg));
                if (session_ident != -1)
                        channel_register_cleanup(session_ident,
                            client_channel_closed, 0);
@@ -1526,7 +1435,10 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
                client_process_net_input(readset);
 
                /* Accept control connections.  */
-               client_process_control(readset);
+               if (muxserver_sock != -1 &&FD_ISSET(muxserver_sock, readset)) {
+                       if (muxserver_accept_control())
+                               quit_pending = 1;
+               }
 
                if (quit_pending)
                        break;
@@ -1541,7 +1453,10 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
                        client_process_output(writeset);
                }
 
-               /* Send as much buffered packet data as possible to the sender. */
+               /*
+                * Send as much buffered packet data as possible to the
+                * sender.
+                */
                if (FD_ISSET(connection_out, writeset))
                        packet_write_poll();
        }
@@ -1586,7 +1501,8 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
         * that the connection has been closed.
         */
        if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) {
-               snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host);
+               snprintf(buf, sizeof buf,
+                   "Connection to %.64s closed.\r\n", host);
                buffer_append(&stderr_buffer, buf, strlen(buf));
        }
 
@@ -1599,7 +1515,6 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
                        break;
                }
                buffer_consume(&stdout_buffer, len);
-               stdout_bytes += len;
        }
 
        /* Output any buffered data for stderr. */
@@ -1611,7 +1526,6 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
                        break;
                }
                buffer_consume(&stderr_buffer, len);
-               stderr_bytes += len;
        }
 
        /* Clear and free any buffers. */
@@ -1622,13 +1536,13 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
 
        /* Report bytes transferred, and transfer rates. */
        total_time = get_current_time() - start_time;
-       debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds",
-           stdin_bytes, stdout_bytes, stderr_bytes, total_time);
+       packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes);
+       packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes);
+       verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds",
+           obytes, ibytes, total_time);
        if (total_time > 0)
-               debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f",
-                   stdin_bytes / total_time, stdout_bytes / total_time,
-                   stderr_bytes / total_time);
-
+               verbose("Bytes per second: sent %.1f, received %.1f",
+                   obytes / total_time, ibytes / total_time);
        /* Return the exit status of the program. */
        debug("Exit status %d", exit_status);
        return exit_status;
@@ -1719,7 +1633,6 @@ client_request_forwarded_tcpip(const char *request_type, int rchan)
        Channel *c = NULL;
        char *listen_address, *originator_address;
        int listen_port, originator_port;
-       int sock;
 
        /* Get rest of the packet */
        listen_address = packet_get_string(NULL);
@@ -1728,19 +1641,13 @@ 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("client_request_forwarded_tcpip: listen %s port %d, "
+           "originator %s port %d", listen_address, listen_port,
+           originator_address, originator_port);
+
+       c = channel_connect_by_listen_address(listen_port,
+           "forwarded-tcpip", originator_address);
 
-       sock = channel_connect_by_listen_address(listen_port);
-       if (sock < 0) {
-               xfree(originator_address);
-               xfree(listen_address);
-               return NULL;
-       }
-       c = channel_new("forwarded-tcpip",
-           SSH_CHANNEL_CONNECTING, sock, sock, -1,
-           CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0,
-           originator_address, 1);
        xfree(originator_address);
        xfree(listen_address);
        return c;
@@ -1756,7 +1663,8 @@ client_request_x11(const char *request_type, int rchan)
 
        if (!options.forward_x11) {
                error("Warning: ssh server tried X11 forwarding.");
-               error("Warning: this is probably a break-in attempt by a malicious server.");
+               error("Warning: this is probably a break-in attempt by a "
+                   "malicious server.");
                return NULL;
        }
        originator = packet_get_string(NULL);
@@ -1789,7 +1697,8 @@ client_request_agent(const char *request_type, int rchan)
 
        if (!options.forward_agent) {
                error("Warning: ssh server tried agent forwarding.");
-               error("Warning: this is probably a break-in attempt by a malicious server.");
+               error("Warning: this is probably a break-in attempt by a "
+                   "malicious server.");
                return NULL;
        }
        sock = ssh_get_authentication_socket();
@@ -1832,7 +1741,7 @@ client_request_tun_fwd(int tun_mode, int local_tun, int remote_tun)
 #if defined(SSH_TUN_FILTER)
        if (options.tun_open == SSH_TUNMODE_POINTOPOINT)
                channel_register_filter(c->self, sys_tun_infilter,
-                   sys_tun_outfilter);
+                   sys_tun_outfilter, NULL, NULL);
 #endif
 
        packet_start(SSH2_MSG_CHANNEL_OPEN);
@@ -1915,7 +1824,11 @@ client_input_channel_req(int type, u_int32_t seq, void *ctxt)
        if (id == -1) {
                error("client_input_channel_req: request for channel -1");
        } else if ((c = channel_lookup(id)) == NULL) {
-               error("client_input_channel_req: channel %d: unknown channel", id);
+               error("client_input_channel_req: channel %d: "
+                   "unknown channel", id);
+       } else if (strcmp(rtype, "eow@openssh.com") == 0) {
+               packet_check_eom();
+               chan_rcvd_eow(c);
        } else if (strcmp(rtype, "exit-status") == 0) {
                exitval = packet_get_int();
                if (id == session_ident) {
@@ -1960,8 +1873,7 @@ client_input_global_request(int type, u_int32_t seq, void *ctxt)
 
 void
 client_session2_setup(int id, int want_tty, int want_subsystem,
-    const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env,
-    dispatch_fn *subsys_repl)
+    const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env)
 {
        int len;
        Channel *c = NULL;
@@ -1973,20 +1885,21 @@ client_session2_setup(int id, int want_tty, int want_subsystem,
 
        if (want_tty) {
                struct winsize ws;
-               struct termios tio;
 
                /* Store window size in the packet. */
                if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0)
                        memset(&ws, 0, sizeof(ws));
 
-               channel_request_start(id, "pty-req", 0);
+               channel_request_start(id, "pty-req", 1);
+               client_expect_confirm(id, "PTY allocation", 0);
                packet_put_cstring(term != NULL ? term : "");
                packet_put_int((u_int)ws.ws_col);
                packet_put_int((u_int)ws.ws_row);
                packet_put_int((u_int)ws.ws_xpixel);
                packet_put_int((u_int)ws.ws_ypixel);
-               tio = get_saved_tio();
-               tty_make_modes(-1, tiop != NULL ? tiop : &tio);
+               if (tiop == NULL)
+                       tiop = get_saved_tio();
+               tty_make_modes(-1, tiop);
                packet_send();
                /* XXX wait for reply */
                c->client_tty = 1;
@@ -2034,22 +1947,21 @@ client_session2_setup(int id, int want_tty, int want_subsystem,
                if (len > 900)
                        len = 900;
                if (want_subsystem) {
-                       debug("Sending subsystem: %.*s", len, (u_char*)buffer_ptr(cmd));
-                       channel_request_start(id, "subsystem", subsys_repl != NULL);
-                       if (subsys_repl != NULL) {
-                               /* register callback for reply */
-                               /* XXX we assume that client_loop has already been called */
-                               dispatch_set(SSH2_MSG_CHANNEL_FAILURE, subsys_repl);
-                               dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, subsys_repl);
-                       }
+                       debug("Sending subsystem: %.*s",
+                           len, (u_char*)buffer_ptr(cmd));
+                       channel_request_start(id, "subsystem", 1);
+                       client_expect_confirm(id, "subsystem", 1);
                } else {
-                       debug("Sending command: %.*s", len, (u_char*)buffer_ptr(cmd));
-                       channel_request_start(id, "exec", 0);
+                       debug("Sending command: %.*s",
+                           len, (u_char*)buffer_ptr(cmd));
+                       channel_request_start(id, "exec", 1);
+                       client_expect_confirm(id, "exec", 1);
                }
                packet_put_string(buffer_ptr(cmd), buffer_len(cmd));
                packet_send();
        } else {
-               channel_request_start(id, "shell", 0);
+               channel_request_start(id, "shell", 1);
+               client_expect_confirm(id, "shell", 1);
                packet_send();
        }
 }
@@ -2068,6 +1980,8 @@ client_init_dispatch_20(void)
        dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
        dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req);
        dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust);
+       dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm);
+       dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm);
        dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request);
 
        /* rekeying */
@@ -2077,6 +1991,7 @@ client_init_dispatch_20(void)
        dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply);
        dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply);
 }
+
 static void
 client_init_dispatch_13(void)
 {
@@ -2096,6 +2011,7 @@ client_init_dispatch_13(void)
        dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ?
            &x11_input_open : &deny_input_open);
 }
+
 static void
 client_init_dispatch_15(void)
 {
@@ -2103,6 +2019,7 @@ client_init_dispatch_15(void)
        dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof);
        dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose);
 }
+
 static void
 client_init_dispatch(void)
 {
@@ -2120,7 +2037,7 @@ cleanup_exit(int i)
 {
        leave_raw_mode();
        leave_non_blocking();
-       if (options.control_path != NULL && control_fd != -1)
+       if (options.control_path != NULL && muxserver_sock != -1)
                unlink(options.control_path);
        _exit(i);
 }
index c7d2233..8bb874b 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: clientloop.h,v 1.17 2007/08/07 07:32:53 djm Exp $ */
+/* $OpenBSD: clientloop.h,v 1.22 2008/06/12 15:19:17 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -43,11 +43,20 @@ void         client_x11_get_proto(const char *, const char *, u_int,
            char **, char **);
 void    client_global_request_reply_fwd(int, u_int32_t, void *);
 void    client_session2_setup(int, int, int, const char *, struct termios *,
-           int, Buffer *, char **, dispatch_fn *);
+           int, Buffer *, char **);
 int     client_request_tun_fwd(int, int, int);
 
+/* Escape filter for protocol 2 sessions */
+void   *client_new_escape_filter_ctx(int);
+void    client_filter_cleanup(int, void *);
+int     client_simple_escape_filter(Channel *, char *, int);
+
+/* Global request confirmation callbacks */
+typedef void global_confirm_cb(int, u_int32_t seq, void *);
+void    client_register_global_confirm(global_confirm_cb *, void *);
+
 /* Multiplexing protocol version */
-#define SSHMUX_VER                     1
+#define SSHMUX_VER                     2
 
 /* Multiplexing control protocol flags */
 #define SSHMUX_COMMAND_OPEN            1       /* Open new connection */
@@ -58,3 +67,7 @@ int    client_request_tun_fwd(int, int, int);
 #define SSHMUX_FLAG_SUBSYS             (1<<1)  /* Subsystem request on open */
 #define SSHMUX_FLAG_X11_FWD            (1<<2)  /* Request X11 forwarding */
 #define SSHMUX_FLAG_AGENT_FWD          (1<<3)  /* Request agent forwarding */
+
+void   muxserver_listen(void);
+int    muxserver_accept_control(void);
+void   muxclient(const char *);
index ac19095..a8203eb 100644 (file)
@@ -25,7 +25,7 @@
 #ifndef _DEFINES_H
 #define _DEFINES_H
 
-/* $Id: defines.h,v 1.146 2008/02/28 08:22:04 dtucker Exp $ */
+/* $Id: defines.h,v 1.151 2008/07/04 13:10:49 djm Exp $ */
 
 
 /* Constants */
@@ -431,10 +431,6 @@ struct winsize {
 # define __attribute__(x)
 #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
 
-#ifndef __dead
-# define __dead        __attribute__((noreturn))
-#endif
-
 #if !defined(HAVE_ATTRIBUTE__SENTINEL__) && !defined(__sentinel__)
 # define __sentinel__
 #endif
@@ -590,6 +586,15 @@ struct winsize {
 # define SSH_SYSFDMAX 10000
 #endif
 
+#ifdef FSID_HAS_VAL
+/* encode f_fsid into a 64 bit value  */
+#define FSID_TO_ULONG(f) \
+       ((((u_int64_t)(f).val[0] & 0xffffffffUL) << 32) | \
+           ((f).val[1] & 0xffffffffUL))
+#else
+# define FSID_TO_ULONG(f) ((f))
+#endif
+
 #if defined(__Lynx__)
  /*
   * LynxOS defines these in param.h which we do not want to include since
@@ -729,4 +734,8 @@ struct winsize {
 # endif
 #endif
 
+#ifndef EWOULDBLOCK
+# define EWOULDBLOCK EAGAIN
+#endif
+
 #endif /* _DEFINES_H */
index 6685810..b766053 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: dh.c,v 1.45 2007/09/27 00:15:57 ray Exp $ */
+/* $OpenBSD: dh.c,v 1.47 2008/06/26 09:19:39 djm Exp $ */
 /*
  * Copyright (c) 2000 Niels Provos.  All rights reserved.
  *
@@ -46,6 +46,7 @@ parse_prime(int linenum, char *line, struct dhgroup *dhg)
        char *cp, *arg;
        char *strsize, *gen, *prime;
        const char *errstr = NULL;
+       long long n;
 
        cp = line;
        if ((arg = strdelim(&cp)) == NULL)
@@ -62,12 +63,24 @@ parse_prime(int linenum, char *line, struct dhgroup *dhg)
        arg = strsep(&cp, " "); /* type */
        if (cp == NULL || *arg == '\0')
                goto fail;
+       /* Ensure this is a safe prime */
+       n = strtonum(arg, 0, 5, &errstr);
+       if (errstr != NULL || n != MODULI_TYPE_SAFE)
+               goto fail;
        arg = strsep(&cp, " "); /* tests */
        if (cp == NULL || *arg == '\0')
                goto fail;
+       /* Ensure prime has been tested and is not composite */
+       n = strtonum(arg, 0, 0x1f, &errstr);
+       if (errstr != NULL ||
+           (n & MODULI_TESTS_COMPOSITE) || !(n & ~MODULI_TESTS_COMPOSITE))
+               goto fail;
        arg = strsep(&cp, " "); /* tries */
        if (cp == NULL || *arg == '\0')
                goto fail;
+       n = strtonum(arg, 0, 1<<30, &errstr);
+       if (errstr != NULL || n == 0)
+               goto fail;
        strsize = strsep(&cp, " "); /* size */
        if (cp == NULL || *strsize == '\0' ||
            (dhg->size = (u_int)strtonum(strsize, 0, 64*1024, &errstr)) == 0 ||
@@ -153,7 +166,7 @@ choose_dh(int min, int wantbits, int max)
        }
 
        linenum = 0;
-       which = arc4random() % bestcount;
+       which = arc4random_uniform(bestcount);
        while (fgets(line, sizeof(line), f)) {
                if (!parse_prime(linenum, line, &dhg))
                        continue;
index 8e580ee..dfc1480 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: dh.h,v 1.9 2006/03/25 22:22:43 djm Exp $ */
+/* $OpenBSD: dh.h,v 1.10 2008/06/26 09:19:40 djm Exp $ */
 
 /*
  * Copyright (c) 2000 Niels Provos.  All rights reserved.
@@ -46,4 +46,28 @@ int   dh_estimate(int);
 #define DH_GRP_MIN     1024
 #define DH_GRP_MAX     8192
 
+/*
+ * Values for "type" field of moduli(5)
+ * Specifies the internal structure of the prime modulus.
+ */
+#define MODULI_TYPE_UNKNOWN            (0)
+#define MODULI_TYPE_UNSTRUCTURED       (1)
+#define MODULI_TYPE_SAFE               (2)
+#define MODULI_TYPE_SCHNORR            (3)
+#define MODULI_TYPE_SOPHIE_GERMAIN     (4)
+#define MODULI_TYPE_STRONG             (5)
+
+/*
+ * Values for "tests" field of moduli(5)
+ * Specifies the methods used in checking for primality.
+ * Usually, more than one test is used.
+ */
+#define MODULI_TESTS_UNTESTED          (0x00)
+#define MODULI_TESTS_COMPOSITE         (0x01)
+#define MODULI_TESTS_SIEVE             (0x02)
+#define MODULI_TESTS_MILLER_RABIN      (0x04)
+#define MODULI_TESTS_JACOBI            (0x08)
+#define MODULI_TESTS_ELLIPTIC          (0x10)
+
+
 #endif
index a89176f..a7da03f 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: dns.c,v 1.24 2007/01/03 03:01:40 stevesk Exp $ */
+/* $OpenBSD: dns.c,v 1.25 2008/06/12 00:03:49 dtucker Exp $ */
 
 /*
  * Copyright (c) 2003 Wesley Griffin. All rights reserved.
@@ -145,11 +145,20 @@ is_numeric_hostname(const char *hostname)
 {
        struct addrinfo hints, *ai;
 
+       /*
+        * We shouldn't ever get a null host but if we do then log an error
+        * and return -1 which stops DNS key fingerprint processing.
+        */
+       if (hostname == NULL) {
+               error("is_numeric_hostname called with NULL hostname");
+               return -1;
+       }
+
        memset(&hints, 0, sizeof(hints));
        hints.ai_socktype = SOCK_DGRAM;
        hints.ai_flags = AI_NUMERICHOST;
 
-       if (getaddrinfo(hostname, "0", &hints, &ai) == 0) {
+       if (getaddrinfo(hostname, NULL, &hints, &ai) == 0) {
                freeaddrinfo(ai);
                return -1;
        }
index e73f62b..2381aeb 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: groupaccess.c,v 1.12 2006/08/03 03:34:42 deraadt Exp $ */
+/* $OpenBSD: groupaccess.c,v 1.13 2008/07/04 03:44:59 djm Exp $ */
 /*
  * Copyright (c) 2001 Kevin Steves.  All rights reserved.
  *
@@ -31,6 +31,7 @@
 #include <grp.h>
 #include <unistd.h>
 #include <stdarg.h>
+#include <string.h>
 
 #include "xmalloc.h"
 #include "groupaccess.h"
@@ -87,6 +88,30 @@ ga_match(char * const *groups, int n)
        return 0;
 }
 
+/*
+ * Return 1 if one of user's groups matches group_pattern list.
+ * Return 0 on negated or no match.
+ */
+int
+ga_match_pattern_list(const char *group_pattern)
+{
+       int i, found = 0;
+       size_t len = strlen(group_pattern);
+
+       for (i = 0; i < ngroups; i++) {
+               switch (match_pattern_list(groups_byname[i],
+                   group_pattern, len, 0)) {
+               case -1:
+                       return 0;       /* Negated match wins */
+               case 0:
+                       continue;
+               case 1:
+                       found = 1;
+               }
+       }
+       return found;
+}
+
 /*
  * Free memory allocated for group access list.
  */
index 04b4498..000578e 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: groupaccess.h,v 1.7 2006/08/03 03:34:42 deraadt Exp $ */
+/* $OpenBSD: groupaccess.h,v 1.8 2008/07/04 03:44:59 djm Exp $ */
 
 /*
  * Copyright (c) 2001 Kevin Steves.  All rights reserved.
@@ -29,6 +29,7 @@
 
 int     ga_init(const char *, gid_t);
 int     ga_match(char * const *, int);
+int     ga_match_pattern_list(const char *);
 void    ga_free(void);
 
 #endif
index bc498fd..2ec7ea1 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: gss-serv.c,v 1.21 2007/06/12 08:20:00 djm Exp $ */
+/* $OpenBSD: gss-serv.c,v 1.22 2008/05/08 12:02:23 djm Exp $ */
 
 /*
  * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
@@ -35,6 +35,7 @@
 #include <string.h>
 #include <unistd.h>
 
+#include "openbsd-compat/sys-queue.h"
 #include "xmalloc.h"
 #include "buffer.h"
 #include "key.h"
index 9fcf1b0..f1b47f6 100644 (file)
 # include <sys/syslog.h>
 #endif
 
+#include <errno.h>
+
 /*
  * On HP-UX 11.11, shadow.h and prot.h provide conflicting declarations
  * of getspnam when _INCLUDE__STDC__ is defined, so we unset it here.
index 62bf836..2ea13d2 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: key.c,v 1.69 2007/07/12 05:48:05 ray Exp $ */
+/* $OpenBSD: key.c,v 1.78 2008/07/07 23:32:51 stevesk Exp $ */
 /*
  * read_bignum():
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -11,6 +11,7 @@
  *
  *
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
+ * Copyright (c) 2008 Alexander von Gernler.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -35,6 +36,7 @@
 
 #include "includes.h"
 
+#include <sys/param.h>
 #include <sys/types.h>
 
 #include <openssl/evp.h>
@@ -172,6 +174,7 @@ key_equal(const Key *a, const Key *b)
        default:
                fatal("key_equal: bad key type %d", a->type);
        }
+       /* NOTREACHED */
 }
 
 u_char*
@@ -295,6 +298,114 @@ key_fingerprint_bubblebabble(u_char *dgst_raw, u_int dgst_raw_len)
        return retval;
 }
 
+/*
+ * Draw an ASCII-Art representing the fingerprint so human brain can
+ * profit from its built-in pattern recognition ability.
+ * This technique is called "random art" and can be found in some
+ * scientific publications like this original paper:
+ *
+ * "Hash Visualization: a New Technique to improve Real-World Security",
+ * Perrig A. and Song D., 1999, International Workshop on Cryptographic
+ * Techniques and E-Commerce (CrypTEC '99)
+ * sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf
+ *
+ * The subject came up in a talk by Dan Kaminsky, too.
+ *
+ * If you see the picture is different, the key is different.
+ * If the picture looks the same, you still know nothing.
+ *
+ * The algorithm used here is a worm crawling over a discrete plane,
+ * leaving a trace (augmenting the field) everywhere it goes.
+ * Movement is taken from dgst_raw 2bit-wise.  Bumping into walls
+ * makes the respective movement vector be ignored for this turn.
+ * Graphs are not unambiguous, because circles in graphs can be
+ * walked in either direction.
+ */
+
+/*
+ * Field sizes for the random art.  Have to be odd, so the starting point
+ * can be in the exact middle of the picture, and FLDBASE should be >=8 .
+ * Else pictures would be too dense, and drawing the frame would
+ * fail, too, because the key type would not fit in anymore.
+ */
+#define        FLDBASE         8
+#define        FLDSIZE_Y       (FLDBASE + 1)
+#define        FLDSIZE_X       (FLDBASE * 2 + 1)
+static char *
+key_fingerprint_randomart(u_char *dgst_raw, u_int dgst_raw_len, const Key *k)
+{
+       /*
+        * Chars to be used after each other every time the worm
+        * intersects with itself.  Matter of taste.
+        */
+       char    *augmentation_string = " .o+=*BOX@%&#/^SE";
+       char    *retval, *p;
+       u_char   field[FLDSIZE_X][FLDSIZE_Y];
+       u_int    i, b;
+       int      x, y;
+       size_t   len = strlen(augmentation_string) - 1;
+
+       retval = xcalloc(1, (FLDSIZE_X + 3) * (FLDSIZE_Y + 2));
+
+       /* initialize field */
+       memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char));
+       x = FLDSIZE_X / 2;
+       y = FLDSIZE_Y / 2;
+
+       /* process raw key */
+       for (i = 0; i < dgst_raw_len; i++) {
+               int input;
+               /* each byte conveys four 2-bit move commands */
+               input = dgst_raw[i];
+               for (b = 0; b < 4; b++) {
+                       /* evaluate 2 bit, rest is shifted later */
+                       x += (input & 0x1) ? 1 : -1;
+                       y += (input & 0x2) ? 1 : -1;
+
+                       /* assure we are still in bounds */
+                       x = MAX(x, 0);
+                       y = MAX(y, 0);
+                       x = MIN(x, FLDSIZE_X - 1);
+                       y = MIN(y, FLDSIZE_Y - 1);
+
+                       /* augment the field */
+                       field[x][y]++;
+                       input = input >> 2;
+               }
+       }
+
+       /* mark starting point and end point*/
+       field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = len - 1;
+       field[x][y] = len;
+
+       /* fill in retval */
+       snprintf(retval, FLDSIZE_X, "+--[%4s %4u]", key_type(k), key_size(k));
+       p = strchr(retval, '\0');
+
+       /* output upper border */
+       for (i = p - retval - 1; i < FLDSIZE_X; i++)
+               *p++ = '-';
+       *p++ = '+';
+       *p++ = '\n';
+
+       /* output content */
+       for (y = 0; y < FLDSIZE_Y; y++) {
+               *p++ = '|';
+               for (x = 0; x < FLDSIZE_X; x++)
+                       *p++ = augmentation_string[MIN(field[x][y], len)];
+               *p++ = '|';
+               *p++ = '\n';
+       }
+
+       /* output lower border */
+       *p++ = '+';
+       for (i = 0; i < FLDSIZE_X; i++)
+               *p++ = '-';
+       *p++ = '+';
+
+       return retval;
+}
+
 char *
 key_fingerprint(const Key *k, enum fp_type dgst_type, enum fp_rep dgst_rep)
 {
@@ -312,6 +423,9 @@ key_fingerprint(const Key *k, enum fp_type dgst_type, enum fp_rep dgst_rep)
        case SSH_FP_BUBBLEBABBLE:
                retval = key_fingerprint_bubblebabble(dgst_raw, dgst_raw_len);
                break;
+       case SSH_FP_RANDOMART:
+               retval = key_fingerprint_randomart(dgst_raw, dgst_raw_len, k);
+               break;
        default:
                fatal("key_fingerprint_ex: bad digest representation %d",
                    dgst_rep);
index 6873dd7..14aac79 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: key.h,v 1.26 2006/08/03 03:34:42 deraadt Exp $ */
+/* $OpenBSD: key.h,v 1.27 2008/06/11 21:01:35 grunk Exp $ */
 
 /*
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
@@ -42,7 +42,8 @@ enum fp_type {
 };
 enum fp_rep {
        SSH_FP_HEX,
-       SSH_FP_BUBBLEBABBLE
+       SSH_FP_BUBBLEBABBLE,
+       SSH_FP_RANDOMART
 };
 
 /* key is stored in external hardware */
index fae5b04..4a8239b 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: log.c,v 1.40 2007/05/17 07:50:31 djm Exp $ */
+/* $OpenBSD: log.c,v 1.41 2008/06/10 04:50:25 dtucker Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -114,6 +114,17 @@ log_facility_number(char *name)
        return SYSLOG_FACILITY_NOT_SET;
 }
 
+const char *
+log_facility_name(SyslogFacility facility)
+{
+       u_int i;
+
+       for (i = 0;  log_facilities[i].name; i++)
+               if (log_facilities[i].val == facility)
+                       return log_facilities[i].name;
+       return NULL;
+}
+
 LogLevel
 log_level_number(char *name)
 {
@@ -126,6 +137,17 @@ log_level_number(char *name)
        return SYSLOG_LEVEL_NOT_SET;
 }
 
+const char *
+log_level_name(LogLevel level)
+{
+       u_int i;
+
+       for (i = 0; log_levels[i].name != NULL; i++)
+               if (log_levels[i].val == level)
+                       return log_levels[i].name;
+       return NULL;
+}
+
 /* Error messages that should be logged. */
 
 void
index 7a8c570..6505827 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: log.h,v 1.15 2006/08/18 09:13:25 deraadt Exp $ */
+/* $OpenBSD: log.h,v 1.17 2008/06/13 00:12:02 dtucker Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -49,11 +49,15 @@ typedef enum {
 void     log_init(char *, LogLevel, SyslogFacility, int);
 
 SyslogFacility log_facility_number(char *);
-LogLevel log_level_number(char *);
+const char *   log_facility_name(SyslogFacility);
+LogLevel       log_level_number(char *);
+const char *   log_level_name(LogLevel);
 
-void     fatal(const char *, ...) __dead __attribute__((format(printf, 1, 2)));
+void     fatal(const char *, ...) __attribute__((noreturn))
+    __attribute__((format(printf, 1, 2)));
 void     error(const char *, ...) __attribute__((format(printf, 1, 2)));
-void     sigdie(const char *, ...) __attribute__((format(printf, 1, 2)));
+void     sigdie(const char *, ...)  __attribute__((noreturn))
+    __attribute__((format(printf, 1, 2)));
 void     logit(const char *, ...) __attribute__((format(printf, 1, 2)));
 void     verbose(const char *, ...) __attribute__((format(printf, 1, 2)));
 void     debug(const char *, ...) __attribute__((format(printf, 1, 2)));
@@ -61,5 +65,5 @@ void     debug2(const char *, ...) __attribute__((format(printf, 1, 2)));
 void     debug3(const char *, ...) __attribute__((format(printf, 1, 2)));
 
 void    do_log(LogLevel, const char *, va_list);
-void    cleanup_exit(int) __dead;
+void    cleanup_exit(int) __attribute__((noreturn));
 #endif
index 3446465..fabc3ed 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: mac.c,v 1.14 2007/06/07 19:37:34 pvalchev Exp $ */
+/* $OpenBSD: mac.c,v 1.15 2008/06/13 00:51:47 dtucker Exp $ */
 /*
  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
  *
@@ -128,7 +128,7 @@ mac_compute(Mac *mac, u_int32_t seqno, u_char *data, int datalen)
 
        if (mac->mac_len > sizeof(m))
                fatal("mac_compute: mac too long %u %lu",
-                   mac->mac_len, sizeof(m));
+                   mac->mac_len, (u_long)sizeof(m));
 
        switch (mac->type) {
        case SSH_EVP:
index e3c9930..2389477 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: match.c,v 1.26 2006/08/03 03:34:42 deraadt Exp $ */
+/* $OpenBSD: match.c,v 1.27 2008/06/10 23:06:19 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -183,7 +183,8 @@ match_hostname(const char *host, const char *pattern, u_int len)
 
 /*
  * returns 0 if we get a negative match for the hostname or the ip
- * or if we get no match at all.  returns 1 otherwise.
+ * or if we get no match at all.  returns -1 on error, or 1 on
+ * successful match.
  */
 int
 match_host_and_ip(const char *host, const char *ipaddr,
@@ -191,9 +192,12 @@ match_host_and_ip(const char *host, const char *ipaddr,
 {
        int mhost, mip;
 
-       /* negative ipaddr match */
-       if ((mip = match_hostname(ipaddr, patterns, strlen(patterns))) == -1)
+       /* error in ipaddr match */
+       if ((mip = addr_match_list(ipaddr, patterns)) == -2)
+               return -1;
+       else if (mip == -1) /* negative ip address match */
                return 0;
+
        /* negative hostname match */
        if ((mhost = match_hostname(host, patterns, strlen(patterns))) == -1)
                return 0;
index d1d5386..18f6830 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: match.h,v 1.13 2006/03/25 22:22:43 djm Exp $ */
+/* $OpenBSD: match.h,v 1.14 2008/06/10 03:57:27 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -21,4 +21,7 @@ int    match_host_and_ip(const char *, const char *, const char *);
 int     match_user(const char *, const char *, const char *, const char *);
 char   *match_list(const char *, const char *, u_int *);
 
+/* addrmatch.c */
+int     addr_match_list(const char *, const char *);
+
 #endif
index b4fe489..8b303f1 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: misc.c,v 1.67 2008/01/01 08:47:04 dtucker Exp $ */
+/* $OpenBSD: misc.c,v 1.69 2008/06/13 01:38:23 dtucker Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  * Copyright (c) 2005,2006 Damien Miller.  All rights reserved.
@@ -534,7 +534,7 @@ tilde_expand_filename(const char *filename, uid_t uid)
                if ((pw = getpwnam(user)) == NULL)
                        fatal("tilde_expand_filename: No such user %s", user);
        } else if ((pw = getpwuid(uid)) == NULL)        /* ~/path */
-               fatal("tilde_expand_filename: No such uid %d", uid);
+               fatal("tilde_expand_filename: No such uid %ld", (long)uid);
 
        if (strlcpy(ret, pw->pw_dir, sizeof(ret)) >= sizeof(ret))
                fatal("tilde_expand_filename: Path too long");
@@ -832,3 +832,23 @@ put_u16(void *vp, u_int16_t v)
        p[0] = (u_char)(v >> 8) & 0xff;
        p[1] = (u_char)v & 0xff;
 }
+
+void
+ms_subtract_diff(struct timeval *start, int *ms)
+{
+       struct timeval diff, finish;
+
+       gettimeofday(&finish, NULL);
+       timersub(&finish, start, &diff);        
+       *ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000);
+}
+
+void
+ms_to_timeval(struct timeval *tv, int ms)
+{
+       if (ms < 0)
+               ms = 0;
+       tv->tv_sec = ms / 1000;
+       tv->tv_usec = (ms % 1000) * 1000;
+}
+
index be05e80..5da170d 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: misc.h,v 1.37 2007/12/27 14:22:08 dtucker Exp $ */
+/* $OpenBSD: misc.h,v 1.38 2008/06/12 20:38:28 dtucker Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -33,6 +33,8 @@ char  *tilde_expand_filename(const char *, uid_t);
 char   *percent_expand(const char *, ...) __attribute__((__sentinel__));
 char   *tohex(const void *, size_t);
 void    sanitise_stdfd(void);
+void    ms_subtract_diff(struct timeval *, int *);
+void    ms_to_timeval(struct timeval *, int);
 
 struct passwd *pwcopy(struct passwd *);
 const char *ssh_gai_strerror(int);
diff --git a/crypto/openssh-5/moduli.5 b/crypto/openssh-5/moduli.5
new file mode 100644 (file)
index 0000000..4a99439
--- /dev/null
@@ -0,0 +1,124 @@
+.\"    $OpenBSD: moduli.5,v 1.12 2008/06/26 05:57:54 djm Exp $
+.\"
+.\" Copyright (c) 2008 Damien Miller <djm@mindrot.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.Dd $Mdocdate: June 26 2008 $
+.Dt MODULI 5
+.Os
+.Sh NAME
+.Nm moduli
+.Nd Diffie Hellman moduli
+.Sh DESCRIPTION
+The
+.Pa /etc/moduli
+file contains prime numbers and generators for use by 
+.Xr sshd 8
+in the Diffie-Hellman Group Exchange key exchange method.
+.Pp
+New moduli may be generated with
+.Xr ssh-keygen 1
+using a two-step process.
+An initial
+.Em candidate generation
+pass, using 
+.Ic ssh-keygen -G ,
+calculates numbers that are likely to be useful.
+A second
+.Em primality testing
+pass, using
+.Ic ssh-keygen -T
+provides a high degree of assurance that the numbers are prime and are
+safe for use in Diffie Hellman operations by
+.Xr sshd 8 .
+This
+.Nm
+format is used as the output from each pass.
+.Pp
+The file consists of newline-separated records, one per modulus,
+containing seven space separated fields.
+These fields are as follows:
+.Pp
+.Bl -tag -width Description -offset indent
+.It timestamp
+The time that the modulus was last processed as YYYYMMDDHHMMSS.
+.It type
+Decimal number specifying the internal structure of the prime modulus.
+Supported types are:
+.Pp
+.Bl -tag -width 0x00 -compact
+.It 0
+Unknown, not tested
+.It 2
+"Safe" prime; (p-1)/2 is also prime.
+.It 4
+Sophie Germain; (p+1)*2 is also prime.
+.El
+.Pp
+Moduli candidates initially produced by
+.Xr ssh-keygen 1
+are Sophie Germain primes (type 4).
+Futher primality testing with
+.Xr ssh-keygen 1
+produces safe prime moduli (type 2) that are ready for use in
+.Xr sshd 8 .
+Other types are not used by OpenSSH.
+.It tests
+Decimal number indicating the type of primality tests that the number
+has been subjected to represented as a bitmask of the following values:
+.Pp
+.Bl -tag -width 0x00 -compact
+.It 0x00
+Not tested
+.It 0x01
+Composite number - not prime.
+.It 0x02
+Sieve of Eratosthenes
+.It 0x04
+Probabalistic Miller-Rabin primality tests.
+.El
+.Pp
+The
+.Xr ssh-keygen 1
+moduli candidate generation uses the Sieve of Eratosthenes (flag 0x02).
+Subsequent
+.Xr ssh-keygen 1
+primality tests are Miller-Rabin tests (flag 0x04).
+.It trials
+Decimal number indicating of primaility trials that have been performed
+on the modulus.
+.It size
+Decimal number indicating the size of the prime in bits.
+.It generator
+The recommended generator for use with this modulus (hexadecimal).
+.It modulus
+The modulus itself in hexadecimal.
+.El
+.Pp
+When performing Diffie Hellman Group Exchange,
+.Xr sshd 8
+first estimates the size of the modulus required to produce enough
+Diffie Hellman output to sufficiently key the selected symmetric cipher.
+.Xr sshd 8
+then randomly selects a modulus from
+.Fa /etc/moduli
+that best meets the size requirement.
+.Pp
+.Sh SEE ALSO
+.Xr ssh-keygen 1 ,
+.Xr sshd 8 ,
+.Rs
+.%R RFC 4419
+.%T "Diffie-Hellman Group Exchange for the Secure Shell (SSH) Transport Layer Protocol"
+.%D 2006
+.Re
index 8fa545d..f737cb3 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: moduli.c,v 1.20 2007/02/24 03:30:11 ray Exp $ */
+/* $OpenBSD: moduli.c,v 1.21 2008/06/26 09:19:40 djm Exp $ */
 /*
  * Copyright 1994 Phil Karn <karn@qualcomm.com>
  * Copyright 1996-1998, 2003 William Allen Simpson <wsimpson@greendragon.com>
@@ -42,6 +42,7 @@
 #include <sys/types.h>
 
 #include <openssl/bn.h>
+#include <openssl/dh.h>
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -50,6 +51,7 @@
 #include <time.h>
 
 #include "xmalloc.h"
+#include "dh.h"
 #include "log.h"
 
 /*
 /* need line long enough for largest moduli plus headers */
 #define QLINESIZE              (100+8192)
 
-/* Type: decimal.
- * Specifies the internal structure of the prime modulus.
- */
-#define QTYPE_UNKNOWN          (0)
-#define QTYPE_UNSTRUCTURED     (1)
-#define QTYPE_SAFE             (2)
-#define QTYPE_SCHNORR          (3)
-#define QTYPE_SOPHIE_GERMAIN   (4)
-#define QTYPE_STRONG           (5)
-
-/* Tests: decimal (bit field).
- * Specifies the methods used in checking for primality.
- * Usually, more than one test is used.
- */
-#define QTEST_UNTESTED         (0x00)
-#define QTEST_COMPOSITE                (0x01)
-#define QTEST_SIEVE            (0x02)
-#define QTEST_MILLER_RABIN     (0x04)
-#define QTEST_JACOBI           (0x08)
-#define QTEST_ELLIPTIC         (0x10)
-
 /*
  * Size: decimal.
  * Specifies the number of the most significant bit (0 to M).
@@ -434,8 +415,9 @@ gen_candidates(FILE *out, u_int32_t memory, u_int32_t power, BIGNUM *start)
                        fatal("BN_set_word failed");
                if (BN_add(q, q, largebase) == 0)
                        fatal("BN_add failed");
-               if (qfileout(out, QTYPE_SOPHIE_GERMAIN, QTEST_SIEVE,
-                   largetries, (power - 1) /* MSB */, (0), q) == -1) {
+               if (qfileout(out, MODULI_TYPE_SOPHIE_GERMAIN,
+                   MODULI_TESTS_SIEVE, largetries,
+                   (power - 1) /* MSB */, (0), q) == -1) {
                        ret = -1;
                        break;
                }
@@ -507,7 +489,7 @@ prime_test(FILE *in, FILE *out, u_int32_t trials, u_int32_t generator_wanted)
                /* tests */
                in_tests = strtoul(cp, &cp, 10);
 
-               if (in_tests & QTEST_COMPOSITE) {
+               if (in_tests & MODULI_TESTS_COMPOSITE) {
                        debug2("%10u: known composite", count_in);
                        continue;
                }
@@ -526,7 +508,7 @@ prime_test(FILE *in, FILE *out, u_int32_t trials, u_int32_t generator_wanted)
 
                /* modulus (hex) */
                switch (in_type) {
-               case QTYPE_SOPHIE_GERMAIN:
+               case MODULI_TYPE_SOPHIE_GERMAIN:
                        debug2("%10u: (%u) Sophie-Germain", count_in, in_type);
                        a = q;
                        if (BN_hex2bn(&a, cp) == 0)
@@ -539,11 +521,11 @@ prime_test(FILE *in, FILE *out, u_int32_t trials, u_int32_t generator_wanted)
                        in_size += 1;
                        generator_known = 0;
                        break;
-               case QTYPE_UNSTRUCTURED:
-               case QTYPE_SAFE:
-               case QTYPE_SCHNORR:
-               case QTYPE_STRONG:
-               case QTYPE_UNKNOWN:
+               case MODULI_TYPE_UNSTRUCTURED:
+               case MODULI_TYPE_SAFE:
+               case MODULI_TYPE_SCHNORR:
+               case MODULI_TYPE_STRONG:
+               case MODULI_TYPE_UNKNOWN:
                        debug2("%10u: (%u)", count_in, in_type);
                        a = p;
                        if (BN_hex2bn(&a, cp) == 0)
@@ -570,7 +552,7 @@ prime_test(FILE *in, FILE *out, u_int32_t trials, u_int32_t generator_wanted)
                        continue;
                }
 
-               if (in_tests & QTEST_MILLER_RABIN)
+               if (in_tests & MODULI_TESTS_MILLER_RABIN)
                        in_tries += trials;
                else
                        in_tries = trials;
@@ -644,7 +626,8 @@ prime_test(FILE *in, FILE *out, u_int32_t trials, u_int32_t generator_wanted)
                }
                debug("%10u: q is almost certainly prime", count_in);
 
-               if (qfileout(out, QTYPE_SAFE, (in_tests | QTEST_MILLER_RABIN),
+               if (qfileout(out, MODULI_TYPE_SAFE,
+                   in_tests | MODULI_TESTS_MILLER_RABIN,
                    in_tries, in_size, generator_known, p)) {
                        res = -1;
                        break;
index cc0e0fc..73cf6bc 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor.c,v 1.94 2007/10/29 04:08:08 dtucker Exp $ */
+/* $OpenBSD: monitor.c,v 1.99 2008/07/10 18:08:11 markus Exp $ */
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
  * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -51,6 +51,7 @@
 
 #include <openssl/dh.h>
 
+#include "openbsd-compat/sys-queue.h"
 #include "xmalloc.h"
 #include "ssh.h"
 #include "key.h"
@@ -1014,6 +1015,8 @@ mm_answer_keyallowed(int sock, Buffer *m)
                        allowed = options.pubkey_authentication &&
                            user_key_allowed(authctxt->pw, key);
                        auth_method = "publickey";
+                       if (options.pubkey_authentication && allowed != 1)
+                               auth_clear_options();
                        break;
                case MM_HOSTKEY:
                        allowed = options.hostbased_authentication &&
@@ -1026,6 +1029,8 @@ mm_answer_keyallowed(int sock, Buffer *m)
                        allowed = options.rhosts_rsa_authentication &&
                            auth_rhosts_rsa_key_allowed(authctxt->pw,
                            cuser, chost, key);
+                       if (options.rhosts_rsa_authentication && allowed != 1)
+                               auth_clear_options();
                        auth_method = "rsa";
                        break;
                default:
@@ -1055,7 +1060,7 @@ mm_answer_keyallowed(int sock, Buffer *m)
        }
 
        debug3("%s: key %p is %s",
-           __func__, key, allowed ? "allowed" : "disallowed");
+           __func__, key, allowed ? "allowed" : "not allowed");
 
        buffer_clear(m);
        buffer_put_int(m, allowed);
@@ -1272,7 +1277,7 @@ mm_session_close(Session *s)
                debug3("%s: tty %s ptyfd %d", __func__, s->tty, s->ptyfd);
                session_pty_cleanup2(s);
        }
-       s->used = 0;
+       session_unused(s->self);
 }
 
 int
@@ -1700,7 +1705,7 @@ mm_get_keystate(struct monitor *pmonitor)
        u_char *blob, *p;
        u_int bloblen, plen;
        u_int32_t seqnr, packets;
-       u_int64_t blocks;
+       u_int64_t blocks, bytes;
 
        debug3("%s: Waiting for new keys", __func__);
 
@@ -1733,11 +1738,13 @@ mm_get_keystate(struct monitor *pmonitor)
        seqnr = buffer_get_int(&m);
        blocks = buffer_get_int64(&m);
        packets = buffer_get_int(&m);
-       packet_set_state(MODE_OUT, seqnr, blocks, packets);
+       bytes = buffer_get_int64(&m);
+       packet_set_state(MODE_OUT, seqnr, blocks, packets, bytes);
        seqnr = buffer_get_int(&m);
        blocks = buffer_get_int64(&m);
        packets = buffer_get_int(&m);
-       packet_set_state(MODE_IN, seqnr, blocks, packets);
+       bytes = buffer_get_int64(&m);
+       packet_set_state(MODE_IN, seqnr, blocks, packets, bytes);
 
  skip:
        /* Get the key context */
index 36a07a0..c890f77 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor_mm.h,v 1.4 2006/08/03 03:34:42 deraadt Exp $ */
+/* $OpenBSD: monitor_mm.h,v 1.5 2008/04/29 11:20:31 otto Exp $ */
 
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
@@ -41,9 +41,6 @@ struct mm_master {
        size_t size;
 
        struct mm_master *mmalloc;      /* Used to completely share */
-
-       int write;              /* used to writing to other party */
-       int read;               /* used for reading from other party */
 };
 
 RB_PROTOTYPE(mmtree, mm_share, next, mm_compare)
index e895f19..40463d0 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor_wrap.c,v 1.60 2007/10/29 04:08:08 dtucker Exp $ */
+/* $OpenBSD: monitor_wrap.c,v 1.63 2008/07/10 18:08:11 markus Exp $ */
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
  * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -41,6 +41,7 @@
 #include <openssl/bn.h>
 #include <openssl/dh.h>
 
+#include "openbsd-compat/sys-queue.h"
 #include "xmalloc.h"
 #include "ssh.h"
 #include "dh.h"
@@ -572,7 +573,7 @@ mm_send_keystate(struct monitor *monitor)
        u_char *blob, *p;
        u_int bloblen, plen;
        u_int32_t seqnr, packets;
-       u_int64_t blocks;
+       u_int64_t blocks, bytes;
 
        buffer_init(&m);
 
@@ -621,14 +622,16 @@ mm_send_keystate(struct monitor *monitor)
        buffer_put_string(&m, blob, bloblen);
        xfree(blob);
 
-       packet_get_state(MODE_OUT, &seqnr, &blocks, &packets);
+       packet_get_state(MODE_OUT, &seqnr, &blocks, &packets, &bytes);
        buffer_put_int(&m, seqnr);
        buffer_put_int64(&m, blocks);
        buffer_put_int(&m, packets);
-       packet_get_state(MODE_IN, &seqnr, &blocks, &packets);
+       buffer_put_int64(&m, bytes);
+       packet_get_state(MODE_IN, &seqnr, &blocks, &packets, &bytes);
        buffer_put_int(&m, seqnr);
        buffer_put_int64(&m, blocks);
        buffer_put_int(&m, packets);
+       buffer_put_int64(&m, bytes);
 
        debug3("%s: New keys have been sent", __func__);
  skip:
@@ -665,7 +668,20 @@ mm_pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, size_t namebuflen)
 {
        Buffer m;
        char *p, *msg;
-       int success = 0;
+       int success = 0, tmp1 = -1, tmp2 = -1;
+
+       /* Kludge: ensure there are fds free to receive the pty/tty */
+       if ((tmp1 = dup(pmonitor->m_recvfd)) == -1 ||
+           (tmp2 = dup(pmonitor->m_recvfd)) == -1) {
+               error("%s: cannot allocate fds for pty", __func__);
+               if (tmp1 > 0)
+                       close(tmp1);
+               if (tmp2 > 0)
+                       close(tmp2);
+               return 0;
+       }
+       close(tmp1);
+       close(tmp2);
 
        buffer_init(&m);
        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PTY, &m);
@@ -710,8 +726,9 @@ mm_session_pty_cleanup2(Session *s)
        buffer_free(&m);
 
        /* closed dup'ed master */
-       if (close(s->ptymaster) < 0)
-               error("close(s->ptymaster): %s", strerror(errno));
+       if (s->ptymaster != -1 && close(s->ptymaster) < 0)
+               error("close(s->ptymaster/%d): %s",
+                   s->ptymaster, strerror(errno));
 
        /* unlink pty from session */
        s->ttyfd = -1;
diff --git a/crypto/openssh-5/mux.c b/crypto/openssh-5/mux.c
new file mode 100644 (file)
index 0000000..79f8376
--- /dev/null
@@ -0,0 +1,728 @@
+/* $OpenBSD: mux.c,v 1.7 2008/06/13 17:21:20 dtucker Exp $ */
+/*
+ * Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* ssh session multiplexing support */
+
+#include "includes.h"
+
+/*
+ * TODO:
+ *   1. partial reads in muxserver_accept_control (maybe make channels
+ *      from accepted connections)
+ *   2. Better signalling from master to slave, especially passing of
+ *      error messages
+ *   3. Better fall-back from mux slave error to new connection.
+ *   3. Add/delete forwardings via slave
+ *   4. ExitOnForwardingFailure (after #3 obviously)
+ *   5. Maybe extension mechanisms for multi-X11/multi-agent forwarding
+ *   6. Document the mux mini-protocol somewhere.
+ *   7. Support ~^Z in mux slaves.
+ *   8. Inspect or control sessions in master.
+ *   9. If we ever support the "signal" channel request, send signals on
+ *      sessions in master.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+#ifdef HAVE_UTIL_H
+# include <util.h>
+#endif
+
+#ifdef HAVE_LIBUTIL_H
+# include <libutil.h>
+#endif
+
+#include "openbsd-compat/sys-queue.h"
+#include "xmalloc.h"
+#include "log.h"
+#include "ssh.h"
+#include "pathnames.h"
+#include "misc.h"
+#include "match.h"
+#include "buffer.h"
+#include "channels.h"
+#include "msg.h"
+#include "packet.h"
+#include "monitor_fdpass.h"
+#include "sshpty.h"
+#include "key.h"
+#include "readconf.h"
+#include "clientloop.h"
+
+/* from ssh.c */
+extern int tty_flag;
+extern Options options;
+extern int stdin_null_flag;
+extern char *host;
+int subsystem_flag;
+extern Buffer command;
+
+/* Context for session open confirmation callback */
+struct mux_session_confirm_ctx {
+       int want_tty;
+       int want_subsys;
+       int want_x_fwd;
+       int want_agent_fwd;
+       Buffer cmd;
+       char *term;
+       struct termios tio;
+       char **env;
+};
+
+/* fd to control socket */
+int muxserver_sock = -1;
+
+/* Multiplexing control command */
+u_int muxclient_command = 0;
+
+/* Set when signalled. */
+static volatile sig_atomic_t muxclient_terminate = 0;
+
+/* PID of multiplex server */
+static u_int muxserver_pid = 0;
+
+
+/* ** Multiplexing master support */
+
+/* Prepare a mux master to listen on a Unix domain socket. */
+void
+muxserver_listen(void)
+{
+       struct sockaddr_un addr;
+       mode_t old_umask;
+       int addr_len;
+
+       if (options.control_path == NULL ||
+           options.control_master == SSHCTL_MASTER_NO)
+               return;
+
+       debug("setting up multiplex master socket");
+
+       memset(&addr, '\0', sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       addr_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))
+               fatal("ControlPath too long");
+
+       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, addr_len) == -1) {
+               muxserver_sock = -1;
+               if (errno == EINVAL || errno == EADDRINUSE) {
+                       error("ControlSocket %s already exists, "
+                           "disabling multiplexing", options.control_path);
+                       close(muxserver_sock);
+                       muxserver_sock = -1;
+                       xfree(options.control_path);
+                       options.control_path = NULL;
+                       options.control_master = SSHCTL_MASTER_NO;
+                       return;
+               } else
+                       fatal("%s bind(): %s", __func__, strerror(errno));
+       }
+       umask(old_umask);
+
+       if (listen(muxserver_sock, 64) == -1)
+               fatal("%s listen(): %s", __func__, strerror(errno));
+
+       set_nonblock(muxserver_sock);
+}
+
+/* Callback on open confirmation in mux master for a mux client session. */
+static void
+mux_session_confirm(int id, void *arg)
+{
+       struct mux_session_confirm_ctx *cctx = arg;
+       const char *display;
+       Channel *c;
+       int i;
+
+       if (cctx == NULL)
+               fatal("%s: cctx == NULL", __func__);
+       if ((c = channel_lookup(id)) == NULL)
+               fatal("%s: no channel for id %d", __func__, id);
+
+       display = getenv("DISPLAY");
+       if (cctx->want_x_fwd && options.forward_x11 && display != NULL) {
+               char *proto, *data;
+               /* Get reasonable local authentication information. */
+               client_x11_get_proto(display, options.xauth_location,
+                   options.forward_x11_trusted, &proto, &data);
+               /* Request forwarding with authentication spoofing. */
+               debug("Requesting X11 forwarding with authentication spoofing.");
+               x11_request_forwarding_with_spoofing(id, display, proto, data);
+               /* XXX wait for reply */
+       }
+
+       if (cctx->want_agent_fwd && options.forward_agent) {
+               debug("Requesting authentication agent forwarding.");
+               channel_request_start(id, "auth-agent-req@openssh.com", 0);
+               packet_send();
+       }
+
+       client_session2_setup(id, cctx->want_tty, cctx->want_subsys,
+           cctx->term, &cctx->tio, c->rfd, &cctx->cmd, cctx->env);
+
+       c->open_confirm_ctx = NULL;
+       buffer_free(&cctx->cmd);
+       xfree(cctx->term);
+       if (cctx->env != NULL) {
+               for (i = 0; cctx->env[i] != NULL; i++)
+                       xfree(cctx->env[i]);
+               xfree(cctx->env);
+       }
+       xfree(cctx);
+}
+
+/*
+ * Accept a connection on the mux master socket and process the
+ * client's request. Returns flag indicating whether mux master should
+ * begin graceful close.
+ */
+int
+muxserver_accept_control(void)
+{
+       Buffer m;
+       Channel *c;
+       int client_fd, new_fd[3], ver, allowed, window, packetmax;
+       socklen_t addrlen;
+       struct sockaddr_storage addr;
+       struct mux_session_confirm_ctx *cctx;
+       char *cmd;
+       u_int i, j, len, env_len, mux_command, flags, escape_char;
+       uid_t euid;
+       gid_t egid;
+       int start_close = 0;
+
+       /*
+        * Accept connection on control socket
+        */
+       memset(&addr, 0, sizeof(addr));
+       addrlen = sizeof(addr);
+       if ((client_fd = accept(muxserver_sock,
+           (struct sockaddr*)&addr, &addrlen)) == -1) {
+               error("%s accept: %s", __func__, strerror(errno));
+               return 0;
+       }
+
+       if (getpeereid(client_fd, &euid, &egid) < 0) {
+               error("%s getpeereid failed: %s", __func__, strerror(errno));
+               close(client_fd);
+               return 0;
+       }
+       if ((euid != 0) && (getuid() != euid)) {
+               error("control mode uid mismatch: peer euid %u != uid %u",
+                   (u_int) euid, (u_int) getuid());
+               close(client_fd);
+               return 0;
+       }
+
+       /* XXX handle asynchronously */
+       unset_nonblock(client_fd);
+
+       /* Read command */
+       buffer_init(&m);
+       if (ssh_msg_recv(client_fd, &m) == -1) {
+               error("%s: client msg_recv failed", __func__);
+               close(client_fd);
+               buffer_free(&m);
+               return 0;
+       }
+       if ((ver = buffer_get_char(&m)) != SSHMUX_VER) {
+               error("%s: wrong client version %d", __func__, ver);
+               buffer_free(&m);
+               close(client_fd);
+               return 0;
+       }
+
+       allowed = 1;
+       mux_command = buffer_get_int(&m);
+       flags = buffer_get_int(&m);
+
+       buffer_clear(&m);
+
+       switch (mux_command) {
+       case SSHMUX_COMMAND_OPEN:
+               if (options.control_master == SSHCTL_MASTER_ASK ||
+                   options.control_master == SSHCTL_MASTER_AUTO_ASK)
+                       allowed = ask_permission("Allow shared connection "
+                           "to %s? ", host);
+               /* continue below */
+               break;
+       case SSHMUX_COMMAND_TERMINATE:
+               if (options.control_master == SSHCTL_MASTER_ASK ||
+                   options.control_master == SSHCTL_MASTER_AUTO_ASK)
+                       allowed = ask_permission("Terminate shared connection "
+                           "to %s? ", host);
+               if (allowed)
+                       start_close = 1;
+               /* FALLTHROUGH */
+       case SSHMUX_COMMAND_ALIVE_CHECK:
+               /* Reply for SSHMUX_COMMAND_TERMINATE and ALIVE_CHECK */
+               buffer_clear(&m);
+               buffer_put_int(&m, allowed);
+               buffer_put_int(&m, getpid());
+               if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
+                       error("%s: client msg_send failed", __func__);
+                       close(client_fd);
+                       buffer_free(&m);
+                       return start_close;
+               }
+               buffer_free(&m);
+               close(client_fd);
+               return start_close;
+       default:
+               error("Unsupported command %d", mux_command);
+               buffer_free(&m);
+               close(client_fd);
+               return 0;
+       }
+
+       /* Reply for SSHMUX_COMMAND_OPEN */
+       buffer_clear(&m);
+       buffer_put_int(&m, allowed);
+       buffer_put_int(&m, getpid());
+       if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
+               error("%s: client msg_send failed", __func__);
+               close(client_fd);
+               buffer_free(&m);
+               return 0;
+       }
+
+       if (!allowed) {
+               error("Refused control connection");
+               close(client_fd);
+               buffer_free(&m);
+               return 0;
+       }
+
+       buffer_clear(&m);
+       if (ssh_msg_recv(client_fd, &m) == -1) {
+               error("%s: client msg_recv failed", __func__);
+               close(client_fd);
+               buffer_free(&m);
+               return 0;
+       }
+       if ((ver = buffer_get_char(&m)) != SSHMUX_VER) {
+               error("%s: wrong client version %d", __func__, ver);
+               buffer_free(&m);
+               close(client_fd);
+               return 0;
+       }
+
+       cctx = xcalloc(1, sizeof(*cctx));
+       cctx->want_tty = (flags & SSHMUX_FLAG_TTY) != 0;
+       cctx->want_subsys = (flags & SSHMUX_FLAG_SUBSYS) != 0;
+       cctx->want_x_fwd = (flags & SSHMUX_FLAG_X11_FWD) != 0;
+       cctx->want_agent_fwd = (flags & SSHMUX_FLAG_AGENT_FWD) != 0;
+       cctx->term = buffer_get_string(&m, &len);
+       escape_char = buffer_get_int(&m);
+
+       cmd = buffer_get_string(&m, &len);
+       buffer_init(&cctx->cmd);
+       buffer_append(&cctx->cmd, cmd, strlen(cmd));
+
+       env_len = buffer_get_int(&m);
+       env_len = MIN(env_len, 4096);
+       debug3("%s: receiving %d env vars", __func__, env_len);
+       if (env_len != 0) {
+               cctx->env = xcalloc(env_len + 1, sizeof(*cctx->env));
+               for (i = 0; i < env_len; i++)
+                       cctx->env[i] = buffer_get_string(&m, &len);
+               cctx->env[i] = NULL;
+       }
+
+       debug2("%s: accepted tty %d, subsys %d, cmd %s", __func__,
+           cctx->want_tty, cctx->want_subsys, cmd);
+       xfree(cmd);
+
+       /* Gather fds from client */
+       for(i = 0; i < 3; i++) {
+               if ((new_fd[i] = mm_receive_fd(client_fd)) == -1) {
+                       error("%s: failed to receive fd %d from slave",
+                           __func__, i);
+                       for (j = 0; j < i; j++)
+                               close(new_fd[j]);
+                       for (j = 0; j < env_len; j++)
+                               xfree(cctx->env[j]);
+                       if (env_len > 0)
+                               xfree(cctx->env);
+                       xfree(cctx->term);
+                       buffer_free(&cctx->cmd);
+                       close(client_fd);
+                       xfree(cctx);
+                       return 0;
+               }
+       }
+
+       debug2("%s: got fds stdin %d, stdout %d, stderr %d", __func__,
+           new_fd[0], new_fd[1], new_fd[2]);
+
+       /* Try to pick up ttymodes from client before it goes raw */
+       if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1)
+               error("%s: tcgetattr: %s", __func__, strerror(errno));
+
+       /* This roundtrip is just for synchronisation of ttymodes */
+       buffer_clear(&m);
+       if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
+               error("%s: client msg_send failed", __func__);
+               close(client_fd);
+               close(new_fd[0]);
+               close(new_fd[1]);
+               close(new_fd[2]);
+               buffer_free(&m);
+               xfree(cctx->term);
+               if (env_len != 0) {
+                       for (i = 0; i < env_len; i++)
+                               xfree(cctx->env[i]);
+                       xfree(cctx->env);
+               }
+               return 0;
+       }
+       buffer_free(&m);
+
+       /* enable nonblocking unless tty */
+       if (!isatty(new_fd[0]))
+               set_nonblock(new_fd[0]);
+       if (!isatty(new_fd[1]))
+               set_nonblock(new_fd[1]);
+       if (!isatty(new_fd[2]))
+               set_nonblock(new_fd[2]);
+
+       set_nonblock(client_fd);
+
+       window = CHAN_SES_WINDOW_DEFAULT;
+       packetmax = CHAN_SES_PACKET_DEFAULT;
+       if (cctx->want_tty) {
+               window >>= 1;
+               packetmax >>= 1;
+       }
+       
+       c = channel_new("session", SSH_CHANNEL_OPENING,
+           new_fd[0], new_fd[1], new_fd[2], window, packetmax,
+           CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0);
+
+       c->ctl_fd = client_fd;
+       if (cctx->want_tty && escape_char != 0xffffffff) {
+               channel_register_filter(c->self,
+                   client_simple_escape_filter, NULL,
+                   client_filter_cleanup,
+                   client_new_escape_filter_ctx((int)escape_char));
+       }
+
+       debug3("%s: channel_new: %d", __func__, c->self);
+
+       channel_send_open(c->self);
+       channel_register_open_confirm(c->self, mux_session_confirm, cctx);
+       return 0;
+}
+
+/* ** Multiplexing client support */
+
+/* Exit signal handler */
+static void
+control_client_sighandler(int signo)
+{
+       muxclient_terminate = signo;
+}
+
+/*
+ * Relay signal handler - used to pass some signals from mux client to
+ * mux master.
+ */
+static void
+control_client_sigrelay(int signo)
+{
+       int save_errno = errno;
+
+       if (muxserver_pid > 1)
+               kill(muxserver_pid, signo);
+
+       errno = save_errno;
+}
+
+/* Check mux client environment variables before passing them to mux master. */
+static int
+env_permitted(char *env)
+{
+       int i, ret;
+       char name[1024], *cp;
+
+       if ((cp = strchr(env, '=')) == NULL || cp == env)
+               return (0);
+       ret = snprintf(name, sizeof(name), "%.*s", (int)(cp - env), env);
+       if (ret <= 0 || (size_t)ret >= sizeof(name))
+               fatal("env_permitted: name '%.100s...' too long", env);
+
+       for (i = 0; i < options.num_send_env; i++)
+               if (match_pattern(name, options.send_env[i]))
+                       return (1);
+
+       return (0);
+}
+
+/* Multiplex client main loop. */
+void
+muxclient(const char *path)
+{
+       struct sockaddr_un addr;
+       int i, r, fd, sock, exitval[2], num_env, addr_len;
+       Buffer m;
+       char *term;
+       extern char **environ;
+       u_int allowed, flags;
+
+       if (muxclient_command == 0)
+               muxclient_command = SSHMUX_COMMAND_OPEN;
+
+       switch (options.control_master) {
+       case SSHCTL_MASTER_AUTO:
+       case SSHCTL_MASTER_AUTO_ASK:
+               debug("auto-mux: Trying existing master");
+               /* FALLTHROUGH */
+       case SSHCTL_MASTER_NO:
+               break;
+       default:
+               return;
+       }
+
+       memset(&addr, '\0', sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       addr_len = offsetof(struct sockaddr_un, sun_path) +
+           strlen(path) + 1;
+
+       if (strlcpy(addr.sun_path, path,
+           sizeof(addr.sun_path)) >= sizeof(addr.sun_path))
+               fatal("ControlPath too long");
+
+       if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
+               fatal("%s socket(): %s", __func__, strerror(errno));
+
+       if (connect(sock, (struct sockaddr *)&addr, addr_len) == -1) {
+               if (muxclient_command != SSHMUX_COMMAND_OPEN) {
+                       fatal("Control socket connect(%.100s): %s", path,
+                           strerror(errno));
+               }
+               if (errno == ENOENT)
+                       debug("Control socket \"%.100s\" does not exist", path);
+               else {
+                       error("Control socket connect(%.100s): %s", path,
+                           strerror(errno));
+               }
+               close(sock);
+               return;
+       }
+
+       if (stdin_null_flag) {
+               if ((fd = open(_PATH_DEVNULL, O_RDONLY)) == -1)
+                       fatal("open(/dev/null): %s", strerror(errno));
+               if (dup2(fd, STDIN_FILENO) == -1)
+                       fatal("dup2: %s", strerror(errno));
+               if (fd > STDERR_FILENO)
+                       close(fd);
+       }
+
+       term = getenv("TERM");
+
+       flags = 0;
+       if (tty_flag)
+               flags |= SSHMUX_FLAG_TTY;
+       if (subsystem_flag)
+               flags |= SSHMUX_FLAG_SUBSYS;
+       if (options.forward_x11)
+               flags |= SSHMUX_FLAG_X11_FWD;
+       if (options.forward_agent)
+               flags |= SSHMUX_FLAG_AGENT_FWD;
+
+       signal(SIGPIPE, SIG_IGN);
+
+       buffer_init(&m);
+
+       /* Send our command to server */
+       buffer_put_int(&m, muxclient_command);
+       buffer_put_int(&m, flags);
+       if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) {
+               error("%s: msg_send", __func__);
+ muxerr:
+               close(sock);
+               buffer_free(&m);
+               if (muxclient_command != SSHMUX_COMMAND_OPEN)
+                       cleanup_exit(255);
+               logit("Falling back to non-multiplexed connection");
+               xfree(options.control_path);
+               options.control_path = NULL;
+               options.control_master = SSHCTL_MASTER_NO;
+               return;
+       }
+       buffer_clear(&m);
+
+       /* Get authorisation status and PID of controlee */
+       if (ssh_msg_recv(sock, &m) == -1) {
+               error("%s: Did not receive reply from master", __func__);
+               goto muxerr;
+       }
+       if (buffer_get_char(&m) != SSHMUX_VER) {
+               error("%s: Master replied with wrong version", __func__);
+               goto muxerr;
+       }
+       if (buffer_get_int_ret(&allowed, &m) != 0) {
+               error("%s: bad server reply", __func__);
+               goto muxerr;
+       }
+       if (allowed != 1) {
+               error("Connection to master denied");
+               goto muxerr;
+       }
+       muxserver_pid = buffer_get_int(&m);
+
+       buffer_clear(&m);
+
+       switch (muxclient_command) {
+       case SSHMUX_COMMAND_ALIVE_CHECK:
+               fprintf(stderr, "Master running (pid=%d)\r\n",
+                   muxserver_pid);
+               exit(0);
+       case SSHMUX_COMMAND_TERMINATE:
+               fprintf(stderr, "Exit request sent.\r\n");
+               exit(0);
+       case SSHMUX_COMMAND_OPEN:
+               buffer_put_cstring(&m, term ? term : "");
+               if (options.escape_char == SSH_ESCAPECHAR_NONE)
+                       buffer_put_int(&m, 0xffffffff);
+               else
+                       buffer_put_int(&m, options.escape_char);
+               buffer_append(&command, "\0", 1);
+               buffer_put_cstring(&m, buffer_ptr(&command));
+
+               if (options.num_send_env == 0 || environ == NULL) {
+                       buffer_put_int(&m, 0);
+               } else {
+                       /* Pass environment */
+                       num_env = 0;
+                       for (i = 0; environ[i] != NULL; i++) {
+                               if (env_permitted(environ[i]))
+                                       num_env++; /* Count */
+                       }
+                       buffer_put_int(&m, num_env);
+               for (i = 0; environ[i] != NULL && num_env >= 0; i++) {
+                               if (env_permitted(environ[i])) {
+                                       num_env--;
+                                       buffer_put_cstring(&m, environ[i]);
+                               }
+                       }
+               }
+               break;
+       default:
+               fatal("unrecognised muxclient_command %d", muxclient_command);
+       }
+
+       if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) {
+               error("%s: msg_send", __func__);
+               goto muxerr;
+       }
+
+       if (mm_send_fd(sock, STDIN_FILENO) == -1 ||
+           mm_send_fd(sock, STDOUT_FILENO) == -1 ||
+           mm_send_fd(sock, STDERR_FILENO) == -1) {
+               error("%s: send fds failed", __func__);
+               goto muxerr;
+       }
+
+       /*
+        * Mux errors are non-recoverable from this point as the master
+        * has ownership of the session now.
+        */
+
+       /* Wait for reply, so master has a chance to gather ttymodes */
+       buffer_clear(&m);
+       if (ssh_msg_recv(sock, &m) == -1)
+               fatal("%s: msg_recv", __func__);
+       if (buffer_get_char(&m) != SSHMUX_VER)
+               fatal("%s: wrong version", __func__);
+       buffer_free(&m);
+
+       signal(SIGHUP, control_client_sighandler);
+       signal(SIGINT, control_client_sighandler);
+       signal(SIGTERM, control_client_sighandler);
+       signal(SIGWINCH, control_client_sigrelay);
+
+       if (tty_flag)
+               enter_raw_mode();
+
+       /*
+        * Stick around until the controlee closes the client_fd.
+        * Before it does, it is expected to write this process' exit
+        * value (one int). This process must read the value and wait for
+        * the closure of the client_fd; if this one closes early, the 
+        * multiplex master will terminate early too (possibly losing data).
+        */
+       exitval[0] = 0;
+       for (i = 0; !muxclient_terminate && i < (int)sizeof(exitval);) {
+               r = read(sock, (char *)exitval + i, sizeof(exitval) - i);
+               if (r == 0) {
+                       debug2("Received EOF from master");
+                       break;
+               }
+               if (r == -1) {
+                       if (errno == EINTR)
+                               continue;
+                       fatal("%s: read %s", __func__, strerror(errno));
+               }
+               i += r;
+       }
+
+       close(sock);
+       leave_raw_mode();
+       if (i > (int)sizeof(int))
+               fatal("%s: master returned too much data (%d > %lu)",
+                   __func__, i, (u_long)sizeof(int));
+       if (muxclient_terminate) {
+               debug2("Exiting on signal %d", muxclient_terminate);
+               exitval[0] = 255;
+       } else if (i < (int)sizeof(int)) {
+               debug2("Control master terminated unexpectedly");
+               exitval[0] = 255;
+       } else
+               debug2("Received exit status from master %d", exitval[0]);
+
+       if (tty_flag && options.log_level != SYSLOG_LEVEL_QUIET)
+               fprintf(stderr, "Shared connection to %s closed.\r\n", host);
+
+       exit(exitval[0]);
+}
index ad461f4..e0ebf43 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: nchan.c,v 1.57 2006/08/03 03:34:42 deraadt Exp $ */
+/* $OpenBSD: nchan.c,v 1.60 2008/06/30 12:16:02 djm Exp $ */
 /*
  * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl.  All rights reserved.
  *
@@ -32,6 +32,7 @@
 #include <string.h>
 #include <stdarg.h>
 
+#include "openbsd-compat/sys-queue.h"
 #include "ssh1.h"
 #include "ssh2.h"
 #include "buffer.h"
@@ -77,6 +78,7 @@ static void   chan_send_ieof1(Channel *);
 static void    chan_send_oclose1(Channel *);
 static void    chan_send_close2(Channel *);
 static void    chan_send_eof2(Channel *);
+static void    chan_send_eow2(Channel *);
 
 /* helper */
 static void    chan_shutdown_write(Channel *);
@@ -305,6 +307,17 @@ chan_rcvd_close2(Channel *c)
                break;
        }
 }
+void
+chan_rcvd_eow(Channel *c)
+{
+       debug2("channel %d: rcvd eow", c->self);
+       switch (c->istate) {
+       case CHAN_INPUT_OPEN:
+               chan_shutdown_read(c);
+               chan_set_istate(c, CHAN_INPUT_CLOSED);
+               break;
+       }
+}
 static void
 chan_rcvd_eof2(Channel *c)
 {
@@ -321,6 +334,8 @@ chan_write_failed2(Channel *c)
        case CHAN_OUTPUT_OPEN:
        case CHAN_OUTPUT_WAIT_DRAIN:
                chan_shutdown_write(c);
+               if (strcmp(c->ctype, "session") == 0)
+                       chan_send_eow2(c);
                chan_set_ostate(c, CHAN_OUTPUT_CLOSED);
                break;
        default:
@@ -363,6 +378,21 @@ chan_send_close2(Channel *c)
                c->flags |= CHAN_CLOSE_SENT;
        }
 }
+static void
+chan_send_eow2(Channel *c)
+{
+       debug2("channel %d: send eow", c->self);
+       if (c->ostate == CHAN_OUTPUT_CLOSED) {
+               error("channel %d: must not sent eow on closed output",
+                   c->self);
+               return;
+       }
+       packet_start(SSH2_MSG_CHANNEL_REQUEST);
+       packet_put_int(c->remote_id);
+       packet_put_cstring("eow@openssh.com");
+       packet_put_char(0);
+       packet_send();
+}
 
 /* shared */
 
diff --git a/crypto/openssh-5/openbsd-compat/bsd-statvfs.h b/crypto/openssh-5/openbsd-compat/bsd-statvfs.h
new file mode 100644 (file)
index 0000000..da215ff
--- /dev/null
@@ -0,0 +1,68 @@
+/* $Id: bsd-statvfs.h,v 1.1 2008/06/08 17:32:29 dtucker Exp $ */
+
+/*
+ * Copyright (c) 2008 Darren Tucker <dtucker@zip.com.au>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_STATFS_H
+#include <sys/statfs.h>
+#endif
+
+#ifndef HAVE_STATVFS
+
+#ifndef HAVE_FSBLKCNT_T
+typedef unsigned long fsblkcnt_t;
+#endif
+#ifndef HAVE_FSFILCNT_T
+typedef unsigned long fsfilcnt_t;
+#endif
+
+#ifndef ST_RDONLY
+#define ST_RDONLY      1
+#endif
+#ifndef ST_NOSUID
+#define ST_NOSUID      2
+#endif
+
+       /* as defined in IEEE Std 1003.1, 2004 Edition */
+struct statvfs {
+       unsigned long f_bsize;  /* File system block size. */
+       unsigned long f_frsize; /* Fundamental file system block size. */
+       fsblkcnt_t f_blocks;    /* Total number of blocks on file system in */
+                               /* units of f_frsize. */
+       fsblkcnt_t    f_bfree;  /* Total number of free blocks. */
+       fsblkcnt_t    f_bavail; /* Number of free blocks available to  */
+                               /* non-privileged process.  */
+       fsfilcnt_t    f_files;  /* Total number of file serial numbers. */
+       fsfilcnt_t    f_ffree;  /* Total number of free file serial numbers. */
+       fsfilcnt_t    f_favail; /* Number of file serial numbers available to */
+                               /* non-privileged process. */
+       unsigned long f_fsid;   /* File system ID. */
+       unsigned long f_flag;   /* BBit mask of f_flag values. */
+       unsigned long f_namemax;/*  Maximum filename length. */
+};
+#endif
+
+#ifndef HAVE_STATVFS
+int statvfs(const char *, struct statvfs *);
+#endif
+
+#ifndef HAVE_FSTATVFS
+int fstatvfs(int, struct statvfs *);
+#endif
index ab9a2af..3e9090f 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: fake-rfc2553.h,v 1.14 2008/02/25 09:21:20 dtucker Exp $ */
+/* $Id: fake-rfc2553.h,v 1.16 2008/07/14 11:37:37 djm Exp $ */
 
 /*
  * Copyright (C) 2000-2003 Damien Miller.  All rights reserved.
@@ -77,6 +77,7 @@ struct sockaddr_in6 {
        u_int16_t       sin6_port;
        u_int32_t       sin6_flowinfo;
        struct in6_addr sin6_addr;
+       u_int32_t       sin6_scope_id;
 };
 #endif /* !HAVE_STRUCT_SOCKADDR_IN6 */
 
@@ -128,6 +129,9 @@ struct sockaddr_in6 {
 #ifndef EAI_SYSTEM
 # define EAI_SYSTEM    (INT_MAX - 4)
 #endif
+#ifndef EAI_FAMILY
+# define EAI_FAMILY    (INT_MAX - 5)
+#endif
 
 #ifndef HAVE_STRUCT_ADDRINFO
 struct addrinfo {
diff --git a/crypto/openssh-5/openbsd-compat/fmt_scaled.c b/crypto/openssh-5/openbsd-compat/fmt_scaled.c
new file mode 100644 (file)
index 0000000..edd682a
--- /dev/null
@@ -0,0 +1,274 @@
+/*     $OpenBSD: fmt_scaled.c,v 1.9 2007/03/20 03:42:52 tedu Exp $     */
+
+/*
+ * Copyright (c) 2001, 2002, 2003 Ian F. Darwin.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* OPENBSD ORIGINAL: lib/libutil/fmt_scaled.c */
+
+/*
+ * fmt_scaled: Format numbers scaled for human comprehension
+ * scan_scaled: Scan numbers in this format.
+ *
+ * "Human-readable" output uses 4 digits max, and puts a unit suffix at
+ * the end.  Makes output compact and easy-to-read esp. on huge disks.
+ * Formatting code was originally in OpenBSD "df", converted to library routine.
+ * Scanning code written for OpenBSD libutil.
+ */
+
+#include "includes.h"
+
+#ifndef HAVE_FMT_SCALED
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+
+typedef enum {
+       NONE = 0, KILO = 1, MEGA = 2, GIGA = 3, TERA = 4, PETA = 5, EXA = 6
+} unit_type;
+
+/* These three arrays MUST be in sync!  XXX make a struct */
+static unit_type units[] = { NONE, KILO, MEGA, GIGA, TERA, PETA, EXA };
+static char scale_chars[] = "BKMGTPE";
+static long long scale_factors[] = {
+       1LL,
+       1024LL,
+       1024LL*1024,
+       1024LL*1024*1024,
+       1024LL*1024*1024*1024,
+       1024LL*1024*1024*1024*1024,
+       1024LL*1024*1024*1024*1024*1024,
+};
+#define        SCALE_LENGTH (sizeof(units)/sizeof(units[0]))
+
+#define MAX_DIGITS (SCALE_LENGTH * 3)  /* XXX strlen(sprintf("%lld", -1)? */
+
+/** Convert the given input string "scaled" into numeric in "result".
+ * Return 0 on success, -1 and errno set on error.
+ */
+int
+scan_scaled(char *scaled, long long *result)
+{
+       char *p = scaled;
+       int sign = 0;
+       unsigned int i, ndigits = 0, fract_digits = 0;
+       long long scale_fact = 1, whole = 0, fpart = 0;
+
+       /* Skip leading whitespace */
+       while (isascii(*p) && isspace(*p))
+               ++p;
+
+       /* Then at most one leading + or - */
+       while (*p == '-' || *p == '+') {
+               if (*p == '-') {
+                       if (sign) {
+                               errno = EINVAL;
+                               return -1;
+                       }
+                       sign = -1;
+                       ++p;
+               } else if (*p == '+') {
+                       if (sign) {
+                               errno = EINVAL;
+                               return -1;
+                       }
+                       sign = +1;
+                       ++p;
+               }
+       }
+
+       /* Main loop: Scan digits, find decimal point, if present.
+        * We don't allow exponentials, so no scientific notation
+        * (but note that E for Exa might look like e to some!).
+        * Advance 'p' to end, to get scale factor.
+        */
+       for (; isascii(*p) && (isdigit(*p) || *p=='.'); ++p) {
+               if (*p == '.') {
+                       if (fract_digits > 0) { /* oops, more than one '.' */
+                               errno = EINVAL;
+                               return -1;
+                       }
+                       fract_digits = 1;
+                       continue;
+               }
+
+               i = (*p) - '0';                 /* whew! finally a digit we can use */
+               if (fract_digits > 0) {
+                       if (fract_digits >= MAX_DIGITS-1)
+                               /* ignore extra fractional digits */
+                               continue;
+                       fract_digits++;         /* for later scaling */
+                       fpart *= 10;
+                       fpart += i;
+               } else {                                /* normal digit */
+                       if (++ndigits >= MAX_DIGITS) {
+                               errno = ERANGE;
+                               return -1;
+                       }
+                       whole *= 10;
+                       whole += i;
+               }
+       }
+
+       if (sign) {
+               whole *= sign;
+               fpart *= sign;
+       }
+
+       /* If no scale factor given, we're done. fraction is discarded. */
+       if (!*p) {
+               *result = whole;
+               return 0;
+       }
+
+       /* Validate scale factor, and scale whole and fraction by it. */
+       for (i = 0; i < SCALE_LENGTH; i++) {
+
+               /** Are we there yet? */
+               if (*p == scale_chars[i] ||
+                       *p == tolower(scale_chars[i])) {
+
+                       /* If it ends with alphanumerics after the scale char, bad. */
+                       if (isalnum(*(p+1))) {
+                               errno = EINVAL;
+                               return -1;
+                       }
+                       scale_fact = scale_factors[i];
+
+                       /* scale whole part */
+                       whole *= scale_fact;
+
+                       /* truncate fpart so it does't overflow.
+                        * then scale fractional part.
+                        */
+                       while (fpart >= LLONG_MAX / scale_fact) {
+                               fpart /= 10;
+                               fract_digits--;
+                       }
+                       fpart *= scale_fact;
+                       if (fract_digits > 0) {
+                               for (i = 0; i < fract_digits -1; i++)
+                                       fpart /= 10;
+                       }
+                       whole += fpart;
+                       *result = whole;
+                       return 0;
+               }
+       }
+       errno = ERANGE;
+       return -1;
+}
+
+/* Format the given "number" into human-readable form in "result".
+ * Result must point to an allocated buffer of length FMT_SCALED_STRSIZE.
+ * Return 0 on success, -1 and errno set if error.
+ */
+int
+fmt_scaled(long long number, char *result)
+{
+       long long abval, fract = 0;
+       unsigned int i;
+       unit_type unit = NONE;
+
+       abval = (number < 0LL) ? -number : number;      /* no long long_abs yet */
+
+       /* Not every negative long long has a positive representation.
+        * Also check for numbers that are just too darned big to format
+        */
+       if (abval < 0 || abval / 1024 >= scale_factors[SCALE_LENGTH-1]) {
+               errno = ERANGE;
+               return -1;
+       }
+
+       /* scale whole part; get unscaled fraction */
+       for (i = 0; i < SCALE_LENGTH; i++) {
+               if (abval/1024 < scale_factors[i]) {
+                       unit = units[i];
+                       fract = (i == 0) ? 0 : abval % scale_factors[i];
+                       number /= scale_factors[i];
+                       if (i > 0)
+                               fract /= scale_factors[i - 1];
+                       break;
+               }
+       }
+
+       fract = (10 * fract + 512) / 1024;
+       /* if the result would be >= 10, round main number */
+       if (fract == 10) {
+               if (number >= 0)
+                       number++;
+               else
+                       number--;
+               fract = 0;
+       }
+
+       if (number == 0)
+               strlcpy(result, "0B", FMT_SCALED_STRSIZE);
+       else if (unit == NONE || number >= 100 || number <= -100) {
+               if (fract >= 5) {
+                       if (number >= 0)
+                               number++;
+                       else
+                               number--;
+               }
+               (void)snprintf(result, FMT_SCALED_STRSIZE, "%lld%c",
+                       number, scale_chars[unit]);
+       } else
+               (void)snprintf(result, FMT_SCALED_STRSIZE, "%lld.%1lld%c",
+                       number, fract, scale_chars[unit]);
+
+       return 0;
+}
+
+#ifdef MAIN
+/*
+ * This is the original version of the program in the man page.
+ * Copy-and-paste whatever you need from it.
+ */
+int
+main(int argc, char **argv)
+{
+       char *cinput = "1.5K", buf[FMT_SCALED_STRSIZE];
+       long long ninput = 10483892, result;
+
+       if (scan_scaled(cinput, &result) == 0)
+               printf("\"%s\" -> %lld\n", cinput, result);
+       else
+               perror(cinput);
+
+       if (fmt_scaled(ninput, buf) == 0)
+               printf("%lld -> \"%s\"\n", ninput, buf);
+       else
+               fprintf(stderr, "%lld invalid (%s)\n", ninput, strerror(errno));
+
+       return 0;
+}
+#endif
+
+#endif /* HAVE_FMT_SCALED */
index 6406af1..50c6d99 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: openbsd-compat.h,v 1.43 2007/06/25 12:15:13 dtucker Exp $ */
+/* $Id: openbsd-compat.h,v 1.46 2008/06/08 17:32:29 dtucker Exp $ */
 
 /*
  * Copyright (c) 1999-2003 Damien Miller.  All rights reserved.
@@ -101,6 +101,11 @@ int daemon(int nochdir, int noclose);
 char *dirname(const char *path);
 #endif
 
+#ifndef HAVE_FMT_SCALED
+#define        FMT_SCALED_STRSIZE      7
+int    fmt_scaled(long long number, char *result);
+#endif
+
 #if defined(BROKEN_INET_NTOA) || !defined(HAVE_INET_NTOA)
 char *inet_ntoa(struct in_addr in);
 #endif
@@ -139,6 +144,7 @@ int writev(int, struct iovec *, int);
 
 /* Home grown routines */
 #include "bsd-misc.h"
+#include "bsd-statvfs.h"
 #include "bsd-waitpid.h"
 #include "bsd-poll.h"
 
@@ -151,6 +157,14 @@ unsigned int arc4random(void);
 void arc4random_stir(void);
 #endif /* !HAVE_ARC4RANDOM */
 
+#ifndef HAVE_ARC4RANDOM_BUF
+void arc4random_buf(void *, size_t);
+#endif
+
+#ifndef HAVE_ARC4RANDOM_UNIFORM
+u_int32_t arc4random_uniform(u_int32_t);
+#endif
+
 #ifndef HAVE_ASPRINTF
 int asprintf(char **, const char *, ...);
 #endif 
index 276474d..ddc92d0 100644 (file)
@@ -29,6 +29,7 @@
 #include <string.h>
 #include <unistd.h>
 
+#include "openbsd-compat/sys-queue.h"
 #include "log.h"
 #include "misc.h"
 #include "buffer.h"
index 6afe24b..8abd43e 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: packet.c,v 1.151 2008/02/22 20:44:02 dtucker Exp $ */
+/* $OpenBSD: packet.c,v 1.157 2008/07/10 18:08:11 markus Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -138,12 +138,16 @@ static int after_authentication = 0;
 
 int keep_alive_timeouts = 0;
 
+/* Set to the maximum time that we will wait to send or receive a packet */
+static int packet_timeout_ms = -1;
+
 /* Session key information for Encryption and MAC */
 Newkeys *newkeys[MODE_MAX];
 static struct packet_state {
        u_int32_t seqnr;
        u_int32_t packets;
        u_int64_t blocks;
+       u_int64_t bytes;
 } p_read, p_send;
 
 static u_int64_t max_blocks_in, max_blocks_out;
@@ -188,7 +192,21 @@ packet_set_connection(int fd_in, int fd_out)
                buffer_init(&outgoing_packet);
                buffer_init(&incoming_packet);
                TAILQ_INIT(&outgoing);
+               p_send.packets = p_read.packets = 0;
+       }
+}
+
+void
+packet_set_timeout(int timeout, int count)
+{
+       if (timeout == 0 || count == 0) {
+               packet_timeout_ms = -1;
+               return;
        }
+       if ((INT_MAX / 1000) / count < timeout)
+               packet_timeout_ms = INT_MAX;
+       else
+               packet_timeout_ms = timeout * count * 1000;
 }
 
 /* Returns 1 if remote host is connected via socket, 0 if not. */
@@ -295,18 +313,25 @@ packet_get_ssh1_cipher(void)
 }
 
 void
-packet_get_state(int mode, u_int32_t *seqnr, u_int64_t *blocks, u_int32_t *packets)
+packet_get_state(int mode, u_int32_t *seqnr, u_int64_t *blocks, u_int32_t *packets,
+    u_int64_t *bytes)
 {
        struct packet_state *state;
 
        state = (mode == MODE_IN) ? &p_read : &p_send;
-       *seqnr = state->seqnr;
-       *blocks = state->blocks;
-       *packets = state->packets;
+       if (seqnr)
+               *seqnr = state->seqnr;
+       if (blocks)
+               *blocks = state->blocks;
+       if (packets)
+               *packets = state->packets;
+       if (bytes)
+               *bytes = state->bytes;
 }
 
 void
-packet_set_state(int mode, u_int32_t seqnr, u_int64_t blocks, u_int32_t packets)
+packet_set_state(int mode, u_int32_t seqnr, u_int64_t blocks, u_int32_t packets,
+    u_int64_t bytes)
 {
        struct packet_state *state;
 
@@ -314,6 +339,7 @@ packet_set_state(int mode, u_int32_t seqnr, u_int64_t blocks, u_int32_t packets)
        state->seqnr = seqnr;
        state->blocks = blocks;
        state->packets = packets;
+       state->bytes = bytes;
 }
 
 /* returns 1 if connection is via ipv4 */
@@ -592,7 +618,8 @@ packet_send1(void)
        fprintf(stderr, "encrypted: ");
        buffer_dump(&output);
 #endif
-
+       p_send.packets++;
+       p_send.bytes += len + buffer_len(&outgoing_packet);
        buffer_clear(&outgoing_packet);
 
        /*
@@ -818,6 +845,7 @@ packet_send2_wrapped(void)
                if (!(datafellows & SSH_BUG_NOREKEY))
                        fatal("XXX too many packets with same key");
        p_send.blocks += (packet_length + 4) / block_size;
+       p_send.bytes += packet_length + 4;
        buffer_clear(&outgoing_packet);
 
        if (type == SSH2_MSG_NEWKEYS)
@@ -891,9 +919,11 @@ packet_send(void)
 int
 packet_read_seqnr(u_int32_t *seqnr_p)
 {
-       int type, len;
+       int type, len, ret, ms_remain;
        fd_set *setp;
        char buf[8192];
+       struct timeval timeout, start, *timeoutp = NULL;
+
        DBG(debug("packet_read()"));
 
        setp = (fd_set *)xcalloc(howmany(connection_in+1, NFDBITS),
@@ -925,11 +955,35 @@ packet_read_seqnr(u_int32_t *seqnr_p)
                    sizeof(fd_mask));
                FD_SET(connection_in, setp);
 
+               if (packet_timeout_ms > 0) {
+                       ms_remain = packet_timeout_ms;
+                       timeoutp = &timeout;
+               }
                /* Wait for some data to arrive. */
-               while (select(connection_in + 1, setp, NULL, NULL, NULL) == -1 &&
-                   (errno == EAGAIN || errno == EINTR))
-                       ;
-
+               for (;;) {
+                       if (packet_timeout_ms != -1) {
+                               ms_to_timeval(&timeout, ms_remain);
+                               gettimeofday(&start, NULL);
+                       }
+                       if ((ret = select(connection_in + 1, setp, NULL,
+                           NULL, timeoutp)) >= 0)
+                               break;
+                       if (errno != EAGAIN && errno != EINTR &&
+                           errno != EWOULDBLOCK)
+                               break;
+                       if (packet_timeout_ms == -1)
+                               continue;
+                       ms_subtract_diff(&start, &ms_remain);
+                       if (ms_remain <= 0) {
+                               ret = 0;
+                               break;
+                       }
+               }
+               if (ret == 0) {
+                       logit("Connection to %.200s timed out while "
+                           "waiting to read", get_remote_ipaddr());
+                       cleanup_exit(255);
+               }
                /* Read data from the socket. */
                len = read(connection_in, buf, sizeof(buf));
                if (len == 0) {
@@ -1054,6 +1108,8 @@ packet_read_poll1(void)
                buffer_append(&incoming_packet, buffer_ptr(&compression_buffer),
                    buffer_len(&compression_buffer));
        }
+       p_read.packets++;
+       p_read.bytes += padded_len + 4;
        type = buffer_get_char(&incoming_packet);
        if (type < SSH_MSG_MIN || type > SSH_MSG_MAX)
                packet_disconnect("Invalid ssh1 packet type: %d", type);
@@ -1142,6 +1198,7 @@ packet_read_poll2(u_int32_t *seqnr_p)
                if (!(datafellows & SSH_BUG_NOREKEY))
                        fatal("XXX too many packets with same key");
        p_read.blocks += (packet_length + 4) / block_size;
+       p_read.bytes += packet_length + 4;
 
        /* get padlen */
        cp = buffer_ptr(&incoming_packet);
@@ -1194,9 +1251,10 @@ packet_read_poll_seqnr(u_int32_t *seqnr_p)
        for (;;) {
                if (compat20) {
                        type = packet_read_poll2(seqnr_p);
-                       keep_alive_timeouts = 0;
-                       if (type)
+                       if (type) {
+                               keep_alive_timeouts = 0;
                                DBG(debug("received packet type %d", type));
+                       }
                        switch (type) {
                        case SSH2_MSG_IGNORE:
                                debug3("Received SSH2_MSG_IGNORE");
@@ -1332,6 +1390,12 @@ packet_get_string(u_int *length_ptr)
        return buffer_get_string(&incoming_packet, length_ptr);
 }
 
+void *
+packet_get_string_ptr(u_int *length_ptr)
+{
+       return buffer_get_string_ptr(&incoming_packet, length_ptr);
+}
+
 /*
  * Sends a diagnostic message from the server to the client.  This message
  * can be sent at any time (but not while constructing another message). The
@@ -1426,16 +1490,19 @@ packet_write_poll(void)
 
        if (len > 0) {
                len = write(connection_out, buffer_ptr(&output), len);
-               if (len <= 0) {
-                       if (errno == EAGAIN)
+               if (len == -1) {
+                       if (errno == EINTR || errno == EAGAIN ||
+                           errno == EWOULDBLOCK)
                                return;
-                       else
-                               fatal("Write failed: %.100s", strerror(errno));
+                       fatal("Write failed: %.100s", strerror(errno));
                }
+               if (len == 0)
+                       fatal("Write connection closed");
                buffer_consume(&output, len);
        }
 }
 
+
 /*
  * Calls packet_write_poll repeatedly until all pending output data has been
  * written.
@@ -1445,6 +1512,8 @@ void
 packet_write_wait(void)
 {
        fd_set *setp;
+       int ret, ms_remain;
+       struct timeval start, timeout, *timeoutp = NULL;
 
        setp = (fd_set *)xcalloc(howmany(connection_out + 1, NFDBITS),
            sizeof(fd_mask));
@@ -1453,9 +1522,35 @@ packet_write_wait(void)
                memset(setp, 0, howmany(connection_out + 1, NFDBITS) *
                    sizeof(fd_mask));
                FD_SET(connection_out, setp);
-               while (select(connection_out + 1, NULL, setp, NULL, NULL) == -1 &&
-                   (errno == EAGAIN || errno == EINTR))
-                       ;
+
+               if (packet_timeout_ms > 0) {
+                       ms_remain = packet_timeout_ms;
+                       timeoutp = &timeout;
+               }
+               for (;;) {
+                       if (packet_timeout_ms != -1) {
+                               ms_to_timeval(&timeout, ms_remain);
+                               gettimeofday(&start, NULL);
+                       }
+                       if ((ret = select(connection_out + 1, NULL, setp,
+                           NULL, timeoutp)) >= 0)
+                               break;
+                       if (errno != EAGAIN && errno != EINTR &&
+                           errno != EWOULDBLOCK)
+                               break;
+                       if (packet_timeout_ms == -1)
+                               continue;
+                       ms_subtract_diff(&start, &ms_remain);
+                       if (ms_remain <= 0) {
+                               ret = 0;
+                               break;
+                       }
+               }
+               if (ret == 0) {
+                       logit("Connection to %.200s timed out while "
+                           "waiting to write", get_remote_ipaddr());
+                       cleanup_exit(255);
+               }
                packet_write_poll();
        }
        xfree(setp);
index c1b9b3b..03bb87c 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: packet.h,v 1.46 2008/02/22 20:44:02 dtucker Exp $ */
+/* $OpenBSD: packet.h,v 1.49 2008/07/10 18:08:11 markus Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -21,6 +21,7 @@
 #include <openssl/bn.h>
 
 void     packet_set_connection(int, int);
+void     packet_set_timeout(int, int);
 void     packet_set_nonblocking(void);
 int      packet_get_connection_in(void);
 int      packet_get_connection_out(void);
@@ -58,6 +59,7 @@ void     packet_get_bignum(BIGNUM * value);
 void     packet_get_bignum2(BIGNUM * value);
 void   *packet_get_raw(u_int *length_ptr);
 void   *packet_get_string(u_int *length_ptr);
+void   *packet_get_string_ptr(u_int *length_ptr);
 void     packet_disconnect(const char *fmt,...) __attribute__((format(printf, 1, 2)));
 void     packet_send_debug(const char *fmt,...) __attribute__((format(printf, 1, 2)));
 
@@ -66,8 +68,8 @@ int    packet_get_keyiv_len(int);
 void    packet_get_keyiv(int, u_char *, u_int);
 int     packet_get_keycontext(int, u_char *);
 void    packet_set_keycontext(int, u_char *);
-void    packet_get_state(int, u_int32_t *, u_int64_t *, u_int32_t *);
-void    packet_set_state(int, u_int32_t, u_int64_t, u_int32_t);
+void    packet_get_state(int, u_int32_t *, u_int64_t *, u_int32_t *, u_int64_t *);
+void    packet_set_state(int, u_int32_t, u_int64_t, u_int32_t, u_int64_t);
 int     packet_get_ssh1_cipher(void);
 void    packet_set_iv(int, u_char *);
 
index 3ddb4d3..73f6eb3 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.c,v 1.165 2008/01/19 23:09:49 djm Exp $ */
+/* $OpenBSD: readconf.c,v 1.167 2008/06/26 11:46:31 grunk Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -130,6 +130,7 @@ typedef enum {
        oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
        oSendEnv, oControlPath, oControlMaster, oHashKnownHosts,
        oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
+       oVisualHostKey,
        oDeprecated, oUnsupported
 } OpCodes;
 
@@ -226,6 +227,7 @@ static struct {
        { "tunneldevice", oTunnelDevice },
        { "localcommand", oLocalCommand },
        { "permitlocalcommand", oPermitLocalCommand },
+       { "visualhostkey", oVisualHostKey },
        { NULL, oBadOption }
 };
 
@@ -915,6 +917,10 @@ parse_int:
                intptr = &options->permit_local_command;
                goto parse_flag;
 
+       case oVisualHostKey:
+               intptr = &options->visual_host_key;
+               goto parse_flag;
+
        case oDeprecated:
                debug("%s line %d: Deprecated option \"%s\"",
                    filename, linenum, keyword);
@@ -1065,6 +1071,7 @@ initialize_options(Options * options)
        options->tun_remote = -1;
        options->local_command = NULL;
        options->permit_local_command = -1;
+       options->visual_host_key = -1;
 }
 
 /*
@@ -1199,6 +1206,8 @@ fill_default_options(Options * options)
                options->tun_remote = SSH_TUNID_ANY;
        if (options->permit_local_command == -1)
                options->permit_local_command = 0;
+       if (options->visual_host_key == -1)
+               options->visual_host_key = 0;
        /* options->local_command should not be set by default */
        /* options->proxy_command should not be set by default */
        /* options->user will be set in the main program if appropriate */
index 6257f4b..47c7aef 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.h,v 1.72 2008/01/19 23:09:49 djm Exp $ */
+/* $OpenBSD: readconf.h,v 1.74 2008/06/26 11:46:31 grunk Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -120,6 +120,7 @@ typedef struct {
 
        char    *local_command;
        int     permit_local_command;
+       int     visual_host_key;
 
 }       Options;
 
index a8bd97f..5033d84 100644 (file)
@@ -9,9 +9,9 @@
 .\"
 .\" Created: Sun May  7 00:14:37 1995 ylo
 .\"
-.\" $OpenBSD: scp.1,v 1.44 2008/01/31 20:06:50 jmc Exp $
+.\" $OpenBSD: scp.1,v 1.46 2008/07/12 05:33:41 djm Exp $
 .\"
-.Dd $Mdocdate: February 10 2008 $
+.Dd $Mdocdate: July 12 2008 $
 .Dt SCP 1
 .Os
 .Sh NAME
@@ -104,7 +104,7 @@ per-user configuration file for
 This option is directly passed to
 .Xr ssh 1 .
 .It Fl i Ar identity_file
-Selects the file from which the identity (private key) for RSA
+Selects the file from which the identity (private key) for public key
 authentication is read.
 This option is directly passed to
 .Xr ssh 1 .
@@ -189,6 +189,9 @@ messages from
 .Xr ssh 1 .
 .It Fl r
 Recursively copy entire directories.
+Note that
+.Nm
+follows symbolic links encountered in the tree traversal.
 .It Fl S Ar program
 Name of
 .Ar program
index c047864..9f8b7a1 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: scp.c,v 1.162 2008/01/01 09:06:39 dtucker Exp $ */
+/* $OpenBSD: scp.c,v 1.163 2008/06/13 18:55:22 dtucker Exp $ */
 /*
  * scp - secure remote copy.  This is basically patched BSD rcp which
  * uses ssh to do the data transfer (instead of using rcmd).
@@ -474,7 +474,7 @@ scpio(ssize_t (*f)(int, void *, size_t), int fd, void *_p, size_t l, off_t *c)
                if (r < 0) {
                        if (errno == EINTR)
                                continue;
-                       if (errno == EAGAIN) {
+                       if (errno == EAGAIN || errno == EWOULDBLOCK) {
                                (void)poll(&pfd, 1, -1); /* Ignore errors */
                                continue;
                        }
@@ -629,7 +629,8 @@ source(int argc, char **argv)
        struct stat stb;
        static BUF buffer;
        BUF *bp;
-       off_t i, amt, statbytes;
+       off_t i, statbytes;
+       size_t amt;
        int fd = -1, haderr, indx;
        char *last, *name, buf[2048], encname[MAXPATHLEN];
        int len;
@@ -650,6 +651,10 @@ source(int argc, char **argv)
 syserr:                        run_err("%s: %s", name, strerror(errno));
                        goto next;
                }
+               if (stb.st_size < 0) {
+                       run_err("%s: %s", name, "Negative file size");
+                       goto next;
+               }
                unset_nonblock(fd);
                switch (stb.st_mode & S_IFMT) {
                case S_IFREG:
@@ -709,7 +714,7 @@ next:                       if (fd != -1) {
                set_nonblock(remout);
                for (haderr = i = 0; i < stb.st_size; i += bp->cnt) {
                        amt = bp->cnt;
-                       if (i + amt > stb.st_size)
+                       if (i + (off_t)amt > stb.st_size)
                                amt = stb.st_size - i;
                        if (!haderr) {
                                if (atomicio(read, fd, bp->buf, amt) != amt)
index 9add96c..66e2297 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: servconf.c,v 1.177 2008/02/10 10:54:28 djm Exp $ */
+/* $OpenBSD: servconf.c,v 1.186 2008/07/04 03:44:59 djm Exp $ */
 /*
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
  *                    All rights reserved
@@ -23,7 +23,9 @@
 #include <signal.h>
 #include <unistd.h>
 #include <stdarg.h>
+#include <errno.h>
 
+#include "openbsd-compat/sys-queue.h"
 #include "xmalloc.h"
 #include "ssh.h"
 #include "log.h"
@@ -99,6 +101,7 @@ initialize_server_options(ServerOptions *options)
        options->use_login = -1;
        options->compression = -1;
        options->allow_tcp_forwarding = -1;
+       options->allow_agent_forwarding = -1;
        options->num_allow_users = 0;
        options->num_deny_users = 0;
        options->num_allow_groups = 0;
@@ -112,6 +115,7 @@ initialize_server_options(ServerOptions *options)
        options->max_startups_rate = -1;
        options->max_startups = -1;
        options->max_authtries = -1;
+       options->max_sessions = -1;
        options->banner = NULL;
        options->use_dns = -1;
        options->client_alive_interval = -1;
@@ -154,7 +158,7 @@ fill_default_server_options(ServerOptions *options)
        if (options->pid_file == NULL)
                options->pid_file = _PATH_SSH_DAEMON_PID_FILE;
        if (options->server_key_bits == -1)
-               options->server_key_bits = 768;
+               options->server_key_bits = 1024;
        if (options->login_grace_time == -1)
                options->login_grace_time = 120;
        if (options->key_regeneration_time == -1)
@@ -223,6 +227,8 @@ fill_default_server_options(ServerOptions *options)
                options->compression = COMP_DELAYED;
        if (options->allow_tcp_forwarding == -1)
                options->allow_tcp_forwarding = 1;
+       if (options->allow_agent_forwarding == -1)
+               options->allow_agent_forwarding = 1;
        if (options->gateway_ports == -1)
                options->gateway_ports = 0;
        if (options->max_startups == -1)
@@ -233,6 +239,8 @@ fill_default_server_options(ServerOptions *options)
                options->max_startups_begin = options->max_startups;
        if (options->max_authtries == -1)
                options->max_authtries = DEFAULT_AUTH_FAIL_MAX;
+       if (options->max_sessions == -1)
+               options->max_sessions = DEFAULT_SESSIONS_MAX;
        if (options->use_dns == -1)
                options->use_dns = 1;
        if (options->client_alive_interval == -1)
@@ -287,13 +295,13 @@ typedef enum {
        sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups,
        sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile,
        sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem,
-       sMaxStartups, sMaxAuthTries,
+       sMaxStartups, sMaxAuthTries, sMaxSessions,
        sBanner, sUseDNS, sHostbasedAuthentication,
        sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
        sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2,
        sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel,
        sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
-       sUsePrivilegeSeparation,
+       sUsePrivilegeSeparation, sAllowAgentForwarding,
        sDeprecated, sUnsupported
 } ServerOpCodes;
 
@@ -379,6 +387,7 @@ static struct {
        { "tcpkeepalive", sTCPKeepAlive, SSHCFG_GLOBAL },
        { "keepalive", sTCPKeepAlive, SSHCFG_GLOBAL },  /* obsolete alias */
        { "allowtcpforwarding", sAllowTcpForwarding, SSHCFG_ALL },
+       { "allowagentforwarding", sAllowAgentForwarding, SSHCFG_ALL },
        { "allowusers", sAllowUsers, SSHCFG_GLOBAL },
        { "denyusers", sDenyUsers, SSHCFG_GLOBAL },
        { "allowgroups", sAllowGroups, SSHCFG_GLOBAL },
@@ -389,7 +398,8 @@ static struct {
        { "gatewayports", sGatewayPorts, SSHCFG_ALL },
        { "subsystem", sSubsystem, SSHCFG_GLOBAL },
        { "maxstartups", sMaxStartups, SSHCFG_GLOBAL },
-       { "maxauthtries", sMaxAuthTries, SSHCFG_GLOBAL },
+       { "maxauthtries", sMaxAuthTries, SSHCFG_ALL },
+       { "maxsessions", sMaxSessions, SSHCFG_ALL },
        { "banner", sBanner, SSHCFG_ALL },
        { "usedns", sUseDNS, SSHCFG_GLOBAL },
        { "verifyreversemapping", sDeprecated, SSHCFG_GLOBAL },
@@ -408,6 +418,17 @@ static struct {
        { NULL, sBadOption, 0 }
 };
 
+static struct {
+       int val;
+       char *text;
+} tunmode_desc[] = {
+       { SSH_TUNMODE_NO, "no" },
+       { SSH_TUNMODE_POINTOPOINT, "point-to-point" },
+       { SSH_TUNMODE_ETHERNET, "ethernet" },
+       { SSH_TUNMODE_YES, "yes" },
+       { -1, NULL }
+};
+
 /*
  * Returns the number of the token pointed to by cp or sBadOption.
  */
@@ -504,24 +525,8 @@ static int
 match_cfg_line_group(const char *grps, int line, const char *user)
 {
        int result = 0;
-       u_int ngrps = 0;
-       char *arg, *p, *cp, *grplist[MAX_MATCH_GROUPS];
        struct passwd *pw;
 
-       /*
-        * Even if we do not have a user yet, we still need to check for
-        * valid syntax.
-        */
-       arg = cp = xstrdup(grps);
-       while ((p = strsep(&cp, ",")) != NULL && *p != '\0') {
-               if (ngrps >= MAX_MATCH_GROUPS) {
-                       error("line %d: too many groups in Match Group", line);
-                       result = -1;
-                       goto out;
- &n