from ftp.openbsd.org
This commit is contained in:
parent
4d63191c6d
commit
9d3c9d9c55
|
@ -0,0 +1,14 @@
|
|||
# $OpenBSD: Makefile,v 1.12 2003/12/01 15:47:20 grange Exp $
|
||||
|
||||
.include <bsd.own.mk>
|
||||
|
||||
SUBDIR= lib ssh sshd ssh-add ssh-keygen ssh-agent scp sftp-server \
|
||||
ssh-keysign ssh-keyscan sftp scard
|
||||
|
||||
distribution:
|
||||
${INSTALL} -C -o root -g wheel -m 0644 ${.CURDIR}/ssh_config \
|
||||
${DESTDIR}/etc/ssh/ssh_config
|
||||
${INSTALL} -C -o root -g wheel -m 0644 ${.CURDIR}/sshd_config \
|
||||
${DESTDIR}/etc/ssh/sshd_config
|
||||
|
||||
.include <bsd.subdir.mk>
|
|
@ -0,0 +1,29 @@
|
|||
# $OpenBSD: Makefile.inc,v 1.32 2008/06/28 14:04:30 djm Exp $
|
||||
|
||||
CFLAGS+= -I${.CURDIR}/..
|
||||
|
||||
CDIAGFLAGS= -Wall
|
||||
#CDIAGFLAGS+= -Werror
|
||||
CDIAGFLAGS+= -Wpointer-arith
|
||||
CDIAGFLAGS+= -Wno-uninitialized
|
||||
CDIAGFLAGS+= -Wstrict-prototypes
|
||||
CDIAGFLAGS+= -Wmissing-prototypes
|
||||
CDIAGFLAGS+= -Wunused
|
||||
CDIAGFLAGS+= -Wsign-compare
|
||||
CDIAGFLAGS+= -Wbounded
|
||||
CDIAGFLAGS+= -Wshadow
|
||||
|
||||
#DEBUG=-g
|
||||
|
||||
#CFLAGS+= -DSMARTCARD
|
||||
#LDADD+= -lsectok
|
||||
|
||||
.include <bsd.obj.mk>
|
||||
|
||||
.if exists(${.CURDIR}/../lib/${__objdir})
|
||||
LDADD+= -L${.CURDIR}/../lib/${__objdir} -lssh
|
||||
DPADD+= ${.CURDIR}/../lib/${__objdir}/libssh.a
|
||||
.else
|
||||
LDADD+= -L${.CURDIR}/../lib -lssh
|
||||
DPADD+= ${.CURDIR}/../lib/libssh.a
|
||||
.endif
|
|
@ -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 $
|
|
@ -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 $
|
|
@ -0,0 +1,419 @@
|
|||
/* $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 <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;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
# $OpenBSD: Makefile,v 1.56 2008/06/10 03:57:27 djm Exp $
|
||||
|
||||
.PATH: ${.CURDIR}/..
|
||||
|
||||
LIB= ssh
|
||||
WANTLINT=
|
||||
SRCS= authfd.c authfile.c bufaux.c bufbn.c buffer.c canohost.c channels.c \
|
||||
cipher.c cipher-3des1.c cipher-bf1.c cipher-ctr.c \
|
||||
cleanup.c compat.c compress.c crc32.c deattack.c fatal.c \
|
||||
hostfile.c log.c match.c nchan.c packet.c readpass.c \
|
||||
rsa.c ttymodes.c xmalloc.c atomicio.c \
|
||||
key.c dispatch.c kex.c mac.c uidswap.c uuencode.c misc.c \
|
||||
ssh-dss.c ssh-rsa.c dh.c kexdh.c kexgex.c \
|
||||
kexdhc.c kexgexc.c scard.c msg.c progressmeter.c dns.c \
|
||||
monitor_fdpass.c md-sha256.c umac.c addrmatch.c
|
||||
|
||||
DEBUGLIBS= no
|
||||
NOPROFILE= yes
|
||||
NOPIC= yes
|
||||
|
||||
install:
|
||||
@echo -n
|
||||
|
||||
.include <bsd.own.mk>
|
||||
|
||||
.if (${KERBEROS5:L} == "yes")
|
||||
CFLAGS+= -DKRB5 -I${DESTDIR}/usr/include/kerberosV
|
||||
|
||||
SRCS+= gss-genr.c
|
||||
CFLAGS+= -DGSSAPI
|
||||
.endif # KERBEROS5
|
||||
|
||||
.include <bsd.lib.mk>
|
|
@ -0,0 +1,716 @@
|
|||
/* $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 */
|
||||
|
||||
/*
|
||||
* 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/queue.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>
|
||||
#include <util.h>
|
||||
#include <paths.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;
|
||||
|
||||
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.sun_len = offsetof(struct sockaddr_un, sun_path) +
|
||||
strlen(options.control_path) + 1;
|
||||
|
||||
if (strlcpy(addr.sun_path, options.control_path,
|
||||
sizeof(addr.sun_path)) >= sizeof(addr.sun_path))
|
||||
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.sun_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;
|
||||
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.sun_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.sun_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]);
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
.\" $NetBSD: nchan.ms,v 1.1.1.4 2005/02/13 00:53:05 christos Exp $
|
||||
.\" $OpenBSD: nchan.ms,v 1.8 2003/11/21 11:57:03 djm Exp $
|
||||
.\"
|
||||
.\"
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# $OpenBSD: Makefile,v 1.16 2003/01/12 16:59:14 markus Exp $
|
||||
|
||||
.PATH: ${.CURDIR}/..
|
||||
|
||||
PROG= scp
|
||||
BINOWN= root
|
||||
|
||||
BINMODE?=555
|
||||
|
||||
BINDIR= /usr/bin
|
||||
MAN= scp.1
|
||||
|
||||
SRCS= scp.c
|
||||
|
||||
.include <bsd.prog.mk>
|
|
@ -1,4 +1,3 @@
|
|||
/* $NetBSD: sftp-server-main.c,v 1.1.1.1 2008/04/06 21:18:28 christos Exp $ */
|
||||
/* $OpenBSD: sftp-server-main.c,v 1.3 2008/03/26 23:44:41 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008 Markus Friedl. All rights reserved.
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# $OpenBSD: Makefile,v 1.7 2008/02/04 21:53:00 markus Exp $
|
||||
|
||||
.PATH: ${.CURDIR}/..
|
||||
|
||||
PROG= sftp-server
|
||||
BINOWN= root
|
||||
|
||||
BINMODE?=555
|
||||
|
||||
BINDIR= /usr/libexec
|
||||
MAN= sftp-server.8
|
||||
|
||||
SRCS= sftp-server.c sftp-common.c sftp-server-main.c
|
||||
|
||||
.include <bsd.prog.mk>
|
|
@ -0,0 +1,17 @@
|
|||
# $OpenBSD: Makefile,v 1.11 2008/04/18 12:32:11 djm Exp $
|
||||
|
||||
.PATH: ${.CURDIR}/..
|
||||
|
||||
PROG= sftp
|
||||
BINOWN= root
|
||||
|
||||
BINMODE?=555
|
||||
|
||||
BINDIR= /usr/bin
|
||||
MAN= sftp.1
|
||||
|
||||
SRCS= sftp.c sftp-client.c sftp-common.c sftp-glob.c
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
||||
LDADD+= -ledit -ltermcap -lutil
|
|
@ -0,0 +1,18 @@
|
|||
# $OpenBSD: Makefile,v 1.20 2001/03/04 00:51:25 markus Exp $
|
||||
|
||||
.PATH: ${.CURDIR}/..
|
||||
|
||||
PROG= ssh-add
|
||||
BINOWN= root
|
||||
|
||||
BINMODE?=555
|
||||
|
||||
BINDIR= /usr/bin
|
||||
MAN= ssh-add.1
|
||||
|
||||
SRCS= ssh-add.c
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
||||
LDADD+= -lcrypto
|
||||
DPADD+= ${LIBCRYPTO}
|
|
@ -0,0 +1,19 @@
|
|||
# $OpenBSD: Makefile,v 1.22 2002/08/12 10:46:35 markus Exp $
|
||||
|
||||
.PATH: ${.CURDIR}/..
|
||||
|
||||
PROG= ssh-agent
|
||||
BINOWN= root
|
||||
BINGRP= _sshagnt
|
||||
|
||||
BINMODE?=2555
|
||||
|
||||
BINDIR= /usr/bin
|
||||
MAN= ssh-agent.1
|
||||
|
||||
SRCS= ssh-agent.c
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
||||
LDADD+= -lcrypto
|
||||
DPADD+= ${LIBCRYPTO}
|
|
@ -0,0 +1,18 @@
|
|||
# $OpenBSD: Makefile,v 1.22 2003/07/28 09:49:56 djm Exp $
|
||||
|
||||
.PATH: ${.CURDIR}/..
|
||||
|
||||
PROG= ssh-keygen
|
||||
BINOWN= root
|
||||
|
||||
BINMODE?=555
|
||||
|
||||
BINDIR= /usr/bin
|
||||
MAN= ssh-keygen.1
|
||||
|
||||
SRCS= ssh-keygen.c moduli.c
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
||||
LDADD+= -lcrypto
|
||||
DPADD+= ${LIBCRYPTO}
|
|
@ -0,0 +1,18 @@
|
|||
# $OpenBSD: Makefile,v 1.4 2001/08/05 23:18:20 markus Exp $
|
||||
|
||||
.PATH: ${.CURDIR}/..
|
||||
|
||||
PROG= ssh-keyscan
|
||||
BINOWN= root
|
||||
|
||||
BINMODE?=555
|
||||
|
||||
BINDIR= /usr/bin
|
||||
MAN= ssh-keyscan.1
|
||||
|
||||
SRCS= ssh-keyscan.c
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
||||
LDADD+= -lcrypto -lz
|
||||
DPADD+= ${LIBCRYPTO} ${LIBZ}
|
|
@ -0,0 +1,32 @@
|
|||
# $OpenBSD: Makefile,v 1.52 2008/05/09 14:18:45 djm Exp $
|
||||
|
||||
.PATH: ${.CURDIR}/..
|
||||
|
||||
PROG= ssh
|
||||
BINOWN= root
|
||||
|
||||
#BINMODE?=4555
|
||||
|
||||
BINDIR= /usr/bin
|
||||
MAN= ssh.1 ssh_config.5
|
||||
LINKS= ${BINDIR}/ssh ${BINDIR}/slogin
|
||||
MLINKS= ssh.1 slogin.1
|
||||
|
||||
SRCS= ssh.c readconf.c clientloop.c sshtty.c \
|
||||
sshconnect.c sshconnect1.c sshconnect2.c mux.c
|
||||
|
||||
.include <bsd.own.mk> # for AFS
|
||||
|
||||
.if (${KERBEROS5:L} == "yes")
|
||||
CFLAGS+= -DKRB5 -I${DESTDIR}/usr/include/kerberosV -DGSSAPI
|
||||
.endif # KERBEROS5
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
||||
.if (${KERBEROS5:L} == "yes")
|
||||
DPADD+= ${LIBGSSAPI} ${LIBKRB5}
|
||||
LDADD+= -lgssapi -lkrb5
|
||||
.endif # KERBEROS5
|
||||
|
||||
DPADD+= ${LIBCRYPTO} ${LIBZ} ${LIBDES}
|
||||
LDADD+= -lcrypto -lz -ldes
|
|
@ -0,0 +1,41 @@
|
|||
# $OpenBSD: Makefile,v 1.67 2008/02/04 21:53:00 markus Exp $
|
||||
|
||||
.PATH: ${.CURDIR}/..
|
||||
|
||||
PROG= sshd
|
||||
BINOWN= root
|
||||
BINMODE=555
|
||||
BINDIR= /usr/sbin
|
||||
MAN= sshd.8 sshd_config.5
|
||||
|
||||
SRCS= sshd.c auth-rhosts.c auth-passwd.c auth-rsa.c auth-rh-rsa.c \
|
||||
sshpty.c sshlogin.c servconf.c serverloop.c \
|
||||
auth.c auth1.c auth2.c auth-options.c session.c \
|
||||
auth-chall.c auth2-chall.c groupaccess.c \
|
||||
auth-bsdauth.c auth2-hostbased.c auth2-kbdint.c \
|
||||
auth2-none.c auth2-passwd.c auth2-pubkey.c \
|
||||
monitor_mm.c monitor.c monitor_wrap.c \
|
||||
kexdhs.c kexgexs.c sftp-server.c sftp-common.c
|
||||
|
||||
.include <bsd.own.mk> # for KERBEROS and AFS
|
||||
|
||||
.if (${KERBEROS5:L} == "yes")
|
||||
CFLAGS+=-DKRB5 -I${DESTDIR}/usr/include/kerberosV -DGSSAPI
|
||||
SRCS+= auth-krb5.c auth2-gss.c gss-serv.c gss-serv-krb5.c
|
||||
.endif
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
||||
.if (${KERBEROS5:L} == "yes")
|
||||
LDADD+= -lgssapi -lkrb5 -lkafs
|
||||
DPADD+= ${LIBGSSAPI} ${LIBKRB5}
|
||||
.endif
|
||||
|
||||
DPADD+= ${LIBCRYPTO} ${LIBUTIL} ${LIBZ} ${LIBDES}
|
||||
LDADD+= -lcrypto -lutil -lz -ldes
|
||||
|
||||
.if (${TCP_WRAPPERS:L} == "yes")
|
||||
CFLAGS+= -DLIBWRAP
|
||||
DPADD+= ${LIBWRAP}
|
||||
LDADD+= -lwrap
|
||||
.endif
|
|
@ -1,4 +1,3 @@
|
|||
/* $NetBSD: umac.h,v 1.1.1.1 2007/12/17 20:15:38 christos Exp $ */
|
||||
/* $OpenBSD: umac.h,v 1.1 2007/06/07 19:37:34 pvalchev Exp $ */
|
||||
/* -----------------------------------------------------------------------
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue