From 856ea928c995e02ea88a6865c1f1983842decc66 Mon Sep 17 00:00:00 2001 From: Peter Avalos Date: Tue, 28 Sep 2010 15:03:24 -1000 Subject: [PATCH] Import OpenSSH-5.6p1. --- crypto/openssh/PROTOCOL | 37 +- crypto/openssh/PROTOCOL.agent | 24 +- crypto/openssh/PROTOCOL.certkeys | 225 ++ crypto/openssh/PROTOCOL.mux | 203 ++ crypto/openssh/README | 4 +- crypto/openssh/README.DELETED | 5 +- crypto/openssh/addrmatch.c | 78 +- crypto/openssh/auth-krb5.c | 13 +- crypto/openssh/auth-options.c | 291 ++- crypto/openssh/auth-options.h | 5 +- crypto/openssh/auth-rh-rsa.c | 5 +- crypto/openssh/auth-rhosts.c | 10 +- crypto/openssh/auth-rsa.c | 12 +- crypto/openssh/auth.c | 140 +- crypto/openssh/auth.h | 8 +- crypto/openssh/auth1.c | 4 +- crypto/openssh/auth2-hostbased.c | 34 +- crypto/openssh/auth2-none.c | 4 +- crypto/openssh/auth2-pubkey.c | 202 +- crypto/openssh/authfd.c | 34 +- crypto/openssh/authfd.h | 3 +- crypto/openssh/authfile.c | 143 +- crypto/openssh/authfile.h | 5 +- crypto/openssh/bufaux.c | 34 +- crypto/openssh/buffer.c | 8 +- crypto/openssh/buffer.h | 9 +- crypto/openssh/canohost.c | 20 +- crypto/openssh/channels.c | 378 ++- crypto/openssh/channels.h | 29 +- crypto/openssh/clientloop.c | 163 +- crypto/openssh/clientloop.h | 15 +- crypto/openssh/defines.h | 12 +- crypto/openssh/dh.c | 4 +- crypto/openssh/dns.c | 8 +- crypto/openssh/dns.h | 6 +- crypto/openssh/hostfile.c | 101 +- crypto/openssh/hostfile.h | 7 +- crypto/openssh/jpake.c | 4 +- crypto/openssh/kex.c | 13 +- crypto/openssh/kex.h | 7 +- crypto/openssh/kexdhs.c | 19 +- crypto/openssh/kexgexs.c | 20 +- crypto/openssh/key.c | 742 +++++- crypto/openssh/key.h | 38 +- crypto/openssh/loginrec.c | 39 +- crypto/openssh/loginrec.h | 2 +- crypto/openssh/match.h | 4 +- crypto/openssh/misc.c | 46 +- crypto/openssh/misc.h | 4 +- crypto/openssh/monitor.c | 38 +- crypto/openssh/monitor_fdpass.c | 23 +- crypto/openssh/monitor_wrap.c | 19 +- crypto/openssh/mux.c | 2064 +++++++++++++---- crypto/openssh/myproposal.h | 9 +- crypto/openssh/nchan.c | 21 +- .../openssh/openbsd-compat/openbsd-compat.h | 15 +- .../openssh/openbsd-compat/openssl-compat.h | 13 +- crypto/openssh/openbsd-compat/port-aix.h | 6 +- crypto/openssh/openbsd-compat/port-linux.h | 8 +- crypto/openssh/openbsd-compat/port-tun.c | 8 +- crypto/openssh/packet.c | 4 +- crypto/openssh/pathnames.h | 7 +- crypto/openssh/pkcs11.h | 1357 +++++++++++ crypto/openssh/platform.c | 24 +- crypto/openssh/platform.h | 7 +- crypto/openssh/readconf.c | 87 +- crypto/openssh/readconf.h | 12 +- crypto/openssh/roaming.h | 8 +- crypto/openssh/roaming_client.c | 280 +++ crypto/openssh/roaming_common.c | 57 +- crypto/openssh/{platform.h => roaming_serv.c} | 20 +- crypto/openssh/scard-opensc.c | 532 ----- crypto/openssh/scard.c | 571 ----- crypto/openssh/scp.1 | 6 +- crypto/openssh/scp.c | 39 +- crypto/openssh/servconf.c | 95 +- crypto/openssh/servconf.h | 9 +- crypto/openssh/session.c | 158 +- crypto/openssh/sftp-client.c | 304 ++- crypto/openssh/sftp-client.h | 21 +- crypto/openssh/sftp-common.c | 31 +- crypto/openssh/sftp-common.h | 4 +- crypto/openssh/sftp-server.8 | 25 +- crypto/openssh/sftp-server.c | 119 +- crypto/openssh/sftp.1 | 93 +- crypto/openssh/sftp.c | 862 +++++-- crypto/openssh/ssh-add.1 | 32 +- crypto/openssh/ssh-add.c | 64 +- crypto/openssh/ssh-agent.1 | 22 +- crypto/openssh/ssh-agent.c | 161 +- crypto/openssh/ssh-dss.c | 8 +- crypto/openssh/ssh-keygen.1 | 274 ++- crypto/openssh/ssh-keygen.c | 1154 +++++++-- crypto/openssh/ssh-keyscan.1 | 4 +- crypto/openssh/ssh-keyscan.c | 165 +- crypto/openssh/ssh-keysign.8 | 7 +- crypto/openssh/ssh-keysign.c | 6 +- crypto/openssh/ssh-pkcs11-client.c | 238 ++ crypto/openssh/ssh-pkcs11-helper.8 | 43 + crypto/openssh/ssh-pkcs11-helper.c | 372 +++ crypto/openssh/ssh-pkcs11.c | 593 +++++ crypto/openssh/{platform.h => ssh-pkcs11.h} | 15 +- crypto/openssh/ssh-rsa.c | 17 +- crypto/openssh/ssh.1 | 142 +- crypto/openssh/ssh.c | 371 ++- crypto/openssh/ssh.h | 5 +- crypto/openssh/ssh2.h | 12 +- crypto/openssh/ssh_config | 3 +- crypto/openssh/ssh_config.5 | 115 +- crypto/openssh/sshconnect.c | 110 +- crypto/openssh/sshconnect2.c | 75 +- crypto/openssh/sshd.8 | 91 +- crypto/openssh/sshd.c | 146 +- crypto/openssh/sshd_config | 8 +- crypto/openssh/sshd_config.5 | 94 +- crypto/openssh/sshpty.h | 6 +- crypto/openssh/sshtty.c | 23 +- 117 files changed, 11338 insertions(+), 3215 deletions(-) create mode 100644 crypto/openssh/PROTOCOL.certkeys create mode 100644 crypto/openssh/PROTOCOL.mux create mode 100644 crypto/openssh/pkcs11.h create mode 100644 crypto/openssh/roaming_client.c copy crypto/openssh/{platform.h => roaming_serv.c} (71%) delete mode 100644 crypto/openssh/scard-opensc.c delete mode 100644 crypto/openssh/scard.c create mode 100644 crypto/openssh/ssh-pkcs11-client.c create mode 100644 crypto/openssh/ssh-pkcs11-helper.8 create mode 100644 crypto/openssh/ssh-pkcs11-helper.c create mode 100644 crypto/openssh/ssh-pkcs11.c copy crypto/openssh/{platform.h => ssh-pkcs11.h} (74%) diff --git a/crypto/openssh/PROTOCOL b/crypto/openssh/PROTOCOL index 5aada630dd..5fc31eade3 100644 --- a/crypto/openssh/PROTOCOL +++ b/crypto/openssh/PROTOCOL @@ -6,8 +6,8 @@ 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. +Newer versions of the draft will not be supported, though some features +are individually implemented as extensions described below. The protocol used by OpenSSH's ssh-agent is described in the file PROTOCOL.agent @@ -31,7 +31,14 @@ 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" +3. transport: New public key algorithms "ssh-rsa-cert-v00@openssh.com" and + "ssh-dsa-cert-v00@openssh.com" + +OpenSSH introduces two new public key algorithms to support certificate +authentication for users and hostkeys. These methods are documented in +the file PROTOCOL.certkeys + +4. 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 @@ -70,7 +77,7 @@ message is only sent to OpenSSH peers (identified by banner). Other SSH implementations may be whitelisted to receive this message upon request. -4. connection: disallow additional sessions extension +5. connection: disallow additional sessions extension "no-more-sessions@openssh.com" Most SSH connections will only ever request a single session, but a @@ -98,7 +105,7 @@ of this message, the no-more-sessions request is only sent to OpenSSH servers (identified by banner). Other SSH implementations may be whitelisted to receive this message upon request. -5. connection: Tunnel forward extension "tun@openssh.com" +6. 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 @@ -121,10 +128,10 @@ layer 2 frames or layer 3 packets. It may take one of the following values: 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. +be 0x7fffffff 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 @@ -151,7 +158,7 @@ It may be one of: 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: +The contents of the "data" field for layer 2 packets is: uint32 packet length byte[packet length] frame @@ -159,7 +166,7 @@ The contents of the "data" field for layer 3 packets is: The "frame" field contains an IEEE 802.3 Ethernet frame, including header. -6. sftp: Reversal of arguments to SSH_FXP_SYMLINK +7. 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, @@ -172,7 +179,7 @@ SSH_FXP_SYMLINK as follows: string targetpath string linkpath -7. sftp: Server extension announcement in SSH_FXP_VERSION +8. 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 @@ -193,7 +200,7 @@ 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" +9. 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 @@ -210,7 +217,7 @@ 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 +10. sftp: Extension requests "statvfs@openssh.com" and "fstatvfs@openssh.com" These requests correspond to the statvfs and fstatvfs POSIX system @@ -251,4 +258,4 @@ The values of the f_flag bitmask are as follows: 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.12 2009/02/14 06:35:49 djm Exp $ +$OpenBSD: PROTOCOL,v 1.15 2010/02/26 20:29:54 djm Exp $ diff --git a/crypto/openssh/PROTOCOL.agent b/crypto/openssh/PROTOCOL.agent index 49adbdd5c7..b34fcd318e 100644 --- a/crypto/openssh/PROTOCOL.agent +++ b/crypto/openssh/PROTOCOL.agent @@ -173,6 +173,15 @@ be added using the following request string key_comment constraint[] key_constraints +DSA certificates may be added with: + byte SSH2_AGENTC_ADD_IDENTITY or + SSH2_AGENTC_ADD_ID_CONSTRAINED + string "ssh-dss-cert-v00@openssh.com" + string certificate + mpint dsa_private_key + string key_comment + constraint[] key_constraints + RSA keys may be added with this request: byte SSH2_AGENTC_ADD_IDENTITY or @@ -187,6 +196,19 @@ RSA keys may be added with this request: string key_comment constraint[] key_constraints +RSA certificates may be added with this request: + + byte SSH2_AGENTC_ADD_IDENTITY or + SSH2_AGENTC_ADD_ID_CONSTRAINED + string "ssh-rsa-cert-v00@openssh.com" + string certificate + 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 @@ -513,4 +535,4 @@ Locking and unlocking affects both protocol 1 and protocol 2 keys. SSH_AGENT_CONSTRAIN_LIFETIME 1 SSH_AGENT_CONSTRAIN_CONFIRM 2 -$OpenBSD: PROTOCOL.agent,v 1.4 2008/07/01 23:12:47 stevesk Exp $ +$OpenBSD: PROTOCOL.agent,v 1.5 2010/02/26 20:29:54 djm Exp $ diff --git a/crypto/openssh/PROTOCOL.certkeys b/crypto/openssh/PROTOCOL.certkeys new file mode 100644 index 0000000000..1d1be13da7 --- /dev/null +++ b/crypto/openssh/PROTOCOL.certkeys @@ -0,0 +1,225 @@ +This document describes a simple public-key certificate authentication +system for use by SSH. + +Background +---------- + +The SSH protocol currently supports a simple public key authentication +mechanism. Unlike other public key implementations, SSH eschews the +use of X.509 certificates and uses raw keys. This approach has some +benefits relating to simplicity of configuration and minimisation +of attack surface, but it does not support the important use-cases +of centrally managed, passwordless authentication and centrally +certified host keys. + +These protocol extensions build on the simple public key authentication +system already in SSH to allow certificate-based authentication. +The certificates used are not traditional X.509 certificates, with +numerous options and complex encoding rules, but something rather +more minimal: a key, some identity information and usage options +that have been signed with some other trusted key. + +A sshd server may be configured to allow authentication via certified +keys, by extending the existing ~/.ssh/authorized_keys mechanism +to allow specification of certification authority keys in addition +to raw user keys. The ssh client will support automatic verification +of acceptance of certified host keys, by adding a similar ability +to specify CA keys in ~/.ssh/known_hosts. + +Certified keys are represented using two new key types: +ssh-rsa-cert-v01@openssh.com and ssh-dss-cert-v01@openssh.com that +include certification information along with the public key that is used +to sign challenges. ssh-keygen performs the CA signing operation. + +Protocol extensions +------------------- + +The SSH wire protocol includes several extensibility mechanisms. +These modifications shall take advantage of namespaced public key +algorithm names to add support for certificate authentication without +breaking the protocol - implementations that do not support the +extensions will simply ignore them. + +Authentication using the new key formats described below proceeds +using the existing SSH "publickey" authentication method described +in RFC4252 section 7. + +New public key formats +---------------------- + +The ssh-rsa-cert-v01@openssh.com and ssh-dss-cert-v01@openssh.com key +types take a similar high-level format (note: data types and +encoding are as per RFC4251 section 5). The serialised wire encoding of +these certificates is also used for storing them on disk. + +#define SSH_CERT_TYPE_USER 1 +#define SSH_CERT_TYPE_HOST 2 + +RSA certificate + + string "ssh-rsa-cert-v01@openssh.com" + string nonce + mpint e + mpint n + uint64 serial + uint32 type + string key id + string valid principals + uint64 valid after + uint64 valid before + string critical options + string extensions + string reserved + string signature key + string signature + +DSA certificate + + string "ssh-dss-cert-v01@openssh.com" + string nonce + mpint p + mpint q + mpint g + mpint y + uint64 serial + uint32 type + string key id + string valid principals + uint64 valid after + uint64 valid before + string critical options + string extensions + string reserved + string signature key + string signature + +The nonce field is a CA-provided random bitstring of arbitrary length +(but typically 16 or 32 bytes) included to make attacks that depend on +inducing collisions in the signature hash infeasible. + +e and n are the RSA exponent and public modulus respectively. + +p, q, g, y are the DSA parameters as described in FIPS-186-2. + +serial is an optional certificate serial number set by the CA to +provide an abbreviated way to refer to certificates from that CA. +If a CA does not wish to number its certificates it must set this +field to zero. + +type specifies whether this certificate is for identification of a user +or a host using a SSH_CERT_TYPE_... value. + +key id is a free-form text field that is filled in by the CA at the time +of signing; the intention is that the contents of this field are used to +identify the identity principal in log messages. + +"valid principals" is a string containing zero or more principals as +strings packed inside it. These principals list the names for which this +certificate is valid; hostnames for SSH_CERT_TYPE_HOST certificates and +usernames for SSH_CERT_TYPE_USER certificates. As a special case, a +zero-length "valid principals" field means the certificate is valid for +any principal of the specified type. XXX DNS wildcards? + +"valid after" and "valid before" specify a validity period for the +certificate. Each represents a time in seconds since 1970-01-01 +00:00:00. A certificate is considered valid if: + valid after <= current time < valid before + +criticial options is a set of zero or more key options encoded as +below. All such options are "critical" in the sense that an implementation +must refuse to authorise a key that has an unrecognised option. + +extensions is a set of zero or more optional extensions. These extensions +are not critical, and an implementation that encounters one that it does +not recognise may safely ignore it. + +The reserved field is currently unused and is ignored in this version of +the protocol. + +signature key contains the CA key used to sign the certificate. +The valid key types for CA keys are ssh-rsa and ssh-dss. "Chained" +certificates, where the signature key type is a certificate type itself +are NOT supported. Note that it is possible for a RSA certificate key to +be signed by a DSS CA key and vice-versa. + +signature is computed over all preceding fields from the initial string +up to, and including the signature key. Signatures are computed and +encoded according to the rules defined for the CA's public key algorithm +(RFC4253 section 6.6 for ssh-rsa and ssh-dss). + +Critical options +---------------- + +The critical options section of the certificate specifies zero or more +options on the certificates validity. The format of this field +is a sequence of zero or more tuples: + + string name + string data + +Options must be lexically ordered by "name" if they appear in the +sequence. + +The name field identifies the option and the data field encodes +option-specific information (see below). All options are +"critical", if an implementation does not recognise a option +then the validating party should refuse to accept the certificate. + +The supported options and the contents and structure of their +data fields are: + +Name Format Description +----------------------------------------------------------------------------- +force-command string Specifies a command that is executed + (replacing any the user specified on the + ssh command-line) whenever this key is + used for authentication. + +source-address string Comma-separated list of source addresses + from which this certificate is accepted + for authentication. Addresses are + specified in CIDR format (nn.nn.nn.nn/nn + or hhhh::hhhh/nn). + If this option is not present then + certificates may be presented from any + source address. + +Extensions +---------- + +The extensions section of the certificate specifies zero or more +non-critical certificate extensions. The encoding and ordering of +extensions in this field is identical to that of the critical options. +If an implementation does not recognise an extension, then it should +ignore it. + +The supported extensions and the contents and structure of their data +fields are: + +Name Format Description +----------------------------------------------------------------------------- +permit-X11-forwarding empty Flag indicating that X11 forwarding + should be permitted. X11 forwarding will + be refused if this option is absent. + +permit-agent-forwarding empty Flag indicating that agent forwarding + should be allowed. Agent forwarding + must not be permitted unless this + option is present. + +permit-port-forwarding empty Flag indicating that port-forwarding + should be allowed. If this option is + not present then no port forwarding will + be allowed. + +permit-pty empty Flag indicating that PTY allocation + should be permitted. In the absence of + this option PTY allocation will be + disabled. + +permit-user-rc empty Flag indicating that execution of + ~/.ssh/rc should be permitted. Execution + of this script will not be permitted if + this option is not present. + +$OpenBSD: PROTOCOL.certkeys,v 1.7 2010/08/04 05:40:39 djm Exp $ diff --git a/crypto/openssh/PROTOCOL.mux b/crypto/openssh/PROTOCOL.mux new file mode 100644 index 0000000000..1d8c463a74 --- /dev/null +++ b/crypto/openssh/PROTOCOL.mux @@ -0,0 +1,203 @@ +This document describes the multiplexing protocol used by ssh(1)'s +ControlMaster connection-sharing. + +Most messages from the client to the server contain a "request id" field. +This field is returned in replies as "client request id" to facilitate +matching of responses to requests. + +1. Connection setup + +When a multiplexing connection is made to a ssh(1) operating as a +ControlMaster from a ssh(1) in multiplex slave mode, the first +action of each is to exchange hello messages: + + uint32 MUX_MSG_HELLO + uint32 protocol version + string extension name [optional] + string extension value [optional] + ... + +The current version of the mux protocol is 4. A slave should refuse +to connect to a master that speaks an unsupported protocol version. +Following the version identifier are zero or more extensions +represented as a name/value pair. No extensions are currently +defined. + +2. Opening sessions + +To open a new multiplexed session, a client may send the following +request: + + uint32 MUX_C_MSG_NEW_SESSION + uint32 request id + string reserved + bool want tty flag + bool want X11 forwarding flag + bool want agent flag + bool subsystem flag + uint32 escape char + string terminal type + string command + string environment string 0 [optional] + ... + +To disable the use of an escape character, "escape char" may be set +to 0xffffffff. "terminal type" is generally set to the value of +$TERM. zero or more environment strings may follow the command. + +The client then sends its standard input, output and error file +descriptors (in that order) using Unix domain socket control messages. + +The contents of "reserved" are currently ignored. + +If successful, the server will reply with MUX_S_SESSION_OPENED + + uint32 MUX_S_SESSION_OPENED + uint32 client request id + uint32 session id + +Otherwise it will reply with an error: MUX_S_PERMISSION_DENIED or +MUX_S_FAILURE. + +Once the server has received the fds, it will respond with MUX_S_OK +indicating that the session is up. The client now waits for the +session to end. When it does, the server will send an exit status +message: + + uint32 MUX_S_EXIT_MESSAGE + uint32 session id + uint32 exit value + +The client should exit with this value to mimic the behaviour of a +non-multiplexed ssh(1) connection. Two additional cases that the +client must cope with are it receiving a signal itself and the +server disconnecting without sending an exit message. + +3. Health checks + +The client may request a health check/PID report from a server: + + uint32 MUX_C_ALIVE_CHECK + uint32 request id + +The server replies with: + + uint32 MUX_S_ALIVE + uint32 client request id + uint32 server pid + +4. Remotely terminating a master + +A client may request that a master terminate immediately: + + uint32 MUX_C_TERMINATE + uint32 request id + +The server will reply with one of MUX_S_OK or MUX_S_PERMISSION_DENIED. + +5. Requesting establishment of port forwards + +A client may request the master to establish a port forward: + + uint32 MUX_C_OPEN_FORWARD + uint32 request id + uint32 forwarding type + string listen host + string listen port + string connect host + string connect port + +forwarding type may be MUX_FWD_LOCAL, MUX_FWD_REMOTE, MUX_FWD_DYNAMIC. + +A server may reply with a MUX_S_OK, a MUX_S_REMOTE_PORT, a +MUX_S_PERMISSION_DENIED or a MUX_S_FAILURE. + +For dynamically allocated listen port the server replies with + + uint32 MUX_S_REMOTE_PORT + uint32 client request id + uint32 allocated remote listen port + +5. Requesting closure of port forwards + +A client may request the master to establish a port forward: + + uint32 MUX_C_OPEN_FORWARD + uint32 request id + uint32 forwarding type + string listen host + string listen port + string connect host + string connect port + +forwarding type may be MUX_FWD_LOCAL, MUX_FWD_REMOTE, MUX_FWD_DYNAMIC. + +A server may reply with a MUX_S_OK, a MUX_S_PERMISSION_DENIED or a +MUX_S_FAILURE. + +6. Requesting stdio forwarding + +A client may request the master to establish a stdio forwarding: + + uint32 MUX_C_NEW_STDIO_FWD + uint32 request id + string reserved + string connect host + string connect port + +The client then sends its standard input and output file descriptors +(in that order) using Unix domain socket control messages. + +The contents of "reserved" are currently ignored. + +A server may reply with a MUX_S_SESSION_OPEED, a MUX_S_PERMISSION_DENIED +or a MUX_S_FAILURE. + +7. Status messages + +The MUX_S_OK message is empty: + + uint32 MUX_S_OK + uint32 client request id + +The MUX_S_PERMISSION_DENIED and MUX_S_FAILURE include a reason: + + uint32 MUX_S_PERMISSION_DENIED + uint32 client request id + string reason + + uint32 MUX_S_FAILURE + uint32 client request id + string reason + +7. Protocol numbers + +#define MUX_MSG_HELLO 0x00000001 +#define MUX_C_NEW_SESSION 0x10000002 +#define MUX_C_ALIVE_CHECK 0x10000004 +#define MUX_C_TERMINATE 0x10000005 +#define MUX_C_OPEN_FORWARD 0x10000006 +#define MUX_C_CLOSE_FORWARD 0x10000007 +#define MUX_S_OK 0x80000001 +#define MUX_S_PERMISSION_DENIED 0x80000002 +#define MUX_S_FAILURE 0x80000003 +#define MUX_S_EXIT_MESSAGE 0x80000004 +#define MUX_S_ALIVE 0x80000005 +#define MUX_S_SESSION_OPENED 0x80000006 +#define MUX_S_REMOTE_PORT 0x80000007 + +#define MUX_FWD_LOCAL 1 +#define MUX_FWD_REMOTE 2 +#define MUX_FWD_DYNAMIC 3 + +XXX TODO +XXX extended status (e.g. report open channels / forwards) +XXX graceful close (delete listening socket, but keep existing sessions active) +XXX lock (maybe) +XXX watch in/out traffic (pre/post crypto) +XXX inject packet (what about replies) +XXX server->client error/warning notifications +XXX port0 rfwd (need custom response message) +XXX send signals via mux + +$OpenBSD: PROTOCOL.mux,v 1.2 2010/05/16 12:55:51 markus Exp $ diff --git a/crypto/openssh/README b/crypto/openssh/README index 8538e8c9bb..4eaa545881 100644 --- a/crypto/openssh/README +++ b/crypto/openssh/README @@ -1,4 +1,4 @@ -See http://www.openssh.com/txt/release-5.3 for the release notes. +See http://www.openssh.com/txt/release-5.6 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.70.4.1 2009/09/26 04:11:47 djm Exp $ +$Id: README,v 1.74 2010/08/08 16:32:06 djm Exp $ diff --git a/crypto/openssh/README.DELETED b/crypto/openssh/README.DELETED index 82b0a5671a..8b7cee9859 100644 --- a/crypto/openssh/README.DELETED +++ b/crypto/openssh/README.DELETED @@ -4,7 +4,6 @@ Makefile.in OVERVIEW README.platform README.privsep -README.smartcard TODO WARNING.RNG aclocal.m4 @@ -57,6 +56,7 @@ openbsd-compat/port-irix.c openbsd-compat/port-linux.c openbsd-compat/port-solaris.c openbsd-compat/port-uw.c +openbsd-compat/pwcache.c openbsd-compat/readpassphrase.c openbsd-compat/realpath.c openbsd-compat/regress/ @@ -68,6 +68,7 @@ openbsd-compat/sigact.c openbsd-compat/strlcat.c openbsd-compat/strlcpy.c openbsd-compat/strmode.c +openbsd-compat/strptime.c openbsd-compat/strsep.c openbsd-compat/strtoll.c openbsd-compat/strtonum.c @@ -79,7 +80,6 @@ openssh.xml.in opensshd.init.in regress/ scard/ -scard.h scp.0 sftp-server.0 sftp.0 @@ -88,6 +88,7 @@ ssh-agent.0 ssh-keygen.0 ssh-keyscan.0 ssh-keysign.0 +ssh-pkcs11-helper.0 ssh-rand-helper.0 ssh-rand-helper.8 ssh-rand-helper.c diff --git a/crypto/openssh/addrmatch.c b/crypto/openssh/addrmatch.c index d39885b7bd..5b6773ccef 100644 --- a/crypto/openssh/addrmatch.c +++ b/crypto/openssh/addrmatch.c @@ -1,4 +1,4 @@ -/* $OpenBSD: addrmatch.c,v 1.4 2008/12/10 03:55:20 stevesk Exp $ */ +/* $OpenBSD: addrmatch.c,v 1.5 2010/02/26 20:29:54 djm Exp $ */ /* * Copyright (c) 2004-2008 Damien Miller @@ -126,6 +126,8 @@ addr_netmask(int af, u_int l, struct xaddr *n) switch (af) { case AF_INET: n->af = AF_INET; + if (l == 0) + return 0; n->v4.s_addr = htonl((0xffffffff << (32 - l)) & 0xffffffff); return 0; case AF_INET6: @@ -422,3 +424,77 @@ addr_match_list(const char *addr, const char *_list) return ret; } + +/* + * Match "addr" against list CIDR list "_list". Lexical wildcards and + * negation are not supported. If "addr" == NULL, will verify structure + * of "_list". + * + * 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 error + */ +int +addr_match_cidr_list(const char *addr, const char *_list) +{ + char *list, *cp, *o; + struct xaddr try_addr, match_addr; + u_int masklen; + 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) { + if (*cp == '\0') { + error("%s: empty entry in list \"%.100s\"", + __func__, o); + ret = -1; + break; + } + + /* + * NB. This function is called in pre-auth with untrusted data, + * so be extra paranoid about junk reaching getaddrino (via + * addr_pton_cidr). + */ + + /* Stop junk from reaching getaddrinfo. +3 is for masklen */ + if (strlen(cp) > INET6_ADDRSTRLEN + 3) { + error("%s: list entry \"%.100s\" too long", + __func__, cp); + ret = -1; + break; + } +#define VALID_CIDR_CHARS "0123456789abcdefABCDEF.:/" + if (strspn(cp, VALID_CIDR_CHARS) != strlen(cp)) { + error("%s: list entry \"%.100s\" contains invalid " + "characters", __func__, cp); + ret = -1; + } + + /* Prefer CIDR address matching */ + r = addr_pton_cidr(cp, &match_addr, &masklen); + if (r == -1) { + error("Invalid network entry \"%.100s\"", cp); + ret = -1; + break; + } else if (r == -2) { + error("Inconsistent mask length for " + "network \"%.100s\"", cp); + ret = -1; + break; + } else if (r == 0 && addr != NULL) { + if (addr_netmatch(&try_addr, &match_addr, + masklen) == 0) + ret = 1; + continue; + } + } + xfree(o); + + return ret; +} diff --git a/crypto/openssh/auth-krb5.c b/crypto/openssh/auth-krb5.c index 8682881262..d019fe202c 100644 --- a/crypto/openssh/auth-krb5.c +++ b/crypto/openssh/auth-krb5.c @@ -78,6 +78,11 @@ auth_krb5_password(Authctxt *authctxt, const char *password) krb5_error_code problem; krb5_ccache ccache = NULL; int len; + char *client, *platform_client; + + /* get platform-specific kerberos client principal name (if it exists) */ + platform_client = platform_krb5_get_principal_name(authctxt->pw->pw_name); + client = platform_client ? platform_client : authctxt->pw->pw_name; temporarily_use_uid(authctxt->pw); @@ -85,7 +90,7 @@ auth_krb5_password(Authctxt *authctxt, const char *password) if (problem) goto out; - problem = krb5_parse_name(authctxt->krb5_ctx, authctxt->pw->pw_name, + problem = krb5_parse_name(authctxt->krb5_ctx, client, &authctxt->krb5_user); if (problem) goto out; @@ -141,8 +146,7 @@ auth_krb5_password(Authctxt *authctxt, const char *password) if (problem) goto out; - if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, - authctxt->pw->pw_name)) { + if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, client)) { problem = -1; goto out; } @@ -176,6 +180,9 @@ auth_krb5_password(Authctxt *authctxt, const char *password) out: restore_uid(); + + if (platform_client != NULL) + xfree(platform_client); if (problem) { if (ccache) diff --git a/crypto/openssh/auth-options.c b/crypto/openssh/auth-options.c index ab085c2339..a7040247fe 100644 --- a/crypto/openssh/auth-options.c +++ b/crypto/openssh/auth-options.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-options.c,v 1.44 2009/01/22 10:09:16 djm Exp $ */ +/* $OpenBSD: auth-options.c,v 1.52 2010/05/20 23:46:02 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -27,10 +27,10 @@ #include "canohost.h" #include "buffer.h" #include "channels.h" -#include "auth-options.h" #include "servconf.h" #include "misc.h" #include "key.h" +#include "auth-options.h" #include "hostfile.h" #include "auth.h" #ifdef GSSAPI @@ -44,6 +44,7 @@ int no_agent_forwarding_flag = 0; int no_x11_forwarding_flag = 0; int no_pty_flag = 0; int no_user_rc = 0; +int key_is_cert_authority = 0; /* "command=" option. */ char *forced_command = NULL; @@ -54,6 +55,9 @@ struct envstring *custom_environment = NULL; /* "tunnel=" option. */ int forced_tun_device = -1; +/* "principals=" option. */ +char *authorized_principals = NULL; + extern ServerOptions options; void @@ -64,6 +68,7 @@ auth_clear_options(void) no_pty_flag = 0; no_x11_forwarding_flag = 0; no_user_rc = 0; + key_is_cert_authority = 0; while (custom_environment) { struct envstring *ce = custom_environment; custom_environment = ce->next; @@ -74,9 +79,12 @@ auth_clear_options(void) xfree(forced_command); forced_command = NULL; } + if (authorized_principals) { + xfree(authorized_principals); + authorized_principals = NULL; + } forced_tun_device = -1; channel_clear_permitted_opens(); - auth_debug_reset(); } /* @@ -96,6 +104,12 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) return 1; while (*opts && *opts != ' ' && *opts != '\t') { + cp = "cert-authority"; + if (strncasecmp(opts, cp, strlen(cp)) == 0) { + key_is_cert_authority = 1; + opts += strlen(cp); + goto next_option; + } cp = "no-port-forwarding"; if (strncasecmp(opts, cp, strlen(cp)) == 0) { auth_debug_add("Port forwarding disabled."); @@ -134,6 +148,8 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) cp = "command=\""; if (strncasecmp(opts, cp, strlen(cp)) == 0) { opts += strlen(cp); + if (forced_command != NULL) + xfree(forced_command); forced_command = xmalloc(strlen(opts) + 1); i = 0; while (*opts) { @@ -160,6 +176,38 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) opts++; goto next_option; } + cp = "principals=\""; + if (strncasecmp(opts, cp, strlen(cp)) == 0) { + opts += strlen(cp); + if (authorized_principals != NULL) + xfree(authorized_principals); + authorized_principals = xmalloc(strlen(opts) + 1); + i = 0; + while (*opts) { + if (*opts == '"') + break; + if (*opts == '\\' && opts[1] == '"') { + opts += 2; + authorized_principals[i++] = '"'; + continue; + } + authorized_principals[i++] = *opts++; + } + if (!*opts) { + debug("%.100s, line %lu: missing end quote", + file, linenum); + auth_debug_add("%.100s, line %lu: missing end quote", + file, linenum); + xfree(authorized_principals); + authorized_principals = NULL; + goto bad_option; + } + authorized_principals[i] = '\0'; + auth_debug_add("principals: %.900s", + authorized_principals); + opts++; + goto next_option; + } cp = "environment=\""; if (options.permit_user_env && strncasecmp(opts, cp, strlen(cp)) == 0) { @@ -356,9 +404,6 @@ next_option: /* Process the next option. */ } - if (!use_privsep) - auth_debug_send(); - /* grant access */ return 1; @@ -368,9 +413,237 @@ bad_option: auth_debug_add("Bad options in %.100s file, line %lu: %.50s", file, linenum, opts); - if (!use_privsep) - auth_debug_send(); - /* deny access */ return 0; } + +#define OPTIONS_CRITICAL 1 +#define OPTIONS_EXTENSIONS 2 +static int +parse_option_list(u_char *optblob, size_t optblob_len, struct passwd *pw, + u_int which, int crit, + int *cert_no_port_forwarding_flag, + int *cert_no_agent_forwarding_flag, + int *cert_no_x11_forwarding_flag, + int *cert_no_pty_flag, + int *cert_no_user_rc, + char **cert_forced_command, + int *cert_source_address_done) +{ + char *command, *allowed; + const char *remote_ip; + u_char *name = NULL, *data_blob = NULL; + u_int nlen, dlen, clen; + Buffer c, data; + int ret = -1, found; + + buffer_init(&data); + + /* Make copy to avoid altering original */ + buffer_init(&c); + buffer_append(&c, optblob, optblob_len); + + while (buffer_len(&c) > 0) { + if ((name = buffer_get_string_ret(&c, &nlen)) == NULL || + (data_blob = buffer_get_string_ret(&c, &dlen)) == NULL) { + error("Certificate options corrupt"); + goto out; + } + buffer_append(&data, data_blob, dlen); + debug3("found certificate option \"%.100s\" len %u", + name, dlen); + if (strlen(name) != nlen) { + error("Certificate constraint name contains \\0"); + goto out; + } + found = 0; + if ((which & OPTIONS_EXTENSIONS) != 0) { + if (strcmp(name, "permit-X11-forwarding") == 0) { + *cert_no_x11_forwarding_flag = 0; + found = 1; + } else if (strcmp(name, + "permit-agent-forwarding") == 0) { + *cert_no_agent_forwarding_flag = 0; + found = 1; + } else if (strcmp(name, + "permit-port-forwarding") == 0) { + *cert_no_port_forwarding_flag = 0; + found = 1; + } else if (strcmp(name, "permit-pty") == 0) { + *cert_no_pty_flag = 0; + found = 1; + } else if (strcmp(name, "permit-user-rc") == 0) { + *cert_no_user_rc = 0; + found = 1; + } + } + if (!found && (which & OPTIONS_CRITICAL) != 0) { + if (strcmp(name, "force-command") == 0) { + if ((command = buffer_get_string_ret(&data, + &clen)) == NULL) { + error("Certificate constraint \"%s\" " + "corrupt", name); + goto out; + } + if (strlen(command) != clen) { + error("force-command constraint " + "contains \\0"); + goto out; + } + if (*cert_forced_command != NULL) { + error("Certificate has multiple " + "force-command options"); + xfree(command); + goto out; + } + *cert_forced_command = command; + found = 1; + } + if (strcmp(name, "source-address") == 0) { + if ((allowed = buffer_get_string_ret(&data, + &clen)) == NULL) { + error("Certificate constraint " + "\"%s\" corrupt", name); + goto out; + } + if (strlen(allowed) != clen) { + error("source-address constraint " + "contains \\0"); + goto out; + } + if ((*cert_source_address_done)++) { + error("Certificate has multiple " + "source-address options"); + xfree(allowed); + goto out; + } + remote_ip = get_remote_ipaddr(); + switch (addr_match_cidr_list(remote_ip, + allowed)) { + case 1: + /* accepted */ + xfree(allowed); + break; + case 0: + /* no match */ + logit("Authentication tried for %.100s " + "with valid certificate but not " + "from a permitted host " + "(ip=%.200s).", pw->pw_name, + remote_ip); + auth_debug_add("Your address '%.200s' " + "is not permitted to use this " + "certificate for login.", + remote_ip); + xfree(allowed); + goto out; + case -1: + error("Certificate source-address " + "contents invalid"); + xfree(allowed); + goto out; + } + found = 1; + } + } + + if (!found) { + if (crit) { + error("Certificate critical option \"%s\" " + "is not supported", name); + goto out; + } else { + logit("Certificate extension \"%s\" " + "is not supported", name); + } + } else if (buffer_len(&data) != 0) { + error("Certificate option \"%s\" corrupt " + "(extra data)", name); + goto out; + } + buffer_clear(&data); + xfree(name); + xfree(data_blob); + name = data_blob = NULL; + } + /* successfully parsed all options */ + ret = 0; + + out: + if (ret != 0 && + cert_forced_command != NULL && + *cert_forced_command != NULL) { + xfree(*cert_forced_command); + *cert_forced_command = NULL; + } + if (name != NULL) + xfree(name); + if (data_blob != NULL) + xfree(data_blob); + buffer_free(&data); + buffer_free(&c); + return ret; +} + +/* + * Set options from critical certificate options. These supersede user key + * options so this must be called after auth_parse_options(). + */ +int +auth_cert_options(Key *k, struct passwd *pw) +{ + int cert_no_port_forwarding_flag = 1; + int cert_no_agent_forwarding_flag = 1; + int cert_no_x11_forwarding_flag = 1; + int cert_no_pty_flag = 1; + int cert_no_user_rc = 1; + char *cert_forced_command = NULL; + int cert_source_address_done = 0; + + if (key_cert_is_legacy(k)) { + /* All options are in the one field for v00 certs */ + if (parse_option_list(buffer_ptr(&k->cert->critical), + buffer_len(&k->cert->critical), pw, + OPTIONS_CRITICAL|OPTIONS_EXTENSIONS, 1, + &cert_no_port_forwarding_flag, + &cert_no_agent_forwarding_flag, + &cert_no_x11_forwarding_flag, + &cert_no_pty_flag, + &cert_no_user_rc, + &cert_forced_command, + &cert_source_address_done) == -1) + return -1; + } else { + /* Separate options and extensions for v01 certs */ + if (parse_option_list(buffer_ptr(&k->cert->critical), + buffer_len(&k->cert->critical), pw, + OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL, + &cert_forced_command, + &cert_source_address_done) == -1) + return -1; + if (parse_option_list(buffer_ptr(&k->cert->extensions), + buffer_len(&k->cert->extensions), pw, + OPTIONS_EXTENSIONS, 1, + &cert_no_port_forwarding_flag, + &cert_no_agent_forwarding_flag, + &cert_no_x11_forwarding_flag, + &cert_no_pty_flag, + &cert_no_user_rc, + NULL, NULL) == -1) + return -1; + } + + no_port_forwarding_flag |= cert_no_port_forwarding_flag; + no_agent_forwarding_flag |= cert_no_agent_forwarding_flag; + no_x11_forwarding_flag |= cert_no_x11_forwarding_flag; + no_pty_flag |= cert_no_pty_flag; + no_user_rc |= cert_no_user_rc; + /* CA-specified forced command supersedes key option */ + if (cert_forced_command != NULL) { + if (forced_command != NULL) + xfree(forced_command); + forced_command = cert_forced_command; + } + return 0; +} + diff --git a/crypto/openssh/auth-options.h b/crypto/openssh/auth-options.h index 14488f72d8..7455c94546 100644 --- a/crypto/openssh/auth-options.h +++ b/crypto/openssh/auth-options.h @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-options.h,v 1.17 2008/03/26 21:28:14 djm Exp $ */ +/* $OpenBSD: auth-options.h,v 1.20 2010/05/07 11:30:29 djm Exp $ */ /* * Author: Tatu Ylonen @@ -30,8 +30,11 @@ extern int no_user_rc; extern char *forced_command; extern struct envstring *custom_environment; extern int forced_tun_device; +extern int key_is_cert_authority; +extern char *authorized_principals; int auth_parse_options(struct passwd *, char *, char *, u_long); void auth_clear_options(void); +int auth_cert_options(Key *, struct passwd *); #endif diff --git a/crypto/openssh/auth-rh-rsa.c b/crypto/openssh/auth-rh-rsa.c index eca7502758..b21a0f4a2f 100644 --- a/crypto/openssh/auth-rh-rsa.c +++ b/crypto/openssh/auth-rh-rsa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-rh-rsa.c,v 1.42 2006/08/03 03:34:41 deraadt Exp $ */ +/* $OpenBSD: auth-rh-rsa.c,v 1.43 2010/03/04 10:36:03 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -44,6 +44,9 @@ auth_rhosts_rsa_key_allowed(struct passwd *pw, char *cuser, char *chost, { HostStatus host_status; + if (auth_key_is_revoked(client_host_key)) + return 0; + /* Check if we would accept it using rhosts authentication. */ if (!auth_rhosts(pw, cuser)) return 0; diff --git a/crypto/openssh/auth-rhosts.c b/crypto/openssh/auth-rhosts.c index 5c12967016..06ae7f0b9e 100644 --- a/crypto/openssh/auth-rhosts.c +++ b/crypto/openssh/auth-rhosts.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-rhosts.c,v 1.43 2008/06/13 14:18:51 dtucker Exp $ */ +/* $OpenBSD: auth-rhosts.c,v 1.44 2010/03/07 11:57:13 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -317,11 +317,5 @@ int auth_rhosts2(struct passwd *pw, const char *client_user, const char *hostname, const char *ipaddr) { - int ret; - - auth_debug_reset(); - ret = auth_rhosts2_raw(pw, client_user, hostname, ipaddr); - if (!use_privsep) - auth_debug_send(); - return ret; + return auth_rhosts2_raw(pw, client_user, hostname, ipaddr); } diff --git a/crypto/openssh/auth-rsa.c b/crypto/openssh/auth-rsa.c index bf54620760..56702d130f 100644 --- a/crypto/openssh/auth-rsa.c +++ b/crypto/openssh/auth-rsa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-rsa.c,v 1.73 2008/07/02 12:03:51 dtucker Exp $ */ +/* $OpenBSD: auth-rsa.c,v 1.78 2010/07/13 23:13:16 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -34,11 +34,11 @@ #include "uidswap.h" #include "match.h" #include "buffer.h" -#include "auth-options.h" #include "pathnames.h" #include "log.h" #include "servconf.h" #include "key.h" +#include "auth-options.h" #include "hostfile.h" #include "auth.h" #ifdef GSSAPI @@ -94,6 +94,9 @@ auth_rsa_verify_response(Key *key, BIGNUM *challenge, u_char response[16]) MD5_CTX md; int len; + if (auth_key_is_revoked(key)) + return 0; + /* don't allow short keys */ if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { error("auth_rsa_verify_response: RSA modulus too small: %d < minimum %d bits", @@ -113,7 +116,7 @@ auth_rsa_verify_response(Key *key, BIGNUM *challenge, u_char response[16]) MD5_Final(mdbuf, &md); /* Verify that the response is the original challenge. */ - if (memcmp(response, mdbuf, 16) != 0) { + if (timingsafe_bcmp(response, mdbuf, 16) != 0) { /* Wrong answer. */ return (0); } @@ -253,7 +256,8 @@ auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) */ if (!auth_parse_options(pw, key_options, file, linenum)) continue; - + if (key_is_cert_authority) + continue; /* break out, this key is allowed */ allowed = 1; break; diff --git a/crypto/openssh/auth.c b/crypto/openssh/auth.c index 3585daadc1..dba1e6555e 100644 --- a/crypto/openssh/auth.c +++ b/crypto/openssh/auth.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.c,v 1.80 2008/11/04 07:58:09 djm Exp $ */ +/* $OpenBSD: auth.c,v 1.89 2010/08/04 05:42:47 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -69,6 +69,7 @@ #ifdef GSSAPI #include "ssh-gss.h" #endif +#include "authfile.h" #include "monitor_wrap.h" /* import */ @@ -95,7 +96,6 @@ allowed_user(struct passwd * pw) { struct stat st; const char *hostname = NULL, *ipaddr = NULL, *passwd = NULL; - char *shell; u_int i; #ifdef USE_SHADOW struct spwd *spw = NULL; @@ -143,7 +143,7 @@ allowed_user(struct passwd * pw) locked = 1; #endif #ifdef USE_LIBIAF - free(passwd); + free((void *) passwd); #endif /* USE_LIBIAF */ if (locked) { logit("User %.100s not allowed because account is locked", @@ -153,22 +153,28 @@ allowed_user(struct passwd * pw) } /* - * Get the shell from the password data. An empty shell field is - * legal, and means /bin/sh. + * Deny if shell does not exist or is not executable unless we + * are chrooting. */ - shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; - - /* deny if shell does not exists or is not executable */ - if (stat(shell, &st) != 0) { - logit("User %.100s not allowed because shell %.100s does not exist", - pw->pw_name, shell); - return 0; - } - if (S_ISREG(st.st_mode) == 0 || - (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) { - logit("User %.100s not allowed because shell %.100s is not executable", - pw->pw_name, shell); - return 0; + if (options.chroot_directory == NULL || + strcasecmp(options.chroot_directory, "none") == 0) { + char *shell = xstrdup((pw->pw_shell[0] == '\0') ? + _PATH_BSHELL : pw->pw_shell); /* empty = /bin/sh */ + + if (stat(shell, &st) != 0) { + logit("User %.100s not allowed because shell %.100s " + "does not exist", pw->pw_name, shell); + xfree(shell); + return 0; + } + if (S_ISREG(st.st_mode) == 0 || + (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) { + logit("User %.100s not allowed because shell %.100s " + "is not executable", pw->pw_name, shell); + xfree(shell); + return 0; + } + xfree(shell); } if (options.num_deny_users > 0 || options.num_allow_users > 0 || @@ -360,6 +366,14 @@ authorized_keys_file2(struct passwd *pw) return expand_authorized_keys(options.authorized_keys_file2, pw); } +char * +authorized_principals_file(struct passwd *pw) +{ + if (options.authorized_principals_file == NULL) + return NULL; + return expand_authorized_keys(options.authorized_principals_file, pw); +} + /* return ok if key exists in sysfile or userfile */ HostStatus check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host, @@ -371,7 +385,7 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host, HostStatus host_status; /* Check if we know the host and its host key. */ - found = key_new(key->type); + found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type); host_status = check_host_in_hostfile(sysfile, host, key, found, NULL); if (host_status != HOST_OK && userfile != NULL) { @@ -383,6 +397,8 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host, logit("Authentication refused for %.100s: " "bad owner or modes for %.200s", pw->pw_name, user_hostfile); + auth_debug_add("Ignored %.200s: bad ownership or modes", + user_hostfile); } else { temporarily_use_uid(pw); host_status = check_host_in_hostfile(user_hostfile, @@ -455,7 +471,7 @@ secure_filename(FILE *f, const char *file, struct passwd *pw, return -1; } - /* If are passed the homedir then we can stop */ + /* If are past the homedir then we can stop */ if (comparehome && strcmp(homedir, buf) == 0) { debug3("secure_filename: terminating check at '%s'", buf); @@ -471,28 +487,29 @@ secure_filename(FILE *f, const char *file, struct passwd *pw, return 0; } -FILE * -auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes) +static FILE * +auth_openfile(const char *file, struct passwd *pw, int strict_modes, + int log_missing, char *file_type) { 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) + if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) { + if (log_missing || errno != ENOENT) + debug("Could not open %s '%s': %s", file_type, file, + strerror(errno)); 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); + logit("User %s %s %s is not a regular file", + pw->pw_name, file_type, file); close(fd); return NULL; } @@ -505,12 +522,27 @@ auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes) secure_filename(f, file, pw, line, sizeof(line)) != 0) { fclose(f); logit("Authentication refused: %s", line); + auth_debug_add("Ignored %s: %s", file_type, line); return NULL; } return f; } + +FILE * +auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes) +{ + return auth_openfile(file, pw, strict_modes, 1, "authorized keys"); +} + +FILE * +auth_openprincipals(const char *file, struct passwd *pw, int strict_modes) +{ + return auth_openfile(file, pw, strict_modes, 0, + "authorized principals"); +} + struct passwd * getpwnamallow(const char *user) { @@ -525,7 +557,28 @@ getpwnamallow(const char *user) parse_server_match_config(&options, user, get_canonical_hostname(options.use_dns), get_remote_ipaddr()); +#if defined(_AIX) && defined(HAVE_SETAUTHDB) + aix_setauthdb(user); +#endif + pw = getpwnam(user); + +#if defined(_AIX) && defined(HAVE_SETAUTHDB) + aix_restoreauthdb(); +#endif +#ifdef HAVE_CYGWIN + /* + * Windows usernames are case-insensitive. To avoid later problems + * when trying to match the username, the user is only allowed to + * login if the username is given in the same case as stored in the + * user database. + */ + if (pw != NULL && strcmp(user, pw->pw_name) != 0) { + logit("Login name %.100s does not match stored username %.100s", + user, pw->pw_name); + pw = NULL; + } +#endif if (pw == NULL) { logit("Invalid user %.100s from %.100s", user, get_remote_ipaddr()); @@ -560,6 +613,35 @@ getpwnamallow(const char *user) return (NULL); } +/* Returns 1 if key is revoked by revoked_keys_file, 0 otherwise */ +int +auth_key_is_revoked(Key *key) +{ + char *key_fp; + + if (options.revoked_keys_file == NULL) + return 0; + + switch (key_in_file(key, options.revoked_keys_file, 0)) { + case 0: + /* key not revoked */ + return 0; + case -1: + /* Error opening revoked_keys_file: refuse all keys */ + error("Revoked keys file is unreadable: refusing public key " + "authentication"); + return 1; + case 1: + /* Key revoked */ + key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + error("WARNING: authentication attempt with a revoked " + "%s key %s ", key_type(key), key_fp); + xfree(key_fp); + return 1; + } + fatal("key_in_file returned junk"); +} + void auth_debug_add(const char *fmt,...) { diff --git a/crypto/openssh/auth.h b/crypto/openssh/auth.h index 3a70f44213..77317aee6d 100644 --- a/crypto/openssh/auth.h +++ b/crypto/openssh/auth.h @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.h,v 1.62 2008/11/04 08:22:12 djm Exp $ */ +/* $OpenBSD: auth.h,v 1.66 2010/05/07 11:30:29 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -169,8 +169,11 @@ void abandon_challenge_response(Authctxt *); char *authorized_keys_file(struct passwd *); char *authorized_keys_file2(struct passwd *); +char *authorized_principals_file(struct passwd *); FILE *auth_openkeyfile(const char *, struct passwd *, int); +FILE *auth_openprincipals(const char *, struct passwd *, int); +int auth_key_is_revoked(Key *); HostStatus check_key_in_hostfiles(struct passwd *, Key *, const char *, @@ -178,7 +181,8 @@ check_key_in_hostfiles(struct passwd *, Key *, const char *, /* hostkey handling */ Key *get_hostkey_by_index(int); -Key *get_hostkey_by_type(int); +Key *get_hostkey_public_by_type(int); +Key *get_hostkey_private_by_type(int); int get_hostkey_index(Key *); int ssh1_session_key(BIGNUM *); diff --git a/crypto/openssh/auth1.c b/crypto/openssh/auth1.c index 1801661fdd..bf442dbf6e 100644 --- a/crypto/openssh/auth1.c +++ b/crypto/openssh/auth1.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth1.c,v 1.73 2008/07/04 23:30:16 djm Exp $ */ +/* $OpenBSD: auth1.c,v 1.74 2010/06/25 08:46:17 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -244,7 +244,7 @@ do_authloop(Authctxt *authctxt) authctxt->valid ? "" : "invalid user ", authctxt->user); /* If the user has no password, accept authentication immediately. */ - if (options.password_authentication && + if (options.permit_empty_passwd && options.password_authentication && #ifdef KRB5 (!options.kerberos_authentication || options.kerberos_or_local_passwd) && #endif diff --git a/crypto/openssh/auth2-hostbased.c b/crypto/openssh/auth2-hostbased.c index 041051c53c..cdf442f97c 100644 --- a/crypto/openssh/auth2-hostbased.c +++ b/crypto/openssh/auth2-hostbased.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-hostbased.c,v 1.12 2008/07/17 08:51:07 djm Exp $ */ +/* $OpenBSD: auth2-hostbased.c,v 1.14 2010/08/04 05:42:47 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -141,9 +141,13 @@ int hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, Key *key) { - const char *resolvedname, *ipaddr, *lookup; + const char *resolvedname, *ipaddr, *lookup, *reason; HostStatus host_status; int len; + char *fp; + + if (auth_key_is_revoked(key)) + return 0; resolvedname = get_canonical_hostname(options.use_dns); ipaddr = get_remote_ipaddr(); @@ -171,16 +175,40 @@ hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, } debug2("userauth_hostbased: access allowed by auth_rhosts2"); + if (key_is_cert(key) && + key_cert_check_authority(key, 1, 0, lookup, &reason)) { + error("%s", reason); + auth_debug_add("%s", reason); + return 0; + } + host_status = check_key_in_hostfiles(pw, key, lookup, _PATH_SSH_SYSTEM_HOSTFILE, options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE); /* backward compat if no key has been found. */ - if (host_status == HOST_NEW) + if (host_status == HOST_NEW) { host_status = check_key_in_hostfiles(pw, key, lookup, _PATH_SSH_SYSTEM_HOSTFILE2, options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE2); + } + + if (host_status == HOST_OK) { + if (key_is_cert(key)) { + fp = key_fingerprint(key->cert->signature_key, + SSH_FP_MD5, SSH_FP_HEX); + verbose("Accepted certificate ID \"%s\" signed by " + "%s CA %s from %s@%s", key->cert->key_id, + key_type(key->cert->signature_key), fp, + cuser, lookup); + } else { + fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + verbose("Accepted %s public key %s from %s@%s", + key_type(key), fp, cuser, lookup); + } + xfree(fp); + } return (host_status == HOST_OK); } diff --git a/crypto/openssh/auth2-none.c b/crypto/openssh/auth2-none.c index 08f2f935fe..c8c6c74a93 100644 --- a/crypto/openssh/auth2-none.c +++ b/crypto/openssh/auth2-none.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-none.c,v 1.15 2008/07/02 12:36:39 djm Exp $ */ +/* $OpenBSD: auth2-none.c,v 1.16 2010/06/25 08:46:17 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -61,7 +61,7 @@ userauth_none(Authctxt *authctxt) { none_enabled = 0; packet_check_eom(); - if (options.password_authentication) + if (options.permit_empty_passwd && options.password_authentication) return (PRIVSEP(auth_password(authctxt, ""))); return (0); } diff --git a/crypto/openssh/auth2-pubkey.c b/crypto/openssh/auth2-pubkey.c index 2886f1275f..35cf79c9f7 100644 --- a/crypto/openssh/auth2-pubkey.c +++ b/crypto/openssh/auth2-pubkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-pubkey.c,v 1.19 2008/07/03 21:46:58 otto Exp $ */ +/* $OpenBSD: auth2-pubkey.c,v 1.26 2010/06/29 23:16:46 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include "xmalloc.h" @@ -54,6 +56,8 @@ #endif #include "monitor_wrap.h" #include "misc.h" +#include "authfile.h" +#include "match.h" /* import */ extern ServerOptions options; @@ -173,11 +177,89 @@ done: return authenticated; } +static int +match_principals_option(const char *principal_list, struct KeyCert *cert) +{ + char *result; + u_int i; + + /* XXX percent_expand() sequences for authorized_principals? */ + + for (i = 0; i < cert->nprincipals; i++) { + if ((result = match_list(cert->principals[i], + principal_list, NULL)) != NULL) { + debug3("matched principal from key options \"%.100s\"", + result); + xfree(result); + return 1; + } + } + return 0; +} + +static int +match_principals_file(char *file, struct passwd *pw, struct KeyCert *cert) +{ + FILE *f; + char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts; + u_long linenum = 0; + u_int i; + + temporarily_use_uid(pw); + debug("trying authorized principals file %s", file); + if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) { + restore_uid(); + return 0; + } + while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { + /* Skip leading whitespace. */ + for (cp = line; *cp == ' ' || *cp == '\t'; cp++) + ; + /* Skip blank and comment lines. */ + if ((ep = strchr(cp, '#')) != NULL) + *ep = '\0'; + if (!*cp || *cp == '\n') + continue; + /* Trim trailing whitespace. */ + ep = cp + strlen(cp) - 1; + while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t')) + *ep-- = '\0'; + /* + * If the line has internal whitespace then assume it has + * key options. + */ + line_opts = NULL; + if ((ep = strrchr(cp, ' ')) != NULL || + (ep = strrchr(cp, '\t')) != NULL) { + for (; *ep == ' ' || *ep == '\t'; ep++) + ;; + line_opts = cp; + cp = ep; + } + for (i = 0; i < cert->nprincipals; i++) { + if (strcmp(cp, cert->principals[i]) == 0) { + debug3("matched principal from file \"%.100s\"", + cert->principals[i]); + if (auth_parse_options(pw, line_opts, + file, linenum) != 1) + continue; + fclose(f); + restore_uid(); + return 1; + } + } + } + fclose(f); + restore_uid(); + return 0; +} + /* return 1 if user allows given key */ static int user_key_allowed2(struct passwd *pw, Key *key, char *file) { char line[SSH_MAX_PUBKEY_BYTES]; + const char *reason; int found_key = 0; FILE *f; u_long linenum = 0; @@ -196,11 +278,13 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) } found_key = 0; - found = key_new(key->type); + found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type); while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { char *cp, *key_options = NULL; + auth_clear_options(); + /* Skip leading whitespace, empty and comment lines. */ for (cp = line; *cp == ' ' || *cp == '\t'; cp++) ; @@ -227,8 +311,54 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) continue; } } - if (key_equal(found, key) && - auth_parse_options(pw, key_options, file, linenum) == 1) { + if (key_is_cert(key)) { + if (!key_equal(found, key->cert->signature_key)) + continue; + if (auth_parse_options(pw, key_options, file, + linenum) != 1) + continue; + if (!key_is_cert_authority) + continue; + fp = key_fingerprint(found, SSH_FP_MD5, + SSH_FP_HEX); + debug("matching CA found: file %s, line %lu, %s %s", + file, linenum, key_type(found), fp); + /* + * If the user has specified a list of principals as + * a key option, then prefer that list to matching + * their username in the certificate principals list. + */ + if (authorized_principals != NULL && + !match_principals_option(authorized_principals, + key->cert)) { + reason = "Certificate does not contain an " + "authorized principal"; + fail_reason: + xfree(fp); + error("%s", reason); + auth_debug_add("%s", reason); + continue; + } + if (key_cert_check_authority(key, 0, 0, + authorized_principals == NULL ? pw->pw_name : NULL, + &reason) != 0) + goto fail_reason; + if (auth_cert_options(key, pw) != 0) { + xfree(fp); + continue; + } + verbose("Accepted certificate ID \"%s\" " + "signed by %s CA %s via %s", key->cert->key_id, + key_type(found), fp, file); + xfree(fp); + found_key = 1; + break; + } else if (key_equal(found, key)) { + if (auth_parse_options(pw, key_options, file, + linenum) != 1) + continue; + if (key_is_cert_authority) + continue; found_key = 1; debug("matching key found: file %s, line %lu", file, linenum); @@ -247,6 +377,61 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) return found_key; } +/* Authenticate a certificate key against TrustedUserCAKeys */ +static int +user_cert_trusted_ca(struct passwd *pw, Key *key) +{ + char *ca_fp, *principals_file = NULL; + const char *reason; + int ret = 0; + + if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL) + return 0; + + ca_fp = key_fingerprint(key->cert->signature_key, + SSH_FP_MD5, SSH_FP_HEX); + + if (key_in_file(key->cert->signature_key, + options.trusted_user_ca_keys, 1) != 1) { + debug2("%s: CA %s %s is not listed in %s", __func__, + key_type(key->cert->signature_key), ca_fp, + options.trusted_user_ca_keys); + goto out; + } + /* + * If AuthorizedPrincipals is in use, then compare the certificate + * principals against the names in that file rather than matching + * against the username. + */ + if ((principals_file = authorized_principals_file(pw)) != NULL) { + if (!match_principals_file(principals_file, pw, key->cert)) { + reason = "Certificate does not contain an " + "authorized principal"; + fail_reason: + error("%s", reason); + auth_debug_add("%s", reason); + goto out; + } + } + if (key_cert_check_authority(key, 0, 1, + principals_file == NULL ? pw->pw_name : NULL, &reason) != 0) + goto fail_reason; + if (auth_cert_options(key, pw) != 0) + goto out; + + verbose("Accepted certificate ID \"%s\" signed by %s CA %s via %s", + key->cert->key_id, key_type(key->cert->signature_key), ca_fp, + options.trusted_user_ca_keys); + ret = 1; + + out: + if (principals_file != NULL) + xfree(principals_file); + if (ca_fp != NULL) + xfree(ca_fp); + return ret; +} + /* check whether given key is in .ssh/authorized_keys* */ int user_key_allowed(struct passwd *pw, Key *key) @@ -254,6 +439,15 @@ user_key_allowed(struct passwd *pw, Key *key) int success; char *file; + if (auth_key_is_revoked(key)) + return 0; + if (key_is_cert(key) && auth_key_is_revoked(key->cert->signature_key)) + return 0; + + success = user_cert_trusted_ca(pw, key); + if (success) + return success; + file = authorized_keys_file(pw); success = user_key_allowed2(pw, key, file); xfree(file); diff --git a/crypto/openssh/authfd.c b/crypto/openssh/authfd.c index 61faad1231..739722fbfb 100644 --- a/crypto/openssh/authfd.c +++ b/crypto/openssh/authfd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: authfd.c,v 1.80 2006/08/03 03:34:41 deraadt Exp $ */ +/* $OpenBSD: authfd.c,v 1.83 2010/04/16 01:47:26 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -483,6 +483,17 @@ ssh_encode_identity_ssh2(Buffer *b, Key *key, const char *comment) buffer_put_bignum2(b, key->rsa->p); buffer_put_bignum2(b, key->rsa->q); break; + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0) + fatal("%s: no cert/certblob", __func__); + buffer_put_string(b, buffer_ptr(&key->cert->certblob), + buffer_len(&key->cert->certblob)); + buffer_put_bignum2(b, key->rsa->d); + buffer_put_bignum2(b, key->rsa->iqmp); + buffer_put_bignum2(b, key->rsa->p); + buffer_put_bignum2(b, key->rsa->q); + break; case KEY_DSA: buffer_put_bignum2(b, key->dsa->p); buffer_put_bignum2(b, key->dsa->q); @@ -490,6 +501,14 @@ ssh_encode_identity_ssh2(Buffer *b, Key *key, const char *comment) buffer_put_bignum2(b, key->dsa->pub_key); buffer_put_bignum2(b, key->dsa->priv_key); break; + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0) + fatal("%s: no cert/certblob", __func__); + buffer_put_string(b, buffer_ptr(&key->cert->certblob), + buffer_len(&key->cert->certblob)); + buffer_put_bignum2(b, key->dsa->priv_key); + break; } buffer_put_cstring(b, comment); } @@ -517,7 +536,11 @@ ssh_add_identity_constrained(AuthenticationConnection *auth, Key *key, ssh_encode_identity_rsa1(&msg, key->rsa, comment); break; case KEY_RSA: + case KEY_RSA_CERT: + case KEY_RSA_CERT_V00: case KEY_DSA: + case KEY_DSA_CERT: + case KEY_DSA_CERT_V00: type = constrained ? SSH2_AGENTC_ADD_ID_CONSTRAINED : SSH2_AGENTC_ADD_IDENTITY; @@ -545,12 +568,6 @@ ssh_add_identity_constrained(AuthenticationConnection *auth, Key *key, return decode_reply(type); } -int -ssh_add_identity(AuthenticationConnection *auth, Key *key, const char *comment) -{ - return ssh_add_identity_constrained(auth, key, comment, 0, 0); -} - /* * Removes an identity from the authentication server. This call is not * meant to be used by normal applications. @@ -571,7 +588,8 @@ ssh_remove_identity(AuthenticationConnection *auth, Key *key) buffer_put_int(&msg, BN_num_bits(key->rsa->n)); buffer_put_bignum(&msg, key->rsa->e); buffer_put_bignum(&msg, key->rsa->n); - } else if (key->type == KEY_DSA || key->type == KEY_RSA) { + } else if (key_type_plain(key->type) == KEY_DSA || + key_type_plain(key->type) == KEY_RSA) { key_to_blob(key, &blob, &blen); buffer_put_char(&msg, SSH2_AGENTC_REMOVE_IDENTITY); buffer_put_string(&msg, blob, blen); diff --git a/crypto/openssh/authfd.h b/crypto/openssh/authfd.h index 3da2561127..2582a27aa5 100644 --- a/crypto/openssh/authfd.h +++ b/crypto/openssh/authfd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: authfd.h,v 1.36 2006/08/03 03:34:41 deraadt Exp $ */ +/* $OpenBSD: authfd.h,v 1.37 2009/08/27 17:44:52 djm Exp $ */ /* * Author: Tatu Ylonen @@ -75,7 +75,6 @@ void ssh_close_authentication_connection(AuthenticationConnection *); int ssh_get_num_identities(AuthenticationConnection *, int); Key *ssh_get_first_identity(AuthenticationConnection *, char **, int); Key *ssh_get_next_identity(AuthenticationConnection *, char **, int); -int ssh_add_identity(AuthenticationConnection *, Key *, const char *); int ssh_add_identity_constrained(AuthenticationConnection *, Key *, const char *, u_int, u_int); int ssh_remove_identity(AuthenticationConnection *, Key *); diff --git a/crypto/openssh/authfile.c b/crypto/openssh/authfile.c index 735c647809..2bd887845c 100644 --- a/crypto/openssh/authfile.c +++ b/crypto/openssh/authfile.c @@ -1,4 +1,4 @@ -/* $OpenBSD: authfile.c,v 1.76 2006/08/03 03:34:41 deraadt Exp $ */ +/* $OpenBSD: authfile.c,v 1.82 2010/08/04 05:49:22 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -47,6 +47,9 @@ #include #include +/* compatibility with old or broken OpenSSL versions */ +#include "openbsd-compat/openssl-compat.h" + #include #include #include @@ -184,7 +187,11 @@ key_save_private_pem(Key *key, const char *filename, const char *_passphrase, int success = 0; int len = strlen(_passphrase); u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL; +#if (OPENSSL_VERSION_NUMBER < 0x00907000L) const EVP_CIPHER *cipher = (len > 0) ? EVP_des_ede3_cbc() : NULL; +#else + const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL; +#endif if (len > 0 && len <= 4) { error("passphrase too short: have %d bytes, need > 4", len); @@ -552,8 +559,13 @@ key_load_private_type(int type, const char *filename, const char *passphrase, int fd; fd = open(filename, O_RDONLY); - if (fd < 0) + if (fd < 0) { + debug("could not open key file '%s': %s", filename, + strerror(errno)); + if (perm_ok != NULL) + *perm_ok = 0; return NULL; + } if (!key_perm_ok(fd, filename)) { if (perm_ok != NULL) *perm_ok = 0; @@ -588,8 +600,11 @@ key_load_private(const char *filename, const char *passphrase, int fd; fd = open(filename, O_RDONLY); - if (fd < 0) + if (fd < 0) { + debug("could not open key file '%s': %s", filename, + strerror(errno)); return NULL; + } if (!key_perm_ok(fd, filename)) { error("bad permissions: ignore key: %s", filename); close(fd); @@ -677,3 +692,125 @@ key_load_public(const char *filename, char **commentp) key_free(pub); return NULL; } + +/* Load the certificate associated with the named private key */ +Key * +key_load_cert(const char *filename) +{ + Key *pub; + char *file; + + pub = key_new(KEY_UNSPEC); + xasprintf(&file, "%s-cert.pub", filename); + if (key_try_load_public(pub, file, NULL) == 1) { + xfree(file); + return pub; + } + xfree(file); + key_free(pub); + return NULL; +} + +/* Load private key and certificate */ +Key * +key_load_private_cert(int type, const char *filename, const char *passphrase, + int *perm_ok) +{ + Key *key, *pub; + + switch (type) { + case KEY_RSA: + case KEY_DSA: + break; + default: + error("%s: unsupported key type", __func__); + return NULL; + } + + if ((key = key_load_private_type(type, filename, + passphrase, NULL, perm_ok)) == NULL) + return NULL; + + if ((pub = key_load_cert(filename)) == NULL) { + key_free(key); + return NULL; + } + + /* Make sure the private key matches the certificate */ + if (key_equal_public(key, pub) == 0) { + error("%s: certificate does not match private key %s", + __func__, filename); + } else if (key_to_certified(key, key_cert_is_legacy(pub)) != 0) { + error("%s: key_to_certified failed", __func__); + } else { + key_cert_copy(pub, key); + key_free(pub); + return key; + } + + key_free(key); + key_free(pub); + return NULL; +} + +/* + * Returns 1 if the specified "key" is listed in the file "filename", + * 0 if the key is not listed or -1 on error. + * If strict_type is set then the key type must match exactly, + * otherwise a comparison that ignores certficiate data is performed. + */ +int +key_in_file(Key *key, const char *filename, int strict_type) +{ + FILE *f; + char line[SSH_MAX_PUBKEY_BYTES]; + char *cp; + u_long linenum = 0; + int ret = 0; + Key *pub; + int (*key_compare)(const Key *, const Key *) = strict_type ? + key_equal : key_equal_public; + + if ((f = fopen(filename, "r")) == NULL) { + if (errno == ENOENT) { + debug("%s: keyfile \"%s\" missing", __func__, filename); + return 0; + } else { + error("%s: could not open keyfile \"%s\": %s", __func__, + filename, strerror(errno)); + return -1; + } + } + + while (read_keyfile_line(f, filename, line, sizeof(line), + &linenum) != -1) { + cp = line; + + /* Skip leading whitespace. */ + for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) + ; + + /* Skip comments and empty lines */ + switch (*cp) { + case '#': + case '\n': + case '\0': + continue; + } + + pub = key_new(KEY_UNSPEC); + if (key_read(pub, &cp) != 1) { + key_free(pub); + continue; + } + if (key_compare(key, pub)) { + ret = 1; + key_free(pub); + break; + } + key_free(pub); + } + fclose(f); + return ret; +} + diff --git a/crypto/openssh/authfile.h b/crypto/openssh/authfile.h index a6c74934d6..6745dc062b 100644 --- a/crypto/openssh/authfile.h +++ b/crypto/openssh/authfile.h @@ -1,4 +1,4 @@ -/* $OpenBSD: authfile.h,v 1.13 2006/04/25 08:02:27 dtucker Exp $ */ +/* $OpenBSD: authfile.h,v 1.15 2010/08/04 05:42:47 djm Exp $ */ /* * Author: Tatu Ylonen @@ -16,11 +16,14 @@ #define AUTHFILE_H int key_save_private(Key *, const char *, const char *, const char *); +Key *key_load_cert(const char *); Key *key_load_public(const char *, char **); Key *key_load_public_type(int, const char *, char **); Key *key_load_private(const char *, const char *, char **); +Key *key_load_private_cert(int, const char *, const char *, int *); Key *key_load_private_type(int, const char *, const char *, char **, int *); Key *key_load_private_pem(int, int, const char *, char **); int key_perm_ok(int, const char *); +int key_in_file(Key *, const char *, int); #endif diff --git a/crypto/openssh/bufaux.c b/crypto/openssh/bufaux.c index cd9a35dedc..854fd510a9 100644 --- a/crypto/openssh/bufaux.c +++ b/crypto/openssh/bufaux.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bufaux.c,v 1.46 2008/06/10 23:21:34 dtucker Exp $ */ +/* $OpenBSD: bufaux.c,v 1.49 2010/03/26 03:13:17 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -84,7 +84,8 @@ buffer_get_int_ret(u_int *ret, Buffer *buffer) if (buffer_get_ret(buffer, (char *) buf, 4) == -1) return (-1); - *ret = get_u32(buf); + if (ret != NULL) + *ret = get_u32(buf); return (0); } @@ -106,7 +107,8 @@ buffer_get_int64_ret(u_int64_t *ret, Buffer *buffer) if (buffer_get_ret(buffer, (char *) buf, 8) == -1) return (-1); - *ret = get_u64(buf); + if (ret != NULL) + *ret = get_u64(buf); return (0); } @@ -166,7 +168,10 @@ buffer_get_string_ret(Buffer *buffer, u_int *length_ptr) u_int len; /* Get the length. */ - len = buffer_get_int(buffer); + if (buffer_get_int_ret(&len, buffer) != 0) { + error("buffer_get_string_ret: cannot extract length"); + return (NULL); + } if (len > 256 * 1024) { error("buffer_get_string_ret: bad string length %u", len); return (NULL); @@ -198,14 +203,17 @@ buffer_get_string(Buffer *buffer, u_int *length_ptr) } void * -buffer_get_string_ptr(Buffer *buffer, u_int *length_ptr) +buffer_get_string_ptr_ret(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); + if (buffer_get_int_ret(&len, buffer) != 0) + return NULL; + if (len > 256 * 1024) { + error("buffer_get_string_ptr: bad string length %u", len); + return NULL; + } ptr = buffer_ptr(buffer); buffer_consume(buffer, len); if (length_ptr) @@ -213,6 +221,16 @@ buffer_get_string_ptr(Buffer *buffer, u_int *length_ptr) return (ptr); } +void * +buffer_get_string_ptr(Buffer *buffer, u_int *length_ptr) +{ + void *ret; + + if ((ret = buffer_get_string_ptr_ret(buffer, length_ptr)) == NULL) + fatal("buffer_get_string_ptr: buffer error"); + return (ret); +} + /* * Stores and arbitrary binary string in the buffer. */ diff --git a/crypto/openssh/buffer.c b/crypto/openssh/buffer.c index e02e1e35c9..ae9700344d 100644 --- a/crypto/openssh/buffer.c +++ b/crypto/openssh/buffer.c @@ -1,4 +1,4 @@ -/* $OpenBSD: buffer.c,v 1.31 2006/08/03 03:34:41 deraadt Exp $ */ +/* $OpenBSD: buffer.c,v 1.32 2010/02/09 03:56:28 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -160,7 +160,7 @@ buffer_check_alloc(Buffer *buffer, u_int len) /* Returns the number of bytes of data in the buffer. */ u_int -buffer_len(Buffer *buffer) +buffer_len(const Buffer *buffer) { return buffer->end - buffer->offset; } @@ -228,7 +228,7 @@ buffer_consume_end(Buffer *buffer, u_int bytes) /* Returns a pointer to the first used byte in the buffer. */ void * -buffer_ptr(Buffer *buffer) +buffer_ptr(const Buffer *buffer) { return buffer->buf + buffer->offset; } @@ -236,7 +236,7 @@ buffer_ptr(Buffer *buffer) /* Dumps the contents of the buffer to stderr. */ void -buffer_dump(Buffer *buffer) +buffer_dump(const Buffer *buffer) { u_int i; u_char *ucp = buffer->buf; diff --git a/crypto/openssh/buffer.h b/crypto/openssh/buffer.h index d0f354ee7b..4ef4f80b35 100644 --- a/crypto/openssh/buffer.h +++ b/crypto/openssh/buffer.h @@ -1,4 +1,4 @@ -/* $OpenBSD: buffer.h,v 1.17 2008/05/08 06:59:01 markus Exp $ */ +/* $OpenBSD: buffer.h,v 1.19 2010/02/09 03:56:28 djm Exp $ */ /* * Author: Tatu Ylonen @@ -27,8 +27,8 @@ void buffer_init(Buffer *); void buffer_clear(Buffer *); void buffer_free(Buffer *); -u_int buffer_len(Buffer *); -void *buffer_ptr(Buffer *); +u_int buffer_len(const Buffer *); +void *buffer_ptr(const Buffer *); void buffer_append(Buffer *, const void *, u_int); void *buffer_append_space(Buffer *, u_int); @@ -40,7 +40,7 @@ void buffer_get(Buffer *, void *, u_int); void buffer_consume(Buffer *, u_int); void buffer_consume_end(Buffer *, u_int); -void buffer_dump(Buffer *); +void buffer_dump(const Buffer *); int buffer_get_ret(Buffer *, void *, u_int); int buffer_consume_ret(Buffer *, u_int); @@ -81,6 +81,7 @@ int buffer_get_short_ret(u_short *, Buffer *); int buffer_get_int_ret(u_int *, Buffer *); int buffer_get_int64_ret(u_int64_t *, Buffer *); void *buffer_get_string_ret(Buffer *, u_int *); +void *buffer_get_string_ptr_ret(Buffer *, u_int *); int buffer_get_char_ret(char *, Buffer *); #endif /* BUFFER_H */ diff --git a/crypto/openssh/canohost.c b/crypto/openssh/canohost.c index 22b19bb9fa..ef94d9155e 100644 --- a/crypto/openssh/canohost.c +++ b/crypto/openssh/canohost.c @@ -1,4 +1,4 @@ -/* $OpenBSD: canohost.c,v 1.65 2009/05/27 06:31:25 andreas Exp $ */ +/* $OpenBSD: canohost.c,v 1.66 2010/01/13 01:20:20 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -27,6 +27,7 @@ #include #include #include +#include #include "xmalloc.h" #include "packet.h" @@ -301,9 +302,22 @@ get_local_ipaddr(int sock) } char * -get_local_name(int sock) +get_local_name(int fd) { - return get_socket_address(sock, 0, NI_NAMEREQD); + char *host, myname[NI_MAXHOST]; + + /* Assume we were passed a socket */ + if ((host = get_socket_address(fd, 0, NI_NAMEREQD)) != NULL) + return host; + + /* Handle the case where we were passed a pipe */ + if (gethostname(myname, sizeof(myname)) == -1) { + verbose("get_local_name: gethostname: %s", strerror(errno)); + } else { + host = xstrdup(myname); + } + + return host; } void diff --git a/crypto/openssh/channels.c b/crypto/openssh/channels.c index e8b8aa07e1..1cd5004c47 100644 --- a/crypto/openssh/channels.c +++ b/crypto/openssh/channels.c @@ -1,4 +1,4 @@ -/* $OpenBSD: channels.c,v 1.296 2009/05/25 06:48:00 andreas Exp $ */ +/* $OpenBSD: channels.c,v 1.309 2010/08/05 13:08:42 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -53,6 +53,7 @@ #include #include +#include #include #include #include @@ -113,10 +114,10 @@ typedef struct { } ForwardPermission; /* List of all permitted host/port pairs to connect by the user. */ -static ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; +static ForwardPermission *permitted_opens = NULL; /* List of all permitted host/port pairs to connect by the admin. */ -static ForwardPermission permitted_adm_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; +static ForwardPermission *permitted_adm_opens = NULL; /* Number of permitted host/port pairs in the array permitted by the user. */ static int num_permitted_opens = 0; @@ -228,12 +229,16 @@ channel_register_fds(Channel *c, int rfd, int wfd, int efd, channel_max_fd = MAX(channel_max_fd, wfd); channel_max_fd = MAX(channel_max_fd, efd); - /* XXX set close-on-exec -markus */ + if (rfd != -1) + fcntl(rfd, F_SETFD, FD_CLOEXEC); + if (wfd != -1 && wfd != rfd) + fcntl(wfd, F_SETFD, FD_CLOEXEC); + if (efd != -1 && efd != rfd && efd != wfd) + fcntl(efd, F_SETFD, FD_CLOEXEC); c->rfd = rfd; c->wfd = wfd; c->sock = (rfd == wfd) ? rfd : -1; - c->ctl_fd = -1; /* XXX: set elsewhere */ c->efd = efd; c->extended_usage = extusage; @@ -322,6 +327,11 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd, c->output_filter = NULL; c->filter_ctx = NULL; c->filter_cleanup = NULL; + c->ctl_chan = -1; + c->mux_rcb = NULL; + c->mux_ctx = NULL; + c->mux_pause = 0; + c->delayed = 1; /* prevent call to channel_post handler */ TAILQ_INIT(&c->status_confirms); debug("channel %d: new [%s]", found, remote_name); return c; @@ -363,11 +373,10 @@ channel_close_fd(int *fdp) static void channel_close_fds(Channel *c) { - debug3("channel %d: close_fds r %d w %d e %d c %d", - c->self, c->rfd, c->wfd, c->efd, c->ctl_fd); + debug3("channel %d: close_fds r %d w %d e %d", + c->self, c->rfd, c->wfd, c->efd); channel_close_fd(&c->sock); - channel_close_fd(&c->ctl_fd); channel_close_fd(&c->rfd); channel_close_fd(&c->wfd); channel_close_fd(&c->efd); @@ -393,8 +402,6 @@ channel_free(Channel *c) if (c->sock != -1) shutdown(c->sock, SHUT_RDWR); - if (c->ctl_fd != -1) - shutdown(c->ctl_fd, SHUT_RDWR); channel_close_fds(c); buffer_free(&c->input); buffer_free(&c->output); @@ -516,6 +523,7 @@ channel_still_open(void) case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: + case SSH_CHANNEL_MUX_LISTENER: case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_AUTH_SOCKET: case SSH_CHANNEL_DYNAMIC: @@ -529,6 +537,7 @@ channel_still_open(void) case SSH_CHANNEL_OPENING: case SSH_CHANNEL_OPEN: case SSH_CHANNEL_X11_OPEN: + case SSH_CHANNEL_MUX_CLIENT: return 1; case SSH_CHANNEL_INPUT_DRAINING: case SSH_CHANNEL_OUTPUT_DRAINING: @@ -560,6 +569,8 @@ channel_find_open(void) case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: + case SSH_CHANNEL_MUX_LISTENER: + case SSH_CHANNEL_MUX_CLIENT: case SSH_CHANNEL_OPENING: case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_ZOMBIE: @@ -610,6 +621,8 @@ channel_open_message(void) case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_AUTH_SOCKET: case SSH_CHANNEL_ZOMBIE: + case SSH_CHANNEL_MUX_CLIENT: + case SSH_CHANNEL_MUX_LISTENER: continue; case SSH_CHANNEL_LARVAL: case SSH_CHANNEL_OPENING: @@ -620,12 +633,12 @@ channel_open_message(void) case SSH_CHANNEL_INPUT_DRAINING: case SSH_CHANNEL_OUTPUT_DRAINING: snprintf(buf, sizeof buf, - " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d cfd %d)\r\n", + " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d cc %d)\r\n", c->self, c->remote_name, c->type, c->remote_id, c->istate, buffer_len(&c->input), c->ostate, buffer_len(&c->output), - c->rfd, c->wfd, c->ctl_fd); + c->rfd, c->wfd, c->ctl_chan); buffer_append(&buffer, buf, strlen(buf)); continue; default: @@ -691,7 +704,7 @@ channel_register_status_confirm(int id, channel_confirm_cb *cb, } void -channel_register_open_confirm(int id, channel_callback_fn *fn, void *ctx) +channel_register_open_confirm(int id, channel_open_fn *fn, void *ctx) { Channel *c = channel_lookup(id); @@ -826,15 +839,13 @@ channel_pre_open(Channel *c, fd_set *readset, fd_set *writeset) if (c->extended_usage == CHAN_EXTENDED_WRITE && buffer_len(&c->extended) > 0) FD_SET(c->efd, writeset); - else if (!(c->flags & CHAN_EOF_SENT) && - c->extended_usage == CHAN_EXTENDED_READ && + else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) && + (c->extended_usage == CHAN_EXTENDED_READ || + c->extended_usage == CHAN_EXTENDED_IGNORE) && buffer_len(&c->extended) < c->remote_window) FD_SET(c->efd, readset); } /* XXX: What about efd? races? */ - if (compat20 && c->ctl_fd != -1 && - c->istate == CHAN_INPUT_OPEN && c->ostate == CHAN_OUTPUT_OPEN) - FD_SET(c->ctl_fd, readset); } /* ARGSUSED */ @@ -906,7 +917,7 @@ x11_open_helper(Buffer *b) } /* Check if authentication data matches our fake data. */ if (data_len != x11_fake_data_len || - memcmp(ucp + 12 + ((proto_len + 3) & ~3), + timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3), x11_fake_data, x11_fake_data_len) != 0) { debug2("X11 auth data does not match fake data."); return -1; @@ -979,6 +990,28 @@ channel_pre_x11_open(Channel *c, fd_set *readset, fd_set *writeset) } } +static void +channel_pre_mux_client(Channel *c, fd_set *readset, fd_set *writeset) +{ + if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause && + buffer_check_alloc(&c->input, CHAN_RBUF)) + FD_SET(c->rfd, readset); + if (c->istate == CHAN_INPUT_WAIT_DRAIN) { + /* clear buffer immediately (discard any partial packet) */ + buffer_clear(&c->input); + chan_ibuf_empty(c); + /* Start output drain. XXX just kill chan? */ + chan_rcvd_oclose(c); + } + if (c->ostate == CHAN_OUTPUT_OPEN || + c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { + if (buffer_len(&c->output) > 0) + FD_SET(c->wfd, writeset); + else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) + chan_obuf_empty(c); + } +} + /* try to decode a socks4 header */ /* ARGSUSED */ static int @@ -1210,6 +1243,30 @@ channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset) return 1; } +Channel * +channel_connect_stdio_fwd(const char *host_to_connect, u_short port_to_connect, + int in, int out) +{ + Channel *c; + + debug("channel_connect_stdio_fwd %s:%d", host_to_connect, + port_to_connect); + + c = channel_new("stdio-forward", SSH_CHANNEL_OPENING, in, out, + -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, + 0, "stdio-forward", /*nonblock*/0); + + c->path = xstrdup(host_to_connect); + c->host_port = port_to_connect; + c->listening_port = 0; + c->force_drain = 1; + + channel_register_fds(c, in, out, -1, 0, 1, 0); + port_open_helper(c, "direct-tcpip"); + + return c; +} + /* dynamic port forwarding */ static void channel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset) @@ -1219,7 +1276,6 @@ channel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset) int ret; have = buffer_len(&c->input); - c->delayed = 0; debug2("channel %d: pre_dynamic: have %d", c->self, have); /* buffer_dump(&c->input); */ /* check if the fixed size part of the packet is in buffer. */ @@ -1322,6 +1378,13 @@ port_open_helper(Channel *c, char *rtype) char *remote_ipaddr = get_peer_ipaddr(c->sock); int remote_port = get_peer_port(c->sock); + if (remote_port == -1) { + /* Fake addr/port to appease peers that validate it (Tectia) */ + xfree(remote_ipaddr); + remote_ipaddr = xstrdup("127.0.0.1"); + remote_port = 65535; + } + direct = (strcmp(rtype, "direct-tcpip") == 0); snprintf(buf, sizeof buf, @@ -1423,16 +1486,8 @@ channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) if (c->path != NULL) nc->path = xstrdup(c->path); - if (nextstate == SSH_CHANNEL_DYNAMIC) { - /* - * do not call the channel_post handler until - * this flag has been reset by a pre-handler. - * otherwise the FD_ISSET calls might overflow - */ - nc->delayed = 1; - } else { + if (nextstate != SSH_CHANNEL_DYNAMIC) port_open_helper(nc, rtype); - } } } @@ -1589,13 +1644,14 @@ channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) { struct termios tio; u_char *data = NULL, *buf; - u_int dlen; + u_int dlen, olen = 0; int len; /* Send buffered output data to the socket. */ if (c->wfd != -1 && FD_ISSET(c->wfd, writeset) && buffer_len(&c->output) > 0) { + olen = buffer_len(&c->output); if (c->output_filter != NULL) { if ((buf = c->output_filter(c, &data, &dlen)) == NULL) { debug2("channel %d: filter stops", c->self); @@ -1614,7 +1670,6 @@ channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) if (c->datagram) { /* ignore truncated writes, datagrams might get lost */ - c->local_consumed += dlen + 4; len = write(c->wfd, buf, dlen); xfree(data); if (len < 0 && (errno == EINTR || errno == EAGAIN || @@ -1627,7 +1682,7 @@ channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) chan_write_failed(c); return -1; } - return 1; + goto out; } #ifdef _AIX /* XXX: Later AIX versions can't push as much data to tty */ @@ -1669,10 +1724,10 @@ channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) } #endif buffer_consume(&c->output, len); - if (compat20 && len > 0) { - c->local_consumed += len; - } } + out: + if (compat20 && olen > 0) + c->local_consumed += olen - buffer_len(&c->output); return 1; } @@ -1702,7 +1757,9 @@ channel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset) buffer_consume(&c->extended, len); c->local_consumed += len; } - } else if (c->extended_usage == CHAN_EXTENDED_READ && + } else if (c->efd != -1 && + (c->extended_usage == CHAN_EXTENDED_READ || + c->extended_usage == CHAN_EXTENDED_IGNORE) && (c->detach_close || FD_ISSET(c->efd, readset))) { len = read(c->efd, buf, sizeof(buf)); debug2("channel %d: read %d from efd %d", @@ -1715,43 +1772,17 @@ channel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset) c->self, c->efd); channel_close_fd(&c->efd); } else { - buffer_append(&c->extended, buf, len); + if (c->extended_usage == CHAN_EXTENDED_IGNORE) { + debug3("channel %d: discard efd", + c->self); + } else + buffer_append(&c->extended, buf, len); } } } return 1; } -/* ARGSUSED */ -static int -channel_handle_ctl(Channel *c, fd_set *readset, fd_set *writeset) -{ - char buf[16]; - int len; - - /* 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 || errno == EWOULDBLOCK)) - return 1; - if (len <= 0) { - debug2("channel %d: ctl read<=0", c->self); - if (c->type != SSH_CHANNEL_OPEN) { - debug2("channel %d: not open", c->self); - chan_mark_dead(c); - return -1; - } else { - chan_read_failed(c); - chan_write_failed(c); - } - return -1; - } else - fatal("%s: unexpected data on ctl fd", __func__); - } - return 1; -} - static int channel_check_window(Channel *c) { @@ -1777,17 +1808,136 @@ channel_check_window(Channel *c) static void channel_post_open(Channel *c, fd_set *readset, fd_set *writeset) { - if (c->delayed) - return; channel_handle_rfd(c, readset, writeset); channel_handle_wfd(c, readset, writeset); if (!compat20) return; channel_handle_efd(c, readset, writeset); - channel_handle_ctl(c, readset, writeset); channel_check_window(c); } +static u_int +read_mux(Channel *c, u_int need) +{ + char buf[CHAN_RBUF]; + int len; + u_int rlen; + + if (buffer_len(&c->input) < need) { + rlen = need - buffer_len(&c->input); + len = read(c->rfd, buf, MIN(rlen, CHAN_RBUF)); + if (len <= 0) { + if (errno != EINTR && errno != EAGAIN) { + debug2("channel %d: ctl read<=0 rfd %d len %d", + c->self, c->rfd, len); + chan_read_failed(c); + return 0; + } + } else + buffer_append(&c->input, buf, len); + } + return buffer_len(&c->input); +} + +static void +channel_post_mux_client(Channel *c, fd_set *readset, fd_set *writeset) +{ + u_int need; + ssize_t len; + + if (!compat20) + fatal("%s: entered with !compat20", __func__); + + if (c->rfd != -1 && !c->mux_pause && FD_ISSET(c->rfd, readset) && + (c->istate == CHAN_INPUT_OPEN || + c->istate == CHAN_INPUT_WAIT_DRAIN)) { + /* + * Don't not read past the precise end of packets to + * avoid disrupting fd passing. + */ + if (read_mux(c, 4) < 4) /* read header */ + return; + need = get_u32(buffer_ptr(&c->input)); +#define CHANNEL_MUX_MAX_PACKET (256 * 1024) + if (need > CHANNEL_MUX_MAX_PACKET) { + debug2("channel %d: packet too big %u > %u", + c->self, CHANNEL_MUX_MAX_PACKET, need); + chan_rcvd_oclose(c); + return; + } + if (read_mux(c, need + 4) < need + 4) /* read body */ + return; + if (c->mux_rcb(c) != 0) { + debug("channel %d: mux_rcb failed", c->self); + chan_mark_dead(c); + return; + } + } + + if (c->wfd != -1 && FD_ISSET(c->wfd, writeset) && + buffer_len(&c->output) > 0) { + len = write(c->wfd, buffer_ptr(&c->output), + buffer_len(&c->output)); + if (len < 0 && (errno == EINTR || errno == EAGAIN)) + return; + if (len <= 0) { + chan_mark_dead(c); + return; + } + buffer_consume(&c->output, len); + } +} + +static void +channel_post_mux_listener(Channel *c, fd_set *readset, fd_set *writeset) +{ + Channel *nc; + struct sockaddr_storage addr; + socklen_t addrlen; + int newsock; + uid_t euid; + gid_t egid; + + if (!FD_ISSET(c->sock, readset)) + return; + + debug("multiplexing control connection"); + + /* + * Accept connection on control socket + */ + memset(&addr, 0, sizeof(addr)); + addrlen = sizeof(addr); + if ((newsock = accept(c->sock, (struct sockaddr*)&addr, + &addrlen)) == -1) { + error("%s accept: %s", __func__, strerror(errno)); + return; + } + + if (getpeereid(newsock, &euid, &egid) < 0) { + error("%s getpeereid failed: %s", __func__, + strerror(errno)); + close(newsock); + return; + } + if ((euid != 0) && (getuid() != euid)) { + error("multiplex uid mismatch: peer euid %u != uid %u", + (u_int)euid, (u_int)getuid()); + close(newsock); + return; + } + nc = channel_new("multiplex client", SSH_CHANNEL_MUX_CLIENT, + newsock, newsock, -1, c->local_window_max, + c->local_maxpacket, 0, "mux-control", 1); + nc->mux_rcb = c->mux_rcb; + debug3("%s: new mux channel %d fd %d", __func__, + nc->self, nc->sock); + /* establish state */ + nc->mux_rcb(nc); + /* mux state transitions must not elicit protocol messages */ + nc->flags |= CHAN_LOCAL; +} + /* ARGSUSED */ static void channel_post_output_drain_13(Channel *c, fd_set *readset, fd_set *writeset) @@ -1816,6 +1966,8 @@ channel_handler_init_20(void) channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; + channel_pre[SSH_CHANNEL_MUX_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_MUX_CLIENT] = &channel_pre_mux_client; channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; @@ -1824,6 +1976,8 @@ channel_handler_init_20(void) channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; + channel_post[SSH_CHANNEL_MUX_LISTENER] = &channel_post_mux_listener; + channel_post[SSH_CHANNEL_MUX_CLIENT] = &channel_post_mux_client; } static void @@ -1910,17 +2064,23 @@ static void channel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset) { static int did_init = 0; - u_int i; + u_int i, oalloc; Channel *c; if (!did_init) { channel_handler_init(); did_init = 1; } - for (i = 0; i < channels_alloc; i++) { + for (i = 0, oalloc = channels_alloc; i < oalloc; i++) { c = channels[i]; if (c == NULL) continue; + if (c->delayed) { + if (ftab == channel_pre) + c->delayed = 0; + else + continue; + } if (ftab[c->type] != NULL) (*ftab[c->type])(c, readset, writeset); channel_garbage_collect(c); @@ -2012,6 +2172,14 @@ channel_output_poll(void) data = buffer_get_string(&c->input, &dlen); + if (dlen > c->remote_window || + dlen > c->remote_maxpacket) { + debug("channel %d: datagram " + "too big for channel", + c->self); + xfree(data); + continue; + } packet_start(SSH2_MSG_CHANNEL_DATA); packet_put_int(c->remote_id); packet_put_string(data, dlen); @@ -2097,7 +2265,7 @@ channel_input_data(int type, u_int32_t seq, void *ctxt) { int id; char *data; - u_int data_len; + u_int data_len, win_len; Channel *c; /* Get the channel number and verify it. */ @@ -2113,6 +2281,9 @@ channel_input_data(int type, u_int32_t seq, void *ctxt) /* Get the data. */ data = packet_get_string_ptr(&data_len); + win_len = data_len; + if (c->datagram) + win_len += 4; /* string length header */ /* * Ignore data for protocol > 1.3 if output end is no longer open. @@ -2123,23 +2294,23 @@ channel_input_data(int type, u_int32_t seq, void *ctxt) */ if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) { if (compat20) { - c->local_window -= data_len; - c->local_consumed += data_len; + c->local_window -= win_len; + c->local_consumed += win_len; } return; } if (compat20) { - if (data_len > c->local_maxpacket) { + if (win_len > c->local_maxpacket) { logit("channel %d: rcvd big packet %d, maxpack %d", - c->self, data_len, c->local_maxpacket); + c->self, win_len, c->local_maxpacket); } - if (data_len > c->local_window) { + if (win_len > c->local_window) { logit("channel %d: rcvd too much data %d, win %d", - c->self, data_len, c->local_window); + c->self, win_len, c->local_window); return; } - c->local_window -= data_len; + c->local_window -= win_len; } if (c->datagram) buffer_put_string(&c->output, data, data_len); @@ -2311,7 +2482,7 @@ channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) c->remote_maxpacket = packet_get_int(); if (c->open_confirm) { debug2("callback start"); - c->open_confirm(c->self, c->open_confirm_ctx); + c->open_confirm(c->self, 1, c->open_confirm_ctx); debug2("callback done"); } debug2("channel %d: open confirm rwindow %u rmax %u", c->self, @@ -2362,6 +2533,11 @@ channel_input_open_failure(int type, u_int32_t seq, void *ctxt) xfree(msg); if (lang != NULL) xfree(lang); + if (c->open_confirm) { + debug2("callback start"); + c->open_confirm(c->self, 0, c->open_confirm_ctx); + debug2("callback done"); + } } packet_check_eom(); /* Schedule the channel for cleanup/deletion. */ @@ -2577,6 +2753,8 @@ channel_setup_fwd_listener(int type, const char *listen_addr, } channel_set_reuseaddr(sock); + if (ai->ai_family == AF_INET6) + sock_set_v6only(sock); debug("Local forwarding listening on %s port %s.", ntop, strport); @@ -2678,10 +2856,6 @@ channel_request_remote_forwarding(const char *listen_host, u_short listen_port, { int type, success = 0; - /* Record locally that connection to this host/port is permitted. */ - if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) - fatal("channel_request_remote_forwarding: too many forwards"); - /* Send the forward request to the remote side. */ if (compat20) { const char *address_to_bind; @@ -2731,6 +2905,9 @@ channel_request_remote_forwarding(const char *listen_host, u_short listen_port, } } if (success) { + /* Record that connection to this host/port is permitted. */ + permitted_opens = xrealloc(permitted_opens, + num_permitted_opens + 1, sizeof(*permitted_opens)); permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect); permitted_opens[num_permitted_opens].port_to_connect = port_to_connect; permitted_opens[num_permitted_opens].listen_port = listen_port; @@ -2828,10 +3005,10 @@ channel_permit_all_opens(void) void channel_add_permitted_opens(char *host, int port) { - if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) - fatal("channel_add_permitted_opens: too many forwards"); debug("allow port forwarding to host %s port %d", host, port); + permitted_opens = xrealloc(permitted_opens, + num_permitted_opens + 1, sizeof(*permitted_opens)); permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); permitted_opens[num_permitted_opens].port_to_connect = port; num_permitted_opens++; @@ -2842,10 +3019,10 @@ channel_add_permitted_opens(char *host, int port) int channel_add_adm_permitted_opens(char *host, int port) { - if (num_adm_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) - fatal("channel_add_adm_permitted_opens: too many forwards"); debug("config allows port forwarding to host %s port %d", host, port); + permitted_adm_opens = xrealloc(permitted_adm_opens, + num_adm_permitted_opens + 1, sizeof(*permitted_adm_opens)); permitted_adm_opens[num_adm_permitted_opens].host_to_connect = xstrdup(host); permitted_adm_opens[num_adm_permitted_opens].port_to_connect = port; @@ -2860,6 +3037,10 @@ channel_clear_permitted_opens(void) for (i = 0; i < num_permitted_opens; i++) if (permitted_opens[i].host_to_connect != NULL) xfree(permitted_opens[i].host_to_connect); + if (num_permitted_opens > 0) { + xfree(permitted_opens); + permitted_opens = NULL; + } num_permitted_opens = 0; } @@ -2871,6 +3052,10 @@ channel_clear_adm_permitted_opens(void) for (i = 0; i < num_adm_permitted_opens; i++) if (permitted_adm_opens[i].host_to_connect != NULL) xfree(permitted_adm_opens[i].host_to_connect); + if (num_adm_permitted_opens > 0) { + xfree(permitted_adm_opens); + permitted_adm_opens = NULL; + } num_adm_permitted_opens = 0; } @@ -3098,7 +3283,11 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock < 0) { - if ((errno != EINVAL) && (errno != EAFNOSUPPORT)) { + if ((errno != EINVAL) && (errno != EAFNOSUPPORT) +#ifdef EPFNOSUPPORT + && (errno != EPFNOSUPPORT) +#endif + ) { error("socket: %.100s", strerror(errno)); freeaddrinfo(aitop); return -1; @@ -3108,13 +3297,8 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, continue; } } -#ifdef IPV6_V6ONLY - if (ai->ai_family == AF_INET6) { - int on = 1; - if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) - error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno)); - } -#endif + if (ai->ai_family == AF_INET6) + sock_set_v6only(sock); if (x11_use_localhost) channel_set_reuseaddr(sock); if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { diff --git a/crypto/openssh/channels.h b/crypto/openssh/channels.h index 1488ed7e59..0680ed00e4 100644 --- a/crypto/openssh/channels.h +++ b/crypto/openssh/channels.h @@ -1,4 +1,4 @@ -/* $OpenBSD: channels.h,v 1.98 2009/02/12 03:00:56 djm Exp $ */ +/* $OpenBSD: channels.h,v 1.104 2010/05/14 23:29:23 djm Exp $ */ /* * Author: Tatu Ylonen @@ -53,11 +53,14 @@ #define SSH_CHANNEL_CONNECTING 12 #define SSH_CHANNEL_DYNAMIC 13 #define SSH_CHANNEL_ZOMBIE 14 /* Almost dead. */ -#define SSH_CHANNEL_MAX_TYPE 15 +#define SSH_CHANNEL_MUX_LISTENER 15 /* Listener for mux conn. */ +#define SSH_CHANNEL_MUX_CLIENT 16 /* Conn. to mux slave */ +#define SSH_CHANNEL_MAX_TYPE 17 struct Channel; typedef struct Channel Channel; +typedef void channel_open_fn(int, int, void *); typedef void channel_callback_fn(int, void *); typedef int channel_infilter_fn(struct Channel *, char *, int); typedef void channel_filter_cleanup_fn(int, void *); @@ -81,6 +84,9 @@ struct channel_connect { struct addrinfo *ai, *aitop; }; +/* Callbacks for mux channels back into client-specific code */ +typedef int mux_callback_fn(struct Channel *); + struct Channel { int type; /* channel type/state */ int self; /* my own channel identifier */ @@ -92,12 +98,16 @@ struct Channel { int wfd; /* write fd */ int efd; /* extended fd */ int sock; /* sock fd */ - int ctl_fd; /* control fd (client sharing) */ + int ctl_chan; /* control channel (multiplexed connections) */ int isatty; /* rfd is a tty */ int wfd_isatty; /* wfd is a tty */ int client_tty; /* (client) TTY has been requested */ int force_drain; /* force close on iEOF */ - int delayed; /* fdset hack */ + int delayed; /* post-select handlers for newly created + * channels are delayed until the first call + * to a matching pre-select handler. + * this way post-select handlers are not + * accidenly called if a FD gets reused */ Buffer input; /* data read from socket, to be sent over * encrypted connection */ Buffer output; /* data received over encrypted connection for @@ -121,7 +131,7 @@ struct Channel { char *ctype; /* type */ /* callback */ - channel_callback_fn *open_confirm; + channel_open_fn *open_confirm; void *open_confirm_ctx; channel_callback_fn *detach_user; int detach_close; @@ -138,6 +148,11 @@ struct Channel { /* non-blocking connect */ struct channel_connect connect_ctx; + + /* multiplexing protocol hook, called for each packet received */ + mux_callback_fn *mux_rcb; + void *mux_ctx; + int mux_pause; }; #define CHAN_EXTENDED_IGNORE 0 @@ -168,6 +183,7 @@ struct Channel { #define CHAN_CLOSE_RCVD 0x02 #define CHAN_EOF_SENT 0x04 #define CHAN_EOF_RCVD 0x08 +#define CHAN_LOCAL 0x10 #define CHAN_RBUF 16*1024 @@ -194,7 +210,7 @@ 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_open_confirm(int, channel_callback_fn *, void *); +void channel_register_open_confirm(int, channel_open_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 *, @@ -239,6 +255,7 @@ void channel_clear_adm_permitted_opens(void); void channel_print_adm_permitted_opens(void); int channel_input_port_forward_request(int, int); Channel *channel_connect_to(const char *, u_short, char *, char *); +Channel *channel_connect_stdio_fwd(const char*, u_short, int, int); Channel *channel_connect_by_listen_address(u_short, char *, char *); int channel_request_remote_forwarding(const char *, u_short, const char *, u_short); diff --git a/crypto/openssh/clientloop.c b/crypto/openssh/clientloop.c index 9a7dc0ab0b..de79793666 100644 --- a/crypto/openssh/clientloop.c +++ b/crypto/openssh/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.213 2009/07/05 19:28:33 stevesk Exp $ */ +/* $OpenBSD: clientloop.c,v 1.222 2010/07/19 09:15:12 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -121,7 +121,7 @@ extern int stdin_null_flag; extern int no_shell_flag; /* Control socket */ -extern int muxserver_sock; +extern int muxserver_sock; /* XXX use mux_client_cleanup() instead */ /* * Name of the host we are connecting to. This is the name given on the @@ -130,6 +130,9 @@ extern int muxserver_sock; */ extern char *host; +/* Force TTY allocation */ +extern int force_tty_flag; + /* * Flag to indicate that we have received a window change signal which has * not yet been processed. This will cause a message indicating the new @@ -142,8 +145,11 @@ static volatile sig_atomic_t received_signal = 0; /* Flag indicating whether the user's terminal is in non-blocking mode. */ static int in_non_blocking_mode = 0; +/* Time when backgrounded control master using ControlPersist should exit */ +static time_t control_persist_exit_time = 0; + /* Common data for the client loop code. */ -static volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */ +volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */ 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. */ @@ -152,15 +158,18 @@ 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_int buffer_high;/* Soft max buffer size. */ +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). */ static int need_rekeying; /* Set to non-zero if rekeying is requested. */ -static int session_closed = 0; /* In SSH2: login session closed. */ +static int session_closed; /* In SSH2: login session closed. */ +static int x11_refuse_time; /* If >0, refuse x11 opens after this time. */ static void client_init_dispatch(void); int session_ident = -1; +int session_resumed = 0; + /* Track escape per proto2 channel */ struct escape_filter_ctx { int escape_pending; @@ -246,10 +255,38 @@ get_current_time(void) return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0; } +/* + * Sets control_persist_exit_time to the absolute time when the + * backgrounded control master should exit due to expiry of the + * ControlPersist timeout. Sets it to 0 if we are not a backgrounded + * control master process, or if there is no ControlPersist timeout. + */ +static void +set_control_persist_exit_time(void) +{ + if (muxserver_sock == -1 || !options.control_persist + || options.control_persist_timeout == 0) + /* not using a ControlPersist timeout */ + control_persist_exit_time = 0; + else if (channel_still_open()) { + /* some client connections are still open */ + if (control_persist_exit_time > 0) + debug2("%s: cancel scheduled exit", __func__); + control_persist_exit_time = 0; + } else if (control_persist_exit_time <= 0) { + /* a client connection has recently closed */ + control_persist_exit_time = time(NULL) + + (time_t)options.control_persist_timeout; + debug2("%s: schedule exit in %d seconds", __func__, + options.control_persist_timeout); + } + /* else we are already counting down to the timeout */ +} + #define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" void client_x11_get_proto(const char *display, const char *xauth_path, - u_int trusted, char **_proto, char **_data) + u_int trusted, u_int timeout, char **_proto, char **_data) { char cmd[1024]; char line[512]; @@ -259,6 +296,7 @@ client_x11_get_proto(const char *display, const char *xauth_path, int got_data = 0, generated = 0, do_unlink = 0, i; char *xauthdir, *xauthfile; struct stat st; + u_int now; xauthdir = xauthfile = NULL; *_proto = proto; @@ -294,11 +332,18 @@ client_x11_get_proto(const char *display, const char *xauth_path, xauthdir); snprintf(cmd, sizeof(cmd), "%s -f %s generate %s " SSH_X11_PROTO - " untrusted timeout 1200 2>" _PATH_DEVNULL, - xauth_path, xauthfile, display); + " untrusted timeout %u 2>" _PATH_DEVNULL, + xauth_path, xauthfile, display, timeout); debug2("x11_get_proto: %s", cmd); if (system(cmd) == 0) generated = 1; + if (x11_refuse_time == 0) { + now = time(NULL) + 1; + if (UINT_MAX - timeout < now) + x11_refuse_time = UINT_MAX; + else + x11_refuse_time = now + timeout; + } } } @@ -519,6 +564,7 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp, u_int *nallocp, int rekeying) { struct timeval tv, *tvp; + int timeout_secs; int ret; /* Add any selections by the channel mechanism. */ @@ -559,22 +605,30 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, if (packet_have_data_to_write()) FD_SET(connection_out, *writesetp); - if (muxserver_sock != -1) - FD_SET(muxserver_sock, *readsetp); - /* * Wait for something to happen. This will suspend the process until * some selected descriptor can be read, written, or has some other - * event pending. + * event pending, or a timeout expires. */ - if (options.server_alive_interval == 0 || !compat20) + timeout_secs = INT_MAX; /* we use INT_MAX to mean no timeout */ + if (options.server_alive_interval > 0 && compat20) + timeout_secs = options.server_alive_interval; + set_control_persist_exit_time(); + if (control_persist_exit_time > 0) { + timeout_secs = MIN(timeout_secs, + control_persist_exit_time - time(NULL)); + if (timeout_secs < 0) + timeout_secs = 0; + } + if (timeout_secs == INT_MAX) tvp = NULL; else { - tv.tv_sec = options.server_alive_interval; + tv.tv_sec = timeout_secs; tv.tv_usec = 0; tvp = &tv; } + ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); if (ret < 0) { char buf[100]; @@ -608,7 +662,7 @@ client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) atomicio(vwrite, fileno(stderr), buffer_ptr(berr), buffer_len(berr)); - leave_raw_mode(); + leave_raw_mode(force_tty_flag); /* * Free (and clear) the buffer to reduce the amount of data that gets @@ -629,7 +683,7 @@ client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) buffer_init(bout); buffer_init(berr); - enter_raw_mode(); + enter_raw_mode(force_tty_flag); } static void @@ -690,7 +744,7 @@ client_status_confirm(int type, Channel *c, void *ctx) /* XXX supress on mux _client_ quietmode */ tochan = options.log_level >= SYSLOG_LEVEL_ERROR && - c->ctl_fd != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; + c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; if (type == SSH2_MSG_CHANNEL_SUCCESS) { debug2("%s request accepted on channel %d", @@ -772,7 +826,7 @@ process_cmdline(void) bzero(&fwd, sizeof(fwd)); fwd.listen_host = fwd.connect_host = NULL; - leave_raw_mode(); + leave_raw_mode(force_tty_flag); handler = signal(SIGINT, SIG_IGN); cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); if (s == NULL) @@ -834,6 +888,7 @@ process_cmdline(void) while (isspace(*++s)) ; + /* XXX update list of forwards in options */ if (delete) { cancel_port = 0; cancel_host = hpdelim(&s); /* may be NULL */ @@ -875,7 +930,7 @@ process_cmdline(void) out: signal(SIGINT, handler); - enter_raw_mode(); + enter_raw_mode(force_tty_flag); if (cmd) xfree(cmd); if (fwd.listen_host != NULL) @@ -931,7 +986,7 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, escape_char); buffer_append(berr, string, strlen(string)); - if (c && c->ctl_fd != -1) { + if (c && c->ctl_chan != -1) { chan_read_failed(c); chan_write_failed(c); return 0; @@ -941,7 +996,7 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, case 'Z' - 64: /* XXX support this for mux clients */ - if (c && c->ctl_fd != -1) { + if (c && c->ctl_chan != -1) { noescape: snprintf(string, sizeof string, "%c%c escape not available to " @@ -986,7 +1041,7 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, continue; case '&': - if (c && c->ctl_fd != -1) + if (c && c->ctl_chan != -1) goto noescape; /* * Detach the program (continue to serve @@ -994,7 +1049,7 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, * more new connections). */ /* Restore tty modes. */ - leave_raw_mode(); + leave_raw_mode(force_tty_flag); /* Stop listening for new connections. */ channel_stop_listening(); @@ -1037,7 +1092,7 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, continue; case '?': - if (c && c->ctl_fd != -1) { + if (c && c->ctl_chan != -1) { snprintf(string, sizeof string, "%c?\r\n\ Supported escape sequences:\r\n\ @@ -1086,7 +1141,7 @@ Supported escape sequences:\r\n\ continue; case 'C': - if (c && c->ctl_fd != -1) + if (c && c->ctl_chan != -1) goto noescape; process_cmdline(); continue; @@ -1289,7 +1344,7 @@ client_channel_closed(int id, void *arg) { channel_cancel_cleanup(id); session_closed = 1; - leave_raw_mode(); + leave_raw_mode(force_tty_flag); } /* @@ -1322,8 +1377,6 @@ 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 (muxserver_sock != -1) - max_fd = MAX(max_fd, muxserver_sock); if (!compat20) { /* enable nonblocking unless tty */ @@ -1362,7 +1415,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) signal(SIGWINCH, window_change_handler); if (have_pty) - enter_raw_mode(); + enter_raw_mode(force_tty_flag); if (compat20) { session_ident = ssh2_chan_id; @@ -1441,12 +1494,6 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) /* Buffer input from the connection. */ client_process_net_input(readset); - /* Accept control connections. */ - if (muxserver_sock != -1 &&FD_ISSET(muxserver_sock, readset)) { - if (muxserver_accept_control()) - quit_pending = 1; - } - if (quit_pending) break; @@ -1460,12 +1507,32 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) client_process_output(writeset); } + if (session_resumed) { + connection_in = packet_get_connection_in(); + connection_out = packet_get_connection_out(); + max_fd = MAX(max_fd, connection_out); + max_fd = MAX(max_fd, connection_in); + session_resumed = 0; + } + /* * Send as much buffered packet data as possible to the * sender. */ if (FD_ISSET(connection_out, writeset)) packet_write_poll(); + + /* + * If we are a backgrounded control master, and the + * timeout has expired without any active client + * connections, then quit. + */ + if (control_persist_exit_time > 0) { + if (time(NULL) >= control_persist_exit_time) { + debug("ControlPersist timeout expired"); + break; + } + } } if (readset) xfree(readset); @@ -1481,6 +1548,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) packet_start(SSH2_MSG_DISCONNECT); packet_put_int(SSH2_DISCONNECT_BY_APPLICATION); packet_put_cstring("disconnected by user"); + packet_put_cstring(""); /* language tag */ packet_send(); packet_write_wait(); } @@ -1488,7 +1556,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) channel_free_all(); if (have_pty) - leave_raw_mode(); + leave_raw_mode(force_tty_flag); /* restore blocking io */ if (!isatty(fileno(stdin))) @@ -1682,6 +1750,11 @@ client_request_x11(const char *request_type, int rchan) "malicious server."); return NULL; } + if (x11_refuse_time != 0 && time(NULL) >= x11_refuse_time) { + verbose("Rejected X11 connection after ForwardX11Timeout " + "expired"); + return NULL; + } originator = packet_get_string(NULL); if (datafellows & SSH_BUG_X11FWD) { debug2("buggy server: x11 request w/o originator_port"); @@ -1846,15 +1919,17 @@ client_input_channel_req(int type, u_int32_t seq, void *ctxt) chan_rcvd_eow(c); } else if (strcmp(rtype, "exit-status") == 0) { exitval = packet_get_int(); - if (id == session_ident) { + if (c->ctl_chan != -1) { + mux_exit_message(c, exitval); + success = 1; + } else if (id == session_ident) { + /* Record exit value of local session */ success = 1; exit_status = exitval; - } else if (c->ctl_fd == -1) { - error("client_input_channel_req: unexpected channel %d", - session_ident); } else { - atomicio(vwrite, c->ctl_fd, &exitval, sizeof(exitval)); - success = 1; + /* Probably for a mux channel that has already closed */ + debug("%s: no sink for exit-status on channel %d", + __func__, id); } packet_check_eom(); } @@ -1906,7 +1981,7 @@ client_session2_setup(int id, int want_tty, int want_subsystem, memset(&ws, 0, sizeof(ws)); channel_request_start(id, "pty-req", 1); - client_expect_confirm(id, "PTY allocation", 0); + client_expect_confirm(id, "PTY allocation", 1); packet_put_cstring(term != NULL ? term : ""); packet_put_int((u_int)ws.ws_col); packet_put_int((u_int)ws.ws_row); @@ -2050,7 +2125,7 @@ client_init_dispatch(void) void cleanup_exit(int i) { - leave_raw_mode(); + leave_raw_mode(force_tty_flag); leave_non_blocking(); if (options.control_path != NULL && muxserver_sock != -1) unlink(options.control_path); diff --git a/crypto/openssh/clientloop.h b/crypto/openssh/clientloop.h index 8bb874b388..52115db6ec 100644 --- a/crypto/openssh/clientloop.h +++ b/crypto/openssh/clientloop.h @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.h,v 1.22 2008/06/12 15:19:17 djm Exp $ */ +/* $OpenBSD: clientloop.h,v 1.25 2010/06/25 23:15:36 djm Exp $ */ /* * Author: Tatu Ylonen @@ -39,7 +39,7 @@ /* Client side main loop for the interactive session. */ int client_loop(int, int, int); -void client_x11_get_proto(const char *, const char *, u_int, +void client_x11_get_proto(const char *, const char *, u_int, 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 *, @@ -56,18 +56,15 @@ 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 2 +#define SSHMUX_VER 4 /* Multiplexing control protocol flags */ #define SSHMUX_COMMAND_OPEN 1 /* Open new connection */ #define SSHMUX_COMMAND_ALIVE_CHECK 2 /* Check master is alive */ #define SSHMUX_COMMAND_TERMINATE 3 /* Ask master to exit */ - -#define SSHMUX_FLAG_TTY (1) /* Request tty on open */ -#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 */ +#define SSHMUX_COMMAND_STDIO_FWD 4 /* Open stdio fwd (ssh -W) */ +#define SSHMUX_COMMAND_FORWARD 5 /* Forward only, no command */ void muxserver_listen(void); -int muxserver_accept_control(void); void muxclient(const char *); +void mux_exit_message(Channel *, int); diff --git a/crypto/openssh/defines.h b/crypto/openssh/defines.h index 2ddfd96d07..fe25170e61 100644 --- a/crypto/openssh/defines.h +++ b/crypto/openssh/defines.h @@ -25,7 +25,7 @@ #ifndef _DEFINES_H #define _DEFINES_H -/* $Id: defines.h,v 1.156 2009/08/28 01:21:07 dtucker Exp $ */ +/* $Id: defines.h,v 1.160 2010/04/09 08:13:27 dtucker Exp $ */ /* Constants */ @@ -674,7 +674,7 @@ struct winsize { #else /* Simply select your favourite login types. */ /* Can't do if-else because some systems use several... */ -# if defined(UTMPX_FILE) && !defined(DISABLE_UTMPX) +# if !defined(DISABLE_UTMPX) # define USE_UTMPX # endif # if defined(UTMP_FILE) && !defined(DISABLE_UTMP) @@ -753,4 +753,12 @@ struct winsize { # define SSH_IOBUFSZ 8192 #endif +#ifndef _NSIG +# ifdef NSIG +# define _NSIG NSIG +# else +# define _NSIG 128 +# endif +#endif + #endif /* _DEFINES_H */ diff --git a/crypto/openssh/dh.c b/crypto/openssh/dh.c index b76605325d..b9029d867e 100644 --- a/crypto/openssh/dh.c +++ b/crypto/openssh/dh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dh.c,v 1.47 2008/06/26 09:19:39 djm Exp $ */ +/* $OpenBSD: dh.c,v 1.48 2009/10/01 11:37:33 grunk Exp $ */ /* * Copyright (c) 2000 Niels Provos. All rights reserved. * @@ -83,7 +83,7 @@ parse_prime(int linenum, char *line, struct dhgroup *dhg) goto fail; strsize = strsep(&cp, " "); /* size */ if (cp == NULL || *strsize == '\0' || - (dhg->size = (u_int)strtonum(strsize, 0, 64*1024, &errstr)) == 0 || + (dhg->size = (int)strtonum(strsize, 0, 64*1024, &errstr)) == 0 || errstr) goto fail; /* The whole group is one bit larger */ diff --git a/crypto/openssh/dns.c b/crypto/openssh/dns.c index a7da03fa3e..2e7bb5aae9 100644 --- a/crypto/openssh/dns.c +++ b/crypto/openssh/dns.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dns.c,v 1.25 2008/06/12 00:03:49 dtucker Exp $ */ +/* $OpenBSD: dns.c,v 1.26 2010/02/26 20:29:54 djm Exp $ */ /* * Copyright (c) 2003 Wesley Griffin. All rights reserved. @@ -75,7 +75,7 @@ dns_result_totext(unsigned int res) */ static int dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, - u_char **digest, u_int *digest_len, const Key *key) + u_char **digest, u_int *digest_len, Key *key) { int success = 0; @@ -172,7 +172,7 @@ is_numeric_hostname(const char *hostname) */ int verify_host_key_dns(const char *hostname, struct sockaddr *address, - const Key *hostkey, int *flags) + Key *hostkey, int *flags) { u_int counter; int result; @@ -271,7 +271,7 @@ verify_host_key_dns(const char *hostname, struct sockaddr *address, * Export the fingerprint of a key as a DNS resource record */ int -export_dns_rr(const char *hostname, const Key *key, FILE *f, int generic) +export_dns_rr(const char *hostname, Key *key, FILE *f, int generic) { u_int8_t rdata_pubkey_algorithm = 0; u_int8_t rdata_digest_type = SSHFP_HASH_SHA1; diff --git a/crypto/openssh/dns.h b/crypto/openssh/dns.h index b2633a1fe9..90cfd7b92c 100644 --- a/crypto/openssh/dns.h +++ b/crypto/openssh/dns.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dns.h,v 1.10 2006/08/03 03:34:42 deraadt Exp $ */ +/* $OpenBSD: dns.h,v 1.11 2010/02/26 20:29:54 djm Exp $ */ /* * Copyright (c) 2003 Wesley Griffin. All rights reserved. @@ -46,7 +46,7 @@ enum sshfp_hashes { #define DNS_VERIFY_MATCH 0x00000002 #define DNS_VERIFY_SECURE 0x00000004 -int verify_host_key_dns(const char *, struct sockaddr *, const Key *, int *); -int export_dns_rr(const char *, const Key *, FILE *, int); +int verify_host_key_dns(const char *, struct sockaddr *, Key *, int *); +int export_dns_rr(const char *, Key *, FILE *, int); #endif /* DNS_H */ diff --git a/crypto/openssh/hostfile.c b/crypto/openssh/hostfile.c index 2cceb352a1..afab6dad18 100644 --- a/crypto/openssh/hostfile.c +++ b/crypto/openssh/hostfile.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hostfile.c,v 1.45 2006/08/03 03:34:42 deraadt Exp $ */ +/* $OpenBSD: hostfile.c,v 1.48 2010/03/04 10:36:03 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -183,6 +183,41 @@ hostfile_check_key(int bits, const Key *key, const char *host, const char *filen return 1; } +static enum { MRK_ERROR, MRK_NONE, MRK_REVOKE, MRK_CA } +check_markers(char **cpp) +{ + char marker[32], *sp, *cp = *cpp; + int ret = MRK_NONE; + + while (*cp == '@') { + /* Only one marker is allowed */ + if (ret != MRK_NONE) + return MRK_ERROR; + /* Markers are terminated by whitespace */ + if ((sp = strchr(cp, ' ')) == NULL && + (sp = strchr(cp, '\t')) == NULL) + return MRK_ERROR; + /* Extract marker for comparison */ + if (sp <= cp + 1 || sp >= cp + sizeof(marker)) + return MRK_ERROR; + memcpy(marker, cp, sp - cp); + marker[sp - cp] = '\0'; + if (strcmp(marker, CA_MARKER) == 0) + ret = MRK_CA; + else if (strcmp(marker, REVOKE_MARKER) == 0) + ret = MRK_REVOKE; + else + return MRK_ERROR; + + /* Skip past marker and any whitespace that follows it */ + cp = sp; + for (; *cp == ' ' || *cp == '\t'; cp++) + ; + } + *cpp = cp; + return ret; +} + /* * Checks whether the given host (which must be in all lowercase) is already * in the list of our known hosts. Returns HOST_OK if the host is known and @@ -195,16 +230,20 @@ hostfile_check_key(int bits, const Key *key, const char *host, const char *filen static HostStatus check_host_in_hostfile_by_key_or_type(const char *filename, - const char *host, const Key *key, int keytype, Key *found, int *numret) + const char *host, const Key *key, int keytype, Key *found, + int want_revocation, int *numret) { FILE *f; char line[8192]; - int linenum = 0; + int want, have, linenum = 0, want_cert = key_is_cert(key); u_int kbits; char *cp, *cp2, *hashed_host; HostStatus end_return; - debug3("check_host_in_hostfile: filename %s", filename); + debug3("check_host_in_hostfile: host %s filename %s", host, filename); + + if (want_revocation && (key == NULL || keytype != 0 || found != NULL)) + fatal("%s: invalid arguments", __func__); /* Open the file containing the list of known hosts. */ f = fopen(filename, "r"); @@ -229,6 +268,20 @@ check_host_in_hostfile_by_key_or_type(const char *filename, if (!*cp || *cp == '#' || *cp == '\n') continue; + if (want_revocation) + want = MRK_REVOKE; + else if (want_cert) + want = MRK_CA; + else + want = MRK_NONE; + + if ((have = check_markers(&cp)) == MRK_ERROR) { + verbose("%s: invalid marker at %s:%d", + __func__, filename, linenum); + continue; + } else if (want != have) + continue; + /* Find the end of the host name portion. */ for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) ; @@ -250,6 +303,9 @@ check_host_in_hostfile_by_key_or_type(const char *filename, /* Got a match. Skip host name. */ cp = cp2; + if (want_revocation) + found = key_new(KEY_UNSPEC); + /* * Extract the key from the line. This will skip any leading * whitespace. Ignore badly formatted lines. @@ -272,9 +328,33 @@ check_host_in_hostfile_by_key_or_type(const char *filename, if (!hostfile_check_key(kbits, found, host, filename, linenum)) continue; + if (want_revocation) { + if (key_is_cert(key) && + key_equal_public(key->cert->signature_key, found)) { + verbose("check_host_in_hostfile: revoked CA " + "line %d", linenum); + key_free(found); + return HOST_REVOKED; + } + if (key_equal_public(key, found)) { + verbose("check_host_in_hostfile: revoked key " + "line %d", linenum); + key_free(found); + return HOST_REVOKED; + } + key_free(found); + continue; + } + /* Check if the current key is the same as the given key. */ - if (key_equal(key, found)) { - /* Ok, they match. */ + if (want_cert && key_equal(key->cert->signature_key, found)) { + /* Found CA cert for key */ + debug3("check_host_in_hostfile: CA match line %d", + linenum); + fclose(f); + return HOST_OK; + } else if (!want_cert && key_equal(key, found)) { + /* Found identical key */ debug3("check_host_in_hostfile: match line %d", linenum); fclose(f); return HOST_OK; @@ -302,8 +382,11 @@ check_host_in_hostfile(const char *filename, const char *host, const Key *key, { if (key == NULL) fatal("no key to look up"); - return (check_host_in_hostfile_by_key_or_type(filename, host, key, 0, - found, numret)); + if (check_host_in_hostfile_by_key_or_type(filename, host, + key, 0, NULL, 1, NULL) == HOST_REVOKED) + return HOST_REVOKED; + return check_host_in_hostfile_by_key_or_type(filename, host, key, 0, + found, 0, numret); } int @@ -311,7 +394,7 @@ lookup_key_in_hostfile_by_type(const char *filename, const char *host, int keytype, Key *found, int *numret) { return (check_host_in_hostfile_by_key_or_type(filename, host, NULL, - keytype, found, numret) == HOST_FOUND); + keytype, found, 0, numret) == HOST_FOUND); } /* diff --git a/crypto/openssh/hostfile.h b/crypto/openssh/hostfile.h index d1983b3e09..1d460c1a91 100644 --- a/crypto/openssh/hostfile.h +++ b/crypto/openssh/hostfile.h @@ -1,4 +1,4 @@ -/* $OpenBSD: hostfile.h,v 1.16 2006/03/25 22:22:43 djm Exp $ */ +/* $OpenBSD: hostfile.h,v 1.18 2010/03/04 10:36:03 djm Exp $ */ /* * Author: Tatu Ylonen @@ -15,7 +15,7 @@ #define HOSTFILE_H typedef enum { - HOST_OK, HOST_NEW, HOST_CHANGED, HOST_FOUND + HOST_OK, HOST_NEW, HOST_CHANGED, HOST_REVOKED, HOST_FOUND } HostStatus; int hostfile_read_key(char **, u_int *, Key *); @@ -28,6 +28,9 @@ int lookup_key_in_hostfile_by_type(const char *, const char *, #define HASH_MAGIC "|1|" #define HASH_DELIM '|' +#define CA_MARKER "@cert-authority" +#define REVOKE_MARKER "@revoked" + char *host_hash(const char *, const char *, u_int); #endif diff --git a/crypto/openssh/jpake.c b/crypto/openssh/jpake.c index 130661069f..cdf65f5092 100644 --- a/crypto/openssh/jpake.c +++ b/crypto/openssh/jpake.c @@ -1,4 +1,4 @@ -/* $OpenBSD: jpake.c,v 1.2 2009/03/05 07:18:19 djm Exp $ */ +/* $OpenBSD: jpake.c,v 1.4 2010/07/13 23:13:16 djm Exp $ */ /* * Copyright (c) 2008 Damien Miller. All rights reserved. * @@ -434,7 +434,7 @@ jpake_check_confirm(const BIGNUM *k, if (peer_confirm_hash_len != expected_confirm_hash_len) error("%s: confirmation length mismatch (my %u them %u)", __func__, expected_confirm_hash_len, peer_confirm_hash_len); - else if (memcmp(peer_confirm_hash, expected_confirm_hash, + else if (timingsafe_bcmp(peer_confirm_hash, expected_confirm_hash, expected_confirm_hash_len) == 0) success = 1; bzero(expected_confirm_hash, expected_confirm_hash_len); diff --git a/crypto/openssh/kex.c b/crypto/openssh/kex.c index f4f44f095d..148cfee804 100644 --- a/crypto/openssh/kex.c +++ b/crypto/openssh/kex.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.c,v 1.81 2009/05/27 06:34:36 andreas Exp $ */ +/* $OpenBSD: kex.c,v 1.82 2009/10/24 11:13:54 andreas Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * @@ -48,6 +48,7 @@ #include "match.h" #include "dispatch.h" #include "monitor.h" +#include "roaming.h" #if OPENSSL_VERSION_NUMBER >= 0x00907000L # if defined(HAVE_EVP_SHA256) @@ -386,6 +387,16 @@ kex_choose_conf(Kex *kex) sprop=peer; } + /* Check whether server offers roaming */ + if (!kex->server) { + char *roaming; + roaming = match_list(KEX_RESUME, peer[PROPOSAL_KEX_ALGS], NULL); + if (roaming) { + kex->roaming = 1; + xfree(roaming); + } + } + /* Algorithm Negotiation */ for (mode = 0; mode < MODE_MAX; mode++) { newkeys = xcalloc(1, sizeof(*newkeys)); diff --git a/crypto/openssh/kex.h b/crypto/openssh/kex.h index 68c80c5a90..62fa2ea50f 100644 --- a/crypto/openssh/kex.h +++ b/crypto/openssh/kex.h @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.h,v 1.47 2009/05/27 06:34:36 andreas Exp $ */ +/* $OpenBSD: kex.h,v 1.49 2010/02/26 20:29:54 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -36,6 +36,7 @@ #define KEX_DH14 "diffie-hellman-group14-sha1" #define KEX_DHGEX_SHA1 "diffie-hellman-group-exchange-sha1" #define KEX_DHGEX_SHA256 "diffie-hellman-group-exchange-sha256" +#define KEX_RESUME "resume@appgate.com" #define COMP_NONE 0 #define COMP_ZLIB 1 @@ -116,6 +117,7 @@ struct Kex { char *name; int hostkey_type; int kex_type; + int roaming; Buffer my; Buffer peer; sig_atomic_t done; @@ -124,7 +126,8 @@ struct Kex { char *client_version_string; char *server_version_string; int (*verify_host_key)(Key *); - Key *(*load_host_key)(int); + Key *(*load_host_public_key)(int); + Key *(*load_host_private_key)(int); int (*host_key_index)(Key *); void (*kex[KEX_MAX])(Kex *); }; diff --git a/crypto/openssh/kexdhs.c b/crypto/openssh/kexdhs.c index a6719f6722..e722877d5f 100644 --- a/crypto/openssh/kexdhs.c +++ b/crypto/openssh/kexdhs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexdhs.c,v 1.10 2009/06/21 07:37:15 dtucker Exp $ */ +/* $OpenBSD: kexdhs.c,v 1.11 2010/02/26 20:29:54 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * @@ -50,7 +50,7 @@ kexdh_server(Kex *kex) { BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; DH *dh; - Key *server_host_key; + Key *server_host_public, *server_host_private; u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; u_int sbloblen, klen, hashlen, slen; int kout; @@ -71,11 +71,16 @@ kexdh_server(Kex *kex) debug("expecting SSH2_MSG_KEXDH_INIT"); packet_read_expect(SSH2_MSG_KEXDH_INIT); - if (kex->load_host_key == NULL) + if (kex->load_host_public_key == NULL || + kex->load_host_private_key == NULL) fatal("Cannot load hostkey"); - server_host_key = kex->load_host_key(kex->hostkey_type); - if (server_host_key == NULL) + server_host_public = kex->load_host_public_key(kex->hostkey_type); + if (server_host_public == NULL) fatal("Unsupported hostkey type %d", kex->hostkey_type); + server_host_private = kex->load_host_private_key(kex->hostkey_type); + if (server_host_private == NULL) + fatal("Missing private key for hostkey type %d", + kex->hostkey_type); /* key, cert */ if ((dh_client_pub = BN_new()) == NULL) @@ -113,7 +118,7 @@ kexdh_server(Kex *kex) memset(kbuf, 0, klen); xfree(kbuf); - key_to_blob(server_host_key, &server_host_key_blob, &sbloblen); + key_to_blob(server_host_public, &server_host_key_blob, &sbloblen); /* calc H */ kex_dh_hash( @@ -137,7 +142,7 @@ kexdh_server(Kex *kex) } /* sign H */ - if (PRIVSEP(key_sign(server_host_key, &signature, &slen, hash, + if (PRIVSEP(key_sign(server_host_private, &signature, &slen, hash, hashlen)) < 0) fatal("kexdh_server: key_sign failed"); diff --git a/crypto/openssh/kexgexs.c b/crypto/openssh/kexgexs.c index 8515568b36..f4156af963 100644 --- a/crypto/openssh/kexgexs.c +++ b/crypto/openssh/kexgexs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kexgexs.c,v 1.12 2009/06/21 07:37:15 dtucker Exp $ */ +/* $OpenBSD: kexgexs.c,v 1.13 2010/02/26 20:29:54 djm Exp $ */ /* * Copyright (c) 2000 Niels Provos. All rights reserved. * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -52,18 +52,24 @@ void kexgex_server(Kex *kex) { BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; - Key *server_host_key; + Key *server_host_public, *server_host_private; DH *dh; u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; u_int sbloblen, klen, slen, hashlen; int omin = -1, min = -1, omax = -1, max = -1, onbits = -1, nbits = -1; int type, kout; - if (kex->load_host_key == NULL) + if (kex->load_host_public_key == NULL || + kex->load_host_private_key == NULL) fatal("Cannot load hostkey"); - server_host_key = kex->load_host_key(kex->hostkey_type); - if (server_host_key == NULL) + server_host_public = kex->load_host_public_key(kex->hostkey_type); + if (server_host_public == NULL) fatal("Unsupported hostkey type %d", kex->hostkey_type); + server_host_private = kex->load_host_private_key(kex->hostkey_type); + if (server_host_private == NULL) + fatal("Missing private key for hostkey type %d", + kex->hostkey_type); + type = packet_read(); switch (type) { @@ -149,7 +155,7 @@ kexgex_server(Kex *kex) memset(kbuf, 0, klen); xfree(kbuf); - key_to_blob(server_host_key, &server_host_key_blob, &sbloblen); + key_to_blob(server_host_public, &server_host_key_blob, &sbloblen); if (type == SSH2_MSG_KEX_DH_GEX_REQUEST_OLD) omin = min = omax = max = -1; @@ -179,7 +185,7 @@ kexgex_server(Kex *kex) } /* sign H */ - if (PRIVSEP(key_sign(server_host_key, &signature, &slen, hash, + if (PRIVSEP(key_sign(server_host_private, &signature, &slen, hash, hashlen)) < 0) fatal("kexgex_server: key_sign failed"); diff --git a/crypto/openssh/key.c b/crypto/openssh/key.c index 3e17da6016..e4aa25c035 100644 --- a/crypto/openssh/key.c +++ b/crypto/openssh/key.c @@ -1,4 +1,4 @@ -/* $OpenBSD: key.c,v 1.80 2008/10/10 05:00:12 stevesk Exp $ */ +/* $OpenBSD: key.c,v 1.90 2010/07/13 23:13:16 djm Exp $ */ /* * read_bignum(): * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -52,6 +52,23 @@ #include "uuencode.h" #include "buffer.h" #include "log.h" +#include "misc.h" +#include "ssh2.h" + +static struct KeyCert * +cert_new(void) +{ + struct KeyCert *cert; + + cert = xcalloc(1, sizeof(*cert)); + buffer_init(&cert->certblob); + buffer_init(&cert->critical); + buffer_init(&cert->extensions); + cert->key_id = NULL; + cert->principals = NULL; + cert->signature_key = NULL; + return cert; +} Key * key_new(int type) @@ -63,9 +80,12 @@ key_new(int type) k->type = type; k->dsa = NULL; k->rsa = NULL; + k->cert = NULL; switch (k->type) { case KEY_RSA1: case KEY_RSA: + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: if ((rsa = RSA_new()) == NULL) fatal("key_new: RSA_new failed"); if ((rsa->n = BN_new()) == NULL) @@ -75,6 +95,8 @@ key_new(int type) k->rsa = rsa; break; case KEY_DSA: + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: if ((dsa = DSA_new()) == NULL) fatal("key_new: DSA_new failed"); if ((dsa->p = BN_new()) == NULL) @@ -93,16 +115,21 @@ key_new(int type) fatal("key_new: bad key type %d", k->type); break; } + + if (key_is_cert(k)) + k->cert = cert_new(); + return k; } -Key * -key_new_private(int type) +void +key_add_private(Key *k) { - Key *k = key_new(type); switch (k->type) { case KEY_RSA1: case KEY_RSA: + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: if ((k->rsa->d = BN_new()) == NULL) fatal("key_new_private: BN_new failed"); if ((k->rsa->iqmp = BN_new()) == NULL) @@ -117,6 +144,8 @@ key_new_private(int type) fatal("key_new_private: BN_new failed"); break; case KEY_DSA: + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: if ((k->dsa->priv_key = BN_new()) == NULL) fatal("key_new_private: BN_new failed"); break; @@ -125,9 +154,35 @@ key_new_private(int type) default: break; } +} + +Key * +key_new_private(int type) +{ + Key *k = key_new(type); + + key_add_private(k); return k; } +static void +cert_free(struct KeyCert *cert) +{ + u_int i; + + buffer_free(&cert->certblob); + buffer_free(&cert->critical); + buffer_free(&cert->extensions); + if (cert->key_id != NULL) + xfree(cert->key_id); + for (i = 0; i < cert->nprincipals; i++) + xfree(cert->principals[i]); + if (cert->principals != NULL) + xfree(cert->principals); + if (cert->signature_key != NULL) + key_free(cert->signature_key); +} + void key_free(Key *k) { @@ -136,11 +191,15 @@ key_free(Key *k) switch (k->type) { case KEY_RSA1: case KEY_RSA: + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: if (k->rsa != NULL) RSA_free(k->rsa); k->rsa = NULL; break; case KEY_DSA: + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: if (k->dsa != NULL) DSA_free(k->dsa); k->dsa = NULL; @@ -151,20 +210,51 @@ key_free(Key *k) fatal("key_free: bad key type %d", k->type); break; } + if (key_is_cert(k)) { + if (k->cert != NULL) + cert_free(k->cert); + k->cert = NULL; + } + xfree(k); } +static int +cert_compare(struct KeyCert *a, struct KeyCert *b) +{ + if (a == NULL && b == NULL) + return 1; + if (a == NULL || b == NULL) + return 0; + if (buffer_len(&a->certblob) != buffer_len(&b->certblob)) + return 0; + if (timingsafe_bcmp(buffer_ptr(&a->certblob), buffer_ptr(&b->certblob), + buffer_len(&a->certblob)) != 0) + return 0; + return 1; +} + +/* + * Compare public portions of key only, allowing comparisons between + * certificates and plain keys too. + */ int -key_equal(const Key *a, const Key *b) +key_equal_public(const Key *a, const Key *b) { - if (a == NULL || b == NULL || a->type != b->type) + if (a == NULL || b == NULL || + key_type_plain(a->type) != key_type_plain(b->type)) return 0; + switch (a->type) { case KEY_RSA1: + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: case KEY_RSA: return a->rsa != NULL && b->rsa != NULL && BN_cmp(a->rsa->e, b->rsa->e) == 0 && BN_cmp(a->rsa->n, b->rsa->n) == 0; + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: case KEY_DSA: return a->dsa != NULL && b->dsa != NULL && BN_cmp(a->dsa->p, b->dsa->p) == 0 && @@ -177,16 +267,27 @@ key_equal(const Key *a, const Key *b) /* NOTREACHED */ } +int +key_equal(const Key *a, const Key *b) +{ + if (a == NULL || b == NULL || a->type != b->type) + return 0; + if (key_is_cert(a)) { + if (!cert_compare(a->cert, b->cert)) + return 0; + } + return key_equal_public(a, b); +} + u_char* -key_fingerprint_raw(const Key *k, enum fp_type dgst_type, - u_int *dgst_raw_length) +key_fingerprint_raw(Key *k, enum fp_type dgst_type, u_int *dgst_raw_length) { const EVP_MD *md = NULL; EVP_MD_CTX ctx; u_char *blob = NULL; u_char *retval = NULL; u_int len = 0; - int nlen, elen; + int nlen, elen, otype; *dgst_raw_length = 0; @@ -214,6 +315,16 @@ key_fingerprint_raw(const Key *k, enum fp_type dgst_type, case KEY_RSA: key_to_blob(k, &blob, &len); break; + case KEY_DSA_CERT_V00: + case KEY_RSA_CERT_V00: + case KEY_DSA_CERT: + case KEY_RSA_CERT: + /* We want a fingerprint of the _key_ not of the cert */ + otype = k->type; + k->type = key_type_plain(k->type); + key_to_blob(k, &blob, &len); + k->type = otype; + break; case KEY_UNSPEC: return retval; default: @@ -408,7 +519,7 @@ key_fingerprint_randomart(u_char *dgst_raw, u_int dgst_raw_len, const Key *k) } char * -key_fingerprint(const Key *k, enum fp_type dgst_type, enum fp_rep dgst_rep) +key_fingerprint(Key *k, enum fp_type dgst_type, enum fp_rep dgst_rep) { char *retval = NULL; u_char *dgst_raw; @@ -522,11 +633,21 @@ key_read(Key *ret, char **cpp) return -1; if (!read_bignum(cpp, ret->rsa->n)) return -1; + /* validate the claimed number of bits */ + if ((u_int)BN_num_bits(ret->rsa->n) != bits) { + verbose("key_read: claimed key size %d does not match " + "actual %d", bits, BN_num_bits(ret->rsa->n)); + return -1; + } success = 1; break; case KEY_UNSPEC: case KEY_RSA: case KEY_DSA: + case KEY_DSA_CERT_V00: + case KEY_RSA_CERT_V00: + case KEY_DSA_CERT: + case KEY_RSA_CERT: space = strchr(cp, ' '); if (space == NULL) { debug3("key_read: missing whitespace"); @@ -571,25 +692,36 @@ key_read(Key *ret, char **cpp) return -1; } /*XXXX*/ - if (ret->type == KEY_RSA) { + if (key_is_cert(ret)) { + if (!key_is_cert(k)) { + error("key_read: loaded key is not a cert"); + key_free(k); + return -1; + } + if (ret->cert != NULL) + cert_free(ret->cert); + ret->cert = k->cert; + k->cert = NULL; + } + if (key_type_plain(ret->type) == KEY_RSA) { if (ret->rsa != NULL) RSA_free(ret->rsa); ret->rsa = k->rsa; k->rsa = NULL; - success = 1; #ifdef DEBUG_PK RSA_print_fp(stderr, ret->rsa, 8); #endif - } else { + } + if (key_type_plain(ret->type) == KEY_DSA) { if (ret->dsa != NULL) DSA_free(ret->dsa); ret->dsa = k->dsa; k->dsa = NULL; - success = 1; #ifdef DEBUG_PK DSA_print_fp(stderr, ret->dsa, 8); #endif } + success = 1; /*XXXX*/ key_free(k); if (success != 1) @@ -616,28 +748,55 @@ key_write(const Key *key, FILE *f) u_char *blob; char *uu; - if (key->type == KEY_RSA1 && key->rsa != NULL) { + if (key_is_cert(key)) { + if (key->cert == NULL) { + error("%s: no cert data", __func__); + return 0; + } + if (buffer_len(&key->cert->certblob) == 0) { + error("%s: no signed certificate blob", __func__); + return 0; + } + } + + switch (key->type) { + case KEY_RSA1: + if (key->rsa == NULL) + return 0; /* size of modulus 'n' */ bits = BN_num_bits(key->rsa->n); fprintf(f, "%u", bits); if (write_bignum(f, key->rsa->e) && - write_bignum(f, key->rsa->n)) { - success = 1; - } else { - error("key_write: failed for RSA key"); - } - } else if ((key->type == KEY_DSA && key->dsa != NULL) || - (key->type == KEY_RSA && key->rsa != NULL)) { - key_to_blob(key, &blob, &len); - uu = xmalloc(2*len); - n = uuencode(blob, len, uu, 2*len); - if (n > 0) { - fprintf(f, "%s %s", key_ssh_name(key), uu); - success = 1; - } - xfree(blob); - xfree(uu); + write_bignum(f, key->rsa->n)) + return 1; + error("key_write: failed for RSA key"); + return 0; + case KEY_DSA: + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + if (key->dsa == NULL) + return 0; + break; + case KEY_RSA: + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + if (key->rsa == NULL) + return 0; + break; + default: + return 0; + } + + key_to_blob(key, &blob, &len); + uu = xmalloc(2*len); + n = uuencode(blob, len, uu, 2*len); + if (n > 0) { + fprintf(f, "%s %s", key_ssh_name(key), uu); + success = 1; } + xfree(blob); + xfree(uu); + return success; } @@ -651,10 +810,31 @@ key_type(const Key *k) return "RSA"; case KEY_DSA: return "DSA"; + case KEY_RSA_CERT_V00: + return "RSA-CERT-V00"; + case KEY_DSA_CERT_V00: + return "DSA-CERT-V00"; + case KEY_RSA_CERT: + return "RSA-CERT"; + case KEY_DSA_CERT: + return "DSA-CERT"; } return "unknown"; } +const char * +key_cert_type(const Key *k) +{ + switch (k->cert->type) { + case SSH2_CERT_TYPE_USER: + return "user"; + case SSH2_CERT_TYPE_HOST: + return "host"; + default: + return "unknown"; + } +} + const char * key_ssh_name(const Key *k) { @@ -663,6 +843,14 @@ key_ssh_name(const Key *k) return "ssh-rsa"; case KEY_DSA: return "ssh-dss"; + case KEY_RSA_CERT_V00: + return "ssh-rsa-cert-v00@openssh.com"; + case KEY_DSA_CERT_V00: + return "ssh-dss-cert-v00@openssh.com"; + case KEY_RSA_CERT: + return "ssh-rsa-cert-v01@openssh.com"; + case KEY_DSA_CERT: + return "ssh-dss-cert-v01@openssh.com"; } return "ssh-unknown"; } @@ -673,8 +861,12 @@ key_size(const Key *k) switch (k->type) { case KEY_RSA1: case KEY_RSA: + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: return BN_num_bits(k->rsa->n); case KEY_DSA: + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: return BN_num_bits(k->dsa->p); } return 0; @@ -685,7 +877,7 @@ rsa_generate_private_key(u_int bits) { RSA *private; - private = RSA_generate_key(bits, 35, NULL, NULL); + private = RSA_generate_key(bits, RSA_F4, NULL, NULL); if (private == NULL) fatal("rsa_generate_private_key: key generation failed."); return private; @@ -717,6 +909,11 @@ key_generate(int type, u_int bits) case KEY_RSA1: k->rsa = rsa_generate_private_key(bits); break; + case KEY_RSA_CERT_V00: + case KEY_DSA_CERT_V00: + case KEY_RSA_CERT: + case KEY_DSA_CERT: + fatal("key_generate: cert keys cannot be generated directly"); default: fatal("key_generate: unknown type %d", type); } @@ -724,12 +921,59 @@ key_generate(int type, u_int bits) return k; } +void +key_cert_copy(const Key *from_key, struct Key *to_key) +{ + u_int i; + const struct KeyCert *from; + struct KeyCert *to; + + if (to_key->cert != NULL) { + cert_free(to_key->cert); + to_key->cert = NULL; + } + + if ((from = from_key->cert) == NULL) + return; + + to = to_key->cert = cert_new(); + + buffer_append(&to->certblob, buffer_ptr(&from->certblob), + buffer_len(&from->certblob)); + + buffer_append(&to->critical, + buffer_ptr(&from->critical), buffer_len(&from->critical)); + buffer_append(&to->extensions, + buffer_ptr(&from->extensions), buffer_len(&from->extensions)); + + to->serial = from->serial; + to->type = from->type; + to->key_id = from->key_id == NULL ? NULL : xstrdup(from->key_id); + to->valid_after = from->valid_after; + to->valid_before = from->valid_before; + to->signature_key = from->signature_key == NULL ? + NULL : key_from_private(from->signature_key); + + to->nprincipals = from->nprincipals; + if (to->nprincipals > CERT_MAX_PRINCIPALS) + fatal("%s: nprincipals (%u) > CERT_MAX_PRINCIPALS (%u)", + __func__, to->nprincipals, CERT_MAX_PRINCIPALS); + if (to->nprincipals > 0) { + to->principals = xcalloc(from->nprincipals, + sizeof(*to->principals)); + for (i = 0; i < to->nprincipals; i++) + to->principals[i] = xstrdup(from->principals[i]); + } +} + Key * key_from_private(const Key *k) { Key *n = NULL; switch (k->type) { case KEY_DSA: + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: n = key_new(k->type); if ((BN_copy(n->dsa->p, k->dsa->p) == NULL) || (BN_copy(n->dsa->q, k->dsa->q) == NULL) || @@ -739,6 +983,8 @@ key_from_private(const Key *k) break; case KEY_RSA: case KEY_RSA1: + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: n = key_new(k->type); if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) || (BN_copy(n->rsa->e, k->rsa->e) == NULL)) @@ -748,6 +994,8 @@ key_from_private(const Key *k) fatal("key_from_private: unknown type %d", k->type); break; } + if (key_is_cert(k)) + key_cert_copy(k, n); return n; } @@ -764,6 +1012,14 @@ key_type_from_name(char *name) return KEY_RSA; } else if (strcmp(name, "ssh-dss") == 0) { return KEY_DSA; + } else if (strcmp(name, "ssh-rsa-cert-v00@openssh.com") == 0) { + return KEY_RSA_CERT_V00; + } else if (strcmp(name, "ssh-dss-cert-v00@openssh.com") == 0) { + return KEY_DSA_CERT_V00; + } else if (strcmp(name, "ssh-rsa-cert-v01@openssh.com") == 0) { + return KEY_RSA_CERT; + } else if (strcmp(name, "ssh-dss-cert-v01@openssh.com") == 0) { + return KEY_DSA_CERT; } debug2("key_type_from_name: unknown key type '%s'", name); return KEY_UNSPEC; @@ -791,6 +1047,146 @@ key_names_valid2(const char *names) return 1; } +static int +cert_parse(Buffer *b, Key *key, const u_char *blob, u_int blen) +{ + u_char *principals, *critical, *exts, *sig_key, *sig; + u_int signed_len, plen, clen, sklen, slen, kidlen, elen; + Buffer tmp; + char *principal; + int ret = -1; + int v00 = key->type == KEY_DSA_CERT_V00 || + key->type == KEY_RSA_CERT_V00; + + buffer_init(&tmp); + + /* Copy the entire key blob for verification and later serialisation */ + buffer_append(&key->cert->certblob, blob, blen); + + elen = 0; /* Not touched for v00 certs */ + principals = exts = critical = sig_key = sig = NULL; + if ((!v00 && buffer_get_int64_ret(&key->cert->serial, b) != 0) || + buffer_get_int_ret(&key->cert->type, b) != 0 || + (key->cert->key_id = buffer_get_string_ret(b, &kidlen)) == NULL || + (principals = buffer_get_string_ret(b, &plen)) == NULL || + buffer_get_int64_ret(&key->cert->valid_after, b) != 0 || + buffer_get_int64_ret(&key->cert->valid_before, b) != 0 || + (critical = buffer_get_string_ret(b, &clen)) == NULL || + (!v00 && (exts = buffer_get_string_ret(b, &elen)) == NULL) || + (v00 && buffer_get_string_ptr_ret(b, NULL) == NULL) || /* nonce */ + buffer_get_string_ptr_ret(b, NULL) == NULL || /* reserved */ + (sig_key = buffer_get_string_ret(b, &sklen)) == NULL) { + error("%s: parse error", __func__); + goto out; + } + + if (kidlen != strlen(key->cert->key_id)) { + error("%s: key ID contains \\0 character", __func__); + goto out; + } + + /* Signature is left in the buffer so we can calculate this length */ + signed_len = buffer_len(&key->cert->certblob) - buffer_len(b); + + if ((sig = buffer_get_string_ret(b, &slen)) == NULL) { + error("%s: parse error", __func__); + goto out; + } + + if (key->cert->type != SSH2_CERT_TYPE_USER && + key->cert->type != SSH2_CERT_TYPE_HOST) { + error("Unknown certificate type %u", key->cert->type); + goto out; + } + + buffer_append(&tmp, principals, plen); + while (buffer_len(&tmp) > 0) { + if (key->cert->nprincipals >= CERT_MAX_PRINCIPALS) { + error("%s: Too many principals", __func__); + goto out; + } + if ((principal = buffer_get_string_ret(&tmp, &plen)) == NULL) { + error("%s: Principals data invalid", __func__); + goto out; + } + if (strlen(principal) != plen) { + error("%s: Principal contains \\0 character", + __func__); + goto out; + } + key->cert->principals = xrealloc(key->cert->principals, + key->cert->nprincipals + 1, sizeof(*key->cert->principals)); + key->cert->principals[key->cert->nprincipals++] = principal; + } + + buffer_clear(&tmp); + + buffer_append(&key->cert->critical, critical, clen); + buffer_append(&tmp, critical, clen); + /* validate structure */ + while (buffer_len(&tmp) != 0) { + if (buffer_get_string_ptr_ret(&tmp, NULL) == NULL || + buffer_get_string_ptr_ret(&tmp, NULL) == NULL) { + error("%s: critical option data invalid", __func__); + goto out; + } + } + buffer_clear(&tmp); + + buffer_append(&key->cert->extensions, exts, elen); + buffer_append(&tmp, exts, elen); + /* validate structure */ + while (buffer_len(&tmp) != 0) { + if (buffer_get_string_ptr_ret(&tmp, NULL) == NULL || + buffer_get_string_ptr_ret(&tmp, NULL) == NULL) { + error("%s: extension data invalid", __func__); + goto out; + } + } + buffer_clear(&tmp); + + if ((key->cert->signature_key = key_from_blob(sig_key, + sklen)) == NULL) { + error("%s: Signature key invalid", __func__); + goto out; + } + if (key->cert->signature_key->type != KEY_RSA && + key->cert->signature_key->type != KEY_DSA) { + error("%s: Invalid signature key type %s (%d)", __func__, + key_type(key->cert->signature_key), + key->cert->signature_key->type); + goto out; + } + + switch (key_verify(key->cert->signature_key, sig, slen, + buffer_ptr(&key->cert->certblob), signed_len)) { + case 1: + ret = 0; + break; /* Good signature */ + case 0: + error("%s: Invalid signature on certificate", __func__); + goto out; + case -1: + error("%s: Certificate signature verification failed", + __func__); + goto out; + } + + out: + buffer_free(&tmp); + if (principals != NULL) + xfree(principals); + if (critical != NULL) + xfree(critical); + if (exts != NULL) + xfree(exts); + if (sig_key != NULL) + xfree(sig_key); + if (sig != NULL) + xfree(sig); + return ret; +} + Key * key_from_blob(const u_char *blob, u_int blen) { @@ -812,11 +1208,16 @@ key_from_blob(const u_char *blob, u_int blen) type = key_type_from_name(ktype); switch (type) { + case KEY_RSA_CERT: + (void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */ + /* FALLTHROUGH */ case KEY_RSA: + case KEY_RSA_CERT_V00: key = key_new(type); if (buffer_get_bignum2_ret(&b, key->rsa->e) == -1 || buffer_get_bignum2_ret(&b, key->rsa->n) == -1) { error("key_from_blob: can't read rsa key"); + badkey: key_free(key); key = NULL; goto out; @@ -825,16 +1226,18 @@ key_from_blob(const u_char *blob, u_int blen) RSA_print_fp(stderr, key->rsa, 8); #endif break; + case KEY_DSA_CERT: + (void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */ + /* FALLTHROUGH */ case KEY_DSA: + case KEY_DSA_CERT_V00: key = key_new(type); if (buffer_get_bignum2_ret(&b, key->dsa->p) == -1 || buffer_get_bignum2_ret(&b, key->dsa->q) == -1 || buffer_get_bignum2_ret(&b, key->dsa->g) == -1 || buffer_get_bignum2_ret(&b, key->dsa->pub_key) == -1) { error("key_from_blob: can't read dsa key"); - key_free(key); - key = NULL; - goto out; + goto badkey; } #ifdef DEBUG_PK DSA_print_fp(stderr, key->dsa, 8); @@ -847,6 +1250,10 @@ key_from_blob(const u_char *blob, u_int blen) error("key_from_blob: cannot handle type %s", ktype); goto out; } + if (key_is_cert(key) && cert_parse(&b, key, blob, blen) == -1) { + error("key_from_blob: can't parse cert data"); + goto badkey; + } rlen = buffer_len(&b); if (key != NULL && rlen != 0) error("key_from_blob: remaining bytes in key blob %d", rlen); @@ -869,6 +1276,14 @@ key_to_blob(const Key *key, u_char **blobp, u_int *lenp) } buffer_init(&b); switch (key->type) { + case KEY_DSA_CERT_V00: + case KEY_RSA_CERT_V00: + case KEY_DSA_CERT: + case KEY_RSA_CERT: + /* Use the existing blob */ + buffer_append(&b, buffer_ptr(&key->cert->certblob), + buffer_len(&key->cert->certblob)); + break; case KEY_DSA: buffer_put_cstring(&b, key_ssh_name(key)); buffer_put_bignum2(&b, key->dsa->p); @@ -905,8 +1320,12 @@ key_sign( const u_char *data, u_int datalen) { switch (key->type) { + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: case KEY_DSA: return ssh_dss_sign(key, sigp, lenp, data, datalen); + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: case KEY_RSA: return ssh_rsa_sign(key, sigp, lenp, data, datalen); default: @@ -929,8 +1348,12 @@ key_verify( return -1; switch (key->type) { + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: case KEY_DSA: return ssh_dss_verify(key, signature, signaturelen, data, datalen); + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: case KEY_RSA: return ssh_rsa_verify(key, signature, signaturelen, data, datalen); default: @@ -952,6 +1375,10 @@ key_demote(const Key *k) pk->rsa = NULL; switch (k->type) { + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + key_cert_copy(k, pk); + /* FALLTHROUGH */ case KEY_RSA1: case KEY_RSA: if ((pk->rsa = RSA_new()) == NULL) @@ -961,6 +1388,10 @@ key_demote(const Key *k) if ((pk->rsa->n = BN_dup(k->rsa->n)) == NULL) fatal("key_demote: BN_dup failed"); break; + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + key_cert_copy(k, pk); + /* FALLTHROUGH */ case KEY_DSA: if ((pk->dsa = DSA_new()) == NULL) fatal("key_demote: DSA_new failed"); @@ -980,3 +1411,244 @@ key_demote(const Key *k) return (pk); } + +int +key_is_cert(const Key *k) +{ + if (k == NULL) + return 0; + switch (k->type) { + case KEY_RSA_CERT_V00: + case KEY_DSA_CERT_V00: + case KEY_RSA_CERT: + case KEY_DSA_CERT: + return 1; + default: + return 0; + } +} + +/* Return the cert-less equivalent to a certified key type */ +int +key_type_plain(int type) +{ + switch (type) { + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + return KEY_RSA; + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + return KEY_DSA; + default: + return type; + } +} + +/* Convert a KEY_RSA or KEY_DSA to their _CERT equivalent */ +int +key_to_certified(Key *k, int legacy) +{ + switch (k->type) { + case KEY_RSA: + k->cert = cert_new(); + k->type = legacy ? KEY_RSA_CERT_V00 : KEY_RSA_CERT; + return 0; + case KEY_DSA: + k->cert = cert_new(); + k->type = legacy ? KEY_DSA_CERT_V00 : KEY_DSA_CERT; + return 0; + default: + error("%s: key has incorrect type %s", __func__, key_type(k)); + return -1; + } +} + +/* Convert a KEY_RSA_CERT or KEY_DSA_CERT to their raw key equivalent */ +int +key_drop_cert(Key *k) +{ + switch (k->type) { + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + cert_free(k->cert); + k->type = KEY_RSA; + return 0; + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + cert_free(k->cert); + k->type = KEY_DSA; + return 0; + default: + error("%s: key has incorrect type %s", __func__, key_type(k)); + return -1; + } +} + +/* Sign a KEY_RSA_CERT or KEY_DSA_CERT, (re-)generating the signed certblob */ +int +key_certify(Key *k, Key *ca) +{ + Buffer principals; + u_char *ca_blob, *sig_blob, nonce[32]; + u_int i, ca_len, sig_len; + + if (k->cert == NULL) { + error("%s: key lacks cert info", __func__); + return -1; + } + + if (!key_is_cert(k)) { + error("%s: certificate has unknown type %d", __func__, + k->cert->type); + return -1; + } + + if (ca->type != KEY_RSA && ca->type != KEY_DSA) { + error("%s: CA key has unsupported type %s", __func__, + key_type(ca)); + return -1; + } + + key_to_blob(ca, &ca_blob, &ca_len); + + buffer_clear(&k->cert->certblob); + buffer_put_cstring(&k->cert->certblob, key_ssh_name(k)); + + /* -v01 certs put nonce first */ + if (k->type == KEY_DSA_CERT || k->type == KEY_RSA_CERT) { + arc4random_buf(&nonce, sizeof(nonce)); + buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce)); + } + + switch (k->type) { + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + buffer_put_bignum2(&k->cert->certblob, k->dsa->p); + buffer_put_bignum2(&k->cert->certblob, k->dsa->q); + buffer_put_bignum2(&k->cert->certblob, k->dsa->g); + buffer_put_bignum2(&k->cert->certblob, k->dsa->pub_key); + break; + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + buffer_put_bignum2(&k->cert->certblob, k->rsa->e); + buffer_put_bignum2(&k->cert->certblob, k->rsa->n); + break; + default: + error("%s: key has incorrect type %s", __func__, key_type(k)); + buffer_clear(&k->cert->certblob); + xfree(ca_blob); + return -1; + } + + /* -v01 certs have a serial number next */ + if (k->type == KEY_DSA_CERT || k->type == KEY_RSA_CERT) + buffer_put_int64(&k->cert->certblob, k->cert->serial); + + buffer_put_int(&k->cert->certblob, k->cert->type); + buffer_put_cstring(&k->cert->certblob, k->cert->key_id); + + buffer_init(&principals); + for (i = 0; i < k->cert->nprincipals; i++) + buffer_put_cstring(&principals, k->cert->principals[i]); + buffer_put_string(&k->cert->certblob, buffer_ptr(&principals), + buffer_len(&principals)); + buffer_free(&principals); + + buffer_put_int64(&k->cert->certblob, k->cert->valid_after); + buffer_put_int64(&k->cert->certblob, k->cert->valid_before); + buffer_put_string(&k->cert->certblob, + buffer_ptr(&k->cert->critical), buffer_len(&k->cert->critical)); + + /* -v01 certs have non-critical options here */ + if (k->type == KEY_DSA_CERT || k->type == KEY_RSA_CERT) { + buffer_put_string(&k->cert->certblob, + buffer_ptr(&k->cert->extensions), + buffer_len(&k->cert->extensions)); + } + + /* -v00 certs put the nonce at the end */ + if (k->type == KEY_DSA_CERT_V00 || k->type == KEY_RSA_CERT_V00) + buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce)); + + buffer_put_string(&k->cert->certblob, NULL, 0); /* reserved */ + buffer_put_string(&k->cert->certblob, ca_blob, ca_len); + xfree(ca_blob); + + /* Sign the whole mess */ + if (key_sign(ca, &sig_blob, &sig_len, buffer_ptr(&k->cert->certblob), + buffer_len(&k->cert->certblob)) != 0) { + error("%s: signature operation failed", __func__); + buffer_clear(&k->cert->certblob); + return -1; + } + /* Append signature and we are done */ + buffer_put_string(&k->cert->certblob, sig_blob, sig_len); + xfree(sig_blob); + + return 0; +} + +int +key_cert_check_authority(const Key *k, int want_host, int require_principal, + const char *name, const char **reason) +{ + u_int i, principal_matches; + time_t now = time(NULL); + + if (want_host) { + if (k->cert->type != SSH2_CERT_TYPE_HOST) { + *reason = "Certificate invalid: not a host certificate"; + return -1; + } + } else { + if (k->cert->type != SSH2_CERT_TYPE_USER) { + *reason = "Certificate invalid: not a user certificate"; + return -1; + } + } + if (now < 0) { + error("%s: system clock lies before epoch", __func__); + *reason = "Certificate invalid: not yet valid"; + return -1; + } + if ((u_int64_t)now < k->cert->valid_after) { + *reason = "Certificate invalid: not yet valid"; + return -1; + } + if ((u_int64_t)now >= k->cert->valid_before) { + *reason = "Certificate invalid: expired"; + return -1; + } + if (k->cert->nprincipals == 0) { + if (require_principal) { + *reason = "Certificate lacks principal list"; + return -1; + } + } else if (name != NULL) { + principal_matches = 0; + for (i = 0; i < k->cert->nprincipals; i++) { + if (strcmp(name, k->cert->principals[i]) == 0) { + principal_matches = 1; + break; + } + } + if (!principal_matches) { + *reason = "Certificate invalid: name is not a listed " + "principal"; + return -1; + } + } + return 0; +} + +int +key_cert_is_legacy(Key *k) +{ + switch (k->type) { + case KEY_DSA_CERT_V00: + case KEY_RSA_CERT_V00: + return 1; + default: + return 0; + } +} diff --git a/crypto/openssh/key.h b/crypto/openssh/key.h index 14aac79c2d..11d30eae68 100644 --- a/crypto/openssh/key.h +++ b/crypto/openssh/key.h @@ -1,4 +1,4 @@ -/* $OpenBSD: key.h,v 1.27 2008/06/11 21:01:35 grunk Exp $ */ +/* $OpenBSD: key.h,v 1.30 2010/04/16 01:47:26 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -26,6 +26,7 @@ #ifndef KEY_H #define KEY_H +#include "buffer.h" #include #include @@ -34,6 +35,10 @@ enum types { KEY_RSA1, KEY_RSA, KEY_DSA, + KEY_RSA_CERT, + KEY_DSA_CERT, + KEY_RSA_CERT_V00, + KEY_DSA_CERT_V00, KEY_UNSPEC }; enum fp_type { @@ -49,21 +54,39 @@ enum fp_rep { /* key is stored in external hardware */ #define KEY_FLAG_EXT 0x0001 +#define CERT_MAX_PRINCIPALS 256 +struct KeyCert { + Buffer certblob; /* Kept around for use on wire */ + u_int type; /* SSH2_CERT_TYPE_USER or SSH2_CERT_TYPE_HOST */ + u_int64_t serial; + char *key_id; + u_int nprincipals; + char **principals; + u_int64_t valid_after, valid_before; + Buffer critical; + Buffer extensions; + Key *signature_key; +}; + struct Key { int type; int flags; RSA *rsa; DSA *dsa; + struct KeyCert *cert; }; Key *key_new(int); +void key_add_private(Key *); Key *key_new_private(int); void key_free(Key *); Key *key_demote(const Key *); +int key_equal_public(const Key *, const Key *); int key_equal(const Key *, const Key *); -char *key_fingerprint(const Key *, enum fp_type, enum fp_rep); -u_char *key_fingerprint_raw(const Key *, enum fp_type, u_int *); +char *key_fingerprint(Key *, enum fp_type, enum fp_rep); +u_char *key_fingerprint_raw(Key *, enum fp_type, u_int *); const char *key_type(const Key *); +const char *key_cert_type(const Key *); int key_write(const Key *, FILE *); int key_read(Key *, char **); u_int key_size(const Key *); @@ -71,6 +94,15 @@ u_int key_size(const Key *); Key *key_generate(int, u_int); Key *key_from_private(const Key *); int key_type_from_name(char *); +int key_is_cert(const Key *); +int key_type_plain(int); +int key_to_certified(Key *, int); +int key_drop_cert(Key *); +int key_certify(Key *, Key *); +void key_cert_copy(const Key *, struct Key *); +int key_cert_check_authority(const Key *, int, int, const char *, + const char **); +int key_cert_is_legacy(Key *); Key *key_from_blob(const u_char *, u_int); int key_to_blob(const Key *, u_char **, u_int *); diff --git a/crypto/openssh/loginrec.c b/crypto/openssh/loginrec.c index f4af067360..6f655cb16c 100644 --- a/crypto/openssh/loginrec.c +++ b/crypto/openssh/loginrec.c @@ -207,6 +207,7 @@ int syslogin_write_entry(struct logininfo *li); int getlast_entry(struct logininfo *li); int lastlog_get_entry(struct logininfo *li); +int utmpx_get_entry(struct logininfo *li); int wtmp_get_entry(struct logininfo *li); int wtmpx_get_entry(struct logininfo *li); @@ -508,6 +509,10 @@ getlast_entry(struct logininfo *li) #ifdef USE_LASTLOG return(lastlog_get_entry(li)); #else /* !USE_LASTLOG */ +#if defined(USE_UTMPX) && defined(HAVE_SETUTXDB) && \ + defined(UTXDB_LASTLOGIN) && defined(HAVE_GETUTXUSER) + return (utmpx_get_entry(li)); +#endif #if defined(DISABLE_LASTLOG) /* On some systems we shouldn't even try to obtain last login @@ -758,8 +763,8 @@ construct_utmpx(struct logininfo *li, struct utmpx *utx) utx->ut_pid = li->pid; /* strncpy(): Don't necessarily want null termination */ - strncpy(utx->ut_name, li->username, - MIN_SIZEOF(utx->ut_name, li->username)); + strncpy(utx->ut_user, li->username, + MIN_SIZEOF(utx->ut_user, li->username)); if (li->type == LTYPE_LOGOUT) return; @@ -1316,8 +1321,8 @@ wtmpx_write_entry(struct logininfo *li) static int wtmpx_islogin(struct logininfo *li, struct utmpx *utx) { - if (strncmp(li->username, utx->ut_name, - MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) { + if (strncmp(li->username, utx->ut_user, + MIN_SIZEOF(li->username, utx->ut_user)) == 0 ) { # ifdef HAVE_TYPE_IN_UTMPX if (utx->ut_type == USER_PROCESS) return (1); @@ -1608,6 +1613,32 @@ lastlog_get_entry(struct logininfo *li) #endif /* HAVE_GETLASTLOGXBYNAME */ #endif /* USE_LASTLOG */ +#if defined(USE_UTMPX) && defined(HAVE_SETUTXDB) && \ + defined(UTXDB_LASTLOGIN) && defined(HAVE_GETUTXUSER) +int +utmpx_get_entry(struct logininfo *li) +{ + struct utmpx *utx; + + if (setutxdb(UTXDB_LASTLOGIN, NULL) != 0) + return (0); + utx = getutxuser(li->username); + if (utx == NULL) { + endutxent(); + return (0); + } + + line_fullname(li->line, utx->ut_line, + MIN_SIZEOF(li->line, utx->ut_line)); + strlcpy(li->hostname, utx->ut_host, + MIN_SIZEOF(li->hostname, utx->ut_host)); + li->tv_sec = utx->ut_tv.tv_sec; + li->tv_usec = utx->ut_tv.tv_usec; + endutxent(); + return (1); +} +#endif /* USE_UTMPX && HAVE_SETUTXDB && UTXDB_LASTLOGIN && HAVE_GETUTXUSER */ + #ifdef USE_BTMP /* * Logs failed login attempts in _PATH_BTMP if that exists. diff --git a/crypto/openssh/loginrec.h b/crypto/openssh/loginrec.h index 859e1a6304..84b4865900 100644 --- a/crypto/openssh/loginrec.h +++ b/crypto/openssh/loginrec.h @@ -56,7 +56,7 @@ union login_netinfo { /* string lengths - set very long */ #define LINFO_PROGSIZE 64 #define LINFO_LINESIZE 64 -#define LINFO_NAMESIZE 128 +#define LINFO_NAMESIZE 512 #define LINFO_HOSTSIZE 256 struct logininfo { diff --git a/crypto/openssh/match.h b/crypto/openssh/match.h index 18f6830703..3d7f70fc01 100644 --- a/crypto/openssh/match.h +++ b/crypto/openssh/match.h @@ -1,4 +1,4 @@ -/* $OpenBSD: match.h,v 1.14 2008/06/10 03:57:27 djm Exp $ */ +/* $OpenBSD: match.h,v 1.15 2010/02/26 20:29:54 djm Exp $ */ /* * Author: Tatu Ylonen @@ -23,5 +23,5 @@ char *match_list(const char *, const char *, u_int *); /* addrmatch.c */ int addr_match_list(const char *, const char *); - +int addr_match_cidr_list(const char *, const char *); #endif diff --git a/crypto/openssh/misc.c b/crypto/openssh/misc.c index 143dbf0e2d..a82e7936ed 100644 --- a/crypto/openssh/misc.c +++ b/crypto/openssh/misc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.c,v 1.71 2009/02/21 19:32:04 tobias Exp $ */ +/* $OpenBSD: misc.c,v 1.80 2010/07/21 02:10:58 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2005,2006 Damien Miller. All rights reserved. @@ -178,6 +178,7 @@ strdelim(char **s) return (NULL); /* no matching quote */ } else { *s[0] = '\0'; + *s += strspn(*s + 1, WHITESPACE) + 1; return (old); } } @@ -425,7 +426,7 @@ colon(char *cp) int flag = 0; if (*cp == ':') /* Leading colon is part of file name. */ - return (0); + return NULL; if (*cp == '[') flag = 1; @@ -437,9 +438,9 @@ colon(char *cp) if (*cp == ':' && !flag) return (cp); if (*cp == '/') - return (0); + return NULL; } - return (0); + return NULL; } /* function to assist building execv() arguments */ @@ -560,11 +561,11 @@ char * percent_expand(const char *string, ...) { #define EXPAND_MAX_KEYS 16 + u_int num_keys, i, j; struct { const char *key; const char *repl; } keys[EXPAND_MAX_KEYS]; - u_int num_keys, i, j; char buf[4096]; va_list ap; @@ -576,13 +577,12 @@ percent_expand(const char *string, ...) break; keys[num_keys].repl = va_arg(ap, char *); if (keys[num_keys].repl == NULL) - fatal("percent_expand: NULL replacement"); + fatal("%s: NULL replacement", __func__); } + if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *) != NULL) + fatal("%s: too many keys", __func__); va_end(ap); - if (num_keys >= EXPAND_MAX_KEYS) - fatal("percent_expand: too many keys"); - /* Expand string */ *buf = '\0'; for (i = 0; *string != '\0'; string++) { @@ -590,23 +590,24 @@ percent_expand(const char *string, ...) append: buf[i++] = *string; if (i >= sizeof(buf)) - fatal("percent_expand: string too long"); + fatal("%s: string too long", __func__); buf[i] = '\0'; continue; } string++; + /* %% case */ if (*string == '%') goto append; for (j = 0; j < num_keys; j++) { if (strchr(keys[j].key, *string) != NULL) { i = strlcat(buf, keys[j].repl, sizeof(buf)); if (i >= sizeof(buf)) - fatal("percent_expand: string too long"); + fatal("%s: string too long", __func__); break; } } if (j >= num_keys) - fatal("percent_expand: unknown key %%%c", *string); + fatal("%s: unknown key %%%c", __func__, *string); } return (xstrdup(buf)); #undef EXPAND_MAX_KEYS @@ -849,3 +850,24 @@ ms_to_timeval(struct timeval *tv, int ms) tv->tv_usec = (ms % 1000) * 1000; } +int +timingsafe_bcmp(const void *b1, const void *b2, size_t n) +{ + const unsigned char *p1 = b1, *p2 = b2; + int ret = 0; + + for (; n > 0; n--) + ret |= *p1++ ^ *p2++; + return (ret != 0); +} +void +sock_set_v6only(int s) +{ +#ifdef IPV6_V6ONLY + int on = 1; + + debug3("%s: set socket %d IPV6_V6ONLY", __func__, s); + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) + error("setsockopt IPV6_V6ONLY: %s", strerror(errno)); +#endif +} diff --git a/crypto/openssh/misc.h b/crypto/openssh/misc.h index 5da170d2fd..bb799f6163 100644 --- a/crypto/openssh/misc.h +++ b/crypto/openssh/misc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.h,v 1.38 2008/06/12 20:38:28 dtucker Exp $ */ +/* $OpenBSD: misc.h,v 1.43 2010/07/13 23:13:16 djm Exp $ */ /* * Author: Tatu Ylonen @@ -35,6 +35,8 @@ char *tohex(const void *, size_t); void sanitise_stdfd(void); void ms_subtract_diff(struct timeval *, int *); void ms_to_timeval(struct timeval *, int); +void sock_set_v6only(int); +int timingsafe_bcmp(const void *, const void *, size_t); struct passwd *pwcopy(struct passwd *); const char *ssh_gai_strerror(int); diff --git a/crypto/openssh/monitor.c b/crypto/openssh/monitor.c index ace25c4047..9eb4e35c97 100644 --- a/crypto/openssh/monitor.c +++ b/crypto/openssh/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.104 2009/06/12 20:43:22 andreas Exp $ */ +/* $OpenBSD: monitor.c,v 1.108 2010/07/13 23:13:16 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -518,7 +518,7 @@ monitor_allowed_key(u_char *blob, u_int bloblen) { /* make sure key is allowed */ if (key_blob == NULL || key_bloblen != bloblen || - memcmp(key_blob, blob, key_bloblen)) + timingsafe_bcmp(key_blob, blob, key_bloblen)) return (0); return (1); } @@ -922,8 +922,8 @@ mm_answer_pam_init_ctx(int sock, Buffer *m) int mm_answer_pam_query(int sock, Buffer *m) { - char *name, *info, **prompts; - u_int i, num, *echo_on; + char *name = NULL, *info = NULL, **prompts = NULL; + u_int i, num = 0, *echo_on = 0; int ret; debug3("%s", __func__); @@ -997,17 +997,6 @@ mm_answer_pam_free_ctx(int sock, Buffer *m) } #endif -static void -mm_append_debug(Buffer *m) -{ - if (auth_debug_init && buffer_len(&auth_debug)) { - debug3("%s: Appending debug messages for child", __func__); - buffer_append(m, buffer_ptr(&auth_debug), - buffer_len(&auth_debug)); - buffer_clear(&auth_debug); - } -} - int mm_answer_keyallowed(int sock, Buffer *m) { @@ -1090,8 +1079,6 @@ mm_answer_keyallowed(int sock, Buffer *m) buffer_put_int(m, allowed); buffer_put_int(m, forced_command != NULL); - mm_append_debug(m); - mm_request_send(sock, MONITOR_ANS_KEYALLOWED, m); if (type == MM_RSAHOSTKEY) @@ -1116,14 +1103,14 @@ monitor_valid_userblob(u_char *data, u_int datalen) len = buffer_len(&b); if ((session_id2 == NULL) || (len < session_id2_len) || - (memcmp(p, session_id2, session_id2_len) != 0)) + (timingsafe_bcmp(p, session_id2, session_id2_len) != 0)) fail++; buffer_consume(&b, session_id2_len); } else { p = buffer_get_string(&b, &len); if ((session_id2 == NULL) || (len != session_id2_len) || - (memcmp(p, session_id2, session_id2_len) != 0)) + (timingsafe_bcmp(p, session_id2, session_id2_len) != 0)) fail++; xfree(p); } @@ -1171,7 +1158,7 @@ monitor_valid_hostbasedblob(u_char *data, u_int datalen, char *cuser, p = buffer_get_string(&b, &len); if ((session_id2 == NULL) || (len != session_id2_len) || - (memcmp(p, session_id2, session_id2_len) != 0)) + (timingsafe_bcmp(p, session_id2, session_id2_len) != 0)) fail++; xfree(p); @@ -1475,8 +1462,6 @@ mm_answer_rsa_keyallowed(int sock, Buffer *m) if (key != NULL) key_free(key); - mm_append_debug(m); - mm_request_send(sock, MONITOR_ANS_RSAKEYALLOWED, m); monitor_permit(mon_dispatch, MONITOR_REQ_RSACHALLENGE, allowed); @@ -1697,9 +1682,9 @@ mm_get_kex(Buffer *m) kex = xcalloc(1, sizeof(*kex)); kex->session_id = buffer_get_string(m, &kex->session_id_len); - if ((session_id2 == NULL) || - (kex->session_id_len != session_id2_len) || - (memcmp(kex->session_id, session_id2, session_id2_len) != 0)) + if (session_id2 == NULL || + kex->session_id_len != session_id2_len || + timingsafe_bcmp(kex->session_id, session_id2, session_id2_len) != 0) fatal("mm_get_get: internal error: bad session id"); kex->we_need = buffer_get_int(m); kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; @@ -1721,7 +1706,8 @@ mm_get_kex(Buffer *m) kex->flags = buffer_get_int(m); kex->client_version_string = buffer_get_string(m, NULL); kex->server_version_string = buffer_get_string(m, NULL); - kex->load_host_key=&get_hostkey_by_type; + kex->load_host_public_key=&get_hostkey_public_by_type; + kex->load_host_private_key=&get_hostkey_private_by_type; kex->host_key_index=&get_hostkey_index; return (kex); diff --git a/crypto/openssh/monitor_fdpass.c b/crypto/openssh/monitor_fdpass.c index 4b9a066bcf..7eb6f5c6e1 100644 --- a/crypto/openssh/monitor_fdpass.c +++ b/crypto/openssh/monitor_fdpass.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor_fdpass.c,v 1.18 2008/11/30 11:59:26 dtucker Exp $ */ +/* $OpenBSD: monitor_fdpass.c,v 1.19 2010/01/12 00:58:25 djm Exp $ */ /* * Copyright 2001 Niels Provos * All rights reserved. @@ -34,6 +34,9 @@ #endif #include +#ifdef HAVE_POLL_H +#include +#endif #include #include @@ -55,6 +58,7 @@ mm_send_fd(int sock, int fd) struct iovec vec; char ch = '\0'; ssize_t n; + struct pollfd pfd; memset(&msg, 0, sizeof(msg)); #ifdef HAVE_ACCRIGHTS_IN_MSGHDR @@ -75,9 +79,13 @@ mm_send_fd(int sock, int fd) msg.msg_iov = &vec; msg.msg_iovlen = 1; - while ((n = sendmsg(sock, &msg, 0)) == -1 && (errno == EAGAIN || - errno == EINTR)) + pfd.fd = sock; + pfd.events = POLLOUT; + while ((n = sendmsg(sock, &msg, 0)) == -1 && + (errno == EAGAIN || errno == EINTR)) { debug3("%s: sendmsg(%d): %s", __func__, fd, strerror(errno)); + (void)poll(&pfd, 1, -1); + } if (n == -1) { error("%s: sendmsg(%d): %s", __func__, fd, strerror(errno)); @@ -112,6 +120,7 @@ mm_receive_fd(int sock) ssize_t n; char ch; int fd; + struct pollfd pfd; memset(&msg, 0, sizeof(msg)); vec.iov_base = &ch; @@ -126,9 +135,13 @@ mm_receive_fd(int sock) msg.msg_controllen = sizeof(cmsgbuf.buf); #endif - while ((n = recvmsg(sock, &msg, 0)) == -1 && (errno == EAGAIN || - errno == EINTR)) + pfd.fd = sock; + pfd.events = POLLIN; + while ((n = recvmsg(sock, &msg, 0)) == -1 && + (errno == EAGAIN || errno == EINTR)) { debug3("%s: recvmsg: %s", __func__, strerror(errno)); + (void)poll(&pfd, 1, -1); + } if (n == -1) { error("%s: recvmsg: %s", __func__, strerror(errno)); return -1; diff --git a/crypto/openssh/monitor_wrap.c b/crypto/openssh/monitor_wrap.c index b8e8710f71..faeb02cfa3 100644 --- a/crypto/openssh/monitor_wrap.c +++ b/crypto/openssh/monitor_wrap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor_wrap.c,v 1.68 2009/06/22 05:39:28 dtucker Exp $ */ +/* $OpenBSD: monitor_wrap.c,v 1.69 2010/03/07 11:57:13 dtucker Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -347,19 +347,6 @@ mm_auth_rhosts_rsa_key_allowed(struct passwd *pw, char *user, return (ret); } -static void -mm_send_debug(Buffer *m) -{ - char *msg; - - while (buffer_len(m)) { - msg = buffer_get_string(m, NULL); - debug3("%s: Sending debug: %s", __func__, msg); - packet_send_debug("%s", msg); - xfree(msg); - } -} - int mm_key_allowed(enum mm_keytype type, char *user, char *host, Key *key) { @@ -393,9 +380,6 @@ mm_key_allowed(enum mm_keytype type, char *user, char *host, Key *key) have_forced = buffer_get_int(&m); forced_command = have_forced ? xstrdup("true") : NULL; - /* Send potential debug messages */ - mm_send_debug(&m); - buffer_free(&m); return (allowed); @@ -1085,7 +1069,6 @@ mm_auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) *rkey = key; xfree(blob); } - mm_send_debug(&m); buffer_free(&m); return (allowed); diff --git a/crypto/openssh/mux.c b/crypto/openssh/mux.c index 79f83768b3..5c3857ee81 100644 --- a/crypto/openssh/mux.c +++ b/crypto/openssh/mux.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mux.c,v 1.7 2008/06/13 17:21:20 dtucker Exp $ */ +/* $OpenBSD: mux.c,v 1.21 2010/06/25 23:15:36 djm Exp $ */ /* * Copyright (c) 2002-2008 Damien Miller * @@ -17,25 +17,21 @@ /* 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 + * - 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. + * - Better fall-back from mux slave error to new connection. + * - ExitOnForwardingFailure + * - Maybe extension mechanisms for multi-X11/multi-agent forwarding + * - Support ~^Z in mux slaves. + * - Inspect or control sessions in master. + * - If we ever support the "signal" channel request, send signals on + * sessions in master. */ +#include "includes.h" + #include #include #include @@ -55,6 +51,14 @@ #include #endif +#ifdef HAVE_POLL_H +#include +#else +# ifdef HAVE_SYS_POLL_H +# include +# endif +#endif + #ifdef HAVE_UTIL_H # include #endif @@ -67,6 +71,7 @@ #include "xmalloc.h" #include "log.h" #include "ssh.h" +#include "ssh2.h" #include "pathnames.h" #include "misc.h" #include "match.h" @@ -82,27 +87,42 @@ /* from ssh.c */ extern int tty_flag; +extern int force_tty_flag; extern Options options; extern int stdin_null_flag; extern char *host; -int subsystem_flag; +extern int subsystem_flag; extern Buffer command; +extern volatile sig_atomic_t quit_pending; +extern char *stdio_forward_host; +extern int stdio_forward_port; /* 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; + u_int want_tty; + u_int want_subsys; + u_int want_x_fwd; + u_int want_agent_fwd; Buffer cmd; char *term; struct termios tio; char **env; + u_int rid; +}; + +/* Context for global channel callback */ +struct mux_channel_confirm_ctx { + u_int cid; /* channel id */ + u_int rid; /* request id */ + int fid; /* forward id */ }; /* fd to control socket */ int muxserver_sock = -1; +/* client request id */ +u_int muxclient_request_id = 0; + /* Multiplexing control command */ u_int muxclient_command = 0; @@ -112,269 +132,244 @@ static volatile sig_atomic_t muxclient_terminate = 0; /* PID of multiplex server */ static u_int muxserver_pid = 0; +static Channel *mux_listener_channel = NULL; -/* ** 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"); +struct mux_master_state { + int hello_rcvd; +}; - if ((muxserver_sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) - fatal("%s socket(): %s", __func__, strerror(errno)); +/* mux protocol messages */ +#define MUX_MSG_HELLO 0x00000001 +#define MUX_C_NEW_SESSION 0x10000002 +#define MUX_C_ALIVE_CHECK 0x10000004 +#define MUX_C_TERMINATE 0x10000005 +#define MUX_C_OPEN_FWD 0x10000006 +#define MUX_C_CLOSE_FWD 0x10000007 +#define MUX_C_NEW_STDIO_FWD 0x10000008 +#define MUX_S_OK 0x80000001 +#define MUX_S_PERMISSION_DENIED 0x80000002 +#define MUX_S_FAILURE 0x80000003 +#define MUX_S_EXIT_MESSAGE 0x80000004 +#define MUX_S_ALIVE 0x80000005 +#define MUX_S_SESSION_OPENED 0x80000006 +#define MUX_S_REMOTE_PORT 0x80000007 + +/* type codes for MUX_C_OPEN_FWD and MUX_C_CLOSE_FWD */ +#define MUX_FWD_LOCAL 1 +#define MUX_FWD_REMOTE 2 +#define MUX_FWD_DYNAMIC 3 + +static void mux_session_confirm(int, int, void *); + +static int process_mux_master_hello(u_int, Channel *, Buffer *, Buffer *); +static int process_mux_new_session(u_int, Channel *, Buffer *, Buffer *); +static int process_mux_alive_check(u_int, Channel *, Buffer *, Buffer *); +static int process_mux_terminate(u_int, Channel *, Buffer *, Buffer *); +static int process_mux_open_fwd(u_int, Channel *, Buffer *, Buffer *); +static int process_mux_close_fwd(u_int, Channel *, Buffer *, Buffer *); +static int process_mux_stdio_fwd(u_int, Channel *, Buffer *, Buffer *); + +static const struct { + u_int type; + int (*handler)(u_int, Channel *, Buffer *, Buffer *); +} mux_master_handlers[] = { + { MUX_MSG_HELLO, process_mux_master_hello }, + { MUX_C_NEW_SESSION, process_mux_new_session }, + { MUX_C_ALIVE_CHECK, process_mux_alive_check }, + { MUX_C_TERMINATE, process_mux_terminate }, + { MUX_C_OPEN_FWD, process_mux_open_fwd }, + { MUX_C_CLOSE_FWD, process_mux_close_fwd }, + { MUX_C_NEW_STDIO_FWD, process_mux_stdio_fwd }, + { 0, NULL } +}; - 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)); +/* Cleanup callback fired on closure of mux slave _session_ channel */ +/* ARGSUSED */ +static void +mux_master_session_cleanup_cb(int cid, void *unused) +{ + Channel *cc, *c = channel_by_id(cid); + + debug3("%s: entering for channel %d", __func__, cid); + if (c == NULL) + fatal("%s: channel_by_id(%i) == NULL", __func__, cid); + if (c->ctl_chan != -1) { + if ((cc = channel_by_id(c->ctl_chan)) == NULL) + fatal("%s: channel %d missing control channel %d", + __func__, c->self, c->ctl_chan); + c->ctl_chan = -1; + cc->remote_id = -1; + chan_rcvd_oclose(cc); } - umask(old_umask); - - if (listen(muxserver_sock, 64) == -1) - fatal("%s listen(): %s", __func__, strerror(errno)); - - set_nonblock(muxserver_sock); + channel_cancel_cleanup(c->self); } -/* Callback on open confirmation in mux master for a mux client session. */ +/* Cleanup callback fired on closure of mux slave _control_ channel */ +/* ARGSUSED */ static void -mux_session_confirm(int id, void *arg) +mux_master_control_cleanup_cb(int cid, void *unused) { - 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); + Channel *sc, *c = channel_by_id(cid); + + debug3("%s: entering for channel %d", __func__, cid); + if (c == NULL) + fatal("%s: channel_by_id(%i) == NULL", __func__, cid); + if (c->remote_id != -1) { + if ((sc = channel_by_id(c->remote_id)) == NULL) + fatal("%s: channel %d missing session channel %d", + __func__, c->self, c->remote_id); + c->remote_id = -1; + sc->ctl_chan = -1; + if (sc->type != SSH_CHANNEL_OPEN) { + debug2("%s: channel %d: not open", __func__, sc->self); + chan_mark_dead(sc); + } else { + if (sc->istate == CHAN_INPUT_OPEN) + chan_read_failed(sc); + if (sc->ostate == CHAN_OUTPUT_OPEN) + chan_write_failed(sc); + } } - xfree(cctx); + channel_cancel_cleanup(c->self); } -/* - * 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) +/* Check mux client environment variables before passing them to mux master. */ +static int +env_permitted(char *env) { - 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; - } + int i, ret; + char name[1024], *cp; - if (getpeereid(client_fd, &euid, &egid) < 0) { - error("%s getpeereid failed: %s", __func__, strerror(errno)); - close(client_fd); + if ((cp = strchr(env, '=')) == NULL || cp == env) 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); + ret = snprintf(name, sizeof(name), "%.*s", (int)(cp - env), env); + if (ret <= 0 || (size_t)ret >= sizeof(name)) { + error("env_permitted: name '%.100s...' too long", env); return 0; } - /* XXX handle asynchronously */ - unset_nonblock(client_fd); + for (i = 0; i < options.num_send_env; i++) + if (match_pattern(name, options.send_env[i])) + return 1; - /* 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; + return 0; +} + +/* Mux master protocol message handlers */ + +static int +process_mux_master_hello(u_int rid, Channel *c, Buffer *m, Buffer *r) +{ + u_int ver; + struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; + + if (state == NULL) + fatal("%s: channel %d: c->mux_ctx == NULL", __func__, c->self); + if (state->hello_rcvd) { + error("%s: HELLO received twice", __func__); + return -1; } - if ((ver = buffer_get_char(&m)) != SSHMUX_VER) { - error("%s: wrong client version %d", __func__, ver); - buffer_free(&m); - close(client_fd); - return 0; + if (buffer_get_int_ret(&ver, m) != 0) { + malf: + error("%s: malformed message", __func__); + return -1; } + if (ver != SSHMUX_VER) { + error("Unsupported multiplexing protocol version %d " + "(expected %d)", ver, SSHMUX_VER); + return -1; + } + debug2("%s: channel %d slave version %u", __func__, c->self, ver); - allowed = 1; - mux_command = buffer_get_int(&m); - flags = buffer_get_int(&m); - - buffer_clear(&m); + /* No extensions are presently defined */ + while (buffer_len(m) > 0) { + char *name = buffer_get_string_ret(m, NULL); + char *value = buffer_get_string_ret(m, NULL); - 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; + if (name == NULL || value == NULL) { + if (name != NULL) + xfree(name); + goto malf; } - buffer_free(&m); - close(client_fd); - return start_close; - default: - error("Unsupported command %d", mux_command); - buffer_free(&m); - close(client_fd); - return 0; + debug2("Unrecognised slave extension \"%s\"", name); + xfree(name); + xfree(value); } + state->hello_rcvd = 1; + return 0; +} + +static int +process_mux_new_session(u_int rid, Channel *c, Buffer *m, Buffer *r) +{ + Channel *nc; + struct mux_session_confirm_ctx *cctx; + char *reserved, *cmd, *cp; + u_int i, j, len, env_len, escape_char, window, packetmax; + int new_fd[3]; /* 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; + cctx = xcalloc(1, sizeof(*cctx)); + cctx->term = NULL; + cctx->rid = rid; + cmd = reserved = NULL; + if ((reserved = buffer_get_string_ret(m, NULL)) == NULL || + buffer_get_int_ret(&cctx->want_tty, m) != 0 || + buffer_get_int_ret(&cctx->want_x_fwd, m) != 0 || + buffer_get_int_ret(&cctx->want_agent_fwd, m) != 0 || + buffer_get_int_ret(&cctx->want_subsys, m) != 0 || + buffer_get_int_ret(&escape_char, m) != 0 || + (cctx->term = buffer_get_string_ret(m, &len)) == NULL || + (cmd = buffer_get_string_ret(m, &len)) == NULL) { + malf: + if (cmd != NULL) + xfree(cmd); + if (reserved != NULL) + xfree(reserved); + if (cctx->term != NULL) + xfree(cctx->term); + error("%s: malformed message", __func__); + return -1; } - - if (!allowed) { - error("Refused control connection"); - close(client_fd); - buffer_free(&m); - return 0; + xfree(reserved); + reserved = NULL; + + cctx->env = NULL; + env_len = 0; + while (buffer_len(m) > 0) { +#define MUX_MAX_ENV_VARS 4096 + if ((cp = buffer_get_string_ret(m, &len)) == NULL) { + xfree(cmd); + goto malf; + } + if (!env_permitted(cp)) { + xfree(cp); + continue; + } + cctx->env = xrealloc(cctx->env, env_len + 2, + sizeof(*cctx->env)); + cctx->env[env_len++] = cp; + cctx->env[env_len] = NULL; + if (env_len > MUX_MAX_ENV_VARS) { + error(">%d environment variables received, ignoring " + "additional", MUX_MAX_ENV_VARS); + break; + } } - 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; - } + debug2("%s: channel %d: request tty %d, X %d, agent %d, subsys %d, " + "term \"%s\", cmd \"%s\", env %u", __func__, c->self, + cctx->want_tty, cctx->want_x_fwd, cctx->want_agent_fwd, + cctx->want_subsys, cctx->term, cmd, env_len); - 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); + cmd = NULL; /* Gather fds from client */ for(i = 0; i < 3; i++) { - if ((new_fd[i] = mm_receive_fd(client_fd)) == -1) { + if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) { error("%s: failed to receive fd %d from slave", __func__, i); for (j = 0; j < i; j++) @@ -385,37 +380,56 @@ muxserver_accept_control(void) xfree(cctx->env); xfree(cctx->term); buffer_free(&cctx->cmd); - close(client_fd); xfree(cctx); - return 0; + + /* prepare reply */ + buffer_put_int(r, MUX_S_FAILURE); + buffer_put_int(r, rid); + buffer_put_cstring(r, + "did not receive file descriptors"); + return -1; } } - debug2("%s: got fds stdin %d, stdout %d, stderr %d", __func__, + debug3("%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); + /* XXX support multiple child sessions in future */ + if (c->remote_id != -1) { + debug2("%s: session already open", __func__); + /* prepare reply */ + buffer_put_int(r, MUX_S_FAILURE); + buffer_put_int(r, rid); + buffer_put_cstring(r, "Multiple sessions not supported"); + cleanup: 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); } + buffer_free(&cctx->cmd); return 0; } - buffer_free(&m); + + if (options.control_master == SSHCTL_MASTER_ASK || + options.control_master == SSHCTL_MASTER_AUTO_ASK) { + if (!ask_permission("Allow shared connection to %s? ", host)) { + debug2("%s: session refused by user", __func__); + /* prepare reply */ + buffer_put_int(r, MUX_S_PERMISSION_DENIED); + buffer_put_int(r, rid); + buffer_put_cstring(r, "Permission denied"); + goto cleanup; + } + } + + /* 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)); /* enable nonblocking unless tty */ if (!isatty(new_fd[0])) @@ -425,257 +439,1192 @@ muxserver_accept_control(void) 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, + + nc = 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; + nc->ctl_chan = c->self; /* link session -> control channel */ + c->remote_id = nc->self; /* link control -> session channel */ + if (cctx->want_tty && escape_char != 0xffffffff) { - channel_register_filter(c->self, + channel_register_filter(nc->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); + debug2("%s: channel_new: %d linked to control channel %d", + __func__, nc->self, nc->ctl_chan); + + channel_send_open(nc->self); + channel_register_open_confirm(nc->self, mux_session_confirm, cctx); + c->mux_pause = 1; /* stop handling messages until open_confirm done */ + channel_register_cleanup(nc->self, mux_master_session_cleanup_cb, 1); - channel_send_open(c->self); - channel_register_open_confirm(c->self, mux_session_confirm, cctx); + /* reply is deferred, sent by mux_session_confirm */ return 0; } -/* ** Multiplexing client support */ - -/* Exit signal handler */ -static void -control_client_sighandler(int signo) +static int +process_mux_alive_check(u_int rid, Channel *c, Buffer *m, Buffer *r) { - muxclient_terminate = signo; + debug2("%s: channel %d: alive check", __func__, c->self); + + /* prepare reply */ + buffer_put_int(r, MUX_S_ALIVE); + buffer_put_int(r, rid); + buffer_put_int(r, (u_int)getpid()); + + return 0; } -/* - * Relay signal handler - used to pass some signals from mux client to - * mux master. - */ -static void -control_client_sigrelay(int signo) +static int +process_mux_terminate(u_int rid, Channel *c, Buffer *m, Buffer *r) { - int save_errno = errno; + debug2("%s: channel %d: terminate request", __func__, c->self); + + if (options.control_master == SSHCTL_MASTER_ASK || + options.control_master == SSHCTL_MASTER_AUTO_ASK) { + if (!ask_permission("Terminate shared connection to %s? ", + host)) { + debug2("%s: termination refused by user", __func__); + buffer_put_int(r, MUX_S_PERMISSION_DENIED); + buffer_put_int(r, rid); + buffer_put_cstring(r, "Permission denied"); + return 0; + } + } - if (muxserver_pid > 1) - kill(muxserver_pid, signo); + quit_pending = 1; + buffer_put_int(r, MUX_S_OK); + buffer_put_int(r, rid); + /* XXX exit happens too soon - message never makes it to client */ + return 0; +} - errno = save_errno; +static char * +format_forward(u_int ftype, Forward *fwd) +{ + char *ret; + + switch (ftype) { + case MUX_FWD_LOCAL: + xasprintf(&ret, "local forward %.200s:%d -> %.200s:%d", + (fwd->listen_host == NULL) ? + (options.gateway_ports ? "*" : "LOCALHOST") : + fwd->listen_host, fwd->listen_port, + fwd->connect_host, fwd->connect_port); + break; + case MUX_FWD_DYNAMIC: + xasprintf(&ret, "dynamic forward %.200s:%d -> *", + (fwd->listen_host == NULL) ? + (options.gateway_ports ? "*" : "LOCALHOST") : + fwd->listen_host, fwd->listen_port); + break; + case MUX_FWD_REMOTE: + xasprintf(&ret, "remote forward %.200s:%d -> %.200s:%d", + (fwd->listen_host == NULL) ? + "LOCALHOST" : fwd->listen_host, + fwd->listen_port, + fwd->connect_host, fwd->connect_port); + break; + default: + fatal("%s: unknown forward type %u", __func__, ftype); + } + return ret; } -/* Check mux client environment variables before passing them to mux master. */ static int -env_permitted(char *env) +compare_host(const char *a, const char *b) { - 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); + if (a == NULL && b == NULL) + return 1; + if (a == NULL || b == NULL) + return 0; + return strcmp(a, b) == 0; +} - for (i = 0; i < options.num_send_env; i++) - if (match_pattern(name, options.send_env[i])) - return (1); +static int +compare_forward(Forward *a, Forward *b) +{ + if (!compare_host(a->listen_host, b->listen_host)) + return 0; + if (a->listen_port != b->listen_port) + return 0; + if (!compare_host(a->connect_host, b->connect_host)) + return 0; + if (a->connect_port != b->connect_port) + return 0; - return (0); + return 1; } -/* Multiplex client main loop. */ -void -muxclient(const char *path) +static void +mux_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) { - 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; + struct mux_channel_confirm_ctx *fctx = ctxt; + char *failmsg = NULL; + Forward *rfwd; + Channel *c; + Buffer out; - 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: + if ((c = channel_by_id(fctx->cid)) == NULL) { + /* no channel for reply */ + error("%s: unknown channel", __func__); return; } - + buffer_init(&out); + if (fctx->fid >= options.num_remote_forwards) { + xasprintf(&failmsg, "unknown forwarding id %d", fctx->fid); + goto fail; + } + rfwd = &options.remote_forwards[fctx->fid]; + debug("%s: %s for: listen %d, connect %s:%d", __func__, + type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", + rfwd->listen_port, rfwd->connect_host, rfwd->connect_port); + if (type == SSH2_MSG_REQUEST_SUCCESS) { + if (rfwd->listen_port == 0) { + rfwd->allocated_port = packet_get_int(); + logit("Allocated port %u for mux remote forward" + " to %s:%d", rfwd->allocated_port, + rfwd->connect_host, rfwd->connect_port); + buffer_put_int(&out, MUX_S_REMOTE_PORT); + buffer_put_int(&out, fctx->rid); + buffer_put_int(&out, rfwd->allocated_port); + } else { + buffer_put_int(&out, MUX_S_OK); + buffer_put_int(&out, fctx->rid); + } + goto out; + } else { + xasprintf(&failmsg, "remote port forwarding failed for " + "listen port %d", rfwd->listen_port); + } + fail: + error("%s: %s", __func__, failmsg); + buffer_put_int(&out, MUX_S_FAILURE); + buffer_put_int(&out, fctx->rid); + buffer_put_cstring(&out, failmsg); + xfree(failmsg); + out: + buffer_put_string(&c->output, buffer_ptr(&out), buffer_len(&out)); + buffer_free(&out); + if (c->mux_pause <= 0) + fatal("%s: mux_pause %d", __func__, c->mux_pause); + c->mux_pause = 0; /* start processing messages again */ +} + +static int +process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) +{ + Forward fwd; + char *fwd_desc = NULL; + u_int ftype; + int i, ret = 0, freefwd = 1; + + fwd.listen_host = fwd.connect_host = NULL; + if (buffer_get_int_ret(&ftype, m) != 0 || + (fwd.listen_host = buffer_get_string_ret(m, NULL)) == NULL || + buffer_get_int_ret(&fwd.listen_port, m) != 0 || + (fwd.connect_host = buffer_get_string_ret(m, NULL)) == NULL || + buffer_get_int_ret(&fwd.connect_port, m) != 0) { + error("%s: malformed message", __func__); + ret = -1; + goto out; + } + + if (*fwd.listen_host == '\0') { + xfree(fwd.listen_host); + fwd.listen_host = NULL; + } + if (*fwd.connect_host == '\0') { + xfree(fwd.connect_host); + fwd.connect_host = NULL; + } + + debug2("%s: channel %d: request %s", __func__, c->self, + (fwd_desc = format_forward(ftype, &fwd))); + + if (ftype != MUX_FWD_LOCAL && ftype != MUX_FWD_REMOTE && + ftype != MUX_FWD_DYNAMIC) { + logit("%s: invalid forwarding type %u", __func__, ftype); + invalid: + if (fwd.listen_host) + xfree(fwd.listen_host); + if (fwd.connect_host) + xfree(fwd.connect_host); + buffer_put_int(r, MUX_S_FAILURE); + buffer_put_int(r, rid); + buffer_put_cstring(r, "Invalid forwarding request"); + return 0; + } + if (fwd.listen_port >= 65536) { + logit("%s: invalid listen port %u", __func__, + fwd.listen_port); + goto invalid; + } + if (fwd.connect_port >= 65536 || (ftype != MUX_FWD_DYNAMIC && + ftype != MUX_FWD_REMOTE && fwd.connect_port == 0)) { + logit("%s: invalid connect port %u", __func__, + fwd.connect_port); + goto invalid; + } + if (ftype != MUX_FWD_DYNAMIC && fwd.connect_host == NULL) { + logit("%s: missing connect host", __func__); + goto invalid; + } + + /* Skip forwards that have already been requested */ + switch (ftype) { + case MUX_FWD_LOCAL: + case MUX_FWD_DYNAMIC: + for (i = 0; i < options.num_local_forwards; i++) { + if (compare_forward(&fwd, + options.local_forwards + i)) { + exists: + debug2("%s: found existing forwarding", + __func__); + buffer_put_int(r, MUX_S_OK); + buffer_put_int(r, rid); + goto out; + } + } + break; + case MUX_FWD_REMOTE: + for (i = 0; i < options.num_remote_forwards; i++) { + if (compare_forward(&fwd, + options.remote_forwards + i)) { + if (fwd.listen_port != 0) + goto exists; + debug2("%s: found allocated port", + __func__); + buffer_put_int(r, MUX_S_REMOTE_PORT); + buffer_put_int(r, rid); + buffer_put_int(r, + options.remote_forwards[i].allocated_port); + goto out; + } + } + break; + } + + if (options.control_master == SSHCTL_MASTER_ASK || + options.control_master == SSHCTL_MASTER_AUTO_ASK) { + if (!ask_permission("Open %s on %s?", fwd_desc, host)) { + debug2("%s: forwarding refused by user", __func__); + buffer_put_int(r, MUX_S_PERMISSION_DENIED); + buffer_put_int(r, rid); + buffer_put_cstring(r, "Permission denied"); + goto out; + } + } + + if (ftype == MUX_FWD_LOCAL || ftype == MUX_FWD_DYNAMIC) { + if (channel_setup_local_fwd_listener(fwd.listen_host, + fwd.listen_port, fwd.connect_host, fwd.connect_port, + options.gateway_ports) < 0) { + fail: + logit("slave-requested %s failed", fwd_desc); + buffer_put_int(r, MUX_S_FAILURE); + buffer_put_int(r, rid); + buffer_put_cstring(r, "Port forwarding failed"); + goto out; + } + add_local_forward(&options, &fwd); + freefwd = 0; + } else { + struct mux_channel_confirm_ctx *fctx; + + if (channel_request_remote_forwarding(fwd.listen_host, + fwd.listen_port, fwd.connect_host, fwd.connect_port) < 0) + goto fail; + add_remote_forward(&options, &fwd); + fctx = xcalloc(1, sizeof(*fctx)); + fctx->cid = c->self; + fctx->rid = rid; + fctx->fid = options.num_remote_forwards - 1; + client_register_global_confirm(mux_confirm_remote_forward, + fctx); + freefwd = 0; + c->mux_pause = 1; /* wait for mux_confirm_remote_forward */ + /* delayed reply in mux_confirm_remote_forward */ + goto out; + } + buffer_put_int(r, MUX_S_OK); + buffer_put_int(r, rid); + out: + if (fwd_desc != NULL) + xfree(fwd_desc); + if (freefwd) { + if (fwd.listen_host != NULL) + xfree(fwd.listen_host); + if (fwd.connect_host != NULL) + xfree(fwd.connect_host); + } + return ret; +} + +static int +process_mux_close_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) +{ + Forward fwd; + char *fwd_desc = NULL; + u_int ftype; + int ret = 0; + + fwd.listen_host = fwd.connect_host = NULL; + if (buffer_get_int_ret(&ftype, m) != 0 || + (fwd.listen_host = buffer_get_string_ret(m, NULL)) == NULL || + buffer_get_int_ret(&fwd.listen_port, m) != 0 || + (fwd.connect_host = buffer_get_string_ret(m, NULL)) == NULL || + buffer_get_int_ret(&fwd.connect_port, m) != 0) { + error("%s: malformed message", __func__); + ret = -1; + goto out; + } + + if (*fwd.listen_host == '\0') { + xfree(fwd.listen_host); + fwd.listen_host = NULL; + } + if (*fwd.connect_host == '\0') { + xfree(fwd.connect_host); + fwd.connect_host = NULL; + } + + debug2("%s: channel %d: request %s", __func__, c->self, + (fwd_desc = format_forward(ftype, &fwd))); + + /* XXX implement this */ + buffer_put_int(r, MUX_S_FAILURE); + buffer_put_int(r, rid); + buffer_put_cstring(r, "unimplemented"); + + out: + if (fwd_desc != NULL) + xfree(fwd_desc); + if (fwd.listen_host != NULL) + xfree(fwd.listen_host); + if (fwd.connect_host != NULL) + xfree(fwd.connect_host); + + return ret; +} + +static int +process_mux_stdio_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) +{ + Channel *nc; + char *reserved, *chost; + u_int cport, i, j; + int new_fd[2]; + + chost = reserved = NULL; + if ((reserved = buffer_get_string_ret(m, NULL)) == NULL || + (chost = buffer_get_string_ret(m, NULL)) == NULL || + buffer_get_int_ret(&cport, m) != 0) { + if (reserved != NULL) + xfree(reserved); + if (chost != NULL) + xfree(chost); + error("%s: malformed message", __func__); + return -1; + } + xfree(reserved); + + debug2("%s: channel %d: request stdio fwd to %s:%u", + __func__, c->self, chost, cport); + + /* Gather fds from client */ + for(i = 0; i < 2; i++) { + if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) { + error("%s: failed to receive fd %d from slave", + __func__, i); + for (j = 0; j < i; j++) + close(new_fd[j]); + xfree(chost); + + /* prepare reply */ + buffer_put_int(r, MUX_S_FAILURE); + buffer_put_int(r, rid); + buffer_put_cstring(r, + "did not receive file descriptors"); + return -1; + } + } + + debug3("%s: got fds stdin %d, stdout %d", __func__, + new_fd[0], new_fd[1]); + + /* XXX support multiple child sessions in future */ + if (c->remote_id != -1) { + debug2("%s: session already open", __func__); + /* prepare reply */ + buffer_put_int(r, MUX_S_FAILURE); + buffer_put_int(r, rid); + buffer_put_cstring(r, "Multiple sessions not supported"); + cleanup: + close(new_fd[0]); + close(new_fd[1]); + xfree(chost); + return 0; + } + + if (options.control_master == SSHCTL_MASTER_ASK || + options.control_master == SSHCTL_MASTER_AUTO_ASK) { + if (!ask_permission("Allow forward to to %s:%u? ", + chost, cport)) { + debug2("%s: stdio fwd refused by user", __func__); + /* prepare reply */ + buffer_put_int(r, MUX_S_PERMISSION_DENIED); + buffer_put_int(r, rid); + buffer_put_cstring(r, "Permission denied"); + goto cleanup; + } + } + + /* enable nonblocking unless tty */ + if (!isatty(new_fd[0])) + set_nonblock(new_fd[0]); + if (!isatty(new_fd[1])) + set_nonblock(new_fd[1]); + + nc = channel_connect_stdio_fwd(chost, cport, new_fd[0], new_fd[1]); + + nc->ctl_chan = c->self; /* link session -> control channel */ + c->remote_id = nc->self; /* link control -> session channel */ + + debug2("%s: channel_new: %d linked to control channel %d", + __func__, nc->self, nc->ctl_chan); + + channel_register_cleanup(nc->self, mux_master_session_cleanup_cb, 1); + + /* prepare reply */ + /* XXX defer until channel confirmed */ + buffer_put_int(r, MUX_S_SESSION_OPENED); + buffer_put_int(r, rid); + buffer_put_int(r, nc->self); + + return 0; +} + +/* Channel callbacks fired on read/write from mux slave fd */ +static int +mux_master_read_cb(Channel *c) +{ + struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; + Buffer in, out; + void *ptr; + u_int type, rid, have, i; + int ret = -1; + + /* Setup ctx and */ + if (c->mux_ctx == NULL) { + state = xcalloc(1, sizeof(*state)); + c->mux_ctx = state; + channel_register_cleanup(c->self, + mux_master_control_cleanup_cb, 0); + + /* Send hello */ + buffer_init(&out); + buffer_put_int(&out, MUX_MSG_HELLO); + buffer_put_int(&out, SSHMUX_VER); + /* no extensions */ + buffer_put_string(&c->output, buffer_ptr(&out), + buffer_len(&out)); + buffer_free(&out); + debug3("%s: channel %d: hello sent", __func__, c->self); + return 0; + } + + buffer_init(&in); + buffer_init(&out); + + /* Channel code ensures that we receive whole packets */ + if ((ptr = buffer_get_string_ptr_ret(&c->input, &have)) == NULL) { + malf: + error("%s: malformed message", __func__); + goto out; + } + buffer_append(&in, ptr, have); + + if (buffer_get_int_ret(&type, &in) != 0) + goto malf; + debug3("%s: channel %d packet type 0x%08x len %u", + __func__, c->self, type, buffer_len(&in)); + + if (type == MUX_MSG_HELLO) + rid = 0; + else { + if (!state->hello_rcvd) { + error("%s: expected MUX_MSG_HELLO(0x%08x), " + "received 0x%08x", __func__, MUX_MSG_HELLO, type); + goto out; + } + if (buffer_get_int_ret(&rid, &in) != 0) + goto malf; + } + + for (i = 0; mux_master_handlers[i].handler != NULL; i++) { + if (type == mux_master_handlers[i].type) { + ret = mux_master_handlers[i].handler(rid, c, &in, &out); + break; + } + } + if (mux_master_handlers[i].handler == NULL) { + error("%s: unsupported mux message 0x%08x", __func__, type); + buffer_put_int(&out, MUX_S_FAILURE); + buffer_put_int(&out, rid); + buffer_put_cstring(&out, "unsupported request"); + ret = 0; + } + /* Enqueue reply packet */ + if (buffer_len(&out) != 0) { + buffer_put_string(&c->output, buffer_ptr(&out), + buffer_len(&out)); + } + out: + buffer_free(&in); + buffer_free(&out); + return ret; +} + +void +mux_exit_message(Channel *c, int exitval) +{ + Buffer m; + Channel *mux_chan; + + debug3("%s: channel %d: exit message, evitval %d", __func__, c->self, + exitval); + + if ((mux_chan = channel_by_id(c->ctl_chan)) == NULL) + fatal("%s: channel %d missing mux channel %d", + __func__, c->self, c->ctl_chan); + + /* Append exit message packet to control socket output queue */ + buffer_init(&m); + buffer_put_int(&m, MUX_S_EXIT_MESSAGE); + buffer_put_int(&m, c->self); + buffer_put_int(&m, exitval); + + buffer_put_string(&mux_chan->output, buffer_ptr(&m), buffer_len(&m)); + buffer_free(&m); +} + +/* Prepare a mux master to listen on a Unix domain socket. */ +void +muxserver_listen(void) +{ + struct sockaddr_un addr; + socklen_t sun_len; + 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_len = offsetof(struct sockaddr_un, sun_path) + - strlen(path) + 1; + sun_len = offsetof(struct sockaddr_un, sun_path) + + strlen(options.control_path) + 1; - if (strlcpy(addr.sun_path, path, + if (strlcpy(addr.sun_path, options.control_path, sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) fatal("ControlPath too long"); - if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) + if ((muxserver_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)); + old_umask = umask(0177); + if (bind(muxserver_sock, (struct sockaddr *)&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); + + mux_listener_channel = channel_new("mux listener", + SSH_CHANNEL_MUX_LISTENER, muxserver_sock, muxserver_sock, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, + 0, addr.sun_path, 1); + mux_listener_channel->mux_rcb = mux_master_read_cb; + debug3("%s: mux listener channel %d fd %d", __func__, + mux_listener_channel->self, mux_listener_channel->sock); +} + +/* Callback on open confirmation in mux master for a mux client session. */ +static void +mux_session_confirm(int id, int success, void *arg) +{ + struct mux_session_confirm_ctx *cctx = arg; + const char *display; + Channel *c, *cc; + int i; + Buffer reply; + + if (cctx == NULL) + fatal("%s: cctx == NULL", __func__); + if ((c = channel_by_id(id)) == NULL) + fatal("%s: no channel for id %d", __func__, id); + if ((cc = channel_by_id(c->ctl_chan)) == NULL) + fatal("%s: channel %d lacks control channel %d", __func__, + id, c->ctl_chan); + + if (!success) { + debug3("%s: sending failure reply", __func__); + /* prepare reply */ + buffer_init(&reply); + buffer_put_int(&reply, MUX_S_FAILURE); + buffer_put_int(&reply, cctx->rid); + buffer_put_cstring(&reply, "Session open refused by peer"); + goto done; + } + + 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, options.forward_x11_timeout, + &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); + + debug3("%s: sending success reply", __func__); + /* prepare reply */ + buffer_init(&reply); + buffer_put_int(&reply, MUX_S_SESSION_OPENED); + buffer_put_int(&reply, cctx->rid); + buffer_put_int(&reply, c->self); + + done: + /* Send reply */ + buffer_put_string(&cc->output, buffer_ptr(&reply), buffer_len(&reply)); + buffer_free(&reply); + + if (cc->mux_pause <= 0) + fatal("%s: mux_pause %d", __func__, cc->mux_pause); + cc->mux_pause = 0; /* start processing messages again */ + 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); +} + +/* ** 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; +} + +static int +mux_client_read(int fd, Buffer *b, u_int need) +{ + u_int have; + ssize_t len; + u_char *p; + struct pollfd pfd; + + pfd.fd = fd; + pfd.events = POLLIN; + p = buffer_append_space(b, need); + for (have = 0; have < need; ) { + if (muxclient_terminate) { + errno = EINTR; + return -1; } - if (errno == ENOENT) - debug("Control socket \"%.100s\" does not exist", path); - else { - error("Control socket connect(%.100s): %s", path, - strerror(errno)); + len = read(fd, p + have, need - have); + if (len < 0) { + switch (errno) { +#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) + case EWOULDBLOCK: +#endif + case EAGAIN: + (void)poll(&pfd, 1, -1); + /* FALLTHROUGH */ + case EINTR: + continue; + default: + return -1; + } } - close(sock); - return; + if (len == 0) { + errno = EPIPE; + return -1; + } + have += (u_int)len; } + return 0; +} - 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); +static int +mux_client_write_packet(int fd, Buffer *m) +{ + Buffer queue; + u_int have, need; + int oerrno, len; + u_char *ptr; + struct pollfd pfd; + + pfd.fd = fd; + pfd.events = POLLOUT; + buffer_init(&queue); + buffer_put_string(&queue, buffer_ptr(m), buffer_len(m)); + + need = buffer_len(&queue); + ptr = buffer_ptr(&queue); + + for (have = 0; have < need; ) { + if (muxclient_terminate) { + buffer_free(&queue); + errno = EINTR; + return -1; + } + len = write(fd, ptr + have, need - have); + if (len < 0) { + switch (errno) { +#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) + case EWOULDBLOCK: +#endif + case EAGAIN: + (void)poll(&pfd, 1, -1); + /* FALLTHROUGH */ + case EINTR: + continue; + default: + oerrno = errno; + buffer_free(&queue); + errno = oerrno; + return -1; + } + } + if (len == 0) { + buffer_free(&queue); + errno = EPIPE; + return -1; + } + have += (u_int)len; } + buffer_free(&queue); + return 0; +} - 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; +static int +mux_client_read_packet(int fd, Buffer *m) +{ + Buffer queue; + u_int need, have; + void *ptr; + int oerrno; + + buffer_init(&queue); + if (mux_client_read(fd, &queue, 4) != 0) { + if ((oerrno = errno) == EPIPE) + debug3("%s: read header failed: %s", __func__, strerror(errno)); + errno = oerrno; + return -1; + } + need = get_u32(buffer_ptr(&queue)); + if (mux_client_read(fd, &queue, need) != 0) { + oerrno = errno; + debug3("%s: read body failed: %s", __func__, strerror(errno)); + errno = oerrno; + return -1; + } + ptr = buffer_get_string_ptr(&queue, &have); + buffer_append(m, ptr, have); + buffer_free(&queue); + return 0; +} - signal(SIGPIPE, SIG_IGN); +static int +mux_client_hello_exchange(int fd) +{ + Buffer m; + u_int type, ver; buffer_init(&m); + buffer_put_int(&m, MUX_MSG_HELLO); + buffer_put_int(&m, SSHMUX_VER); + /* no extensions */ - /* 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); + if (mux_client_write_packet(fd, &m) != 0) + fatal("%s: write packet: %s", __func__, strerror(errno)); + + buffer_clear(&m); + + /* Read their HELLO */ + if (mux_client_read_packet(fd, &m) != 0) { 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; + return -1; } + + type = buffer_get_int(&m); + if (type != MUX_MSG_HELLO) + fatal("%s: expected HELLO (%u) received %u", + __func__, MUX_MSG_HELLO, type); + ver = buffer_get_int(&m); + if (ver != SSHMUX_VER) + fatal("Unsupported multiplexing protocol version %d " + "(expected %d)", ver, SSHMUX_VER); + debug2("%s: master version %u", __func__, ver); + /* No extensions are presently defined */ + while (buffer_len(&m) > 0) { + char *name = buffer_get_string(&m, NULL); + char *value = buffer_get_string(&m, NULL); + + debug2("Unrecognised master extension \"%s\"", name); + xfree(name); + xfree(value); + } + buffer_free(&m); + return 0; +} + +static u_int +mux_client_request_alive(int fd) +{ + Buffer m; + char *e; + u_int pid, type, rid; + + debug3("%s: entering", __func__); + + buffer_init(&m); + buffer_put_int(&m, MUX_C_ALIVE_CHECK); + buffer_put_int(&m, muxclient_request_id); + + if (mux_client_write_packet(fd, &m) != 0) + fatal("%s: write packet: %s", __func__, strerror(errno)); + 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; + /* Read their reply */ + if (mux_client_read_packet(fd, &m) != 0) { + buffer_free(&m); + return 0; } - if (buffer_get_char(&m) != SSHMUX_VER) { - error("%s: Master replied with wrong version", __func__); - goto muxerr; + + type = buffer_get_int(&m); + if (type != MUX_S_ALIVE) { + e = buffer_get_string(&m, NULL); + fatal("%s: master returned error: %s", __func__, e); } - if (buffer_get_int_ret(&allowed, &m) != 0) { - error("%s: bad server reply", __func__); - goto muxerr; + + if ((rid = buffer_get_int(&m)) != muxclient_request_id) + fatal("%s: out of sequence reply: my id %u theirs %u", + __func__, muxclient_request_id, rid); + pid = buffer_get_int(&m); + buffer_free(&m); + + debug3("%s: done pid = %u", __func__, pid); + + muxclient_request_id++; + + return pid; +} + +static void +mux_client_request_terminate(int fd) +{ + Buffer m; + char *e; + u_int type, rid; + + debug3("%s: entering", __func__); + + buffer_init(&m); + buffer_put_int(&m, MUX_C_TERMINATE); + buffer_put_int(&m, muxclient_request_id); + + if (mux_client_write_packet(fd, &m) != 0) + fatal("%s: write packet: %s", __func__, strerror(errno)); + + buffer_clear(&m); + + /* Read their reply */ + if (mux_client_read_packet(fd, &m) != 0) { + /* Remote end exited already */ + if (errno == EPIPE) { + buffer_free(&m); + return; + } + fatal("%s: read from master failed: %s", + __func__, strerror(errno)); } - if (allowed != 1) { - error("Connection to master denied"); - goto muxerr; + + type = buffer_get_int(&m); + if ((rid = buffer_get_int(&m)) != muxclient_request_id) + fatal("%s: out of sequence reply: my id %u theirs %u", + __func__, muxclient_request_id, rid); + switch (type) { + case MUX_S_OK: + break; + case MUX_S_PERMISSION_DENIED: + e = buffer_get_string(&m, NULL); + fatal("Master refused termination request: %s", e); + case MUX_S_FAILURE: + e = buffer_get_string(&m, NULL); + fatal("%s: termination request failed: %s", __func__, e); + default: + fatal("%s: unexpected response from master 0x%08x", + __func__, type); } - muxserver_pid = buffer_get_int(&m); + buffer_free(&m); + muxclient_request_id++; +} + +static int +mux_client_request_forward(int fd, u_int ftype, Forward *fwd) +{ + Buffer m; + char *e, *fwd_desc; + u_int type, rid; + + fwd_desc = format_forward(ftype, fwd); + debug("Requesting %s", fwd_desc); + xfree(fwd_desc); + + buffer_init(&m); + buffer_put_int(&m, MUX_C_OPEN_FWD); + buffer_put_int(&m, muxclient_request_id); + buffer_put_int(&m, ftype); + buffer_put_cstring(&m, + fwd->listen_host == NULL ? "" : fwd->listen_host); + buffer_put_int(&m, fwd->listen_port); + buffer_put_cstring(&m, + fwd->connect_host == NULL ? "" : fwd->connect_host); + buffer_put_int(&m, fwd->connect_port); + + if (mux_client_write_packet(fd, &m) != 0) + fatal("%s: write packet: %s", __func__, strerror(errno)); 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)); + /* Read their reply */ + if (mux_client_read_packet(fd, &m) != 0) { + buffer_free(&m); + return -1; + } - 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]); - } - } - } + type = buffer_get_int(&m); + if ((rid = buffer_get_int(&m)) != muxclient_request_id) + fatal("%s: out of sequence reply: my id %u theirs %u", + __func__, muxclient_request_id, rid); + switch (type) { + case MUX_S_OK: break; + case MUX_S_REMOTE_PORT: + fwd->allocated_port = buffer_get_int(&m); + logit("Allocated port %u for remote forward to %s:%d", + fwd->allocated_port, + fwd->connect_host ? fwd->connect_host : "", + fwd->connect_port); + if (muxclient_command == SSHMUX_COMMAND_FORWARD) + fprintf(stdout, "%u\n", fwd->allocated_port); + break; + case MUX_S_PERMISSION_DENIED: + e = buffer_get_string(&m, NULL); + buffer_free(&m); + error("Master refused forwarding request: %s", e); + return -1; + case MUX_S_FAILURE: + e = buffer_get_string(&m, NULL); + buffer_free(&m); + error("%s: session request failed: %s", __func__, e); + return -1; default: - fatal("unrecognised muxclient_command %d", muxclient_command); + fatal("%s: unexpected response from master 0x%08x", + __func__, type); } + buffer_free(&m); + + muxclient_request_id++; + return 0; +} - if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) { - error("%s: msg_send", __func__); - goto muxerr; +static int +mux_client_request_forwards(int fd) +{ + int i; + + debug3("%s: requesting forwardings: %d local, %d remote", __func__, + options.num_local_forwards, options.num_remote_forwards); + + /* XXX ExitOnForwardingFailure */ + for (i = 0; i < options.num_local_forwards; i++) { + if (mux_client_request_forward(fd, + options.local_forwards[i].connect_port == 0 ? + MUX_FWD_DYNAMIC : MUX_FWD_LOCAL, + options.local_forwards + i) != 0) + return -1; } + for (i = 0; i < options.num_remote_forwards; i++) { + if (mux_client_request_forward(fd, MUX_FWD_REMOTE, + options.remote_forwards + i) != 0) + return -1; + } + return 0; +} - 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; +static int +mux_client_request_session(int fd) +{ + Buffer m; + char *e, *term; + u_int i, rid, sid, esid, exitval, type, exitval_seen; + extern char **environ; + int devnull; + + debug3("%s: entering", __func__); + + if ((muxserver_pid = mux_client_request_alive(fd)) == 0) { + error("%s: master alive request failed", __func__); + return -1; } - /* - * Mux errors are non-recoverable from this point as the master - * has ownership of the session now. - */ + signal(SIGPIPE, SIG_IGN); + + if (stdin_null_flag) { + if ((devnull = open(_PATH_DEVNULL, O_RDONLY)) == -1) + fatal("open(/dev/null): %s", strerror(errno)); + if (dup2(devnull, STDIN_FILENO) == -1) + fatal("dup2: %s", strerror(errno)); + if (devnull > STDERR_FILENO) + close(devnull); + } - /* Wait for reply, so master has a chance to gather ttymodes */ + term = getenv("TERM"); + + buffer_init(&m); + buffer_put_int(&m, MUX_C_NEW_SESSION); + buffer_put_int(&m, muxclient_request_id); + buffer_put_cstring(&m, ""); /* reserved */ + buffer_put_int(&m, tty_flag); + buffer_put_int(&m, options.forward_x11); + buffer_put_int(&m, options.forward_agent); + buffer_put_int(&m, subsystem_flag); + buffer_put_int(&m, options.escape_char == SSH_ESCAPECHAR_NONE ? + 0xffffffff : (u_int)options.escape_char); + buffer_put_cstring(&m, term == NULL ? "" : term); + buffer_put_string(&m, buffer_ptr(&command), buffer_len(&command)); + + if (options.num_send_env > 0 && environ != NULL) { + /* Pass environment */ + for (i = 0; environ[i] != NULL; i++) { + if (env_permitted(environ[i])) { + buffer_put_cstring(&m, environ[i]); + } + } + } + + if (mux_client_write_packet(fd, &m) != 0) + fatal("%s: write packet: %s", __func__, strerror(errno)); + + /* Send the stdio file descriptors */ + if (mm_send_fd(fd, STDIN_FILENO) == -1 || + mm_send_fd(fd, STDOUT_FILENO) == -1 || + mm_send_fd(fd, STDERR_FILENO) == -1) + fatal("%s: send fds failed", __func__); + + debug3("%s: session request sent", __func__); + + /* Read their reply */ 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); + if (mux_client_read_packet(fd, &m) != 0) { + error("%s: read from master failed: %s", + __func__, strerror(errno)); + buffer_free(&m); + return -1; + } + + type = buffer_get_int(&m); + if ((rid = buffer_get_int(&m)) != muxclient_request_id) + fatal("%s: out of sequence reply: my id %u theirs %u", + __func__, muxclient_request_id, rid); + switch (type) { + case MUX_S_SESSION_OPENED: + sid = buffer_get_int(&m); + debug("%s: master session id: %u", __func__, sid); + break; + case MUX_S_PERMISSION_DENIED: + e = buffer_get_string(&m, NULL); + buffer_free(&m); + error("Master refused forwarding request: %s", e); + return -1; + case MUX_S_FAILURE: + e = buffer_get_string(&m, NULL); + buffer_free(&m); + error("%s: forwarding request failed: %s", __func__, e); + return -1; + default: + buffer_free(&m); + error("%s: unexpected response from master 0x%08x", + __func__, type); + return -1; + } + muxclient_request_id++; signal(SIGHUP, control_client_sighandler); signal(SIGINT, control_client_sighandler); @@ -683,46 +1632,239 @@ muxclient(const char *path) signal(SIGWINCH, control_client_sigrelay); if (tty_flag) - enter_raw_mode(); + enter_raw_mode(force_tty_flag); /* * 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). + * Before it does, it is expected to write an exit message. + * 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"); + for (exitval = 255, exitval_seen = 0;;) { + buffer_clear(&m); + if (mux_client_read_packet(fd, &m) != 0) break; + type = buffer_get_int(&m); + if (type != MUX_S_EXIT_MESSAGE) { + e = buffer_get_string(&m, NULL); + fatal("%s: master returned error: %s", __func__, e); } - if (r == -1) { - if (errno == EINTR) - continue; - fatal("%s: read %s", __func__, strerror(errno)); - } - i += r; + if ((esid = buffer_get_int(&m)) != sid) + fatal("%s: exit on unknown session: my id %u theirs %u", + __func__, sid, esid); + debug("%s: master session id: %u", __func__, sid); + if (exitval_seen) + fatal("%s: exitval sent twice", __func__); + exitval = buffer_get_int(&m); + exitval_seen = 1; } - 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)); + close(fd); + leave_raw_mode(force_tty_flag); + if (muxclient_terminate) { debug2("Exiting on signal %d", muxclient_terminate); - exitval[0] = 255; - } else if (i < (int)sizeof(int)) { + exitval = 255; + } else if (!exitval_seen) { debug2("Control master terminated unexpectedly"); - exitval[0] = 255; + exitval = 255; } else - debug2("Received exit status from master %d", exitval[0]); + debug2("Received exit status from master %d", exitval); if (tty_flag && options.log_level != SYSLOG_LEVEL_QUIET) fprintf(stderr, "Shared connection to %s closed.\r\n", host); - exit(exitval[0]); + exit(exitval); +} + +static int +mux_client_request_stdio_fwd(int fd) +{ + Buffer m; + char *e; + u_int type, rid, sid; + int devnull; + + debug3("%s: entering", __func__); + + if ((muxserver_pid = mux_client_request_alive(fd)) == 0) { + error("%s: master alive request failed", __func__); + return -1; + } + + signal(SIGPIPE, SIG_IGN); + + if (stdin_null_flag) { + if ((devnull = open(_PATH_DEVNULL, O_RDONLY)) == -1) + fatal("open(/dev/null): %s", strerror(errno)); + if (dup2(devnull, STDIN_FILENO) == -1) + fatal("dup2: %s", strerror(errno)); + if (devnull > STDERR_FILENO) + close(devnull); + } + + buffer_init(&m); + buffer_put_int(&m, MUX_C_NEW_STDIO_FWD); + buffer_put_int(&m, muxclient_request_id); + buffer_put_cstring(&m, ""); /* reserved */ + buffer_put_cstring(&m, stdio_forward_host); + buffer_put_int(&m, stdio_forward_port); + + if (mux_client_write_packet(fd, &m) != 0) + fatal("%s: write packet: %s", __func__, strerror(errno)); + + /* Send the stdio file descriptors */ + if (mm_send_fd(fd, STDIN_FILENO) == -1 || + mm_send_fd(fd, STDOUT_FILENO) == -1) + fatal("%s: send fds failed", __func__); + + debug3("%s: stdio forward request sent", __func__); + + /* Read their reply */ + buffer_clear(&m); + + if (mux_client_read_packet(fd, &m) != 0) { + error("%s: read from master failed: %s", + __func__, strerror(errno)); + buffer_free(&m); + return -1; + } + + type = buffer_get_int(&m); + if ((rid = buffer_get_int(&m)) != muxclient_request_id) + fatal("%s: out of sequence reply: my id %u theirs %u", + __func__, muxclient_request_id, rid); + switch (type) { + case MUX_S_SESSION_OPENED: + sid = buffer_get_int(&m); + debug("%s: master session id: %u", __func__, sid); + break; + case MUX_S_PERMISSION_DENIED: + e = buffer_get_string(&m, NULL); + buffer_free(&m); + fatal("Master refused forwarding request: %s", e); + case MUX_S_FAILURE: + e = buffer_get_string(&m, NULL); + buffer_free(&m); + fatal("%s: stdio forwarding request failed: %s", __func__, e); + default: + buffer_free(&m); + error("%s: unexpected response from master 0x%08x", + __func__, type); + return -1; + } + muxclient_request_id++; + + signal(SIGHUP, control_client_sighandler); + signal(SIGINT, control_client_sighandler); + signal(SIGTERM, control_client_sighandler); + signal(SIGWINCH, control_client_sigrelay); + + /* + * Stick around until the controlee closes the client_fd. + */ + buffer_clear(&m); + if (mux_client_read_packet(fd, &m) != 0) { + if (errno == EPIPE || + (errno == EINTR && muxclient_terminate != 0)) + return 0; + fatal("%s: mux_client_read_packet: %s", + __func__, strerror(errno)); + } + fatal("%s: master returned unexpected message %u", __func__, type); +} + +/* Multiplex client main loop. */ +void +muxclient(const char *path) +{ + struct sockaddr_un addr; + socklen_t sun_len; + int sock; + u_int pid; + + if (muxclient_command == 0) { + if (stdio_forward_host != NULL) + muxclient_command = SSHMUX_COMMAND_STDIO_FWD; + else + 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; + 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, sun_len) == -1) { + switch (muxclient_command) { + case SSHMUX_COMMAND_OPEN: + case SSHMUX_COMMAND_STDIO_FWD: + break; + default: + 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; + } + set_nonblock(sock); + + if (mux_client_hello_exchange(sock) != 0) { + error("%s: master hello exchange failed", __func__); + close(sock); + return; + } + + switch (muxclient_command) { + case SSHMUX_COMMAND_ALIVE_CHECK: + if ((pid = mux_client_request_alive(sock)) == 0) + fatal("%s: master alive check failed", __func__); + fprintf(stderr, "Master running (pid=%d)\r\n", pid); + exit(0); + case SSHMUX_COMMAND_TERMINATE: + mux_client_request_terminate(sock); + fprintf(stderr, "Exit request sent.\r\n"); + exit(0); + case SSHMUX_COMMAND_FORWARD: + if (mux_client_request_forwards(sock) != 0) + fatal("%s: master forward request failed", __func__); + exit(0); + case SSHMUX_COMMAND_OPEN: + if (mux_client_request_forwards(sock) != 0) { + error("%s: master forward request failed", __func__); + return; + } + mux_client_request_session(sock); + return; + case SSHMUX_COMMAND_STDIO_FWD: + mux_client_request_stdio_fwd(sock); + exit(0); + default: + fatal("unrecognised muxclient_command %d", muxclient_command); + } } diff --git a/crypto/openssh/myproposal.h b/crypto/openssh/myproposal.h index 7bca3bcae4..7bedfab0a9 100644 --- a/crypto/openssh/myproposal.h +++ b/crypto/openssh/myproposal.h @@ -1,4 +1,4 @@ -/* $OpenBSD: myproposal.h,v 1.23 2009/01/23 07:58:11 djm Exp $ */ +/* $OpenBSD: myproposal.h,v 1.25 2010/04/16 01:47:26 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -40,7 +40,12 @@ "diffie-hellman-group1-sha1" #endif -#define KEX_DEFAULT_PK_ALG "ssh-rsa,ssh-dss" +#define KEX_DEFAULT_PK_ALG \ + "ssh-rsa-cert-v01@openssh.com," \ + "ssh-dss-cert-v01@openssh.com," \ + "ssh-rsa-cert-v00@openssh.com," \ + "ssh-dss-cert-v00@openssh.com," \ + "ssh-rsa,ssh-dss" #define KEX_DEFAULT_ENCRYPT \ "aes128-ctr,aes192-ctr,aes256-ctr," \ diff --git a/crypto/openssh/nchan.c b/crypto/openssh/nchan.c index 160445e5a2..20f6a2f499 100644 --- a/crypto/openssh/nchan.c +++ b/crypto/openssh/nchan.c @@ -1,4 +1,4 @@ -/* $OpenBSD: nchan.c,v 1.62 2008/11/07 18:50:18 stevesk Exp $ */ +/* $OpenBSD: nchan.c,v 1.63 2010/01/26 01:28:35 djm Exp $ */ /* * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. * @@ -161,7 +161,7 @@ chan_ibuf_empty(Channel *c) switch (c->istate) { case CHAN_INPUT_WAIT_DRAIN: if (compat20) { - if (!(c->flags & CHAN_CLOSE_SENT)) + if (!(c->flags & (CHAN_CLOSE_SENT|CHAN_LOCAL))) chan_send_eof2(c); chan_set_istate(c, CHAN_INPUT_CLOSED); } else { @@ -278,9 +278,12 @@ static void chan_rcvd_close2(Channel *c) { debug2("channel %d: rcvd close", c->self); - if (c->flags & CHAN_CLOSE_RCVD) - error("channel %d: protocol error: close rcvd twice", c->self); - c->flags |= CHAN_CLOSE_RCVD; + if (!(c->flags & CHAN_LOCAL)) { + if (c->flags & CHAN_CLOSE_RCVD) + error("channel %d: protocol error: close rcvd twice", + c->self); + c->flags |= CHAN_CLOSE_RCVD; + } if (c->type == SSH_CHANNEL_LARVAL) { /* tear down larval channels immediately */ chan_set_ostate(c, CHAN_OUTPUT_CLOSED); @@ -302,11 +305,13 @@ chan_rcvd_close2(Channel *c) chan_set_istate(c, CHAN_INPUT_CLOSED); break; case CHAN_INPUT_WAIT_DRAIN: - chan_send_eof2(c); + if (!(c->flags & CHAN_LOCAL)) + chan_send_eof2(c); chan_set_istate(c, CHAN_INPUT_CLOSED); break; } } + void chan_rcvd_eow(Channel *c) { @@ -454,6 +459,10 @@ chan_is_dead(Channel *c, int do_send) c->self, c->efd, buffer_len(&c->extended)); return 0; } + if (c->flags & CHAN_LOCAL) { + debug2("channel %d: is dead (local)", c->self); + return 1; + } if (!(c->flags & CHAN_CLOSE_SENT)) { if (do_send) { chan_send_close2(c); diff --git a/crypto/openssh/openbsd-compat/openbsd-compat.h b/crypto/openssh/openbsd-compat/openbsd-compat.h index 50c6d990bf..e15d2bd969 100644 --- a/crypto/openssh/openbsd-compat/openbsd-compat.h +++ b/crypto/openssh/openbsd-compat/openbsd-compat.h @@ -1,4 +1,4 @@ -/* $Id: openbsd-compat.h,v 1.46 2008/06/08 17:32:29 dtucker Exp $ */ +/* $Id: openbsd-compat.h,v 1.50 2010/08/16 03:15:23 dtucker Exp $ */ /* * Copyright (c) 1999-2003 Damien Miller. All rights reserved. @@ -87,6 +87,11 @@ int setenv(register const char *name, register const char *value, int rewrite); void strmode(int mode, char *p); #endif +#ifndef HAVE_STRPTIME +#include +char *strptime(const char *buf, const char *fmt, struct tm *tm); +#endif + #if !defined(HAVE_MKDTEMP) || defined(HAVE_STRICT_MKSTEMP) int mkstemps(char *path, int slen); int mkstemp(char *path); @@ -200,6 +205,14 @@ int vasprintf(char **, const char *, va_list); int vsnprintf(char *, size_t, const char *, va_list); #endif +#ifndef HAVE_USER_FROM_UID +char *user_from_uid(uid_t, int); +#endif + +#ifndef HAVE_GROUP_FROM_GID +char *group_from_gid(gid_t, int); +#endif + void *xmmap(size_t size); char *xcrypt(const char *password, const char *salt); char *shadow_pw(struct passwd *pw); diff --git a/crypto/openssh/openbsd-compat/openssl-compat.h b/crypto/openssh/openbsd-compat/openssl-compat.h index fcc762867d..b7caa650c2 100644 --- a/crypto/openssh/openbsd-compat/openssl-compat.h +++ b/crypto/openssh/openbsd-compat/openssl-compat.h @@ -1,4 +1,4 @@ -/* $Id: openssl-compat.h,v 1.14 2009/03/07 11:22:35 dtucker Exp $ */ +/* $Id: openssl-compat.h,v 1.15 2010/05/12 07:50:02 djm Exp $ */ /* * Copyright (c) 2005 Darren Tucker @@ -18,6 +18,16 @@ #include "includes.h" #include +#include +#include + +/* Only in 0.9.8 */ +#ifndef OPENSSL_DSA_MAX_MODULUS_BITS +# define OPENSSL_DSA_MAX_MODULUS_BITS 10000 +#endif +#ifndef OPENSSL_RSA_MAX_MODULUS_BITS +# define OPENSSL_RSA_MAX_MODULUS_BITS 16384 +#endif /* OPENSSL_free() is Free() in versions before OpenSSL 0.9.6 */ #if !defined(OPENSSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x0090600f) @@ -97,3 +107,4 @@ int ssh_EVP_Cipher(EVP_CIPHER_CTX *, char *, char *, int); int ssh_EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *); void ssh_SSLeay_add_all_algorithms(void); #endif /* SSH_DONT_OVERLOAD_OPENSSL_FUNCS */ + diff --git a/crypto/openssh/openbsd-compat/port-aix.h b/crypto/openssh/openbsd-compat/port-aix.h index 3ac76ae155..53e4e88a0c 100644 --- a/crypto/openssh/openbsd-compat/port-aix.h +++ b/crypto/openssh/openbsd-compat/port-aix.h @@ -1,4 +1,4 @@ -/* $Id: port-aix.h,v 1.31 2009/08/20 06:20:50 dtucker Exp $ */ +/* $Id: port-aix.h,v 1.32 2009/12/20 23:49:22 dtucker Exp $ */ /* * @@ -95,6 +95,10 @@ int sys_auth_record_login(const char *, const char *, const char *, Buffer *); # define CUSTOM_SYS_AUTH_GET_LASTLOGIN_MSG char *sys_auth_get_lastlogin_msg(const char *, uid_t); # define CUSTOM_FAILED_LOGIN 1 +# if defined(S_AUTHDOMAIN) && defined (S_AUTHNAME) +# define USE_AIX_KRB_NAME +char *aix_krb5_get_principal_name(char *); +# endif #endif void aix_setauthdb(const char *); diff --git a/crypto/openssh/openbsd-compat/port-linux.h b/crypto/openssh/openbsd-compat/port-linux.h index 5cd39bf83c..209d9a7a24 100644 --- a/crypto/openssh/openbsd-compat/port-linux.h +++ b/crypto/openssh/openbsd-compat/port-linux.h @@ -1,4 +1,4 @@ -/* $Id: port-linux.h,v 1.2 2008/03/26 20:27:21 dtucker Exp $ */ +/* $Id: port-linux.h,v 1.4 2009/12/08 02:39:48 dtucker Exp $ */ /* * Copyright (c) 2006 Damien Miller @@ -23,6 +23,12 @@ int ssh_selinux_enabled(void); void ssh_selinux_setup_pty(char *, const char *); void ssh_selinux_setup_exec_context(char *); +void ssh_selinux_change_context(const char *); +#endif + +#ifdef LINUX_OOM_ADJUST +void oom_adjust_restore(void); +void oom_adjust_setup(void); #endif #endif /* ! _PORT_LINUX_H */ diff --git a/crypto/openssh/openbsd-compat/port-tun.c b/crypto/openssh/openbsd-compat/port-tun.c index ddc92d0f3f..0d756f74f8 100644 --- a/crypto/openssh/openbsd-compat/port-tun.c +++ b/crypto/openssh/openbsd-compat/port-tun.c @@ -173,9 +173,11 @@ sys_tun_open(int tun, int mode) if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) goto failed; - ifr.ifr_flags |= IFF_UP; - if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) - goto failed; + if ((ifr.ifr_flags & IFF_UP) == 0) { + ifr.ifr_flags |= IFF_UP; + if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) + goto failed; + } close(sock); return (fd); diff --git a/crypto/openssh/packet.c b/crypto/openssh/packet.c index 994e35b6df..48f7fe6133 100644 --- a/crypto/openssh/packet.c +++ b/crypto/openssh/packet.c @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.c,v 1.166 2009/06/27 09:29:06 andreas Exp $ */ +/* $OpenBSD: packet.c,v 1.168 2010/07/13 23:13:16 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1307,7 +1307,7 @@ packet_read_poll2(u_int32_t *seqnr_p) macbuf = mac_compute(mac, active_state->p_read.seqnr, buffer_ptr(&active_state->incoming_packet), buffer_len(&active_state->incoming_packet)); - if (memcmp(macbuf, buffer_ptr(&active_state->input), + if (timingsafe_bcmp(macbuf, buffer_ptr(&active_state->input), mac->mac_len) != 0) { logit("Corrupted MAC on input."); if (need > PACKET_MAX_SIZE) diff --git a/crypto/openssh/pathnames.h b/crypto/openssh/pathnames.h index 80c5d9cbb7..9e50950fe1 100644 --- a/crypto/openssh/pathnames.h +++ b/crypto/openssh/pathnames.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pathnames.h,v 1.17 2008/12/29 02:23:26 stevesk Exp $ */ +/* $OpenBSD: pathnames.h,v 1.19 2010/02/11 20:37:47 djm Exp $ */ /* * Author: Tatu Ylonen @@ -125,6 +125,11 @@ #define _PATH_SSH_KEY_SIGN "/usr/libexec/ssh-keysign" #endif +/* Location of ssh-pkcs11-helper to support keys in tokens */ +#ifndef _PATH_SSH_PKCS11_HELPER +#define _PATH_SSH_PKCS11_HELPER "/usr/libexec/ssh-pkcs11-helper" +#endif + /* xauth for X11 forwarding */ #ifndef _PATH_XAUTH #define _PATH_XAUTH "/usr/X11R6/bin/xauth" diff --git a/crypto/openssh/pkcs11.h b/crypto/openssh/pkcs11.h new file mode 100644 index 0000000000..2cde5b3f4f --- /dev/null +++ b/crypto/openssh/pkcs11.h @@ -0,0 +1,1357 @@ +/* $OpenBSD: pkcs11.h,v 1.2 2010/02/24 06:12:53 djm Exp $ */ +/* pkcs11.h + Copyright 2006, 2007 g10 Code GmbH + Copyright 2006 Andreas Jellinghaus + + This file is free software; as a special exception the author gives + unlimited permission to copy and/or distribute it, with or without + modifications, as long as this notice is preserved. + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, to the extent permitted by law; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. */ + +/* Please submit changes back to the Scute project at + http://www.scute.org/ (or send them to marcus@g10code.com), so that + they can be picked up by other projects from there as well. */ + +/* This file is a modified implementation of the PKCS #11 standard by + RSA Security Inc. It is mostly a drop-in replacement, with the + following change: + + This header file does not require any macro definitions by the user + (like CK_DEFINE_FUNCTION etc). In fact, it defines those macros + for you (if useful, some are missing, let me know if you need + more). + + There is an additional API available that does comply better to the + GNU coding standard. It can be switched on by defining + CRYPTOKI_GNU before including this header file. For this, the + following changes are made to the specification: + + All structure types are changed to a "struct ck_foo" where CK_FOO + is the type name in PKCS #11. + + All non-structure types are changed to ck_foo_t where CK_FOO is the + lowercase version of the type name in PKCS #11. The basic types + (CK_ULONG et al.) are removed without substitute. + + All members of structures are modified in the following way: Type + indication prefixes are removed, and underscore characters are + inserted before words. Then the result is lowercased. + + Note that function names are still in the original case, as they + need for ABI compatibility. + + CK_FALSE, CK_TRUE and NULL_PTR are removed without substitute. Use + . + + If CRYPTOKI_COMPAT is defined before including this header file, + then none of the API changes above take place, and the API is the + one defined by the PKCS #11 standard. */ + +#ifndef PKCS11_H +#define PKCS11_H 1 + +#if defined(__cplusplus) +extern "C" { +#endif + + +/* The version of cryptoki we implement. The revision is changed with + each modification of this file. If you do not use the "official" + version of this file, please consider deleting the revision macro + (you may use a macro with a different name to keep track of your + versions). */ +#define CRYPTOKI_VERSION_MAJOR 2 +#define CRYPTOKI_VERSION_MINOR 20 +#define CRYPTOKI_VERSION_REVISION 6 + + +/* Compatibility interface is default, unless CRYPTOKI_GNU is + given. */ +#ifndef CRYPTOKI_GNU +#ifndef CRYPTOKI_COMPAT +#define CRYPTOKI_COMPAT 1 +#endif +#endif + +/* System dependencies. */ + +#if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32) + +/* There is a matching pop below. */ +#pragma pack(push, cryptoki, 1) + +#ifdef CRYPTOKI_EXPORTS +#define CK_SPEC __declspec(dllexport) +#else +#define CK_SPEC __declspec(dllimport) +#endif + +#else + +#define CK_SPEC + +#endif + + +#ifdef CRYPTOKI_COMPAT + /* If we are in compatibility mode, switch all exposed names to the + PKCS #11 variant. There are corresponding #undefs below. */ + +#define ck_flags_t CK_FLAGS +#define ck_version _CK_VERSION + +#define ck_info _CK_INFO +#define cryptoki_version cryptokiVersion +#define manufacturer_id manufacturerID +#define library_description libraryDescription +#define library_version libraryVersion + +#define ck_notification_t CK_NOTIFICATION +#define ck_slot_id_t CK_SLOT_ID + +#define ck_slot_info _CK_SLOT_INFO +#define slot_description slotDescription +#define hardware_version hardwareVersion +#define firmware_version firmwareVersion + +#define ck_token_info _CK_TOKEN_INFO +#define serial_number serialNumber +#define max_session_count ulMaxSessionCount +#define session_count ulSessionCount +#define max_rw_session_count ulMaxRwSessionCount +#define rw_session_count ulRwSessionCount +#define max_pin_len ulMaxPinLen +#define min_pin_len ulMinPinLen +#define total_public_memory ulTotalPublicMemory +#define free_public_memory ulFreePublicMemory +#define total_private_memory ulTotalPrivateMemory +#define free_private_memory ulFreePrivateMemory +#define utc_time utcTime + +#define ck_session_handle_t CK_SESSION_HANDLE +#define ck_user_type_t CK_USER_TYPE +#define ck_state_t CK_STATE + +#define ck_session_info _CK_SESSION_INFO +#define slot_id slotID +#define device_error ulDeviceError + +#define ck_object_handle_t CK_OBJECT_HANDLE +#define ck_object_class_t CK_OBJECT_CLASS +#define ck_hw_feature_type_t CK_HW_FEATURE_TYPE +#define ck_key_type_t CK_KEY_TYPE +#define ck_certificate_type_t CK_CERTIFICATE_TYPE +#define ck_attribute_type_t CK_ATTRIBUTE_TYPE + +#define ck_attribute _CK_ATTRIBUTE +#define value pValue +#define value_len ulValueLen + +#define ck_date _CK_DATE + +#define ck_mechanism_type_t CK_MECHANISM_TYPE + +#define ck_mechanism _CK_MECHANISM +#define parameter pParameter +#define parameter_len ulParameterLen + +#define ck_mechanism_info _CK_MECHANISM_INFO +#define min_key_size ulMinKeySize +#define max_key_size ulMaxKeySize + +#define ck_rv_t CK_RV +#define ck_notify_t CK_NOTIFY + +#define ck_function_list _CK_FUNCTION_LIST + +#define ck_createmutex_t CK_CREATEMUTEX +#define ck_destroymutex_t CK_DESTROYMUTEX +#define ck_lockmutex_t CK_LOCKMUTEX +#define ck_unlockmutex_t CK_UNLOCKMUTEX + +#define ck_c_initialize_args _CK_C_INITIALIZE_ARGS +#define create_mutex CreateMutex +#define destroy_mutex DestroyMutex +#define lock_mutex LockMutex +#define unlock_mutex UnlockMutex +#define reserved pReserved + +#endif /* CRYPTOKI_COMPAT */ + + + +typedef unsigned long ck_flags_t; + +struct ck_version +{ + unsigned char major; + unsigned char minor; +}; + + +struct ck_info +{ + struct ck_version cryptoki_version; + unsigned char manufacturer_id[32]; + ck_flags_t flags; + unsigned char library_description[32]; + struct ck_version library_version; +}; + + +typedef unsigned long ck_notification_t; + +#define CKN_SURRENDER (0) + + +typedef unsigned long ck_slot_id_t; + + +struct ck_slot_info +{ + unsigned char slot_description[64]; + unsigned char manufacturer_id[32]; + ck_flags_t flags; + struct ck_version hardware_version; + struct ck_version firmware_version; +}; + + +#define CKF_TOKEN_PRESENT (1 << 0) +#define CKF_REMOVABLE_DEVICE (1 << 1) +#define CKF_HW_SLOT (1 << 2) +#define CKF_ARRAY_ATTRIBUTE (1 << 30) + + +struct ck_token_info +{ + unsigned char label[32]; + unsigned char manufacturer_id[32]; + unsigned char model[16]; + unsigned char serial_number[16]; + ck_flags_t flags; + unsigned long max_session_count; + unsigned long session_count; + unsigned long max_rw_session_count; + unsigned long rw_session_count; + unsigned long max_pin_len; + unsigned long min_pin_len; + unsigned long total_public_memory; + unsigned long free_public_memory; + unsigned long total_private_memory; + unsigned long free_private_memory; + struct ck_version hardware_version; + struct ck_version firmware_version; + unsigned char utc_time[16]; +}; + + +#define CKF_RNG (1 << 0) +#define CKF_WRITE_PROTECTED (1 << 1) +#define CKF_LOGIN_REQUIRED (1 << 2) +#define CKF_USER_PIN_INITIALIZED (1 << 3) +#define CKF_RESTORE_KEY_NOT_NEEDED (1 << 5) +#define CKF_CLOCK_ON_TOKEN (1 << 6) +#define CKF_PROTECTED_AUTHENTICATION_PATH (1 << 8) +#define CKF_DUAL_CRYPTO_OPERATIONS (1 << 9) +#define CKF_TOKEN_INITIALIZED (1 << 10) +#define CKF_SECONDARY_AUTHENTICATION (1 << 11) +#define CKF_USER_PIN_COUNT_LOW (1 << 16) +#define CKF_USER_PIN_FINAL_TRY (1 << 17) +#define CKF_USER_PIN_LOCKED (1 << 18) +#define CKF_USER_PIN_TO_BE_CHANGED (1 << 19) +#define CKF_SO_PIN_COUNT_LOW (1 << 20) +#define CKF_SO_PIN_FINAL_TRY (1 << 21) +#define CKF_SO_PIN_LOCKED (1 << 22) +#define CKF_SO_PIN_TO_BE_CHANGED (1 << 23) + +#define CK_UNAVAILABLE_INFORMATION ((unsigned long) -1) +#define CK_EFFECTIVELY_INFINITE (0) + + +typedef unsigned long ck_session_handle_t; + +#define CK_INVALID_HANDLE (0) + + +typedef unsigned long ck_user_type_t; + +#define CKU_SO (0) +#define CKU_USER (1) +#define CKU_CONTEXT_SPECIFIC (2) + + +typedef unsigned long ck_state_t; + +#define CKS_RO_PUBLIC_SESSION (0) +#define CKS_RO_USER_FUNCTIONS (1) +#define CKS_RW_PUBLIC_SESSION (2) +#define CKS_RW_USER_FUNCTIONS (3) +#define CKS_RW_SO_FUNCTIONS (4) + + +struct ck_session_info +{ + ck_slot_id_t slot_id; + ck_state_t state; + ck_flags_t flags; + unsigned long device_error; +}; + +#define CKF_RW_SESSION (1 << 1) +#define CKF_SERIAL_SESSION (1 << 2) + + +typedef unsigned long ck_object_handle_t; + + +typedef unsigned long ck_object_class_t; + +#define CKO_DATA (0) +#define CKO_CERTIFICATE (1) +#define CKO_PUBLIC_KEY (2) +#define CKO_PRIVATE_KEY (3) +#define CKO_SECRET_KEY (4) +#define CKO_HW_FEATURE (5) +#define CKO_DOMAIN_PARAMETERS (6) +#define CKO_MECHANISM (7) +#define CKO_VENDOR_DEFINED ((unsigned long) (1 << 31)) + + +typedef unsigned long ck_hw_feature_type_t; + +#define CKH_MONOTONIC_COUNTER (1) +#define CKH_CLOCK (2) +#define CKH_USER_INTERFACE (3) +#define CKH_VENDOR_DEFINED ((unsigned long) (1 << 31)) + + +typedef unsigned long ck_key_type_t; + +#define CKK_RSA (0) +#define CKK_DSA (1) +#define CKK_DH (2) +#define CKK_ECDSA (3) +#define CKK_EC (3) +#define CKK_X9_42_DH (4) +#define CKK_KEA (5) +#define CKK_GENERIC_SECRET (0x10) +#define CKK_RC2 (0x11) +#define CKK_RC4 (0x12) +#define CKK_DES (0x13) +#define CKK_DES2 (0x14) +#define CKK_DES3 (0x15) +#define CKK_CAST (0x16) +#define CKK_CAST3 (0x17) +#define CKK_CAST128 (0x18) +#define CKK_RC5 (0x19) +#define CKK_IDEA (0x1a) +#define CKK_SKIPJACK (0x1b) +#define CKK_BATON (0x1c) +#define CKK_JUNIPER (0x1d) +#define CKK_CDMF (0x1e) +#define CKK_AES (0x1f) +#define CKK_BLOWFISH (0x20) +#define CKK_TWOFISH (0x21) +#define CKK_VENDOR_DEFINED ((unsigned long) (1 << 31)) + +typedef unsigned long ck_certificate_type_t; + +#define CKC_X_509 (0) +#define CKC_X_509_ATTR_CERT (1) +#define CKC_WTLS (2) +#define CKC_VENDOR_DEFINED ((unsigned long) (1 << 31)) + + +typedef unsigned long ck_attribute_type_t; + +#define CKA_CLASS (0) +#define CKA_TOKEN (1) +#define CKA_PRIVATE (2) +#define CKA_LABEL (3) +#define CKA_APPLICATION (0x10) +#define CKA_VALUE (0x11) +#define CKA_OBJECT_ID (0x12) +#define CKA_CERTIFICATE_TYPE (0x80) +#define CKA_ISSUER (0x81) +#define CKA_SERIAL_NUMBER (0x82) +#define CKA_AC_ISSUER (0x83) +#define CKA_OWNER (0x84) +#define CKA_ATTR_TYPES (0x85) +#define CKA_TRUSTED (0x86) +#define CKA_CERTIFICATE_CATEGORY (0x87) +#define CKA_JAVA_MIDP_SECURITY_DOMAIN (0x88) +#define CKA_URL (0x89) +#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY (0x8a) +#define CKA_HASH_OF_ISSUER_PUBLIC_KEY (0x8b) +#define CKA_CHECK_VALUE (0x90) +#define CKA_KEY_TYPE (0x100) +#define CKA_SUBJECT (0x101) +#define CKA_ID (0x102) +#define CKA_SENSITIVE (0x103) +#define CKA_ENCRYPT (0x104) +#define CKA_DECRYPT (0x105) +#define CKA_WRAP (0x106) +#define CKA_UNWRAP (0x107) +#define CKA_SIGN (0x108) +#define CKA_SIGN_RECOVER (0x109) +#define CKA_VERIFY (0x10a) +#define CKA_VERIFY_RECOVER (0x10b) +#define CKA_DERIVE (0x10c) +#define CKA_START_DATE (0x110) +#define CKA_END_DATE (0x111) +#define CKA_MODULUS (0x120) +#define CKA_MODULUS_BITS (0x121) +#define CKA_PUBLIC_EXPONENT (0x122) +#define CKA_PRIVATE_EXPONENT (0x123) +#define CKA_PRIME_1 (0x124) +#define CKA_PRIME_2 (0x125) +#define CKA_EXPONENT_1 (0x126) +#define CKA_EXPONENT_2 (0x127) +#define CKA_COEFFICIENT (0x128) +#define CKA_PRIME (0x130) +#define CKA_SUBPRIME (0x131) +#define CKA_BASE (0x132) +#define CKA_PRIME_BITS (0x133) +#define CKA_SUB_PRIME_BITS (0x134) +#define CKA_VALUE_BITS (0x160) +#define CKA_VALUE_LEN (0x161) +#define CKA_EXTRACTABLE (0x162) +#define CKA_LOCAL (0x163) +#define CKA_NEVER_EXTRACTABLE (0x164) +#define CKA_ALWAYS_SENSITIVE (0x165) +#define CKA_KEY_GEN_MECHANISM (0x166) +#define CKA_MODIFIABLE (0x170) +#define CKA_ECDSA_PARAMS (0x180) +#define CKA_EC_PARAMS (0x180) +#define CKA_EC_POINT (0x181) +#define CKA_SECONDARY_AUTH (0x200) +#define CKA_AUTH_PIN_FLAGS (0x201) +#define CKA_ALWAYS_AUTHENTICATE (0x202) +#define CKA_WRAP_WITH_TRUSTED (0x210) +#define CKA_HW_FEATURE_TYPE (0x300) +#define CKA_RESET_ON_INIT (0x301) +#define CKA_HAS_RESET (0x302) +#define CKA_PIXEL_X (0x400) +#define CKA_PIXEL_Y (0x401) +#define CKA_RESOLUTION (0x402) +#define CKA_CHAR_ROWS (0x403) +#define CKA_CHAR_COLUMNS (0x404) +#define CKA_COLOR (0x405) +#define CKA_BITS_PER_PIXEL (0x406) +#define CKA_CHAR_SETS (0x480) +#define CKA_ENCODING_METHODS (0x481) +#define CKA_MIME_TYPES (0x482) +#define CKA_MECHANISM_TYPE (0x500) +#define CKA_REQUIRED_CMS_ATTRIBUTES (0x501) +#define CKA_DEFAULT_CMS_ATTRIBUTES (0x502) +#define CKA_SUPPORTED_CMS_ATTRIBUTES (0x503) +#define CKA_WRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x211) +#define CKA_UNWRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x212) +#define CKA_ALLOWED_MECHANISMS (CKF_ARRAY_ATTRIBUTE | 0x600) +#define CKA_VENDOR_DEFINED ((unsigned long) (1 << 31)) + + +struct ck_attribute +{ + ck_attribute_type_t type; + void *value; + unsigned long value_len; +}; + + +struct ck_date +{ + unsigned char year[4]; + unsigned char month[2]; + unsigned char day[2]; +}; + + +typedef unsigned long ck_mechanism_type_t; + +#define CKM_RSA_PKCS_KEY_PAIR_GEN (0) +#define CKM_RSA_PKCS (1) +#define CKM_RSA_9796 (2) +#define CKM_RSA_X_509 (3) +#define CKM_MD2_RSA_PKCS (4) +#define CKM_MD5_RSA_PKCS (5) +#define CKM_SHA1_RSA_PKCS (6) +#define CKM_RIPEMD128_RSA_PKCS (7) +#define CKM_RIPEMD160_RSA_PKCS (8) +#define CKM_RSA_PKCS_OAEP (9) +#define CKM_RSA_X9_31_KEY_PAIR_GEN (0xa) +#define CKM_RSA_X9_31 (0xb) +#define CKM_SHA1_RSA_X9_31 (0xc) +#define CKM_RSA_PKCS_PSS (0xd) +#define CKM_SHA1_RSA_PKCS_PSS (0xe) +#define CKM_DSA_KEY_PAIR_GEN (0x10) +#define CKM_DSA (0x11) +#define CKM_DSA_SHA1 (0x12) +#define CKM_DH_PKCS_KEY_PAIR_GEN (0x20) +#define CKM_DH_PKCS_DERIVE (0x21) +#define CKM_X9_42_DH_KEY_PAIR_GEN (0x30) +#define CKM_X9_42_DH_DERIVE (0x31) +#define CKM_X9_42_DH_HYBRID_DERIVE (0x32) +#define CKM_X9_42_MQV_DERIVE (0x33) +#define CKM_SHA256_RSA_PKCS (0x40) +#define CKM_SHA384_RSA_PKCS (0x41) +#define CKM_SHA512_RSA_PKCS (0x42) +#define CKM_SHA256_RSA_PKCS_PSS (0x43) +#define CKM_SHA384_RSA_PKCS_PSS (0x44) +#define CKM_SHA512_RSA_PKCS_PSS (0x45) +#define CKM_RC2_KEY_GEN (0x100) +#define CKM_RC2_ECB (0x101) +#define CKM_RC2_CBC (0x102) +#define CKM_RC2_MAC (0x103) +#define CKM_RC2_MAC_GENERAL (0x104) +#define CKM_RC2_CBC_PAD (0x105) +#define CKM_RC4_KEY_GEN (0x110) +#define CKM_RC4 (0x111) +#define CKM_DES_KEY_GEN (0x120) +#define CKM_DES_ECB (0x121) +#define CKM_DES_CBC (0x122) +#define CKM_DES_MAC (0x123) +#define CKM_DES_MAC_GENERAL (0x124) +#define CKM_DES_CBC_PAD (0x125) +#define CKM_DES2_KEY_GEN (0x130) +#define CKM_DES3_KEY_GEN (0x131) +#define CKM_DES3_ECB (0x132) +#define CKM_DES3_CBC (0x133) +#define CKM_DES3_MAC (0x134) +#define CKM_DES3_MAC_GENERAL (0x135) +#define CKM_DES3_CBC_PAD (0x136) +#define CKM_CDMF_KEY_GEN (0x140) +#define CKM_CDMF_ECB (0x141) +#define CKM_CDMF_CBC (0x142) +#define CKM_CDMF_MAC (0x143) +#define CKM_CDMF_MAC_GENERAL (0x144) +#define CKM_CDMF_CBC_PAD (0x145) +#define CKM_MD2 (0x200) +#define CKM_MD2_HMAC (0x201) +#define CKM_MD2_HMAC_GENERAL (0x202) +#define CKM_MD5 (0x210) +#define CKM_MD5_HMAC (0x211) +#define CKM_MD5_HMAC_GENERAL (0x212) +#define CKM_SHA_1 (0x220) +#define CKM_SHA_1_HMAC (0x221) +#define CKM_SHA_1_HMAC_GENERAL (0x222) +#define CKM_RIPEMD128 (0x230) +#define CKM_RIPEMD128_HMAC (0x231) +#define CKM_RIPEMD128_HMAC_GENERAL (0x232) +#define CKM_RIPEMD160 (0x240) +#define CKM_RIPEMD160_HMAC (0x241) +#define CKM_RIPEMD160_HMAC_GENERAL (0x242) +#define CKM_SHA256 (0x250) +#define CKM_SHA256_HMAC (0x251) +#define CKM_SHA256_HMAC_GENERAL (0x252) +#define CKM_SHA384 (0x260) +#define CKM_SHA384_HMAC (0x261) +#define CKM_SHA384_HMAC_GENERAL (0x262) +#define CKM_SHA512 (0x270) +#define CKM_SHA512_HMAC (0x271) +#define CKM_SHA512_HMAC_GENERAL (0x272) +#define CKM_CAST_KEY_GEN (0x300) +#define CKM_CAST_ECB (0x301) +#define CKM_CAST_CBC (0x302) +#define CKM_CAST_MAC (0x303) +#define CKM_CAST_MAC_GENERAL (0x304) +#define CKM_CAST_CBC_PAD (0x305) +#define CKM_CAST3_KEY_GEN (0x310) +#define CKM_CAST3_ECB (0x311) +#define CKM_CAST3_CBC (0x312) +#define CKM_CAST3_MAC (0x313) +#define CKM_CAST3_MAC_GENERAL (0x314) +#define CKM_CAST3_CBC_PAD (0x315) +#define CKM_CAST5_KEY_GEN (0x320) +#define CKM_CAST128_KEY_GEN (0x320) +#define CKM_CAST5_ECB (0x321) +#define CKM_CAST128_ECB (0x321) +#define CKM_CAST5_CBC (0x322) +#define CKM_CAST128_CBC (0x322) +#define CKM_CAST5_MAC (0x323) +#define CKM_CAST128_MAC (0x323) +#define CKM_CAST5_MAC_GENERAL (0x324) +#define CKM_CAST128_MAC_GENERAL (0x324) +#define CKM_CAST5_CBC_PAD (0x325) +#define CKM_CAST128_CBC_PAD (0x325) +#define CKM_RC5_KEY_GEN (0x330) +#define CKM_RC5_ECB (0x331) +#define CKM_RC5_CBC (0x332) +#define CKM_RC5_MAC (0x333) +#define CKM_RC5_MAC_GENERAL (0x334) +#define CKM_RC5_CBC_PAD (0x335) +#define CKM_IDEA_KEY_GEN (0x340) +#define CKM_IDEA_ECB (0x341) +#define CKM_IDEA_CBC (0x342) +#define CKM_IDEA_MAC (0x343) +#define CKM_IDEA_MAC_GENERAL (0x344) +#define CKM_IDEA_CBC_PAD (0x345) +#define CKM_GENERIC_SECRET_KEY_GEN (0x350) +#define CKM_CONCATENATE_BASE_AND_KEY (0x360) +#define CKM_CONCATENATE_BASE_AND_DATA (0x362) +#define CKM_CONCATENATE_DATA_AND_BASE (0x363) +#define CKM_XOR_BASE_AND_DATA (0x364) +#define CKM_EXTRACT_KEY_FROM_KEY (0x365) +#define CKM_SSL3_PRE_MASTER_KEY_GEN (0x370) +#define CKM_SSL3_MASTER_KEY_DERIVE (0x371) +#define CKM_SSL3_KEY_AND_MAC_DERIVE (0x372) +#define CKM_SSL3_MASTER_KEY_DERIVE_DH (0x373) +#define CKM_TLS_PRE_MASTER_KEY_GEN (0x374) +#define CKM_TLS_MASTER_KEY_DERIVE (0x375) +#define CKM_TLS_KEY_AND_MAC_DERIVE (0x376) +#define CKM_TLS_MASTER_KEY_DERIVE_DH (0x377) +#define CKM_SSL3_MD5_MAC (0x380) +#define CKM_SSL3_SHA1_MAC (0x381) +#define CKM_MD5_KEY_DERIVATION (0x390) +#define CKM_MD2_KEY_DERIVATION (0x391) +#define CKM_SHA1_KEY_DERIVATION (0x392) +#define CKM_PBE_MD2_DES_CBC (0x3a0) +#define CKM_PBE_MD5_DES_CBC (0x3a1) +#define CKM_PBE_MD5_CAST_CBC (0x3a2) +#define CKM_PBE_MD5_CAST3_CBC (0x3a3) +#define CKM_PBE_MD5_CAST5_CBC (0x3a4) +#define CKM_PBE_MD5_CAST128_CBC (0x3a4) +#define CKM_PBE_SHA1_CAST5_CBC (0x3a5) +#define CKM_PBE_SHA1_CAST128_CBC (0x3a5) +#define CKM_PBE_SHA1_RC4_128 (0x3a6) +#define CKM_PBE_SHA1_RC4_40 (0x3a7) +#define CKM_PBE_SHA1_DES3_EDE_CBC (0x3a8) +#define CKM_PBE_SHA1_DES2_EDE_CBC (0x3a9) +#define CKM_PBE_SHA1_RC2_128_CBC (0x3aa) +#define CKM_PBE_SHA1_RC2_40_CBC (0x3ab) +#define CKM_PKCS5_PBKD2 (0x3b0) +#define CKM_PBA_SHA1_WITH_SHA1_HMAC (0x3c0) +#define CKM_KEY_WRAP_LYNKS (0x400) +#define CKM_KEY_WRAP_SET_OAEP (0x401) +#define CKM_SKIPJACK_KEY_GEN (0x1000) +#define CKM_SKIPJACK_ECB64 (0x1001) +#define CKM_SKIPJACK_CBC64 (0x1002) +#define CKM_SKIPJACK_OFB64 (0x1003) +#define CKM_SKIPJACK_CFB64 (0x1004) +#define CKM_SKIPJACK_CFB32 (0x1005) +#define CKM_SKIPJACK_CFB16 (0x1006) +#define CKM_SKIPJACK_CFB8 (0x1007) +#define CKM_SKIPJACK_WRAP (0x1008) +#define CKM_SKIPJACK_PRIVATE_WRAP (0x1009) +#define CKM_SKIPJACK_RELAYX (0x100a) +#define CKM_KEA_KEY_PAIR_GEN (0x1010) +#define CKM_KEA_KEY_DERIVE (0x1011) +#define CKM_FORTEZZA_TIMESTAMP (0x1020) +#define CKM_BATON_KEY_GEN (0x1030) +#define CKM_BATON_ECB128 (0x1031) +#define CKM_BATON_ECB96 (0x1032) +#define CKM_BATON_CBC128 (0x1033) +#define CKM_BATON_COUNTER (0x1034) +#define CKM_BATON_SHUFFLE (0x1035) +#define CKM_BATON_WRAP (0x1036) +#define CKM_ECDSA_KEY_PAIR_GEN (0x1040) +#define CKM_EC_KEY_PAIR_GEN (0x1040) +#define CKM_ECDSA (0x1041) +#define CKM_ECDSA_SHA1 (0x1042) +#define CKM_ECDH1_DERIVE (0x1050) +#define CKM_ECDH1_COFACTOR_DERIVE (0x1051) +#define CKM_ECMQV_DERIVE (0x1052) +#define CKM_JUNIPER_KEY_GEN (0x1060) +#define CKM_JUNIPER_ECB128 (0x1061) +#define CKM_JUNIPER_CBC128 (0x1062) +#define CKM_JUNIPER_COUNTER (0x1063) +#define CKM_JUNIPER_SHUFFLE (0x1064) +#define CKM_JUNIPER_WRAP (0x1065) +#define CKM_FASTHASH (0x1070) +#define CKM_AES_KEY_GEN (0x1080) +#define CKM_AES_ECB (0x1081) +#define CKM_AES_CBC (0x1082) +#define CKM_AES_MAC (0x1083) +#define CKM_AES_MAC_GENERAL (0x1084) +#define CKM_AES_CBC_PAD (0x1085) +#define CKM_DSA_PARAMETER_GEN (0x2000) +#define CKM_DH_PKCS_PARAMETER_GEN (0x2001) +#define CKM_X9_42_DH_PARAMETER_GEN (0x2002) +#define CKM_VENDOR_DEFINED ((unsigned long) (1 << 31)) + + +struct ck_mechanism +{ + ck_mechanism_type_t mechanism; + void *parameter; + unsigned long parameter_len; +}; + + +struct ck_mechanism_info +{ + unsigned long min_key_size; + unsigned long max_key_size; + ck_flags_t flags; +}; + +#define CKF_HW (1 << 0) +#define CKF_ENCRYPT (1 << 8) +#define CKF_DECRYPT (1 << 9) +#define CKF_DIGEST (1 << 10) +#define CKF_SIGN (1 << 11) +#define CKF_SIGN_RECOVER (1 << 12) +#define CKF_VERIFY (1 << 13) +#define CKF_VERIFY_RECOVER (1 << 14) +#define CKF_GENERATE (1 << 15) +#define CKF_GENERATE_KEY_PAIR (1 << 16) +#define CKF_WRAP (1 << 17) +#define CKF_UNWRAP (1 << 18) +#define CKF_DERIVE (1 << 19) +#define CKF_EXTENSION ((unsigned long) (1 << 31)) + + +/* Flags for C_WaitForSlotEvent. */ +#define CKF_DONT_BLOCK (1) + + +typedef unsigned long ck_rv_t; + + +typedef ck_rv_t (*ck_notify_t) (ck_session_handle_t session, + ck_notification_t event, void *application); + +/* Forward reference. */ +struct ck_function_list; + +#define _CK_DECLARE_FUNCTION(name, args) \ +typedef ck_rv_t (*CK_ ## name) args; \ +ck_rv_t CK_SPEC name args + +_CK_DECLARE_FUNCTION (C_Initialize, (void *init_args)); +_CK_DECLARE_FUNCTION (C_Finalize, (void *reserved)); +_CK_DECLARE_FUNCTION (C_GetInfo, (struct ck_info *info)); +_CK_DECLARE_FUNCTION (C_GetFunctionList, + (struct ck_function_list **function_list)); + +_CK_DECLARE_FUNCTION (C_GetSlotList, + (unsigned char token_present, ck_slot_id_t *slot_list, + unsigned long *count)); +_CK_DECLARE_FUNCTION (C_GetSlotInfo, + (ck_slot_id_t slot_id, struct ck_slot_info *info)); +_CK_DECLARE_FUNCTION (C_GetTokenInfo, + (ck_slot_id_t slot_id, struct ck_token_info *info)); +_CK_DECLARE_FUNCTION (C_WaitForSlotEvent, + (ck_flags_t flags, ck_slot_id_t *slot, void *reserved)); +_CK_DECLARE_FUNCTION (C_GetMechanismList, + (ck_slot_id_t slot_id, + ck_mechanism_type_t *mechanism_list, + unsigned long *count)); +_CK_DECLARE_FUNCTION (C_GetMechanismInfo, + (ck_slot_id_t slot_id, ck_mechanism_type_t type, + struct ck_mechanism_info *info)); +_CK_DECLARE_FUNCTION (C_InitToken, + (ck_slot_id_t slot_id, unsigned char *pin, + unsigned long pin_len, unsigned char *label)); +_CK_DECLARE_FUNCTION (C_InitPIN, + (ck_session_handle_t session, unsigned char *pin, + unsigned long pin_len)); +_CK_DECLARE_FUNCTION (C_SetPIN, + (ck_session_handle_t session, unsigned char *old_pin, + unsigned long old_len, unsigned char *new_pin, + unsigned long new_len)); + +_CK_DECLARE_FUNCTION (C_OpenSession, + (ck_slot_id_t slot_id, ck_flags_t flags, + void *application, ck_notify_t notify, + ck_session_handle_t *session)); +_CK_DECLARE_FUNCTION (C_CloseSession, (ck_session_handle_t session)); +_CK_DECLARE_FUNCTION (C_CloseAllSessions, (ck_slot_id_t slot_id)); +_CK_DECLARE_FUNCTION (C_GetSessionInfo, + (ck_session_handle_t session, + struct ck_session_info *info)); +_CK_DECLARE_FUNCTION (C_GetOperationState, + (ck_session_handle_t session, + unsigned char *operation_state, + unsigned long *operation_state_len)); +_CK_DECLARE_FUNCTION (C_SetOperationState, + (ck_session_handle_t session, + unsigned char *operation_state, + unsigned long operation_state_len, + ck_object_handle_t encryption_key, + ck_object_handle_t authentiation_key)); +_CK_DECLARE_FUNCTION (C_Login, + (ck_session_handle_t session, ck_user_type_t user_type, + unsigned char *pin, unsigned long pin_len)); +_CK_DECLARE_FUNCTION (C_Logout, (ck_session_handle_t session)); + +_CK_DECLARE_FUNCTION (C_CreateObject, + (ck_session_handle_t session, + struct ck_attribute *templ, + unsigned long count, ck_object_handle_t *object)); +_CK_DECLARE_FUNCTION (C_CopyObject, + (ck_session_handle_t session, ck_object_handle_t object, + struct ck_attribute *templ, unsigned long count, + ck_object_handle_t *new_object)); +_CK_DECLARE_FUNCTION (C_DestroyObject, + (ck_session_handle_t session, + ck_object_handle_t object)); +_CK_DECLARE_FUNCTION (C_GetObjectSize, + (ck_session_handle_t session, + ck_object_handle_t object, + unsigned long *size)); +_CK_DECLARE_FUNCTION (C_GetAttributeValue, + (ck_session_handle_t session, + ck_object_handle_t object, + struct ck_attribute *templ, + unsigned long count)); +_CK_DECLARE_FUNCTION (C_SetAttributeValue, + (ck_session_handle_t session, + ck_object_handle_t object, + struct ck_attribute *templ, + unsigned long count)); +_CK_DECLARE_FUNCTION (C_FindObjectsInit, + (ck_session_handle_t session, + struct ck_attribute *templ, + unsigned long count)); +_CK_DECLARE_FUNCTION (C_FindObjects, + (ck_session_handle_t session, + ck_object_handle_t *object, + unsigned long max_object_count, + unsigned long *object_count)); +_CK_DECLARE_FUNCTION (C_FindObjectsFinal, + (ck_session_handle_t session)); + +_CK_DECLARE_FUNCTION (C_EncryptInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_Encrypt, + (ck_session_handle_t session, + unsigned char *data, unsigned long data_len, + unsigned char *encrypted_data, + unsigned long *encrypted_data_len)); +_CK_DECLARE_FUNCTION (C_EncryptUpdate, + (ck_session_handle_t session, + unsigned char *part, unsigned long part_len, + unsigned char *encrypted_part, + unsigned long *encrypted_part_len)); +_CK_DECLARE_FUNCTION (C_EncryptFinal, + (ck_session_handle_t session, + unsigned char *last_encrypted_part, + unsigned long *last_encrypted_part_len)); + +_CK_DECLARE_FUNCTION (C_DecryptInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_Decrypt, + (ck_session_handle_t session, + unsigned char *encrypted_data, + unsigned long encrypted_data_len, + unsigned char *data, unsigned long *data_len)); +_CK_DECLARE_FUNCTION (C_DecryptUpdate, + (ck_session_handle_t session, + unsigned char *encrypted_part, + unsigned long encrypted_part_len, + unsigned char *part, unsigned long *part_len)); +_CK_DECLARE_FUNCTION (C_DecryptFinal, + (ck_session_handle_t session, + unsigned char *last_part, + unsigned long *last_part_len)); + +_CK_DECLARE_FUNCTION (C_DigestInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism)); +_CK_DECLARE_FUNCTION (C_Digest, + (ck_session_handle_t session, + unsigned char *data, unsigned long data_len, + unsigned char *digest, + unsigned long *digest_len)); +_CK_DECLARE_FUNCTION (C_DigestUpdate, + (ck_session_handle_t session, + unsigned char *part, unsigned long part_len)); +_CK_DECLARE_FUNCTION (C_DigestKey, + (ck_session_handle_t session, ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_DigestFinal, + (ck_session_handle_t session, + unsigned char *digest, + unsigned long *digest_len)); + +_CK_DECLARE_FUNCTION (C_SignInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_Sign, + (ck_session_handle_t session, + unsigned char *data, unsigned long data_len, + unsigned char *signature, + unsigned long *signature_len)); +_CK_DECLARE_FUNCTION (C_SignUpdate, + (ck_session_handle_t session, + unsigned char *part, unsigned long part_len)); +_CK_DECLARE_FUNCTION (C_SignFinal, + (ck_session_handle_t session, + unsigned char *signature, + unsigned long *signature_len)); +_CK_DECLARE_FUNCTION (C_SignRecoverInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_SignRecover, + (ck_session_handle_t session, + unsigned char *data, unsigned long data_len, + unsigned char *signature, + unsigned long *signature_len)); + +_CK_DECLARE_FUNCTION (C_VerifyInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_Verify, + (ck_session_handle_t session, + unsigned char *data, unsigned long data_len, + unsigned char *signature, + unsigned long signature_len)); +_CK_DECLARE_FUNCTION (C_VerifyUpdate, + (ck_session_handle_t session, + unsigned char *part, unsigned long part_len)); +_CK_DECLARE_FUNCTION (C_VerifyFinal, + (ck_session_handle_t session, + unsigned char *signature, + unsigned long signature_len)); +_CK_DECLARE_FUNCTION (C_VerifyRecoverInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_VerifyRecover, + (ck_session_handle_t session, + unsigned char *signature, + unsigned long signature_len, + unsigned char *data, + unsigned long *data_len)); + +_CK_DECLARE_FUNCTION (C_DigestEncryptUpdate, + (ck_session_handle_t session, + unsigned char *part, unsigned long part_len, + unsigned char *encrypted_part, + unsigned long *encrypted_part_len)); +_CK_DECLARE_FUNCTION (C_DecryptDigestUpdate, + (ck_session_handle_t session, + unsigned char *encrypted_part, + unsigned long encrypted_part_len, + unsigned char *part, + unsigned long *part_len)); +_CK_DECLARE_FUNCTION (C_SignEncryptUpdate, + (ck_session_handle_t session, + unsigned char *part, unsigned long part_len, + unsigned char *encrypted_part, + unsigned long *encrypted_part_len)); +_CK_DECLARE_FUNCTION (C_DecryptVerifyUpdate, + (ck_session_handle_t session, + unsigned char *encrypted_part, + unsigned long encrypted_part_len, + unsigned char *part, + unsigned long *part_len)); + +_CK_DECLARE_FUNCTION (C_GenerateKey, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + struct ck_attribute *templ, + unsigned long count, + ck_object_handle_t *key)); +_CK_DECLARE_FUNCTION (C_GenerateKeyPair, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + struct ck_attribute *public_key_template, + unsigned long public_key_attribute_count, + struct ck_attribute *private_key_template, + unsigned long private_key_attribute_count, + ck_object_handle_t *public_key, + ck_object_handle_t *private_key)); +_CK_DECLARE_FUNCTION (C_WrapKey, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t wrapping_key, + ck_object_handle_t key, + unsigned char *wrapped_key, + unsigned long *wrapped_key_len)); +_CK_DECLARE_FUNCTION (C_UnwrapKey, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t unwrapping_key, + unsigned char *wrapped_key, + unsigned long wrapped_key_len, + struct ck_attribute *templ, + unsigned long attribute_count, + ck_object_handle_t *key)); +_CK_DECLARE_FUNCTION (C_DeriveKey, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t base_key, + struct ck_attribute *templ, + unsigned long attribute_count, + ck_object_handle_t *key)); + +_CK_DECLARE_FUNCTION (C_SeedRandom, + (ck_session_handle_t session, unsigned char *seed, + unsigned long seed_len)); +_CK_DECLARE_FUNCTION (C_GenerateRandom, + (ck_session_handle_t session, + unsigned char *random_data, + unsigned long random_len)); + +_CK_DECLARE_FUNCTION (C_GetFunctionStatus, (ck_session_handle_t session)); +_CK_DECLARE_FUNCTION (C_CancelFunction, (ck_session_handle_t session)); + + +struct ck_function_list +{ + struct ck_version version; + CK_C_Initialize C_Initialize; + CK_C_Finalize C_Finalize; + CK_C_GetInfo C_GetInfo; + CK_C_GetFunctionList C_GetFunctionList; + CK_C_GetSlotList C_GetSlotList; + CK_C_GetSlotInfo C_GetSlotInfo; + CK_C_GetTokenInfo C_GetTokenInfo; + CK_C_GetMechanismList C_GetMechanismList; + CK_C_GetMechanismInfo C_GetMechanismInfo; + CK_C_InitToken C_InitToken; + CK_C_InitPIN C_InitPIN; + CK_C_SetPIN C_SetPIN; + CK_C_OpenSession C_OpenSession; + CK_C_CloseSession C_CloseSession; + CK_C_CloseAllSessions C_CloseAllSessions; + CK_C_GetSessionInfo C_GetSessionInfo; + CK_C_GetOperationState C_GetOperationState; + CK_C_SetOperationState C_SetOperationState; + CK_C_Login C_Login; + CK_C_Logout C_Logout; + CK_C_CreateObject C_CreateObject; + CK_C_CopyObject C_CopyObject; + CK_C_DestroyObject C_DestroyObject; + CK_C_GetObjectSize C_GetObjectSize; + CK_C_GetAttributeValue C_GetAttributeValue; + CK_C_SetAttributeValue C_SetAttributeValue; + CK_C_FindObjectsInit C_FindObjectsInit; + CK_C_FindObjects C_FindObjects; + CK_C_FindObjectsFinal C_FindObjectsFinal; + CK_C_EncryptInit C_EncryptInit; + CK_C_Encrypt C_Encrypt; + CK_C_EncryptUpdate C_EncryptUpdate; + CK_C_EncryptFinal C_EncryptFinal; + CK_C_DecryptInit C_DecryptInit; + CK_C_Decrypt C_Decrypt; + CK_C_DecryptUpdate C_DecryptUpdate; + CK_C_DecryptFinal C_DecryptFinal; + CK_C_DigestInit C_DigestInit; + CK_C_Digest C_Digest; + CK_C_DigestUpdate C_DigestUpdate; + CK_C_DigestKey C_DigestKey; + CK_C_DigestFinal C_DigestFinal; + CK_C_SignInit C_SignInit; + CK_C_Sign C_Sign; + CK_C_SignUpdate C_SignUpdate; + CK_C_SignFinal C_SignFinal; + CK_C_SignRecoverInit C_SignRecoverInit; + CK_C_SignRecover C_SignRecover; + CK_C_VerifyInit C_VerifyInit; + CK_C_Verify C_Verify; + CK_C_VerifyUpdate C_VerifyUpdate; + CK_C_VerifyFinal C_VerifyFinal; + CK_C_VerifyRecoverInit C_VerifyRecoverInit; + CK_C_VerifyRecover C_VerifyRecover; + CK_C_DigestEncryptUpdate C_DigestEncryptUpdate; + CK_C_DecryptDigestUpdate C_DecryptDigestUpdate; + CK_C_SignEncryptUpdate C_SignEncryptUpdate; + CK_C_DecryptVerifyUpdate C_DecryptVerifyUpdate; + CK_C_GenerateKey C_GenerateKey; + CK_C_GenerateKeyPair C_GenerateKeyPair; + CK_C_WrapKey C_WrapKey; + CK_C_UnwrapKey C_UnwrapKey; + CK_C_DeriveKey C_DeriveKey; + CK_C_SeedRandom C_SeedRandom; + CK_C_GenerateRandom C_GenerateRandom; + CK_C_GetFunctionStatus C_GetFunctionStatus; + CK_C_CancelFunction C_CancelFunction; + CK_C_WaitForSlotEvent C_WaitForSlotEvent; +}; + + +typedef ck_rv_t (*ck_createmutex_t) (void **mutex); +typedef ck_rv_t (*ck_destroymutex_t) (void *mutex); +typedef ck_rv_t (*ck_lockmutex_t) (void *mutex); +typedef ck_rv_t (*ck_unlockmutex_t) (void *mutex); + + +struct ck_c_initialize_args +{ + ck_createmutex_t create_mutex; + ck_destroymutex_t destroy_mutex; + ck_lockmutex_t lock_mutex; + ck_unlockmutex_t unlock_mutex; + ck_flags_t flags; + void *reserved; +}; + + +#define CKF_LIBRARY_CANT_CREATE_OS_THREADS (1 << 0) +#define CKF_OS_LOCKING_OK (1 << 1) + +#define CKR_OK (0) +#define CKR_CANCEL (1) +#define CKR_HOST_MEMORY (2) +#define CKR_SLOT_ID_INVALID (3) +#define CKR_GENERAL_ERROR (5) +#define CKR_FUNCTION_FAILED (6) +#define CKR_ARGUMENTS_BAD (7) +#define CKR_NO_EVENT (8) +#define CKR_NEED_TO_CREATE_THREADS (9) +#define CKR_CANT_LOCK (0xa) +#define CKR_ATTRIBUTE_READ_ONLY (0x10) +#define CKR_ATTRIBUTE_SENSITIVE (0x11) +#define CKR_ATTRIBUTE_TYPE_INVALID (0x12) +#define CKR_ATTRIBUTE_VALUE_INVALID (0x13) +#define CKR_DATA_INVALID (0x20) +#define CKR_DATA_LEN_RANGE (0x21) +#define CKR_DEVICE_ERROR (0x30) +#define CKR_DEVICE_MEMORY (0x31) +#define CKR_DEVICE_REMOVED (0x32) +#define CKR_ENCRYPTED_DATA_INVALID (0x40) +#define CKR_ENCRYPTED_DATA_LEN_RANGE (0x41) +#define CKR_FUNCTION_CANCELED (0x50) +#define CKR_FUNCTION_NOT_PARALLEL (0x51) +#define CKR_FUNCTION_NOT_SUPPORTED (0x54) +#define CKR_KEY_HANDLE_INVALID (0x60) +#define CKR_KEY_SIZE_RANGE (0x62) +#define CKR_KEY_TYPE_INCONSISTENT (0x63) +#define CKR_KEY_NOT_NEEDED (0x64) +#define CKR_KEY_CHANGED (0x65) +#define CKR_KEY_NEEDED (0x66) +#define CKR_KEY_INDIGESTIBLE (0x67) +#define CKR_KEY_FUNCTION_NOT_PERMITTED (0x68) +#define CKR_KEY_NOT_WRAPPABLE (0x69) +#define CKR_KEY_UNEXTRACTABLE (0x6a) +#define CKR_MECHANISM_INVALID (0x70) +#define CKR_MECHANISM_PARAM_INVALID (0x71) +#define CKR_OBJECT_HANDLE_INVALID (0x82) +#define CKR_OPERATION_ACTIVE (0x90) +#define CKR_OPERATION_NOT_INITIALIZED (0x91) +#define CKR_PIN_INCORRECT (0xa0) +#define CKR_PIN_INVALID (0xa1) +#define CKR_PIN_LEN_RANGE (0xa2) +#define CKR_PIN_EXPIRED (0xa3) +#define CKR_PIN_LOCKED (0xa4) +#define CKR_SESSION_CLOSED (0xb0) +#define CKR_SESSION_COUNT (0xb1) +#define CKR_SESSION_HANDLE_INVALID (0xb3) +#define CKR_SESSION_PARALLEL_NOT_SUPPORTED (0xb4) +#define CKR_SESSION_READ_ONLY (0xb5) +#define CKR_SESSION_EXISTS (0xb6) +#define CKR_SESSION_READ_ONLY_EXISTS (0xb7) +#define CKR_SESSION_READ_WRITE_SO_EXISTS (0xb8) +#define CKR_SIGNATURE_INVALID (0xc0) +#define CKR_SIGNATURE_LEN_RANGE (0xc1) +#define CKR_TEMPLATE_INCOMPLETE (0xd0) +#define CKR_TEMPLATE_INCONSISTENT (0xd1) +#define CKR_TOKEN_NOT_PRESENT (0xe0) +#define CKR_TOKEN_NOT_RECOGNIZED (0xe1) +#define CKR_TOKEN_WRITE_PROTECTED (0xe2) +#define CKR_UNWRAPPING_KEY_HANDLE_INVALID (0xf0) +#define CKR_UNWRAPPING_KEY_SIZE_RANGE (0xf1) +#define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT (0xf2) +#define CKR_USER_ALREADY_LOGGED_IN (0x100) +#define CKR_USER_NOT_LOGGED_IN (0x101) +#define CKR_USER_PIN_NOT_INITIALIZED (0x102) +#define CKR_USER_TYPE_INVALID (0x103) +#define CKR_USER_ANOTHER_ALREADY_LOGGED_IN (0x104) +#define CKR_USER_TOO_MANY_TYPES (0x105) +#define CKR_WRAPPED_KEY_INVALID (0x110) +#define CKR_WRAPPED_KEY_LEN_RANGE (0x112) +#define CKR_WRAPPING_KEY_HANDLE_INVALID (0x113) +#define CKR_WRAPPING_KEY_SIZE_RANGE (0x114) +#define CKR_WRAPPING_KEY_TYPE_INCONSISTENT (0x115) +#define CKR_RANDOM_SEED_NOT_SUPPORTED (0x120) +#define CKR_RANDOM_NO_RNG (0x121) +#define CKR_DOMAIN_PARAMS_INVALID (0x130) +#define CKR_BUFFER_TOO_SMALL (0x150) +#define CKR_SAVED_STATE_INVALID (0x160) +#define CKR_INFORMATION_SENSITIVE (0x170) +#define CKR_STATE_UNSAVEABLE (0x180) +#define CKR_CRYPTOKI_NOT_INITIALIZED (0x190) +#define CKR_CRYPTOKI_ALREADY_INITIALIZED (0x191) +#define CKR_MUTEX_BAD (0x1a0) +#define CKR_MUTEX_NOT_LOCKED (0x1a1) +#define CKR_FUNCTION_REJECTED (0x200) +#define CKR_VENDOR_DEFINED ((unsigned long) (1 << 31)) + + + +/* Compatibility layer. */ + +#ifdef CRYPTOKI_COMPAT + +#undef CK_DEFINE_FUNCTION +#define CK_DEFINE_FUNCTION(retval, name) retval CK_SPEC name + +/* For NULL. */ +#include + +typedef unsigned char CK_BYTE; +typedef unsigned char CK_CHAR; +typedef unsigned char CK_UTF8CHAR; +typedef unsigned char CK_BBOOL; +typedef unsigned long int CK_ULONG; +typedef long int CK_LONG; +typedef CK_BYTE *CK_BYTE_PTR; +typedef CK_CHAR *CK_CHAR_PTR; +typedef CK_UTF8CHAR *CK_UTF8CHAR_PTR; +typedef CK_ULONG *CK_ULONG_PTR; +typedef void *CK_VOID_PTR; +typedef void **CK_VOID_PTR_PTR; +#define CK_FALSE 0 +#define CK_TRUE 1 +#ifndef CK_DISABLE_TRUE_FALSE +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif +#endif + +typedef struct ck_version CK_VERSION; +typedef struct ck_version *CK_VERSION_PTR; + +typedef struct ck_info CK_INFO; +typedef struct ck_info *CK_INFO_PTR; + +typedef ck_slot_id_t *CK_SLOT_ID_PTR; + +typedef struct ck_slot_info CK_SLOT_INFO; +typedef struct ck_slot_info *CK_SLOT_INFO_PTR; + +typedef struct ck_token_info CK_TOKEN_INFO; +typedef struct ck_token_info *CK_TOKEN_INFO_PTR; + +typedef ck_session_handle_t *CK_SESSION_HANDLE_PTR; + +typedef struct ck_session_info CK_SESSION_INFO; +typedef struct ck_session_info *CK_SESSION_INFO_PTR; + +typedef ck_object_handle_t *CK_OBJECT_HANDLE_PTR; + +typedef ck_object_class_t *CK_OBJECT_CLASS_PTR; + +typedef struct ck_attribute CK_ATTRIBUTE; +typedef struct ck_attribute *CK_ATTRIBUTE_PTR; + +typedef struct ck_date CK_DATE; +typedef struct ck_date *CK_DATE_PTR; + +typedef ck_mechanism_type_t *CK_MECHANISM_TYPE_PTR; + +typedef struct ck_mechanism CK_MECHANISM; +typedef struct ck_mechanism *CK_MECHANISM_PTR; + +typedef struct ck_mechanism_info CK_MECHANISM_INFO; +typedef struct ck_mechanism_info *CK_MECHANISM_INFO_PTR; + +typedef struct ck_function_list CK_FUNCTION_LIST; +typedef struct ck_function_list *CK_FUNCTION_LIST_PTR; +typedef struct ck_function_list **CK_FUNCTION_LIST_PTR_PTR; + +typedef struct ck_c_initialize_args CK_C_INITIALIZE_ARGS; +typedef struct ck_c_initialize_args *CK_C_INITIALIZE_ARGS_PTR; + +#define NULL_PTR NULL + +/* Delete the helper macros defined at the top of the file. */ +#undef ck_flags_t +#undef ck_version + +#undef ck_info +#undef cryptoki_version +#undef manufacturer_id +#undef library_description +#undef library_version + +#undef ck_notification_t +#undef ck_slot_id_t + +#undef ck_slot_info +#undef slot_description +#undef hardware_version +#undef firmware_version + +#undef ck_token_info +#undef serial_number +#undef max_session_count +#undef session_count +#undef max_rw_session_count +#undef rw_session_count +#undef max_pin_len +#undef min_pin_len +#undef total_public_memory +#undef free_public_memory +#undef total_private_memory +#undef free_private_memory +#undef utc_time + +#undef ck_session_handle_t +#undef ck_user_type_t +#undef ck_state_t + +#undef ck_session_info +#undef slot_id +#undef device_error + +#undef ck_object_handle_t +#undef ck_object_class_t +#undef ck_hw_feature_type_t +#undef ck_key_type_t +#undef ck_certificate_type_t +#undef ck_attribute_type_t + +#undef ck_attribute +#undef value +#undef value_len + +#undef ck_date + +#undef ck_mechanism_type_t + +#undef ck_mechanism +#undef parameter +#undef parameter_len + +#undef ck_mechanism_info +#undef min_key_size +#undef max_key_size + +#undef ck_rv_t +#undef ck_notify_t + +#undef ck_function_list + +#undef ck_createmutex_t +#undef ck_destroymutex_t +#undef ck_lockmutex_t +#undef ck_unlockmutex_t + +#undef ck_c_initialize_args +#undef create_mutex +#undef destroy_mutex +#undef lock_mutex +#undef unlock_mutex +#undef reserved + +#endif /* CRYPTOKI_COMPAT */ + + +/* System dependencies. */ +#if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32) +#pragma pack(pop, cryptoki) +#endif + +#if defined(__cplusplus) +} +#endif + +#endif /* PKCS11_H */ diff --git a/crypto/openssh/platform.c b/crypto/openssh/platform.c index aee4b01e7c..e3a428aaa0 100644 --- a/crypto/openssh/platform.c +++ b/crypto/openssh/platform.c @@ -1,4 +1,4 @@ -/* $Id: platform.c,v 1.1 2006/08/30 17:24:41 djm Exp $ */ +/* $Id: platform.c,v 1.3 2009/12/20 23:49:22 dtucker Exp $ */ /* * Copyright (c) 2006 Darren Tucker. All rights reserved. @@ -21,6 +21,15 @@ #include "openbsd-compat/openbsd-compat.h" +void +platform_pre_listen(void) +{ +#ifdef LINUX_OOM_ADJUST + /* Adjust out-of-memory killer so listening process is not killed */ + oom_adjust_setup(); +#endif +} + void platform_pre_fork(void) { @@ -43,4 +52,17 @@ platform_post_fork_child(void) #ifdef USE_SOLARIS_PROCESS_CONTRACTS solaris_contract_post_fork_child(); #endif +#ifdef LINUX_OOM_ADJUST + oom_adjust_restore(); +#endif +} + +char * +platform_krb5_get_principal_name(const char *pw_name) +{ +#ifdef USE_AIX_KRB_NAME + return aix_krb5_get_principal_name(pw_name); +#else + return NULL; +#endif } diff --git a/crypto/openssh/platform.h b/crypto/openssh/platform.h index cf93bc57c0..30a1d2259d 100644 --- a/crypto/openssh/platform.h +++ b/crypto/openssh/platform.h @@ -1,4 +1,4 @@ -/* $Id: platform.h,v 1.1 2006/08/30 17:24:41 djm Exp $ */ +/* $Id: platform.h,v 1.4 2010/01/14 01:44:16 djm Exp $ */ /* * Copyright (c) 2006 Darren Tucker. All rights reserved. @@ -18,6 +18,11 @@ #include +void platform_pre_listen(void); void platform_pre_fork(void); void platform_post_fork_parent(pid_t child_pid); void platform_post_fork_child(void); +char *platform_get_krb5_client(const char *); +char *platform_krb5_get_principal_name(const char *); + + diff --git a/crypto/openssh/readconf.c b/crypto/openssh/readconf.c index 0bf5d7cb48..0296590e2f 100644 --- a/crypto/openssh/readconf.c +++ b/crypto/openssh/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.177 2009/06/27 09:35:06 andreas Exp $ */ +/* $OpenBSD: readconf.c,v 1.187 2010/07/19 09:15:12 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -110,8 +110,8 @@ typedef enum { oBadOption, - oForwardAgent, oForwardX11, oForwardX11Trusted, oGatewayPorts, - oExitOnForwardFailure, + oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout, + oGatewayPorts, oExitOnForwardFailure, oPasswordAuthentication, oRSAAuthentication, oChallengeResponseAuthentication, oXAuthLocation, oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, @@ -123,12 +123,13 @@ typedef enum { oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication, oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, - oHostKeyAlgorithms, oBindAddress, oSmartcardDevice, + oHostKeyAlgorithms, oBindAddress, oPKCS11Provider, oClearAllForwardings, oNoHostAuthenticationForLocalhost, oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, oAddressFamily, oGssAuthentication, oGssDelegateCreds, oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, - oSendEnv, oControlPath, oControlMaster, oHashKnownHosts, + oSendEnv, oControlPath, oControlMaster, oControlPersist, + oHashKnownHosts, oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication, oDeprecated, oUnsupported @@ -143,6 +144,7 @@ static struct { { "forwardagent", oForwardAgent }, { "forwardx11", oForwardX11 }, { "forwardx11trusted", oForwardX11Trusted }, + { "forwardx11timeout", oForwardX11Timeout }, { "exitonforwardfailure", oExitOnForwardFailure }, { "xauthlocation", oXAuthLocation }, { "gatewayports", oGatewayPorts }, @@ -205,10 +207,12 @@ static struct { { "preferredauthentications", oPreferredAuthentications }, { "hostkeyalgorithms", oHostKeyAlgorithms }, { "bindaddress", oBindAddress }, -#ifdef SMARTCARD - { "smartcarddevice", oSmartcardDevice }, +#ifdef ENABLE_PKCS11 + { "smartcarddevice", oPKCS11Provider }, + { "pkcs11provider", oPKCS11Provider }, #else { "smartcarddevice", oUnsupported }, + { "pkcs11provider", oUnsupported }, #endif { "clearallforwardings", oClearAllForwardings }, { "enablesshkeysign", oEnableSSHKeysign }, @@ -222,6 +226,7 @@ static struct { { "sendenv", oSendEnv }, { "controlpath", oControlPath }, { "controlmaster", oControlMaster }, + { "controlpersist", oControlPersist }, { "hashknownhosts", oHashKnownHosts }, { "tunnel", oTunnel }, { "tunneldevice", oTunnelDevice }, @@ -253,8 +258,9 @@ add_local_forward(Options *options, const Forward *newfwd) if (newfwd->listen_port < IPPORT_RESERVED && original_real_uid != 0) fatal("Privileged ports can only be forwarded by root."); #endif - if (options->num_local_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION) - fatal("Too many local forwards (max %d).", SSH_MAX_FORWARDS_PER_DIRECTION); + options->local_forwards = xrealloc(options->local_forwards, + options->num_local_forwards + 1, + sizeof(*options->local_forwards)); fwd = &options->local_forwards[options->num_local_forwards++]; fwd->listen_host = newfwd->listen_host; @@ -272,15 +278,17 @@ void add_remote_forward(Options *options, const Forward *newfwd) { Forward *fwd; - if (options->num_remote_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION) - fatal("Too many remote forwards (max %d).", - SSH_MAX_FORWARDS_PER_DIRECTION); + + options->remote_forwards = xrealloc(options->remote_forwards, + options->num_remote_forwards + 1, + sizeof(*options->remote_forwards)); fwd = &options->remote_forwards[options->num_remote_forwards++]; fwd->listen_host = newfwd->listen_host; fwd->listen_port = newfwd->listen_port; fwd->connect_host = newfwd->connect_host; fwd->connect_port = newfwd->connect_port; + fwd->allocated_port = 0; } static void @@ -293,12 +301,20 @@ clear_forwardings(Options *options) xfree(options->local_forwards[i].listen_host); xfree(options->local_forwards[i].connect_host); } + if (options->num_local_forwards > 0) { + xfree(options->local_forwards); + options->local_forwards = NULL; + } options->num_local_forwards = 0; for (i = 0; i < options->num_remote_forwards; i++) { if (options->remote_forwards[i].listen_host != NULL) xfree(options->remote_forwards[i].listen_host); xfree(options->remote_forwards[i].connect_host); } + if (options->num_remote_forwards > 0) { + xfree(options->remote_forwards); + options->remote_forwards = NULL; + } options->num_remote_forwards = 0; options->tun_open = SSH_TUNMODE_NO; } @@ -401,6 +417,10 @@ parse_flag: case oForwardX11Trusted: intptr = &options->forward_x11_trusted; goto parse_flag; + + case oForwardX11Timeout: + intptr = &options->forward_x11_timeout; + goto parse_time; case oGatewayPorts: intptr = &options->gateway_ports; @@ -609,8 +629,8 @@ parse_string: charptr = &options->bind_address; goto parse_string; - case oSmartcardDevice: - charptr = &options->smartcard_device; + case oPKCS11Provider: + charptr = &options->pkcs11_provider; goto parse_string; case oProxyCommand: @@ -864,6 +884,30 @@ parse_int: *intptr = value; break; + case oControlPersist: + /* no/false/yes/true, or a time spec */ + intptr = &options->control_persist; + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing ControlPersist" + " argument.", filename, linenum); + value = 0; + value2 = 0; /* timeout */ + if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0) + value = 0; + else if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0) + value = 1; + else if ((value2 = convtime(arg)) >= 0) + value = 1; + else + fatal("%.200s line %d: Bad ControlPersist argument.", + filename, linenum); + if (*activep && *intptr == -1) { + *intptr = value; + options->control_persist_timeout = value2; + } + break; + case oHashKnownHosts: intptr = &options->hash_known_hosts; goto parse_flag; @@ -1005,6 +1049,7 @@ initialize_options(Options * options) options->forward_agent = -1; options->forward_x11 = -1; options->forward_x11_trusted = -1; + options->forward_x11_timeout = -1; options->exit_on_forward_failure = -1; options->xauth_location = NULL; options->gateway_ports = -1; @@ -1045,13 +1090,15 @@ initialize_options(Options * options) options->user_hostfile = NULL; options->system_hostfile2 = NULL; options->user_hostfile2 = NULL; + options->local_forwards = NULL; options->num_local_forwards = 0; + options->remote_forwards = NULL; options->num_remote_forwards = 0; options->clear_forwardings = -1; options->log_level = SYSLOG_LEVEL_NOT_SET; options->preferred_authentications = NULL; options->bind_address = NULL; - options->smartcard_device = NULL; + options->pkcs11_provider = NULL; options->enable_ssh_keysign = - 1; options->no_host_authentication_for_localhost = - 1; options->identities_only = - 1; @@ -1062,6 +1109,8 @@ initialize_options(Options * options) options->num_send_env = 0; options->control_path = NULL; options->control_master = -1; + options->control_persist = -1; + options->control_persist_timeout = 0; options->hash_known_hosts = -1; options->tun_open = -1; options->tun_local = -1; @@ -1089,6 +1138,8 @@ fill_default_options(Options * options) options->forward_x11 = 0; if (options->forward_x11_trusted == -1) options->forward_x11_trusted = 0; + if (options->forward_x11_timeout == -1) + options->forward_x11_timeout = 1200; if (options->exit_on_forward_failure == -1) options->exit_on_forward_failure = 0; if (options->xauth_location == NULL) @@ -1142,7 +1193,7 @@ fill_default_options(Options * options) /* options->macs, default set in myproposals.h */ /* options->hostkeyalgorithms, default set in myproposals.h */ if (options->protocol == SSH_PROTO_UNKNOWN) - options->protocol = SSH_PROTO_1|SSH_PROTO_2; + options->protocol = SSH_PROTO_2; if (options->num_identity_files == 0) { if (options->protocol & SSH_PROTO_1) { len = 2 + strlen(_PATH_SSH_CLIENT_IDENTITY) + 1; @@ -1195,6 +1246,10 @@ fill_default_options(Options * options) options->server_alive_count_max = 3; if (options->control_master == -1) options->control_master = 0; + if (options->control_persist == -1) { + options->control_persist = 0; + options->control_persist_timeout = 0; + } if (options->hash_known_hosts == -1) options->hash_known_hosts = 0; if (options->tun_open == -1) diff --git a/crypto/openssh/readconf.h b/crypto/openssh/readconf.h index 2ebfebe946..95d1046747 100644 --- a/crypto/openssh/readconf.h +++ b/crypto/openssh/readconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.h,v 1.79 2009/06/27 09:35:06 andreas Exp $ */ +/* $OpenBSD: readconf.h,v 1.86 2010/07/19 09:15:12 djm Exp $ */ /* * Author: Tatu Ylonen @@ -23,6 +23,7 @@ typedef struct { int listen_port; /* Port to forward. */ char *connect_host; /* Host to connect. */ int connect_port; /* Port to connect on connect_host. */ + int allocated_port; /* Dynamically allocated listen port */ } Forward; /* Data structure for representing option data. */ @@ -31,6 +32,7 @@ typedef struct { typedef struct { int forward_agent; /* Forward authentication agent. */ int forward_x11; /* Forward X11 display. */ + int forward_x11_timeout; /* Expiration for Cookies */ int forward_x11_trusted; /* Trust Forward X11 display. */ int exit_on_forward_failure; /* Exit if bind(2) fails for -L/-R */ char *xauth_location; /* Location for xauth program */ @@ -84,7 +86,7 @@ typedef struct { char *user_hostfile2; char *preferred_authentications; char *bind_address; /* local socket address for connection to sshd */ - char *smartcard_device; /* Smartcard reader device */ + char *pkcs11_provider; /* PKCS#11 provider */ int verify_host_key_dns; /* Verify host key using DNS */ int num_identity_files; /* Number of files for RSA/DSA identities. */ @@ -93,11 +95,11 @@ typedef struct { /* Local TCP/IP forward requests. */ int num_local_forwards; - Forward local_forwards[SSH_MAX_FORWARDS_PER_DIRECTION]; + Forward *local_forwards; /* Remote TCP/IP forward requests. */ int num_remote_forwards; - Forward remote_forwards[SSH_MAX_FORWARDS_PER_DIRECTION]; + Forward *remote_forwards; int clear_forwardings; int enable_ssh_keysign; @@ -112,6 +114,8 @@ typedef struct { char *control_path; int control_master; + int control_persist; /* ControlPersist flag */ + int control_persist_timeout; /* ControlPersist timeout (seconds) */ int hash_known_hosts; diff --git a/crypto/openssh/roaming.h b/crypto/openssh/roaming.h index e517161f62..6bb94cc391 100644 --- a/crypto/openssh/roaming.h +++ b/crypto/openssh/roaming.h @@ -1,4 +1,4 @@ -/* $OpenBSD: roaming.h,v 1.4 2009/06/27 09:32:43 andreas Exp $ */ +/* $OpenBSD: roaming.h,v 1.5 2009/10/24 11:11:58 andreas Exp $ */ /* * Copyright (c) 2004-2009 AppGate Network Security AB * @@ -19,12 +19,17 @@ #define ROAMING_H #define DEFAULT_ROAMBUF 65536 +#define ROAMING_REQUEST "roaming@appgate.com" +extern int roaming_enabled; extern int resume_in_progress; +void request_roaming(void); int get_snd_buf_size(void); int get_recv_buf_size(void); void add_recv_bytes(u_int64_t); +int wait_for_roaming_reconnect(void); +void roaming_reply(int, u_int32_t, void *); void set_out_buffer_size(size_t); ssize_t roaming_write(int, const void *, size_t, int *); ssize_t roaming_read(int, void *, size_t, int *); @@ -33,6 +38,7 @@ u_int64_t get_recv_bytes(void); u_int64_t get_sent_bytes(void); void roam_set_bytes(u_int64_t, u_int64_t); void resend_bytes(int, u_int64_t *); +void calculate_new_key(u_int64_t *, u_int64_t, u_int64_t); int resume_kex(void); #endif /* ROAMING */ diff --git a/crypto/openssh/roaming_client.c b/crypto/openssh/roaming_client.c new file mode 100644 index 0000000000..cea8e7360a --- /dev/null +++ b/crypto/openssh/roaming_client.c @@ -0,0 +1,280 @@ +/* $OpenBSD: roaming_client.c,v 1.3 2010/01/18 01:50:27 dtucker Exp $ */ +/* + * Copyright (c) 2004-2009 AppGate Network Security AB + * + * 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 "openbsd-compat/sys-queue.h" +#include +#include + +#ifdef HAVE_INTTYPES_H +#include +#endif +#include +#include +#include + +#include +#include + +#include "xmalloc.h" +#include "buffer.h" +#include "channels.h" +#include "cipher.h" +#include "dispatch.h" +#include "clientloop.h" +#include "log.h" +#include "match.h" +#include "misc.h" +#include "packet.h" +#include "ssh.h" +#include "key.h" +#include "kex.h" +#include "readconf.h" +#include "roaming.h" +#include "ssh2.h" +#include "sshconnect.h" + +/* import */ +extern Options options; +extern char *host; +extern struct sockaddr_storage hostaddr; +extern int session_resumed; + +static u_int32_t roaming_id; +static u_int64_t cookie; +static u_int64_t lastseenchall; +static u_int64_t key1, key2, oldkey1, oldkey2; + +void +roaming_reply(int type, u_int32_t seq, void *ctxt) +{ + if (type == SSH2_MSG_REQUEST_FAILURE) { + logit("Server denied roaming"); + return; + } + verbose("Roaming enabled"); + roaming_id = packet_get_int(); + cookie = packet_get_int64(); + key1 = oldkey1 = packet_get_int64(); + key2 = oldkey2 = packet_get_int64(); + set_out_buffer_size(packet_get_int() + get_snd_buf_size()); + roaming_enabled = 1; +} + +void +request_roaming(void) +{ + packet_start(SSH2_MSG_GLOBAL_REQUEST); + packet_put_cstring(ROAMING_REQUEST); + packet_put_char(1); + packet_put_int(get_recv_buf_size()); + packet_send(); + client_register_global_confirm(roaming_reply, NULL); +} + +static void +roaming_auth_required(void) +{ + u_char digest[SHA_DIGEST_LENGTH]; + EVP_MD_CTX md; + Buffer b; + const EVP_MD *evp_md = EVP_sha1(); + u_int64_t chall, oldchall; + + chall = packet_get_int64(); + oldchall = packet_get_int64(); + if (oldchall != lastseenchall) { + key1 = oldkey1; + key2 = oldkey2; + } + lastseenchall = chall; + + buffer_init(&b); + buffer_put_int64(&b, cookie); + buffer_put_int64(&b, chall); + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); + EVP_DigestFinal(&md, digest, NULL); + buffer_free(&b); + + packet_start(SSH2_MSG_KEX_ROAMING_AUTH); + packet_put_int64(key1 ^ get_recv_bytes()); + packet_put_raw(digest, sizeof(digest)); + packet_send(); + + oldkey1 = key1; + oldkey2 = key2; + calculate_new_key(&key1, cookie, chall); + calculate_new_key(&key2, cookie, chall); + + debug("Received %llu bytes", (unsigned long long)get_recv_bytes()); + debug("Sent roaming_auth packet"); +} + +int +resume_kex(void) +{ + /* + * This should not happen - if the client sends the kex method + * resume@appgate.com then the kex is done in roaming_resume(). + */ + return 1; +} + +static int +roaming_resume(void) +{ + u_int64_t recv_bytes; + char *str = NULL, *kexlist = NULL, *c; + int i, type; + int timeout_ms = options.connection_timeout * 1000; + u_int len; + u_int32_t rnd = 0; + + resume_in_progress = 1; + + /* Exchange banners */ + ssh_exchange_identification(timeout_ms); + packet_set_nonblocking(); + + /* Send a kexinit message with resume@appgate.com as only kex algo */ + packet_start(SSH2_MSG_KEXINIT); + for (i = 0; i < KEX_COOKIE_LEN; i++) { + if (i % 4 == 0) + rnd = arc4random(); + packet_put_char(rnd & 0xff); + rnd >>= 8; + } + packet_put_cstring(KEX_RESUME); + for (i = 1; i < PROPOSAL_MAX; i++) { + /* kex algorithm added so start with i=1 and not 0 */ + packet_put_cstring(""); /* Not used when we resume */ + } + packet_put_char(1); /* first kex_packet follows */ + packet_put_int(0); /* reserved */ + packet_send(); + + /* Assume that resume@appgate.com will be accepted */ + packet_start(SSH2_MSG_KEX_ROAMING_RESUME); + packet_put_int(roaming_id); + packet_send(); + + /* Read the server's kexinit and check for resume@appgate.com */ + if ((type = packet_read()) != SSH2_MSG_KEXINIT) { + debug("expected kexinit on resume, got %d", type); + goto fail; + } + for (i = 0; i < KEX_COOKIE_LEN; i++) + (void)packet_get_char(); + kexlist = packet_get_string(&len); + if (!kexlist + || (str = match_list(KEX_RESUME, kexlist, NULL)) == NULL) { + debug("server doesn't allow resume"); + goto fail; + } + xfree(str); + for (i = 1; i < PROPOSAL_MAX; i++) { + /* kex algorithm taken care of so start with i=1 and not 0 */ + xfree(packet_get_string(&len)); + } + i = packet_get_char(); /* first_kex_packet_follows */ + if (i && (c = strchr(kexlist, ','))) + *c = 0; + if (i && strcmp(kexlist, KEX_RESUME)) { + debug("server's kex guess (%s) was wrong, skipping", kexlist); + (void)packet_read(); /* Wrong guess - discard packet */ + } + + /* + * Read the ROAMING_AUTH_REQUIRED challenge from the server and + * send ROAMING_AUTH + */ + if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_REQUIRED) { + debug("expected roaming_auth_required, got %d", type); + goto fail; + } + roaming_auth_required(); + + /* Read ROAMING_AUTH_OK from the server */ + if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_OK) { + debug("expected roaming_auth_ok, got %d", type); + goto fail; + } + recv_bytes = packet_get_int64() ^ oldkey2; + debug("Peer received %llu bytes", (unsigned long long)recv_bytes); + resend_bytes(packet_get_connection_out(), &recv_bytes); + + resume_in_progress = 0; + + session_resumed = 1; /* Tell clientloop */ + + return 0; + +fail: + if (kexlist) + xfree(kexlist); + if (packet_get_connection_in() == packet_get_connection_out()) + close(packet_get_connection_in()); + else { + close(packet_get_connection_in()); + close(packet_get_connection_out()); + } + return 1; +} + +int +wait_for_roaming_reconnect(void) +{ + static int reenter_guard = 0; + int timeout_ms = options.connection_timeout * 1000; + int c; + + if (reenter_guard != 0) + fatal("Server refused resume, roaming timeout may be exceeded"); + reenter_guard = 1; + + fprintf(stderr, "[connection suspended, press return to resume]"); + fflush(stderr); + packet_backup_state(); + /* TODO Perhaps we should read from tty here */ + while ((c = fgetc(stdin)) != EOF) { + if (c == 'Z' - 64) { + kill(getpid(), SIGTSTP); + continue; + } + if (c != '\n' && c != '\r') + continue; + + if (ssh_connect(host, &hostaddr, options.port, + options.address_family, 1, &timeout_ms, + options.tcp_keep_alive, options.use_privileged_port, + options.proxy_command) == 0 && roaming_resume() == 0) { + packet_restore_state(); + reenter_guard = 0; + fprintf(stderr, "[connection resumed]\n"); + fflush(stderr); + return 0; + } + + fprintf(stderr, "[reconnect failed, press return to retry]"); + fflush(stderr); + } + fprintf(stderr, "[exiting]\n"); + fflush(stderr); + exit(0); +} diff --git a/crypto/openssh/roaming_common.c b/crypto/openssh/roaming_common.c index 73db09d798..9adbe56fcb 100644 --- a/crypto/openssh/roaming_common.c +++ b/crypto/openssh/roaming_common.c @@ -1,4 +1,4 @@ -/* $OpenBSD: roaming_common.c,v 1.5 2009/06/27 09:32:43 andreas Exp $ */ +/* $OpenBSD: roaming_common.c,v 1.8 2010/01/12 00:59:29 djm Exp $ */ /* * Copyright (c) 2004-2009 AppGate Network Security AB * @@ -52,9 +52,9 @@ int get_snd_buf_size() { int fd = packet_get_connection_out(); - int optval, optvallen; + int optval; + socklen_t optvallen = sizeof(optval); - optvallen = sizeof(optval); if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &optval, &optvallen) != 0) optval = DEFAULT_ROAMBUF; return optval; @@ -64,9 +64,9 @@ int get_recv_buf_size() { int fd = packet_get_connection_in(); - int optval, optvallen; + int optval; + socklen_t optvallen = sizeof(optval); - optvallen = sizeof(optval); if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &optval, &optvallen) != 0) optval = DEFAULT_ROAMBUF; return optval; @@ -145,8 +145,16 @@ roaming_write(int fd, const void *buf, size_t count, int *cont) if (out_buf_size > 0) buf_append(buf, ret); } - debug3("Wrote %ld bytes for a total of %llu", (long)ret, - (unsigned long long)write_bytes); + if (out_buf_size > 0 && + (ret == 0 || (ret == -1 && errno == EPIPE))) { + if (wait_for_roaming_reconnect() != 0) { + ret = 0; + *cont = 1; + } else { + ret = -1; + errno = EAGAIN; + } + } return ret; } @@ -158,6 +166,15 @@ roaming_read(int fd, void *buf, size_t count, int *cont) if (!resume_in_progress) { read_bytes += ret; } + } else if (out_buf_size > 0 && + (ret == 0 || (ret == -1 && (errno == ECONNRESET + || errno == ECONNABORTED || errno == ETIMEDOUT + || errno == EHOSTUNREACH)))) { + debug("roaming_read failed for %d ret=%ld errno=%d", + fd, (long)ret, errno); + ret = 0; + if (wait_for_roaming_reconnect() == 0) + *cont = 1; } return ret; } @@ -199,3 +216,29 @@ resend_bytes(int fd, u_int64_t *offset) atomicio(vwrite, fd, out_buf + (out_last - needed), needed); } } + +/* + * Caclulate a new key after a reconnect + */ +void +calculate_new_key(u_int64_t *key, u_int64_t cookie, u_int64_t challenge) +{ + const EVP_MD *md = EVP_sha1(); + EVP_MD_CTX ctx; + char hash[EVP_MAX_MD_SIZE]; + Buffer b; + + buffer_init(&b); + buffer_put_int64(&b, *key); + buffer_put_int64(&b, cookie); + buffer_put_int64(&b, challenge); + + EVP_DigestInit(&ctx, md); + EVP_DigestUpdate(&ctx, buffer_ptr(&b), buffer_len(&b)); + EVP_DigestFinal(&ctx, hash, NULL); + + buffer_clear(&b); + buffer_append(&b, hash, EVP_MD_size(md)); + *key = buffer_get_int64(&b); + buffer_free(&b); +} diff --git a/crypto/openssh/platform.h b/crypto/openssh/roaming_serv.c similarity index 71% copy from crypto/openssh/platform.h copy to crypto/openssh/roaming_serv.c index cf93bc57c0..511ca84610 100644 --- a/crypto/openssh/platform.h +++ b/crypto/openssh/roaming_serv.c @@ -1,7 +1,6 @@ -/* $Id: platform.h,v 1.1 2006/08/30 17:24:41 djm Exp $ */ - +/* $OpenBSD: roaming_serv.c,v 1.1 2009/10/24 11:18:23 andreas Exp $ */ /* - * Copyright (c) 2006 Darren Tucker. All rights reserved. + * Copyright (c) 2004-2009 AppGate Network Security AB * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -16,8 +15,17 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include -void platform_pre_fork(void); -void platform_post_fork_parent(pid_t child_pid); -void platform_post_fork_child(void); +#include "roaming.h" + +/* + * Wait for the roaming client to reconnect. Returns 0 if a connect ocurred. + */ +int +wait_for_roaming_reconnect(void) +{ + return 1; +} diff --git a/crypto/openssh/scard-opensc.c b/crypto/openssh/scard-opensc.c deleted file mode 100644 index 36dae05fd5..0000000000 --- a/crypto/openssh/scard-opensc.c +++ /dev/null @@ -1,532 +0,0 @@ -/* - * Copyright (c) 2002 Juha Yrjölä. All rights reserved. - * Copyright (c) 2001 Markus Friedl. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" -#if defined(SMARTCARD) && defined(USE_OPENSC) - -#include - -#include -#include - -#include -#include - -#include -#include - -#include "key.h" -#include "log.h" -#include "xmalloc.h" -#include "misc.h" -#include "scard.h" - -#if OPENSSL_VERSION_NUMBER < 0x00907000L && defined(CRYPTO_LOCK_ENGINE) -#define USE_ENGINE -#define RSA_get_default_method RSA_get_default_openssl_method -#else -#endif - -#ifdef USE_ENGINE -#include -#define sc_get_rsa sc_get_engine -#else -#define sc_get_rsa sc_get_rsa_method -#endif - -static int sc_reader_id; -static sc_context_t *ctx = NULL; -static sc_card_t *card = NULL; -static sc_pkcs15_card_t *p15card = NULL; - -static char *sc_pin = NULL; - -struct sc_priv_data -{ - struct sc_pkcs15_id cert_id; - int ref_count; -}; - -void -sc_close(void) -{ - if (p15card) { - sc_pkcs15_unbind(p15card); - p15card = NULL; - } - if (card) { - sc_disconnect_card(card, 0); - card = NULL; - } - if (ctx) { - sc_release_context(ctx); - ctx = NULL; - } -} - -static int -sc_init(void) -{ - int r; - - r = sc_establish_context(&ctx, "openssh"); - if (r) - goto err; - if (sc_reader_id >= ctx->reader_count) { - r = SC_ERROR_NO_READERS_FOUND; - error("Illegal reader number %d (max %d)", sc_reader_id, - ctx->reader_count -1); - goto err; - } - r = sc_connect_card(ctx->reader[sc_reader_id], 0, &card); - if (r) - goto err; - r = sc_pkcs15_bind(card, &p15card); - if (r) - goto err; - return 0; -err: - sc_close(); - return r; -} - -/* private key operations */ - -static int -sc_prkey_op_init(RSA *rsa, struct sc_pkcs15_object **key_obj_out, - unsigned int usage) -{ - int r; - struct sc_priv_data *priv; - struct sc_pkcs15_object *key_obj; - struct sc_pkcs15_prkey_info *key; - struct sc_pkcs15_object *pin_obj; - struct sc_pkcs15_pin_info *pin; - - priv = (struct sc_priv_data *) RSA_get_app_data(rsa); - if (priv == NULL) - return -1; - if (p15card == NULL) { - sc_close(); - r = sc_init(); - if (r) { - error("SmartCard init failed: %s", sc_strerror(r)); - goto err; - } - } - r = sc_pkcs15_find_prkey_by_id_usage(p15card, &priv->cert_id, - usage, &key_obj); - if (r) { - error("Unable to find private key from SmartCard: %s", - sc_strerror(r)); - goto err; - } - key = key_obj->data; - r = sc_pkcs15_find_pin_by_auth_id(p15card, &key_obj->auth_id, - &pin_obj); - if (r == SC_ERROR_OBJECT_NOT_FOUND) { - /* no pin required */ - r = sc_lock(card); - if (r) { - error("Unable to lock smartcard: %s", sc_strerror(r)); - goto err; - } - *key_obj_out = key_obj; - return 0; - } else if (r) { - error("Unable to find PIN object from SmartCard: %s", - sc_strerror(r)); - goto err; - } - pin = pin_obj->data; - r = sc_lock(card); - if (r) { - error("Unable to lock smartcard: %s", sc_strerror(r)); - goto err; - } - if (sc_pin != NULL) { - r = sc_pkcs15_verify_pin(p15card, pin, sc_pin, - strlen(sc_pin)); - if (r) { - sc_unlock(card); - error("PIN code verification failed: %s", - sc_strerror(r)); - goto err; - } - } - *key_obj_out = key_obj; - return 0; -err: - sc_close(); - return -1; -} - -#define SC_USAGE_DECRYPT SC_PKCS15_PRKEY_USAGE_DECRYPT | \ - SC_PKCS15_PRKEY_USAGE_UNWRAP - -static int -sc_private_decrypt(int flen, u_char *from, u_char *to, RSA *rsa, - int padding) -{ - struct sc_pkcs15_object *key_obj; - int r; - - if (padding != RSA_PKCS1_PADDING) - return -1; - r = sc_prkey_op_init(rsa, &key_obj, SC_USAGE_DECRYPT); - if (r) - return -1; - r = sc_pkcs15_decipher(p15card, key_obj, SC_ALGORITHM_RSA_PAD_PKCS1, - from, flen, to, flen); - sc_unlock(card); - if (r < 0) { - error("sc_pkcs15_decipher() failed: %s", sc_strerror(r)); - goto err; - } - return r; -err: - sc_close(); - return -1; -} - -#define SC_USAGE_SIGN SC_PKCS15_PRKEY_USAGE_SIGN | \ - SC_PKCS15_PRKEY_USAGE_SIGNRECOVER - -static int -sc_sign(int type, u_char *m, unsigned int m_len, - unsigned char *sigret, unsigned int *siglen, RSA *rsa) -{ - struct sc_pkcs15_object *key_obj; - int r; - unsigned long flags = 0; - - /* XXX: sc_prkey_op_init will search for a pkcs15 private - * key object with the sign or signrecover usage flag set. - * If the signing key has only the non-repudiation flag set - * the key will be rejected as using a non-repudiation key - * for authentication is not recommended. Note: This does not - * prevent the use of a non-repudiation key for authentication - * if the sign or signrecover flag is set as well. - */ - r = sc_prkey_op_init(rsa, &key_obj, SC_USAGE_SIGN); - if (r) - return -1; - /* FIXME: length of sigret correct? */ - /* FIXME: check 'type' and modify flags accordingly */ - flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_SHA1; - r = sc_pkcs15_compute_signature(p15card, key_obj, flags, - m, m_len, sigret, RSA_size(rsa)); - sc_unlock(card); - if (r < 0) { - error("sc_pkcs15_compute_signature() failed: %s", - sc_strerror(r)); - goto err; - } - *siglen = r; - return 1; -err: - sc_close(); - return 0; -} - -static int -sc_private_encrypt(int flen, u_char *from, u_char *to, RSA *rsa, - int padding) -{ - error("Private key encryption not supported"); - return -1; -} - -/* called on free */ - -static int (*orig_finish)(RSA *rsa) = NULL; - -static int -sc_finish(RSA *rsa) -{ - struct sc_priv_data *priv; - - priv = RSA_get_app_data(rsa); - priv->ref_count--; - if (priv->ref_count == 0) { - free(priv); - sc_close(); - } - if (orig_finish) - orig_finish(rsa); - return 1; -} - -/* engine for overloading private key operations */ - -static RSA_METHOD * -sc_get_rsa_method(void) -{ - static RSA_METHOD smart_rsa; - const RSA_METHOD *def = RSA_get_default_method(); - - /* use the OpenSSL version */ - memcpy(&smart_rsa, def, sizeof(smart_rsa)); - - smart_rsa.name = "opensc"; - - /* overload */ - smart_rsa.rsa_priv_enc = sc_private_encrypt; - smart_rsa.rsa_priv_dec = sc_private_decrypt; - smart_rsa.rsa_sign = sc_sign; - - /* save original */ - orig_finish = def->finish; - smart_rsa.finish = sc_finish; - - return &smart_rsa; -} - -#ifdef USE_ENGINE -static ENGINE * -sc_get_engine(void) -{ - static ENGINE *smart_engine = NULL; - - if ((smart_engine = ENGINE_new()) == NULL) - fatal("ENGINE_new failed"); - - ENGINE_set_id(smart_engine, "opensc"); - ENGINE_set_name(smart_engine, "OpenSC"); - - ENGINE_set_RSA(smart_engine, sc_get_rsa_method()); - ENGINE_set_DSA(smart_engine, DSA_get_default_openssl_method()); - ENGINE_set_DH(smart_engine, DH_get_default_openssl_method()); - ENGINE_set_RAND(smart_engine, RAND_SSLeay()); - ENGINE_set_BN_mod_exp(smart_engine, BN_mod_exp); - - return smart_engine; -} -#endif - -static void -convert_rsa_to_rsa1(Key * in, Key * out) -{ - struct sc_priv_data *priv; - - out->rsa->flags = in->rsa->flags; - out->flags = in->flags; - RSA_set_method(out->rsa, RSA_get_method(in->rsa)); - BN_copy(out->rsa->n, in->rsa->n); - BN_copy(out->rsa->e, in->rsa->e); - priv = RSA_get_app_data(in->rsa); - priv->ref_count++; - RSA_set_app_data(out->rsa, priv); - return; -} - -static int -sc_read_pubkey(Key * k, const struct sc_pkcs15_object *cert_obj) -{ - int r; - sc_pkcs15_cert_t *cert = NULL; - struct sc_priv_data *priv = NULL; - sc_pkcs15_cert_info_t *cinfo = cert_obj->data; - - X509 *x509 = NULL; - EVP_PKEY *pubkey = NULL; - u8 *p; - char *tmp; - - debug("sc_read_pubkey() with cert id %02X", cinfo->id.value[0]); - r = sc_pkcs15_read_certificate(p15card, cinfo, &cert); - if (r) { - logit("Certificate read failed: %s", sc_strerror(r)); - goto err; - } - x509 = X509_new(); - if (x509 == NULL) { - r = -1; - goto err; - } - p = cert->data; - if (!d2i_X509(&x509, &p, cert->data_len)) { - logit("Unable to parse X.509 certificate"); - r = -1; - goto err; - } - sc_pkcs15_free_certificate(cert); - cert = NULL; - pubkey = X509_get_pubkey(x509); - X509_free(x509); - x509 = NULL; - if (pubkey->type != EVP_PKEY_RSA) { - logit("Public key is of unknown type"); - r = -1; - goto err; - } - k->rsa = EVP_PKEY_get1_RSA(pubkey); - EVP_PKEY_free(pubkey); - - k->rsa->flags |= RSA_FLAG_SIGN_VER; - RSA_set_method(k->rsa, sc_get_rsa_method()); - priv = xmalloc(sizeof(struct sc_priv_data)); - priv->cert_id = cinfo->id; - priv->ref_count = 1; - RSA_set_app_data(k->rsa, priv); - - k->flags = KEY_FLAG_EXT; - tmp = key_fingerprint(k, SSH_FP_MD5, SSH_FP_HEX); - debug("fingerprint %d %s", key_size(k), tmp); - xfree(tmp); - - return 0; -err: - if (cert) - sc_pkcs15_free_certificate(cert); - if (pubkey) - EVP_PKEY_free(pubkey); - if (x509) - X509_free(x509); - return r; -} - -Key ** -sc_get_keys(const char *id, const char *pin) -{ - Key *k, **keys; - int i, r, real_count = 0, key_count; - sc_pkcs15_id_t cert_id; - sc_pkcs15_object_t *certs[32]; - char *buf = xstrdup(id), *p; - - debug("sc_get_keys called: id = %s", id); - - if (sc_pin != NULL) - xfree(sc_pin); - sc_pin = (pin == NULL) ? NULL : xstrdup(pin); - - cert_id.len = 0; - if ((p = strchr(buf, ':')) != NULL) { - *p = 0; - p++; - sc_pkcs15_hex_string_to_id(p, &cert_id); - } - r = sscanf(buf, "%d", &sc_reader_id); - xfree(buf); - if (r != 1) - goto err; - if (p15card == NULL) { - sc_close(); - r = sc_init(); - if (r) { - error("Smartcard init failed: %s", sc_strerror(r)); - goto err; - } - } - if (cert_id.len) { - r = sc_pkcs15_find_cert_by_id(p15card, &cert_id, &certs[0]); - if (r < 0) - goto err; - key_count = 1; - } else { - r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_CERT_X509, - certs, 32); - if (r == 0) { - logit("No certificates found on smartcard"); - r = -1; - goto err; - } else if (r < 0) { - error("Certificate enumeration failed: %s", - sc_strerror(r)); - goto err; - } - key_count = r; - } - if (key_count > 1024) - fatal("Too many keys (%u), expected <= 1024", key_count); - keys = xcalloc(key_count * 2 + 1, sizeof(Key *)); - for (i = 0; i < key_count; i++) { - sc_pkcs15_object_t *tmp_obj = NULL; - cert_id = ((sc_pkcs15_cert_info_t *)(certs[i]->data))->id; - if (sc_pkcs15_find_prkey_by_id(p15card, &cert_id, &tmp_obj)) - /* skip the public key (certificate) if no - * corresponding private key is present */ - continue; - k = key_new(KEY_RSA); - if (k == NULL) - break; - r = sc_read_pubkey(k, certs[i]); - if (r) { - error("sc_read_pubkey failed: %s", sc_strerror(r)); - key_free(k); - continue; - } - keys[real_count] = k; - real_count++; - k = key_new(KEY_RSA1); - if (k == NULL) - break; - convert_rsa_to_rsa1(keys[real_count-1], k); - keys[real_count] = k; - real_count++; - } - keys[real_count] = NULL; - - return keys; -err: - sc_close(); - return NULL; -} - -int -sc_put_key(Key *prv, const char *id) -{ - error("key uploading not yet supported"); - return -1; -} - -char * -sc_get_key_label(Key *key) -{ - int r; - const struct sc_priv_data *priv; - struct sc_pkcs15_object *key_obj; - - priv = (const struct sc_priv_data *) RSA_get_app_data(key->rsa); - if (priv == NULL || p15card == NULL) { - logit("SmartCard key not loaded"); - /* internal error => return default label */ - return xstrdup("smartcard key"); - } - r = sc_pkcs15_find_prkey_by_id(p15card, &priv->cert_id, &key_obj); - if (r) { - logit("Unable to find private key from SmartCard: %s", - sc_strerror(r)); - return xstrdup("smartcard key"); - } - if (key_obj == NULL || key_obj->label == NULL) - /* the optional PKCS#15 label does not exists - * => return the default label */ - return xstrdup("smartcard key"); - return xstrdup(key_obj->label); -} - -#endif /* SMARTCARD */ diff --git a/crypto/openssh/scard.c b/crypto/openssh/scard.c deleted file mode 100644 index 9fd3ca1b4e..0000000000 --- a/crypto/openssh/scard.c +++ /dev/null @@ -1,571 +0,0 @@ -/* $OpenBSD: scard.c,v 1.36 2006/11/06 21:25:28 markus Exp $ */ -/* - * Copyright (c) 2001 Markus Friedl. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" -#if defined(SMARTCARD) && defined(USE_SECTOK) - -#include - -#include -#include -#include - -#include - -#include "xmalloc.h" -#include "key.h" -#include "log.h" -#include "misc.h" -#include "scard.h" - -#if OPENSSL_VERSION_NUMBER < 0x00907000L -#define USE_ENGINE -#define RSA_get_default_method RSA_get_default_openssl_method -#else -#endif - -#ifdef USE_ENGINE -#include -#define sc_get_rsa sc_get_engine -#else -#define sc_get_rsa sc_get_rsa_method -#endif - -#define CLA_SSH 0x05 -#define INS_DECRYPT 0x10 -#define INS_GET_KEYLENGTH 0x20 -#define INS_GET_PUBKEY 0x30 -#define INS_GET_RESPONSE 0xc0 - -#define MAX_BUF_SIZE 256 - -u_char DEFAUT0[] = {0xad, 0x9f, 0x61, 0xfe, 0xfa, 0x20, 0xce, 0x63}; - -static int sc_fd = -1; -static char *sc_reader_id = NULL; -static char *sc_pin = NULL; -static int cla = 0x00; /* class */ - -static void sc_mk_digest(const char *pin, u_char *digest); -static int get_AUT0(u_char *aut0); -static int try_AUT0(void); - -/* interface to libsectok */ - -static int -sc_open(void) -{ - int sw; - - if (sc_fd >= 0) - return sc_fd; - - sc_fd = sectok_friendly_open(sc_reader_id, STONOWAIT, &sw); - if (sc_fd < 0) { - error("sectok_open failed: %s", sectok_get_sw(sw)); - return SCARD_ERROR_FAIL; - } - if (! sectok_cardpresent(sc_fd)) { - debug("smartcard in reader %s not present, skipping", - sc_reader_id); - sc_close(); - return SCARD_ERROR_NOCARD; - } - if (sectok_reset(sc_fd, 0, NULL, &sw) <= 0) { - error("sectok_reset failed: %s", sectok_get_sw(sw)); - sc_fd = -1; - return SCARD_ERROR_FAIL; - } - if ((cla = cyberflex_inq_class(sc_fd)) < 0) - cla = 0; - - debug("sc_open ok %d", sc_fd); - return sc_fd; -} - -static int -sc_enable_applet(void) -{ - static u_char aid[] = {0xfc, 0x53, 0x73, 0x68, 0x2e, 0x62, 0x69, 0x6e}; - int sw = 0; - - /* select applet id */ - sectok_apdu(sc_fd, cla, 0xa4, 0x04, 0, sizeof aid, aid, 0, NULL, &sw); - if (!sectok_swOK(sw)) { - error("sectok_apdu failed: %s", sectok_get_sw(sw)); - sc_close(); - return -1; - } - return 0; -} - -static int -sc_init(void) -{ - int status; - - status = sc_open(); - if (status == SCARD_ERROR_NOCARD) { - return SCARD_ERROR_NOCARD; - } - if (status < 0) { - error("sc_open failed"); - return status; - } - if (sc_enable_applet() < 0) { - error("sc_enable_applet failed"); - return SCARD_ERROR_APPLET; - } - return 0; -} - -static int -sc_read_pubkey(Key * k) -{ - u_char buf[2], *n; - char *p; - int len, sw, status = -1; - - len = sw = 0; - n = NULL; - - if (sc_fd < 0) { - if (sc_init() < 0) - goto err; - } - - /* get key size */ - sectok_apdu(sc_fd, CLA_SSH, INS_GET_KEYLENGTH, 0, 0, 0, NULL, - sizeof(buf), buf, &sw); - if (!sectok_swOK(sw)) { - error("could not obtain key length: %s", sectok_get_sw(sw)); - goto err; - } - len = (buf[0] << 8) | buf[1]; - len /= 8; - debug("INS_GET_KEYLENGTH: len %d sw %s", len, sectok_get_sw(sw)); - - n = xmalloc(len); - /* get n */ - sectok_apdu(sc_fd, CLA_SSH, INS_GET_PUBKEY, 0, 0, 0, NULL, len, n, &sw); - - if (sw == 0x6982) { - if (try_AUT0() < 0) - goto err; - sectok_apdu(sc_fd, CLA_SSH, INS_GET_PUBKEY, 0, 0, 0, NULL, len, n, &sw); - } - if (!sectok_swOK(sw)) { - error("could not obtain public key: %s", sectok_get_sw(sw)); - goto err; - } - - debug("INS_GET_KEYLENGTH: sw %s", sectok_get_sw(sw)); - - if (BN_bin2bn(n, len, k->rsa->n) == NULL) { - error("c_read_pubkey: BN_bin2bn failed"); - goto err; - } - - /* currently the java applet just stores 'n' */ - if (!BN_set_word(k->rsa->e, 35)) { - error("c_read_pubkey: BN_set_word(e, 35) failed"); - goto err; - } - - status = 0; - p = key_fingerprint(k, SSH_FP_MD5, SSH_FP_HEX); - debug("fingerprint %u %s", key_size(k), p); - xfree(p); - -err: - if (n != NULL) - xfree(n); - sc_close(); - return status; -} - -/* private key operations */ - -static int -sc_private_decrypt(int flen, u_char *from, u_char *to, RSA *rsa, - int padding) -{ - u_char *padded = NULL; - int sw, len, olen, status = -1; - - debug("sc_private_decrypt called"); - - olen = len = sw = 0; - if (sc_fd < 0) { - status = sc_init(); - if (status < 0) - goto err; - } - if (padding != RSA_PKCS1_PADDING) - goto err; - - len = BN_num_bytes(rsa->n); - padded = xmalloc(len); - - sectok_apdu(sc_fd, CLA_SSH, INS_DECRYPT, 0, 0, len, from, len, padded, &sw); - - if (sw == 0x6982) { - if (try_AUT0() < 0) - goto err; - sectok_apdu(sc_fd, CLA_SSH, INS_DECRYPT, 0, 0, len, from, len, padded, &sw); - } - if (!sectok_swOK(sw)) { - error("sc_private_decrypt: INS_DECRYPT failed: %s", - sectok_get_sw(sw)); - goto err; - } - olen = RSA_padding_check_PKCS1_type_2(to, len, padded + 1, len - 1, - len); -err: - if (padded) - xfree(padded); - sc_close(); - return (olen >= 0 ? olen : status); -} - -static int -sc_private_encrypt(int flen, u_char *from, u_char *to, RSA *rsa, - int padding) -{ - u_char *padded = NULL; - int sw, len, status = -1; - - len = sw = 0; - if (sc_fd < 0) { - status = sc_init(); - if (status < 0) - goto err; - } - if (padding != RSA_PKCS1_PADDING) - goto err; - - debug("sc_private_encrypt called"); - len = BN_num_bytes(rsa->n); - padded = xmalloc(len); - - if (RSA_padding_add_PKCS1_type_1(padded, len, (u_char *)from, flen) <= 0) { - error("RSA_padding_add_PKCS1_type_1 failed"); - goto err; - } - sectok_apdu(sc_fd, CLA_SSH, INS_DECRYPT, 0, 0, len, padded, len, to, &sw); - if (sw == 0x6982) { - if (try_AUT0() < 0) - goto err; - sectok_apdu(sc_fd, CLA_SSH, INS_DECRYPT, 0, 0, len, padded, len, to, &sw); - } - if (!sectok_swOK(sw)) { - error("sc_private_encrypt: INS_DECRYPT failed: %s", - sectok_get_sw(sw)); - goto err; - } -err: - if (padded) - xfree(padded); - sc_close(); - return (len >= 0 ? len : status); -} - -/* called on free */ - -static int (*orig_finish)(RSA *rsa) = NULL; - -static int -sc_finish(RSA *rsa) -{ - if (orig_finish) - orig_finish(rsa); - sc_close(); - return 1; -} - -/* engine for overloading private key operations */ - -static RSA_METHOD * -sc_get_rsa_method(void) -{ - static RSA_METHOD smart_rsa; - const RSA_METHOD *def = RSA_get_default_method(); - - /* use the OpenSSL version */ - memcpy(&smart_rsa, def, sizeof(smart_rsa)); - - smart_rsa.name = "sectok"; - - /* overload */ - smart_rsa.rsa_priv_enc = sc_private_encrypt; - smart_rsa.rsa_priv_dec = sc_private_decrypt; - - /* save original */ - orig_finish = def->finish; - smart_rsa.finish = sc_finish; - - return &smart_rsa; -} - -#ifdef USE_ENGINE -static ENGINE * -sc_get_engine(void) -{ - static ENGINE *smart_engine = NULL; - - if ((smart_engine = ENGINE_new()) == NULL) - fatal("ENGINE_new failed"); - - ENGINE_set_id(smart_engine, "sectok"); - ENGINE_set_name(smart_engine, "libsectok"); - - ENGINE_set_RSA(smart_engine, sc_get_rsa_method()); - ENGINE_set_DSA(smart_engine, DSA_get_default_openssl_method()); - ENGINE_set_DH(smart_engine, DH_get_default_openssl_method()); - ENGINE_set_RAND(smart_engine, RAND_SSLeay()); - ENGINE_set_BN_mod_exp(smart_engine, BN_mod_exp); - - return smart_engine; -} -#endif - -void -sc_close(void) -{ - if (sc_fd >= 0) { - sectok_close(sc_fd); - sc_fd = -1; - } -} - -Key ** -sc_get_keys(const char *id, const char *pin) -{ - Key *k, *n, **keys; - int status, nkeys = 2; - - if (sc_reader_id != NULL) - xfree(sc_reader_id); - sc_reader_id = xstrdup(id); - - if (sc_pin != NULL) - xfree(sc_pin); - sc_pin = (pin == NULL) ? NULL : xstrdup(pin); - - k = key_new(KEY_RSA); - if (k == NULL) { - return NULL; - } - status = sc_read_pubkey(k); - if (status == SCARD_ERROR_NOCARD) { - key_free(k); - return NULL; - } - if (status < 0) { - error("sc_read_pubkey failed"); - key_free(k); - return NULL; - } - keys = xcalloc((nkeys+1), sizeof(Key *)); - - n = key_new(KEY_RSA1); - if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) || - (BN_copy(n->rsa->e, k->rsa->e) == NULL)) - fatal("sc_get_keys: BN_copy failed"); - RSA_set_method(n->rsa, sc_get_rsa()); - n->flags |= KEY_FLAG_EXT; - keys[0] = n; - - n = key_new(KEY_RSA); - if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) || - (BN_copy(n->rsa->e, k->rsa->e) == NULL)) - fatal("sc_get_keys: BN_copy failed"); - RSA_set_method(n->rsa, sc_get_rsa()); - n->flags |= KEY_FLAG_EXT; - keys[1] = n; - - keys[2] = NULL; - - key_free(k); - return keys; -} - -#define NUM_RSA_KEY_ELEMENTS 5+1 -#define COPY_RSA_KEY(x, i) \ - do { \ - len = BN_num_bytes(prv->rsa->x); \ - elements[i] = xmalloc(len); \ - debug("#bytes %d", len); \ - if (BN_bn2bin(prv->rsa->x, elements[i]) < 0) \ - goto done; \ - } while (0) - -static void -sc_mk_digest(const char *pin, u_char *digest) -{ - const EVP_MD *evp_md = EVP_sha1(); - EVP_MD_CTX md; - - EVP_DigestInit(&md, evp_md); - EVP_DigestUpdate(&md, pin, strlen(pin)); - EVP_DigestFinal(&md, digest, NULL); -} - -static int -get_AUT0(u_char *aut0) -{ - char *pass; - - pass = read_passphrase("Enter passphrase for smartcard: ", RP_ALLOW_STDIN); - if (pass == NULL) - return -1; - if (!strcmp(pass, "-")) { - memcpy(aut0, DEFAUT0, sizeof DEFAUT0); - return 0; - } - sc_mk_digest(pass, aut0); - memset(pass, 0, strlen(pass)); - xfree(pass); - return 0; -} - -static int -try_AUT0(void) -{ - u_char aut0[EVP_MAX_MD_SIZE]; - - /* permission denied; try PIN if provided */ - if (sc_pin && strlen(sc_pin) > 0) { - sc_mk_digest(sc_pin, aut0); - if (cyberflex_verify_AUT0(sc_fd, cla, aut0, 8) < 0) { - error("smartcard passphrase incorrect"); - return (-1); - } - } else { - /* try default AUT0 key */ - if (cyberflex_verify_AUT0(sc_fd, cla, DEFAUT0, 8) < 0) { - /* default AUT0 key failed; prompt for passphrase */ - if (get_AUT0(aut0) < 0 || - cyberflex_verify_AUT0(sc_fd, cla, aut0, 8) < 0) { - error("smartcard passphrase incorrect"); - return (-1); - } - } - } - return (0); -} - -int -sc_put_key(Key *prv, const char *id) -{ - u_char *elements[NUM_RSA_KEY_ELEMENTS]; - u_char key_fid[2]; - u_char AUT0[EVP_MAX_MD_SIZE]; - int len, status = -1, i, fd = -1, ret; - int sw = 0, cla = 0x00; - - for (i = 0; i < NUM_RSA_KEY_ELEMENTS; i++) - elements[i] = NULL; - - COPY_RSA_KEY(q, 0); - COPY_RSA_KEY(p, 1); - COPY_RSA_KEY(iqmp, 2); - COPY_RSA_KEY(dmq1, 3); - COPY_RSA_KEY(dmp1, 4); - COPY_RSA_KEY(n, 5); - len = BN_num_bytes(prv->rsa->n); - fd = sectok_friendly_open(id, STONOWAIT, &sw); - if (fd < 0) { - error("sectok_open failed: %s", sectok_get_sw(sw)); - goto done; - } - if (! sectok_cardpresent(fd)) { - error("smartcard in reader %s not present", id); - goto done; - } - ret = sectok_reset(fd, 0, NULL, &sw); - if (ret <= 0) { - error("sectok_reset failed: %s", sectok_get_sw(sw)); - goto done; - } - if ((cla = cyberflex_inq_class(fd)) < 0) { - error("cyberflex_inq_class failed"); - goto done; - } - memcpy(AUT0, DEFAUT0, sizeof(DEFAUT0)); - if (cyberflex_verify_AUT0(fd, cla, AUT0, sizeof(DEFAUT0)) < 0) { - if (get_AUT0(AUT0) < 0 || - cyberflex_verify_AUT0(fd, cla, AUT0, sizeof(DEFAUT0)) < 0) { - memset(AUT0, 0, sizeof(DEFAUT0)); - error("smartcard passphrase incorrect"); - goto done; - } - } - memset(AUT0, 0, sizeof(DEFAUT0)); - key_fid[0] = 0x00; - key_fid[1] = 0x12; - if (cyberflex_load_rsa_priv(fd, cla, key_fid, 5, 8*len, elements, - &sw) < 0) { - error("cyberflex_load_rsa_priv failed: %s", sectok_get_sw(sw)); - goto done; - } - if (!sectok_swOK(sw)) - goto done; - logit("cyberflex_load_rsa_priv done"); - key_fid[0] = 0x73; - key_fid[1] = 0x68; - if (cyberflex_load_rsa_pub(fd, cla, key_fid, len, elements[5], - &sw) < 0) { - error("cyberflex_load_rsa_pub failed: %s", sectok_get_sw(sw)); - goto done; - } - if (!sectok_swOK(sw)) - goto done; - logit("cyberflex_load_rsa_pub done"); - status = 0; - -done: - memset(elements[0], '\0', BN_num_bytes(prv->rsa->q)); - memset(elements[1], '\0', BN_num_bytes(prv->rsa->p)); - memset(elements[2], '\0', BN_num_bytes(prv->rsa->iqmp)); - memset(elements[3], '\0', BN_num_bytes(prv->rsa->dmq1)); - memset(elements[4], '\0', BN_num_bytes(prv->rsa->dmp1)); - memset(elements[5], '\0', BN_num_bytes(prv->rsa->n)); - - for (i = 0; i < NUM_RSA_KEY_ELEMENTS; i++) - if (elements[i]) - xfree(elements[i]); - if (fd != -1) - sectok_close(fd); - return (status); -} - -char * -sc_get_key_label(Key *key) -{ - return xstrdup("smartcard key"); -} - -#endif /* SMARTCARD && USE_SECTOK */ diff --git a/crypto/openssh/scp.1 b/crypto/openssh/scp.1 index 5033d84f22..bc5e259f54 100644 --- a/crypto/openssh/scp.1 +++ b/crypto/openssh/scp.1 @@ -9,9 +9,9 @@ .\" .\" Created: Sun May 7 00:14:37 1995 ylo .\" -.\" $OpenBSD: scp.1,v 1.46 2008/07/12 05:33:41 djm Exp $ +.\" $OpenBSD: scp.1,v 1.50 2010/02/08 10:50:20 markus Exp $ .\" -.Dd $Mdocdate: July 12 2008 $ +.Dd $Mdocdate: February 8 2010 $ .Dt SCP 1 .Os .Sh NAME @@ -153,6 +153,7 @@ For full details of the options listed below, and their possible values, see .It NoHostAuthenticationForLocalhost .It NumberOfPasswordPrompts .It PasswordAuthentication +.It PKCS11Provider .It Port .It PreferredAuthentications .It Protocol @@ -164,7 +165,6 @@ For full details of the options listed below, and their possible values, see .It SendEnv .It ServerAliveInterval .It ServerAliveCountMax -.It SmartcardDevice .It StrictHostKeyChecking .It TCPKeepAlive .It UsePrivilegedPort diff --git a/crypto/openssh/scp.c b/crypto/openssh/scp.c index 3237478068..e07de42f77 100644 --- a/crypto/openssh/scp.c +++ b/crypto/openssh/scp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: scp.c,v 1.164 2008/10/10 04:55:16 stevesk Exp $ */ +/* $OpenBSD: scp.c,v 1.166 2010/07/01 13:06:59 millert Exp $ */ /* * scp - secure remote copy. This is basically patched BSD rcp which * uses ssh to do the data transfer (instead of using rcmd). @@ -156,6 +156,20 @@ killchild(int signo) exit(1); } +static void +suspchild(int signo) +{ + int status; + + if (do_cmd_pid > 1) { + kill(do_cmd_pid, signo); + while (waitpid(do_cmd_pid, &status, WUNTRACED) == -1 && + errno == EINTR) + ; + kill(getpid(), SIGSTOP); + } +} + static int do_local_cmd(arglist *a) { @@ -232,6 +246,10 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) close(reserved[0]); close(reserved[1]); + signal(SIGTSTP, suspchild); + signal(SIGTTIN, suspchild); + signal(SIGTTOU, suspchild); + /* Fork a child to execute the command on the remote host using ssh. */ do_cmd_pid = fork(); if (do_cmd_pid == 0) { @@ -244,8 +262,11 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) close(pout[1]); replacearg(&args, 0, "%s", ssh_program); - if (remuser != NULL) - addargs(&args, "-l%s", remuser); + if (remuser != NULL) { + addargs(&args, "-l"); + addargs(&args, "%s", remuser); + } + addargs(&args, "--"); addargs(&args, "%s", host); addargs(&args, "%s", cmd); @@ -337,10 +358,12 @@ main(int argc, char **argv) case 'c': case 'i': case 'F': - addargs(&args, "-%c%s", ch, optarg); + addargs(&args, "-%c", ch); + addargs(&args, "%s", optarg); break; case 'P': - addargs(&args, "-p%s", optarg); + addargs(&args, "-p"); + addargs(&args, "%s", optarg); break; case 'B': addargs(&args, "-oBatchmode yes"); @@ -548,6 +571,7 @@ toremote(char *targ, int argc, char **argv) } else { host = cleanhostname(argv[i]); } + addargs(&alist, "--"); addargs(&alist, "%s", host); addargs(&alist, "%s", cmd); addargs(&alist, "%s", src); @@ -558,7 +582,7 @@ toremote(char *targ, int argc, char **argv) errs = 1; } else { /* local to remote */ if (remin == -1) { - xasprintf(&bp, "%s -t %s", cmd, targ); + xasprintf(&bp, "%s -t -- %s", cmd, targ); host = cleanhostname(thost); if (do_cmd(host, tuser, bp, &remin, &remout) < 0) @@ -591,6 +615,7 @@ tolocal(int argc, char **argv) addargs(&alist, "-r"); if (pflag) addargs(&alist, "-p"); + addargs(&alist, "--"); addargs(&alist, "%s", argv[i]); addargs(&alist, "%s", argv[argc-1]); if (do_local_cmd(&alist)) @@ -610,7 +635,7 @@ tolocal(int argc, char **argv) suser = pwd->pw_name; } host = cleanhostname(host); - xasprintf(&bp, "%s -f %s", cmd, src); + xasprintf(&bp, "%s -f -- %s", cmd, src); if (do_cmd(host, suser, bp, &remin, &remout) < 0) { (void) xfree(bp); ++errs; diff --git a/crypto/openssh/servconf.c b/crypto/openssh/servconf.c index b51b86a8f5..986a5b92f6 100644 --- a/crypto/openssh/servconf.c +++ b/crypto/openssh/servconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.c,v 1.195 2009/04/14 21:10:54 jj Exp $ */ +/* $OpenBSD: servconf.c,v 1.209 2010/06/22 04:22:59 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -65,6 +65,7 @@ initialize_server_options(ServerOptions *options) options->listen_addrs = NULL; options->address_family = -1; options->num_host_key_files = 0; + options->num_host_cert_files = 0; options->pid_file = NULL; options->server_key_bits = -1; options->login_grace_time = -1; @@ -128,6 +129,9 @@ initialize_server_options(ServerOptions *options) options->adm_forced_command = NULL; options->chroot_directory = NULL; options->zero_knowledge_password_authentication = -1; + options->revoked_keys_file = NULL; + options->trusted_user_ca_keys = NULL; + options->authorized_principals_file = NULL; } void @@ -139,7 +143,7 @@ fill_default_server_options(ServerOptions *options) /* Standard Options */ if (options->protocol == SSH_PROTO_UNKNOWN) - options->protocol = SSH_PROTO_1|SSH_PROTO_2; + options->protocol = SSH_PROTO_2; if (options->num_host_key_files == 0) { /* fill default hostkeys for protocols */ if (options->protocol & SSH_PROTO_1) @@ -152,6 +156,7 @@ fill_default_server_options(ServerOptions *options) _PATH_HOST_DSA_KEY_FILE; } } + /* No certificates by default */ if (options->num_ports == 0) options->ports[options->num_ports++] = SSH_DEFAULT_PORT; if (options->listen_addrs == NULL) @@ -305,7 +310,8 @@ typedef enum { sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, sMatch, sPermitOpen, sForceCommand, sChrootDirectory, sUsePrivilegeSeparation, sAllowAgentForwarding, - sZeroKnowledgePasswordAuthentication, + sZeroKnowledgePasswordAuthentication, sHostCertificate, + sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, sDeprecated, sUnsupported } ServerOpCodes; @@ -340,7 +346,7 @@ static struct { { "rhostsauthentication", sDeprecated, SSHCFG_GLOBAL }, { "rhostsrsaauthentication", sRhostsRSAAuthentication, SSHCFG_ALL }, { "hostbasedauthentication", sHostbasedAuthentication, SSHCFG_ALL }, - { "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly, SSHCFG_GLOBAL }, + { "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly, SSHCFG_ALL }, { "rsaauthentication", sRSAAuthentication, SSHCFG_ALL }, { "pubkeyauthentication", sPubkeyAuthentication, SSHCFG_ALL }, { "dsaauthentication", sPubkeyAuthentication, SSHCFG_GLOBAL }, /* alias */ @@ -415,15 +421,19 @@ static struct { { "reversemappingcheck", sDeprecated, SSHCFG_GLOBAL }, { "clientaliveinterval", sClientAliveInterval, SSHCFG_GLOBAL }, { "clientalivecountmax", sClientAliveCountMax, SSHCFG_GLOBAL }, - { "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_GLOBAL }, - { "authorizedkeysfile2", sAuthorizedKeysFile2, SSHCFG_GLOBAL }, + { "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_ALL }, + { "authorizedkeysfile2", sAuthorizedKeysFile2, SSHCFG_ALL }, { "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL}, { "acceptenv", sAcceptEnv, SSHCFG_GLOBAL }, - { "permittunnel", sPermitTunnel, SSHCFG_GLOBAL }, + { "permittunnel", sPermitTunnel, SSHCFG_ALL }, { "match", sMatch, SSHCFG_ALL }, { "permitopen", sPermitOpen, SSHCFG_ALL }, { "forcecommand", sForceCommand, SSHCFG_ALL }, { "chrootdirectory", sChrootDirectory, SSHCFG_ALL }, + { "hostcertificate", sHostCertificate, SSHCFG_GLOBAL }, + { "revokedkeys", sRevokedKeys, SSHCFG_ALL }, + { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL }, + { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL }, { NULL, sBadOption, 0 } }; @@ -459,6 +469,21 @@ parse_token(const char *cp, const char *filename, return sBadOption; } +char * +derelativise_path(const char *path) +{ + char *expanded, *ret, cwd[MAXPATHLEN]; + + expanded = tilde_expand_filename(path, getuid()); + if (*expanded == '/') + return expanded; + if (getcwd(cwd, sizeof(cwd)) == NULL) + fatal("%s: getcwd: %s", __func__, strerror(errno)); + xasprintf(&ret, "%s/%s", cwd, expanded); + xfree(expanded); + return ret; +} + static void add_listen_addr(ServerOptions *options, char *addr, int port) { @@ -793,13 +818,23 @@ process_server_config_line(ServerOptions *options, char *line, fatal("%s line %d: missing file name.", filename, linenum); if (*activep && *charptr == NULL) { - *charptr = tilde_expand_filename(arg, getuid()); + *charptr = derelativise_path(arg); /* increase optional counter */ if (intptr != NULL) *intptr = *intptr + 1; } break; + case sHostCertificate: + intptr = &options->num_host_cert_files; + if (*intptr >= MAX_HOSTKEYS) + fatal("%s line %d: too many host certificates " + "specified (max %d).", filename, linenum, + MAX_HOSTCERTS); + charptr = &options->host_cert_files[*intptr]; + goto parse_filename; + break; + case sPidFile: charptr = &options->pid_file; goto parse_filename; @@ -1185,11 +1220,25 @@ process_server_config_line(ServerOptions *options, char *line, * AuthorizedKeysFile /etc/ssh_keys/%u */ case sAuthorizedKeysFile: + charptr = &options->authorized_keys_file; + goto parse_tilde_filename; case sAuthorizedKeysFile2: - charptr = (opcode == sAuthorizedKeysFile) ? - &options->authorized_keys_file : - &options->authorized_keys_file2; - goto parse_filename; + charptr = &options->authorized_keys_file2; + goto parse_tilde_filename; + case sAuthorizedPrincipalsFile: + charptr = &options->authorized_principals_file; + parse_tilde_filename: + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: missing file name.", + filename, linenum); + if (*activep && *charptr == NULL) { + *charptr = tilde_expand_filename(arg, getuid()); + /* increase optional counter */ + if (intptr != NULL) + *intptr = *intptr + 1; + } + break; case sClientAliveInterval: intptr = &options->client_alive_interval; @@ -1294,6 +1343,14 @@ process_server_config_line(ServerOptions *options, char *line, *charptr = xstrdup(arg); break; + case sTrustedUserCAKeys: + charptr = &options->trusted_user_ca_keys; + goto parse_filename; + + case sRevokedKeys: + charptr = &options->revoked_keys_file; + goto parse_filename; + case sDeprecated: logit("%s line %d: Deprecated option %s", filename, linenum, arg); @@ -1389,6 +1446,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) M_CP_INTOPT(pubkey_authentication); M_CP_INTOPT(kerberos_authentication); M_CP_INTOPT(hostbased_authentication); + M_CP_INTOPT(hostbased_uses_name_from_packet_only); M_CP_INTOPT(kbd_interactive_authentication); M_CP_INTOPT(zero_knowledge_password_authentication); M_CP_INTOPT(permit_root_login); @@ -1396,6 +1454,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) M_CP_INTOPT(allow_tcp_forwarding); M_CP_INTOPT(allow_agent_forwarding); + M_CP_INTOPT(permit_tun); M_CP_INTOPT(gateway_ports); M_CP_INTOPT(x11_display_offset); M_CP_INTOPT(x11_forwarding); @@ -1408,6 +1467,11 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) return; M_CP_STROPT(adm_forced_command); M_CP_STROPT(chroot_directory); + M_CP_STROPT(trusted_user_ca_keys); + M_CP_STROPT(revoked_keys_file); + M_CP_STROPT(authorized_keys_file); + M_CP_STROPT(authorized_keys_file2); + M_CP_STROPT(authorized_principals_file); } #undef M_CP_INTOPT @@ -1626,6 +1690,11 @@ dump_config(ServerOptions *o) dump_cfg_string(sAuthorizedKeysFile, o->authorized_keys_file); dump_cfg_string(sAuthorizedKeysFile2, o->authorized_keys_file2); dump_cfg_string(sForceCommand, o->adm_forced_command); + dump_cfg_string(sChrootDirectory, o->chroot_directory); + dump_cfg_string(sTrustedUserCAKeys, o->trusted_user_ca_keys); + dump_cfg_string(sRevokedKeys, o->revoked_keys_file); + dump_cfg_string(sAuthorizedPrincipalsFile, + o->authorized_principals_file); /* string arguments requiring a lookup */ dump_cfg_string(sLogLevel, log_level_name(o->log_level)); @@ -1634,6 +1703,8 @@ dump_config(ServerOptions *o) /* string array arguments */ dump_cfg_strarray(sHostKeyFile, o->num_host_key_files, o->host_key_files); + dump_cfg_strarray(sHostKeyFile, o->num_host_cert_files, + o->host_cert_files); dump_cfg_strarray(sAllowUsers, o->num_allow_users, o->allow_users); dump_cfg_strarray(sDenyUsers, o->num_deny_users, o->deny_users); dump_cfg_strarray(sAllowGroups, o->num_allow_groups, o->allow_groups); diff --git a/crypto/openssh/servconf.h b/crypto/openssh/servconf.h index b3ac7da4b6..45d2a2ae35 100644 --- a/crypto/openssh/servconf.h +++ b/crypto/openssh/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.87 2009/01/22 10:02:34 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.93 2010/05/07 11:30:30 djm Exp $ */ /* * Author: Tatu Ylonen @@ -24,6 +24,7 @@ #define MAX_DENY_GROUPS 256 /* Max # groups on deny list. */ #define MAX_SUBSYSTEMS 256 /* Max # subsystems. */ #define MAX_HOSTKEYS 256 /* Max # hostkeys. */ +#define MAX_HOSTCERTS 256 /* Max # host certificates. */ #define MAX_ACCEPT_ENV 256 /* Max # of env vars. */ #define MAX_MATCH_GROUPS 256 /* Max # of groups for Match. */ @@ -49,6 +50,8 @@ typedef struct { int address_family; /* Address family used by the server. */ char *host_key_files[MAX_HOSTKEYS]; /* Files containing host keys. */ int num_host_key_files; /* Number of files for host keys. */ + char *host_cert_files[MAX_HOSTCERTS]; /* Files containing host certs. */ + int num_host_cert_files; /* Number of files for host certs. */ char *pid_file; /* Where to put our pid */ int server_key_bits;/* Size of the server key. */ int login_grace_time; /* Disconnect if no auth in this time @@ -151,6 +154,9 @@ typedef struct { int num_permitted_opens; char *chroot_directory; + char *revoked_keys_file; + char *trusted_user_ca_keys; + char *authorized_principals_file; } ServerOptions; void initialize_server_options(ServerOptions *); @@ -164,5 +170,6 @@ void parse_server_match_config(ServerOptions *, const char *, const char *, const char *); void copy_set_server_options(ServerOptions *, ServerOptions *, int); void dump_config(ServerOptions *); +char *derelativise_path(const char *); #endif /* SERVCONF_H */ diff --git a/crypto/openssh/session.c b/crypto/openssh/session.c index f4a3635437..71e4fbe7cf 100644 --- a/crypto/openssh/session.c +++ b/crypto/openssh/session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.246 2009/04/17 19:23:06 stevesk Exp $ */ +/* $OpenBSD: session.c,v 1.256 2010/06/25 07:20:04 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -47,6 +47,7 @@ #include #include +#include #include #ifdef HAVE_PATHS_H #include @@ -104,7 +105,7 @@ /* func */ Session *session_new(void); -void session_set_fds(Session *, int, int, int, int); +void session_set_fds(Session *, int, int, int, int, int); void session_pty_cleanup(Session *); void session_proctitle(Session *); int session_setup_x11fwd(Session *); @@ -142,9 +143,10 @@ static int sessions_first_unused = -1; static int sessions_nalloc = 0; static Session *sessions = NULL; -#define SUBSYSTEM_NONE 0 -#define SUBSYSTEM_EXT 1 -#define SUBSYSTEM_INT_SFTP 2 +#define SUBSYSTEM_NONE 0 +#define SUBSYSTEM_EXT 1 +#define SUBSYSTEM_INT_SFTP 2 +#define SUBSYSTEM_INT_SFTP_ERROR 3 #ifdef HAVE_LOGIN_CAP login_cap_t *lc; @@ -270,6 +272,8 @@ do_authenticated(Authctxt *authctxt) if (!no_port_forwarding_flag && options.allow_tcp_forwarding) channel_permit_all_opens(); + auth_debug_send(); + if (compat20) do_authenticated2(authctxt); else @@ -444,6 +448,9 @@ do_exec_no_pty(Session *s, const char *command) #ifdef USE_PIPES int pin[2], pout[2], perr[2]; + if (s == NULL) + fatal("do_exec_no_pty: no session"); + /* Allocate pipes for communicating with the program. */ if (pipe(pin) < 0) { error("%s: pipe in: %.100s", __func__, strerror(errno)); @@ -456,7 +463,8 @@ do_exec_no_pty(Session *s, const char *command) return -1; } if (pipe(perr) < 0) { - error("%s: pipe err: %.100s", __func__, strerror(errno)); + error("%s: pipe err: %.100s", __func__, + strerror(errno)); close(pin[0]); close(pin[1]); close(pout[0]); @@ -466,22 +474,23 @@ do_exec_no_pty(Session *s, const char *command) #else int inout[2], err[2]; + if (s == NULL) + fatal("do_exec_no_pty: no session"); + /* Uses socket pairs to communicate with the program. */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0) { error("%s: socketpair #1: %.100s", __func__, strerror(errno)); return -1; } if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) { - error("%s: socketpair #2: %.100s", __func__, strerror(errno)); + error("%s: socketpair #2: %.100s", __func__, + strerror(errno)); close(inout[0]); close(inout[1]); return -1; } #endif - if (s == NULL) - fatal("do_exec_no_pty: no session"); - session_proctitle(s); /* Fork the child. */ @@ -592,11 +601,8 @@ do_exec_no_pty(Session *s, const char *command) close(perr[1]); if (compat20) { - if (s->is_subsystem) { - close(perr[0]); - perr[0] = -1; - } - session_set_fds(s, pin[1], pout[0], perr[0], 0); + session_set_fds(s, pin[1], pout[0], perr[0], + s->is_subsystem, 0); } else { /* Enter the interactive session. */ server_loop(pid, pin[1], pout[0], perr[0]); @@ -612,10 +618,8 @@ do_exec_no_pty(Session *s, const char *command) * handle the case that fdin and fdout are the same. */ if (compat20) { - session_set_fds(s, inout[1], inout[1], - s->is_subsystem ? -1 : err[1], 0); - if (s->is_subsystem) - close(err[1]); + session_set_fds(s, inout[1], inout[1], err[1], + s->is_subsystem, 0); } else { server_loop(pid, inout[1], inout[1], err[1]); /* server_loop has closed inout[1] and err[1]. */ @@ -737,7 +741,7 @@ do_exec_pty(Session *s, const char *command) s->ptymaster = ptymaster; packet_set_interactive(1); if (compat20) { - session_set_fds(s, ptyfd, fdout, -1, 1); + session_set_fds(s, ptyfd, fdout, -1, 1, 1); } else { server_loop(pid, ptyfd, fdout, -1); /* server_loop _has_ closed ptyfd and fdout. */ @@ -785,17 +789,19 @@ do_exec(Session *s, const char *command) if (options.adm_forced_command) { original_command = command; command = options.adm_forced_command; - if (IS_INTERNAL_SFTP(command)) - s->is_subsystem = SUBSYSTEM_INT_SFTP; - else if (s->is_subsystem) + if (IS_INTERNAL_SFTP(command)) { + s->is_subsystem = s->is_subsystem ? + SUBSYSTEM_INT_SFTP : SUBSYSTEM_INT_SFTP_ERROR; + } else if (s->is_subsystem) s->is_subsystem = SUBSYSTEM_EXT; debug("Forced command (config) '%.900s'", command); } else if (forced_command) { original_command = command; command = forced_command; - if (IS_INTERNAL_SFTP(command)) - s->is_subsystem = SUBSYSTEM_INT_SFTP; - else if (s->is_subsystem) + if (IS_INTERNAL_SFTP(command)) { + s->is_subsystem = s->is_subsystem ? + SUBSYSTEM_INT_SFTP : SUBSYSTEM_INT_SFTP_ERROR; + } else if (s->is_subsystem) s->is_subsystem = SUBSYSTEM_EXT; debug("Forced command (key option) '%.900s'", command); } @@ -1374,26 +1380,32 @@ static void do_nologin(struct passwd *pw) { FILE *f = NULL; - char buf[1024]; + char buf[1024], *nl, *def_nl = _PATH_NOLOGIN; + struct stat sb; #ifdef HAVE_LOGIN_CAP - if (!login_getcapbool(lc, "ignorenologin", 0) && pw->pw_uid) - f = fopen(login_getcapstr(lc, "nologin", _PATH_NOLOGIN, - _PATH_NOLOGIN), "r"); + if (login_getcapbool(lc, "ignorenologin", 0) && pw->pw_uid) + return; + nl = login_getcapstr(lc, "nologin", def_nl, def_nl); #else - if (pw->pw_uid) - f = fopen(_PATH_NOLOGIN, "r"); + if (pw->pw_uid == 0) + return; + nl = def_nl; #endif - if (f) { - /* /etc/nologin exists. Print its contents and exit. */ - logit("User %.100s not allowed because %s exists", - pw->pw_name, _PATH_NOLOGIN); - while (fgets(buf, sizeof(buf), f)) - fputs(buf, stderr); - fclose(f); - fflush(NULL); - exit(254); + if (stat(nl, &sb) == -1) { + if (nl != def_nl) + xfree(nl); + return; } + + /* /etc/nologin exists. Print its contents if we can and exit. */ + logit("User %.100s not allowed because %s exists", pw->pw_name, nl); + if ((f = fopen(nl, "r")) != NULL) { + while (fgets(buf, sizeof(buf), f)) + fputs(buf, stderr); + fclose(f); + } + exit(254); } /* @@ -1520,6 +1532,28 @@ do_setusercontext(struct passwd *pw) exit(1); } # endif /* USE_LIBIAF */ +#endif +#ifdef HAVE_SETPCRED + /* + * If we have a chroot directory, we set all creds except real + * uid which we will need for chroot. If we don't have a + * chroot directory, we don't override anything. + */ + { + char **creds = NULL, *chroot_creds[] = + { "REAL_USER=root", NULL }; + + if (options.chroot_directory != NULL && + strcasecmp(options.chroot_directory, "none") != 0) + creds = chroot_creds; + + if (setpcred(pw->pw_name, creds) == -1) + fatal("Failed to set process credentials"); + } +#endif /* HAVE_SETPCRED */ + +#ifdef WITH_SELINUX + ssh_selinux_setup_exec_context(pw->pw_name); #endif if (options.chroot_directory != NULL && @@ -1533,10 +1567,6 @@ do_setusercontext(struct passwd *pw) free(chroot_path); } -#ifdef HAVE_SETPCRED - if (setpcred(pw->pw_name, (char **)NULL) == -1) - fatal("Failed to set process credentials"); -#endif /* HAVE_SETPCRED */ #ifdef HAVE_LOGIN_CAP if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUSER) < 0) { perror("unable to set user context (setuser)"); @@ -1550,10 +1580,6 @@ do_setusercontext(struct passwd *pw) if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) fatal("Failed to set uids to %u.", (u_int) pw->pw_uid); - -#ifdef WITH_SELINUX - ssh_selinux_setup_exec_context(pw->pw_name); -#endif } static void @@ -1767,7 +1793,8 @@ do_child(Session *s, const char *command) #ifdef HAVE_LOGIN_CAP r = login_getcapbool(lc, "requirehome", 0); #endif - if (r || options.chroot_directory == NULL) + if (r || options.chroot_directory == NULL || + strcasecmp(options.chroot_directory, "none") == 0) fprintf(stderr, "Could not chdir to home " "directory %s: %s\n", pw->pw_dir, strerror(errno)); @@ -1783,7 +1810,11 @@ do_child(Session *s, const char *command) /* restore SIGPIPE for child */ signal(SIGPIPE, SIG_DFL); - if (s->is_subsystem == SUBSYSTEM_INT_SFTP) { + if (s->is_subsystem == SUBSYSTEM_INT_SFTP_ERROR) { + printf("This service allows sftp connections only.\n"); + fflush(NULL); + exit(1); + } else if (s->is_subsystem == SUBSYSTEM_INT_SFTP) { extern int optind, optreset; int i; char *p, *args; @@ -1796,9 +1827,14 @@ do_child(Session *s, const char *command) argv[i] = NULL; optind = optreset = 1; __progname = argv[0]; +#ifdef WITH_SELINUX + ssh_selinux_change_context("sftpd_t"); +#endif exit(sftp_server_main(i, argv, s->pw)); } + fflush(NULL); + if (options.use_login) { launch_login(pw, hostname); /* NEVERREACHED */ @@ -2103,22 +2139,23 @@ session_subsystem_req(Session *s) u_int i; packet_check_eom(); - logit("subsystem request for %.100s", subsys); + logit("subsystem request for %.100s by user %s", subsys, + s->pw->pw_name); for (i = 0; i < options.num_subsystems; i++) { if (strcmp(subsys, options.subsystem_name[i]) == 0) { prog = options.subsystem_command[i]; cmd = options.subsystem_args[i]; - if (!strcmp(INTERNAL_SFTP_NAME, prog)) { + if (strcmp(INTERNAL_SFTP_NAME, prog) == 0) { s->is_subsystem = SUBSYSTEM_INT_SFTP; - } else if (stat(prog, &st) < 0) { - error("subsystem: cannot stat %s: %s", prog, - strerror(errno)); - break; + debug("subsystem: %s", prog); } else { + if (stat(prog, &st) < 0) + debug("subsystem: cannot stat %s: %s", + prog, strerror(errno)); s->is_subsystem = SUBSYSTEM_EXT; + debug("subsystem: exec() %s", cmd); } - debug("subsystem: exec() %s", cmd); success = do_exec(s, cmd) == 0; break; } @@ -2285,7 +2322,8 @@ session_input_channel_req(Channel *c, const char *rtype) } void -session_set_fds(Session *s, int fdin, int fdout, int fderr, int is_tty) +session_set_fds(Session *s, int fdin, int fdout, int fderr, int ignore_fderr, + int is_tty) { if (!compat20) fatal("session_set_fds: called for proto != 2.0"); @@ -2297,7 +2335,7 @@ session_set_fds(Session *s, int fdin, int fdout, int fderr, int is_tty) fatal("no channel for session %d", s->self); channel_set_fds(s->chanid, fdout, fdin, fderr, - fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ, + ignore_fderr ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ, 1, is_tty, CHAN_SES_WINDOW_DEFAULT); } diff --git a/crypto/openssh/sftp-client.c b/crypto/openssh/sftp-client.c index 0990b7912f..9dab477806 100644 --- a/crypto/openssh/sftp-client.c +++ b/crypto/openssh/sftp-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-client.c,v 1.87 2009/06/22 05:39:28 dtucker Exp $ */ +/* $OpenBSD: sftp-client.c,v 1.92 2010/07/19 03:16:33 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -36,6 +36,7 @@ #endif #include +#include #include #include #include @@ -61,6 +62,9 @@ extern int showprogress; /* Minimum amount of data to read at a time */ #define MIN_READ_SIZE 512 +/* Maximum depth to descend in directory trees */ +#define MAX_DIR_DEPTH 64 + struct sftp_conn { int fd_in; int fd_out; @@ -74,6 +78,10 @@ struct sftp_conn { u_int exts; }; +static char * +get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...) + __attribute__((format(printf, 4, 5))); + static void send_msg(int fd, Buffer *m) { @@ -179,11 +187,18 @@ get_status(int fd, u_int expected_id) } static char * -get_handle(int fd, u_int expected_id, u_int *len) +get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...) { Buffer msg; u_int type, id; - char *handle; + char *handle, errmsg[256]; + va_list args; + int status; + + va_start(args, errfmt); + if (errfmt != NULL) + vsnprintf(errmsg, sizeof(errmsg), errfmt, args); + va_end(args); buffer_init(&msg); get_msg(fd, &msg); @@ -191,16 +206,17 @@ get_handle(int fd, u_int expected_id, u_int *len) id = buffer_get_int(&msg); if (id != expected_id) - fatal("ID mismatch (%u != %u)", id, expected_id); + fatal("%s: ID mismatch (%u != %u)", + errfmt == NULL ? __func__ : errmsg, id, expected_id); if (type == SSH2_FXP_STATUS) { - int status = buffer_get_int(&msg); - - error("Couldn't get handle: %s", fx2txt(status)); + status = buffer_get_int(&msg); + if (errfmt != NULL) + error("%s: %s", errmsg, fx2txt(status)); buffer_free(&msg); return(NULL); } else if (type != SSH2_FXP_HANDLE) - fatal("Expected SSH2_FXP_HANDLE(%u) packet, got %u", - SSH2_FXP_HANDLE, type); + fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u", + errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type); handle = buffer_get_string(&msg, len); buffer_free(&msg); @@ -418,7 +434,8 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, buffer_clear(&msg); - handle = get_handle(conn->fd_in, id, &handle_len); + handle = get_handle(conn->fd_in, id, &handle_len, + "remote readdir(\"%s\")", path); if (handle == NULL) return(-1); @@ -484,6 +501,17 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, if (printflag) printf("%s\n", longname); + /* + * Directory entries should never contain '/' + * These can be used to attack recursive ops + * (e.g. send '../../../../etc/passwd') + */ + if (strchr(filename, '/') != NULL) { + error("Server sent suspect path \"%s\" " + "during readdir of \"%s\"", filename, path); + goto next; + } + if (dir) { *dir = xrealloc(*dir, ents + 2, sizeof(**dir)); (*dir)[ents] = xmalloc(sizeof(***dir)); @@ -492,7 +520,7 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, memcpy(&(*dir)[ents]->a, a, sizeof(*a)); (*dir)[++ents] = NULL; } - + next: xfree(filename); xfree(longname); } @@ -547,7 +575,7 @@ do_rm(struct sftp_conn *conn, char *path) } int -do_mkdir(struct sftp_conn *conn, char *path, Attrib *a) +do_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int printflag) { u_int status, id; @@ -556,7 +584,7 @@ do_mkdir(struct sftp_conn *conn, char *path, Attrib *a) strlen(path), a); status = get_status(conn->fd_in, id); - if (status != SSH2_FX_OK) + if (status != SSH2_FX_OK && printflag) error("Couldn't create directory: %s", fx2txt(status)); return(status); @@ -685,7 +713,8 @@ do_realpath(struct sftp_conn *conn, char *path) u_int status = buffer_get_int(&msg); error("Couldn't canonicalise: %s", fx2txt(status)); - return(NULL); + buffer_free(&msg); + return NULL; } else if (type != SSH2_FXP_NAME) fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", SSH2_FXP_NAME, type); @@ -895,9 +924,9 @@ send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len, int do_download(struct sftp_conn *conn, char *remote_path, char *local_path, - int pflag) + Attrib *a, int pflag) { - Attrib junk, *a; + Attrib junk; Buffer msg; char *handle; int local_fd, status = 0, write_error; @@ -916,9 +945,8 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, TAILQ_INIT(&requests); - a = do_stat(conn, remote_path, 0); - if (a == NULL) - return(-1); + if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL) + return -1; /* Do not preserve set[ug]id here, as we do not preserve ownership */ if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) @@ -951,7 +979,8 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, send_msg(conn->fd_out, &msg); debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); - handle = get_handle(conn->fd_in, id, &handle_len); + handle = get_handle(conn->fd_in, id, &handle_len, + "remote open(\"%s\")", remote_path); if (handle == NULL) { buffer_free(&msg); return(-1); @@ -1132,6 +1161,114 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, return(status); } +static int +download_dir_internal(struct sftp_conn *conn, char *src, char *dst, + Attrib *dirattrib, int pflag, int printflag, int depth) +{ + int i, ret = 0; + SFTP_DIRENT **dir_entries; + char *filename, *new_src, *new_dst; + mode_t mode = 0777; + + if (depth >= MAX_DIR_DEPTH) { + error("Maximum directory depth exceeded: %d levels", depth); + return -1; + } + + if (dirattrib == NULL && + (dirattrib = do_stat(conn, src, 1)) == NULL) { + error("Unable to stat remote directory \"%s\"", src); + return -1; + } + if (!S_ISDIR(dirattrib->perm)) { + error("\"%s\" is not a directory", src); + return -1; + } + if (printflag) + printf("Retrieving %s\n", src); + + if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) + mode = dirattrib->perm & 01777; + else { + debug("Server did not send permissions for " + "directory \"%s\"", dst); + } + + if (mkdir(dst, mode) == -1 && errno != EEXIST) { + error("mkdir %s: %s", dst, strerror(errno)); + return -1; + } + + if (do_readdir(conn, src, &dir_entries) == -1) { + error("%s: Failed to get directory contents", src); + return -1; + } + + for (i = 0; dir_entries[i] != NULL && !interrupted; i++) { + filename = dir_entries[i]->filename; + + new_dst = path_append(dst, filename); + new_src = path_append(src, filename); + + if (S_ISDIR(dir_entries[i]->a.perm)) { + if (strcmp(filename, ".") == 0 || + strcmp(filename, "..") == 0) + continue; + if (download_dir_internal(conn, new_src, new_dst, + &(dir_entries[i]->a), pflag, printflag, + depth + 1) == -1) + ret = -1; + } else if (S_ISREG(dir_entries[i]->a.perm) ) { + if (do_download(conn, new_src, new_dst, + &(dir_entries[i]->a), pflag) == -1) { + error("Download of file %s to %s failed", + new_src, new_dst); + ret = -1; + } + } else + logit("%s: not a regular file\n", new_src); + + xfree(new_dst); + xfree(new_src); + } + + if (pflag) { + if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { + struct timeval tv[2]; + tv[0].tv_sec = dirattrib->atime; + tv[1].tv_sec = dirattrib->mtime; + tv[0].tv_usec = tv[1].tv_usec = 0; + if (utimes(dst, tv) == -1) + error("Can't set times on \"%s\": %s", + dst, strerror(errno)); + } else + debug("Server did not send times for directory " + "\"%s\"", dst); + } + + free_sftp_dirents(dir_entries); + + return ret; +} + +int +download_dir(struct sftp_conn *conn, char *src, char *dst, + Attrib *dirattrib, int pflag, int printflag) +{ + char *src_canon; + int ret; + + if ((src_canon = do_realpath(conn, src)) == NULL) { + error("Unable to canonicalise path \"%s\"", src); + return -1; + } + + ret = download_dir_internal(conn, src_canon, dst, + dirattrib, pflag, printflag, 0); + xfree(src_canon); + return ret; +} + int do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, int pflag) @@ -1195,7 +1332,8 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, buffer_clear(&msg); - handle = get_handle(conn->fd_in, id, &handle_len); + handle = get_handle(conn->fd_in, id, &handle_len, + "remote open(\"%s\")", remote_path); if (handle == NULL) { close(local_fd); buffer_free(&msg); @@ -1313,3 +1451,127 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, return status; } + +static int +upload_dir_internal(struct sftp_conn *conn, char *src, char *dst, + int pflag, int printflag, int depth) +{ + int ret = 0, status; + DIR *dirp; + struct dirent *dp; + char *filename, *new_src, *new_dst; + struct stat sb; + Attrib a; + + if (depth >= MAX_DIR_DEPTH) { + error("Maximum directory depth exceeded: %d levels", depth); + return -1; + } + + if (stat(src, &sb) == -1) { + error("Couldn't stat directory \"%s\": %s", + src, strerror(errno)); + return -1; + } + if (!S_ISDIR(sb.st_mode)) { + error("\"%s\" is not a directory", src); + return -1; + } + if (printflag) + printf("Entering %s\n", src); + + attrib_clear(&a); + stat_to_attrib(&sb, &a); + a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; + a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; + a.perm &= 01777; + if (!pflag) + a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; + + status = do_mkdir(conn, dst, &a, 0); + /* + * we lack a portable status for errno EEXIST, + * so if we get a SSH2_FX_FAILURE back we must check + * if it was created successfully. + */ + if (status != SSH2_FX_OK) { + if (status != SSH2_FX_FAILURE) + return -1; + if (do_stat(conn, dst, 0) == NULL) + return -1; + } + + if ((dirp = opendir(src)) == NULL) { + error("Failed to open dir \"%s\": %s", src, strerror(errno)); + return -1; + } + + while (((dp = readdir(dirp)) != NULL) && !interrupted) { + if (dp->d_ino == 0) + continue; + filename = dp->d_name; + new_dst = path_append(dst, filename); + new_src = path_append(src, filename); + + if (lstat(new_src, &sb) == -1) { + logit("%s: lstat failed: %s", filename, + strerror(errno)); + ret = -1; + } else if (S_ISDIR(sb.st_mode)) { + if (strcmp(filename, ".") == 0 || + strcmp(filename, "..") == 0) + continue; + + if (upload_dir_internal(conn, new_src, new_dst, + pflag, printflag, depth + 1) == -1) + ret = -1; + } else if (S_ISREG(sb.st_mode)) { + if (do_upload(conn, new_src, new_dst, pflag) == -1) { + error("Uploading of file %s to %s failed!", + new_src, new_dst); + ret = -1; + } + } else + logit("%s: not a regular file\n", filename); + xfree(new_dst); + xfree(new_src); + } + + do_setstat(conn, dst, &a); + + (void) closedir(dirp); + return ret; +} + +int +upload_dir(struct sftp_conn *conn, char *src, char *dst, int printflag, + int pflag) +{ + char *dst_canon; + int ret; + + if ((dst_canon = do_realpath(conn, dst)) == NULL) { + error("Unable to canonicalise path \"%s\"", dst); + return -1; + } + + ret = upload_dir_internal(conn, src, dst_canon, pflag, printflag, 0); + xfree(dst_canon); + return ret; +} + +char * +path_append(char *p1, char *p2) +{ + char *ret; + size_t len = strlen(p1) + strlen(p2) + 2; + + ret = xmalloc(len); + strlcpy(ret, p1, len); + if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/') + strlcat(ret, "/", len); + strlcat(ret, p2, len); + + return(ret); +} + diff --git a/crypto/openssh/sftp-client.h b/crypto/openssh/sftp-client.h index edb46790f3..1d08c40493 100644 --- a/crypto/openssh/sftp-client.h +++ b/crypto/openssh/sftp-client.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-client.h,v 1.17 2008/06/08 20:15:29 dtucker Exp $ */ +/* $OpenBSD: sftp-client.h,v 1.18 2009/08/18 18:36:20 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller @@ -68,7 +68,7 @@ void free_sftp_dirents(SFTP_DIRENT **); int do_rm(struct sftp_conn *, char *); /* Create directory 'path' */ -int do_mkdir(struct sftp_conn *, char *, Attrib *); +int do_mkdir(struct sftp_conn *, char *, Attrib *, int); /* Remove directory 'path' */ int do_rmdir(struct sftp_conn *, char *); @@ -103,7 +103,13 @@ int do_symlink(struct sftp_conn *, char *, char *); * Download 'remote_path' to 'local_path'. Preserve permissions and times * if 'pflag' is set */ -int do_download(struct sftp_conn *, char *, char *, int); +int do_download(struct sftp_conn *, char *, char *, Attrib *, int); + +/* + * Recursively download 'remote_directory' to 'local_directory'. Preserve + * times if 'pflag' is set + */ +int download_dir(struct sftp_conn *, char *, char *, Attrib *, int, int); /* * Upload 'local_path' to 'remote_path'. Preserve permissions and times @@ -111,4 +117,13 @@ int do_download(struct sftp_conn *, char *, char *, int); */ int do_upload(struct sftp_conn *, char *, char *, int); +/* + * Recursively upload 'local_directory' to 'remote_directory'. Preserve + * times if 'pflag' is set + */ +int upload_dir(struct sftp_conn *, char *, char *, int, int); + +/* Concatenate paths, taking care of slashes. Caller must free result. */ +char *path_append(char *, char *); + #endif diff --git a/crypto/openssh/sftp-common.c b/crypto/openssh/sftp-common.c index 7ebadcc53b..a042875c69 100644 --- a/crypto/openssh/sftp-common.c +++ b/crypto/openssh/sftp-common.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-common.c,v 1.20 2006/08/03 03:34:42 deraadt Exp $ */ +/* $OpenBSD: sftp-common.c,v 1.23 2010/01/15 09:24:23 markus Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2001 Damien Miller. All rights reserved. @@ -36,6 +36,9 @@ #include #include #include +#ifdef HAVE_UTIL_H +#include +#endif #include "xmalloc.h" #include "buffer.h" @@ -184,24 +187,23 @@ fx2txt(int status) * drwxr-xr-x 5 markus markus 1024 Jan 13 18:39 .ssh */ char * -ls_file(const char *name, const struct stat *st, int remote) +ls_file(const char *name, const struct stat *st, int remote, int si_units) { int ulen, glen, sz = 0; - struct passwd *pw; - struct group *gr; struct tm *ltime = localtime(&st->st_mtime); char *user, *group; char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1]; + char sbuf[FMT_SCALED_STRSIZE]; strmode(st->st_mode, mode); - if (!remote && (pw = getpwuid(st->st_uid)) != NULL) { - user = pw->pw_name; + if (!remote) { + user = user_from_uid(st->st_uid, 0); } else { snprintf(ubuf, sizeof ubuf, "%u", (u_int)st->st_uid); user = ubuf; } - if (!remote && (gr = getgrgid(st->st_gid)) != NULL) { - group = gr->gr_name; + if (!remote) { + group = group_from_gid(st->st_gid, 0); } else { snprintf(gbuf, sizeof gbuf, "%u", (u_int)st->st_gid); group = gbuf; @@ -216,8 +218,15 @@ ls_file(const char *name, const struct stat *st, int remote) tbuf[0] = '\0'; ulen = MAX(strlen(user), 8); glen = MAX(strlen(group), 8); - snprintf(buf, sizeof buf, "%s %3u %-*s %-*s %8llu %s %s", mode, - (u_int)st->st_nlink, ulen, user, glen, group, - (unsigned long long)st->st_size, tbuf, name); + if (si_units) { + fmt_scaled((long long)st->st_size, sbuf); + snprintf(buf, sizeof buf, "%s %3u %-*s %-*s %8s %s %s", mode, + (u_int)st->st_nlink, ulen, user, glen, group, + sbuf, tbuf, name); + } else { + snprintf(buf, sizeof buf, "%s %3u %-*s %-*s %8llu %s %s", mode, + (u_int)st->st_nlink, ulen, user, glen, group, + (unsigned long long)st->st_size, tbuf, name); + } return xstrdup(buf); } diff --git a/crypto/openssh/sftp-common.h b/crypto/openssh/sftp-common.h index 9b5848462a..9ed86c070d 100644 --- a/crypto/openssh/sftp-common.h +++ b/crypto/openssh/sftp-common.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-common.h,v 1.10 2006/08/03 03:34:42 deraadt Exp $ */ +/* $OpenBSD: sftp-common.h,v 1.11 2010/01/13 01:40:16 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -46,6 +46,6 @@ void stat_to_attrib(const struct stat *, Attrib *); void attrib_to_stat(const Attrib *, struct stat *); Attrib *decode_attrib(Buffer *); void encode_attrib(Buffer *, const Attrib *); -char *ls_file(const char *, const struct stat *, int); +char *ls_file(const char *, const struct stat *, int, int); const char *fx2txt(int); diff --git a/crypto/openssh/sftp-server.8 b/crypto/openssh/sftp-server.8 index 372b0770e9..bb19c15e1a 100644 --- a/crypto/openssh/sftp-server.8 +++ b/crypto/openssh/sftp-server.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: sftp-server.8,v 1.15 2009/03/26 08:38:39 sobrado Exp $ +.\" $OpenBSD: sftp-server.8,v 1.19 2010/01/09 03:36:00 jmc Exp $ .\" .\" Copyright (c) 2000 Markus Friedl. All rights reserved. .\" @@ -22,7 +22,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: March 26 2009 $ +.Dd $Mdocdate: January 9 2010 $ .Dt SFTP-SERVER 8 .Os .Sh NAME @@ -30,8 +30,10 @@ .Nd SFTP server subsystem .Sh SYNOPSIS .Nm sftp-server +.Op Fl ehR .Op Fl f Ar log_facility .Op Fl l Ar log_level +.Op Fl u Ar umask .Sh DESCRIPTION .Nm is a program that speaks the server side of SFTP protocol @@ -54,12 +56,20 @@ for more information. .Pp Valid options are: .Bl -tag -width Ds +.It Fl e +Causes +.Nm +to print logging information to stderr instead of syslog for debugging. .It Fl f Ar log_facility Specifies the facility code that is used when logging messages from .Nm . The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. The default is AUTH. +.It Fl h +Displays +.Nm +usage information. .It Fl l Ar log_level Specifies which messages will be logged by .Nm . @@ -71,6 +81,17 @@ performs on behalf of the client. DEBUG and DEBUG1 are equivalent. DEBUG2 and DEBUG3 each specify higher levels of debugging output. The default is ERROR. +.It Fl R +Places this instance of +.Nm +into a read-only mode. +Attempts to open files for writing, as well as other operations that change +the state of the filesystem, will be denied. +.It Fl u Ar umask +Sets an explicit +.Xr umask 2 +to be applied to newly-created files and directories, instead of the +user's default mask. .El .Pp For logging to work, diff --git a/crypto/openssh/sftp-server.c b/crypto/openssh/sftp-server.c index d984e60493..a98ac2b6dd 100644 --- a/crypto/openssh/sftp-server.c +++ b/crypto/openssh/sftp-server.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-server.c,v 1.85 2009/04/14 16:33:42 stevesk Exp $ */ +/* $OpenBSD: sftp-server.c,v 1.91 2010/01/13 01:40:16 djm Exp $ */ /* * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. * @@ -70,6 +70,9 @@ Buffer oqueue; /* Version of client */ int version; +/* Disable writes */ +int readonly; + /* portable attributes, etc. */ typedef struct Stat Stat; @@ -553,16 +556,21 @@ process_open(void) mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; logit("open \"%s\" flags %s mode 0%o", name, string_from_portable(pflags), mode); - fd = open(name, flags, mode); - if (fd < 0) { - status = errno_to_portable(errno); - } else { - handle = handle_new(HANDLE_FILE, name, fd, NULL); - if (handle < 0) { - close(fd); + if (readonly && + ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR)) + status = SSH2_FX_PERMISSION_DENIED; + else { + fd = open(name, flags, mode); + if (fd < 0) { + status = errno_to_portable(errno); } else { - send_handle(id, handle); - status = SSH2_FX_OK; + handle = handle_new(HANDLE_FILE, name, fd, NULL); + if (handle < 0) { + close(fd); + } else { + send_handle(id, handle); + status = SSH2_FX_OK; + } } } if (status != SSH2_FX_OK) @@ -632,7 +640,7 @@ process_write(void) u_int32_t id; u_int64_t off; u_int len; - int handle, fd, ret, status = SSH2_FX_FAILURE; + int handle, fd, ret, status; char *data; id = get_int(); @@ -643,7 +651,12 @@ process_write(void) debug("request %u: write \"%s\" (handle %d) off %llu len %d", id, handle_to_name(handle), handle, (unsigned long long)off, len); fd = handle_to_fd(handle); - if (fd >= 0) { + + if (fd < 0) + status = SSH2_FX_FAILURE; + else if (readonly) + status = SSH2_FX_PERMISSION_DENIED; + else { if (lseek(fd, off, SEEK_SET) < 0) { status = errno_to_portable(errno); error("process_write: seek failed"); @@ -658,6 +671,7 @@ process_write(void) handle_update_write(handle, ret); } else { debug2("nothing at all written"); + status = SSH2_FX_FAILURE; } } } @@ -754,6 +768,10 @@ process_setstat(void) name = get_string(NULL); a = get_attrib(); debug("request %u: setstat name \"%s\"", id, name); + if (readonly) { + status = SSH2_FX_PERMISSION_DENIED; + a->flags = 0; + } if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { logit("set \"%s\" size %llu", name, (unsigned long long)a->size); @@ -802,9 +820,11 @@ process_fsetstat(void) a = get_attrib(); debug("request %u: fsetstat handle %d", id, handle); fd = handle_to_fd(handle); - if (fd < 0) { + if (fd < 0) status = SSH2_FX_FAILURE; - } else { + else if (readonly) + status = SSH2_FX_PERMISSION_DENIED; + else { char *name = handle_to_name(handle); if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { @@ -920,7 +940,7 @@ process_readdir(void) continue; stat_to_attrib(&st, &(stats[count].attrib)); stats[count].name = xstrdup(dp->d_name); - stats[count].long_name = ls_file(dp->d_name, &st, 0); + stats[count].long_name = ls_file(dp->d_name, &st, 0, 0); count++; /* send up to 100 entries in one message */ /* XXX check packet size instead */ @@ -952,8 +972,12 @@ process_remove(void) name = get_string(NULL); debug3("request %u: remove", id); logit("remove name \"%s\"", name); - ret = unlink(name); - status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + if (readonly) + status = SSH2_FX_PERMISSION_DENIED; + else { + ret = unlink(name); + status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + } send_status(id, status); xfree(name); } @@ -973,8 +997,12 @@ process_mkdir(void) a->perm & 07777 : 0777; debug3("request %u: mkdir", id); logit("mkdir name \"%s\" mode 0%o", name, mode); - ret = mkdir(name, mode); - status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + if (readonly) + status = SSH2_FX_PERMISSION_DENIED; + else { + ret = mkdir(name, mode); + status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + } send_status(id, status); xfree(name); } @@ -990,8 +1018,12 @@ process_rmdir(void) name = get_string(NULL); debug3("request %u: rmdir", id); logit("rmdir name \"%s\"", name); - ret = rmdir(name); - status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + if (readonly) + status = SSH2_FX_PERMISSION_DENIED; + else { + ret = rmdir(name); + status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + } send_status(id, status); xfree(name); } @@ -1036,7 +1068,9 @@ process_rename(void) debug3("request %u: rename", id); logit("rename old \"%s\" new \"%s\"", oldpath, newpath); status = SSH2_FX_FAILURE; - if (lstat(oldpath, &sb) == -1) + if (readonly) + status = SSH2_FX_PERMISSION_DENIED; + else if (lstat(oldpath, &sb) == -1) status = errno_to_portable(errno); else if (S_ISREG(sb.st_mode)) { /* Race-free rename of regular files */ @@ -1120,8 +1154,12 @@ process_symlink(void) debug3("request %u: symlink", id); logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); /* this will fail if 'newpath' exists */ - ret = symlink(oldpath, newpath); - status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + if (readonly) + status = SSH2_FX_PERMISSION_DENIED; + else { + ret = symlink(oldpath, newpath); + status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + } send_status(id, status); xfree(oldpath); xfree(newpath); @@ -1131,15 +1169,19 @@ static void process_extended_posix_rename(u_int32_t id) { char *oldpath, *newpath; + int ret, status; oldpath = get_string(NULL); newpath = get_string(NULL); debug3("request %u: posix-rename", id); logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); - if (rename(oldpath, newpath) == -1) - send_status(id, errno_to_portable(errno)); - else - send_status(id, SSH2_FX_OK); + if (readonly) + status = SSH2_FX_PERMISSION_DENIED; + else { + ret = rename(oldpath, newpath); + status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + } + send_status(id, status); xfree(oldpath); xfree(newpath); } @@ -1322,7 +1364,8 @@ sftp_server_usage(void) extern char *__progname; fprintf(stderr, - "usage: %s [-he] [-l log_level] [-f log_facility]\n", __progname); + "usage: %s [-ehR] [-f log_facility] [-l log_level] [-u umask]\n", + __progname); exit(1); } @@ -1334,6 +1377,8 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) ssize_t len, olen, set_size; SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; char *cp, buf[4*4096]; + const char *errmsg; + mode_t mask; extern char *optarg; extern char *__progname; @@ -1341,8 +1386,11 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) __progname = ssh_get_progname(argv[0]); log_init(__progname, log_level, log_facility, log_stderr); - while (!skipargs && (ch = getopt(argc, argv, "f:l:che")) != -1) { + while (!skipargs && (ch = getopt(argc, argv, "f:l:u:cehR")) != -1) { switch (ch) { + case 'R': + readonly = 1; + break; case 'c': /* * Ignore all arguments if we are invoked as a @@ -1363,6 +1411,13 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) if (log_facility == SYSLOG_FACILITY_NOT_SET) error("Invalid log facility \"%s\"", optarg); break; + case 'u': + mask = (mode_t)strtonum(optarg, 0, 0777, &errmsg); + if (errmsg != NULL) + fatal("Invalid umask \"%s\": %s", + optarg, errmsg); + (void)umask(mask); + break; case 'h': default: sftp_server_usage(); @@ -1387,8 +1442,8 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) logit("session opened for local user %s from [%s]", pw->pw_name, client_addr); - in = dup(STDIN_FILENO); - out = dup(STDOUT_FILENO); + in = STDIN_FILENO; + out = STDOUT_FILENO; #ifdef HAVE_CYGWIN setmode(in, O_BINARY); diff --git a/crypto/openssh/sftp.1 b/crypto/openssh/sftp.1 index 37ccb3a38b..777b02a584 100644 --- a/crypto/openssh/sftp.1 +++ b/crypto/openssh/sftp.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: sftp.1,v 1.69 2008/12/09 15:35:00 sobrado Exp $ +.\" $OpenBSD: sftp.1,v 1.83 2010/02/08 10:50:20 markus Exp $ .\" .\" Copyright (c) 2001 Damien Miller. All rights reserved. .\" @@ -22,7 +22,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: December 9 2008 $ +.Dd $Mdocdate: February 8 2010 $ .Dt SFTP 1 .Os .Sh NAME @@ -31,12 +31,15 @@ .Sh SYNOPSIS .Nm sftp .Bk -words -.Op Fl 1Cv +.Op Fl 1246Cpqrv .Op Fl B Ar buffer_size .Op Fl b Ar batchfile +.Op Fl c Ar cipher +.Op Fl D Ar sftp_server_path .Op Fl F Ar ssh_config +.Op Fl i Ar identity_file .Op Fl o Ar ssh_option -.Op Fl P Ar sftp_server_path +.Op Fl P Ar port .Op Fl R Ar num_requests .Op Fl S Ar program .Op Fl s Ar subsystem | sftp_server @@ -87,6 +90,16 @@ The options are as follows: .Bl -tag -width Ds .It Fl 1 Specify the use of protocol version 1. +.It Fl 2 +Specify the use of protocol version 2. +.It Fl 4 +Forces +.Nm +to use IPv4 addresses only. +.It Fl 6 +Forces +.Nm +to use IPv6 addresses only. .It Fl B Ar buffer_size Specify the size of the buffer that .Nm @@ -124,12 +137,26 @@ character (for example, Enables compression (via ssh's .Fl C flag). +.It Fl c Ar cipher +Selects the cipher to use for encrypting the data transfers. +This option is directly passed to +.Xr ssh 1 . +.It Fl D Ar sftp_server_path +Connect directly to a local sftp server +(rather than via +.Xr ssh 1 ) . +This option may be useful in debugging the client and server. .It Fl F Ar ssh_config Specifies an alternative per-user configuration file for .Xr ssh 1 . 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 public key +authentication is read. +This option is directly passed to +.Xr ssh 1 . .It Fl o Ar ssh_option Can be used to pass options to .Nm ssh @@ -175,6 +202,7 @@ For full details of the options listed below, and their possible values, see .It NoHostAuthenticationForLocalhost .It NumberOfPasswordPrompts .It PasswordAuthentication +.It PKCS11Provider .It Port .It PreferredAuthentications .It Protocol @@ -186,7 +214,6 @@ For full details of the options listed below, and their possible values, see .It SendEnv .It ServerAliveInterval .It ServerAliveCountMax -.It SmartcardDevice .It StrictHostKeyChecking .It TCPKeepAlive .It UsePrivilegedPort @@ -194,16 +221,25 @@ For full details of the options listed below, and their possible values, see .It UserKnownHostsFile .It VerifyHostKeyDNS .El -.It Fl P Ar sftp_server_path -Connect directly to a local sftp server -(rather than via -.Xr ssh 1 ) . -This option may be useful in debugging the client and server. +.It Fl P Ar port +Specifies the port to connect to on the remote host. +.It Fl p +Preserves modification times, access times, and modes from the +original files transferred. +.It Fl q +Quiet mode: disables the progress meter as well as warning and +diagnostic messages from +.Xr ssh 1 . .It Fl R Ar num_requests Specify how many requests may be outstanding at any one time. Increasing this may slightly improve file transfer speed but will increase memory usage. The default is 64 outstanding requests. +.It Fl r +Recursively copy entire directories when uploading and downloading. +Note that +.Nm +does not follow symbolic links encountered in the tree traversal. .It Fl S Ar program Name of the .Ar program @@ -294,7 +330,7 @@ extension. Quit .Nm sftp . .It Xo Ic get -.Op Fl P +.Op Fl Ppr .Ar remote-path .Op Ar local-path .Xc @@ -313,10 +349,20 @@ If it does and is specified, then .Ar local-path must specify a directory. -If the +.Pp +If either the .Fl P +or +.Fl p flag is specified, then full file permissions and access times are copied too. +.Pp +If the +.Fl r +flag is specified then directories will be copied recursively. +Note that +.Nm +does not follow symbolic links when performing recursive transfers. .It Ic help Display help text. .It Ic lcd Ar path @@ -347,7 +393,7 @@ to .It Ic lpwd Print local working directory. .It Xo Ic ls -.Op Fl 1aflnrSt +.Op Fl 1afhlnrSt .Op Ar path .Xc Display a remote directory listing of either @@ -372,6 +418,11 @@ List files beginning with a dot .It Fl f Do not sort the listing. The default sort order is lexicographical. +.It Fl h +When used with a long format option, use unit suffixes: Byte, Kilobyte, +Megabyte, Gigabyte, Terabyte, Petabyte, and Exabyte in order to reduce +the number of digits to four or fewer using powers of 2 for sizes (K=1024, +M=1048576, etc.). .It Fl l Display additional details including permissions and ownership information. @@ -394,7 +445,7 @@ Create remote directory specified by .It Ic progress Toggle display of progress meter. .It Xo Ic put -.Op Fl P +.Op Fl Ppr .Ar local-path .Op Ar remote-path .Xc @@ -412,10 +463,20 @@ If it does and is specified, then .Ar remote-path must specify a directory. -If the +.Pp +If ether the .Fl P -flag is specified, then the file's full permission and access time are +or +.Fl p +flag is specified, then full file permissions and access times are copied too. +.Pp +If the +.Fl r +flag is specified then directories will be copied recursively. +Note that +.Nm +does not follow symbolic links when performing recursive transfers. .It Ic pwd Display remote working directory. .It Ic quit diff --git a/crypto/openssh/sftp.c b/crypto/openssh/sftp.c index 66bd111b12..229f12987e 100644 --- a/crypto/openssh/sftp.c +++ b/crypto/openssh/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.107 2009/02/02 11:15:14 dtucker Exp $ */ +/* $OpenBSD: sftp.c,v 1.125 2010/06/18 00:58:39 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -35,6 +35,9 @@ #ifdef HAVE_PATHS_H # include #endif +#ifdef HAVE_LIBGEN_H +#include +#endif #ifdef USE_LIBEDIT #include #else @@ -65,30 +68,39 @@ typedef void EditLine; #include "sftp-common.h" #include "sftp-client.h" +#define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */ +#define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */ + /* File to read commands from */ FILE* infile; /* Are we in batchfile mode? */ int batchmode = 0; -/* Size of buffer used when copying files */ -size_t copy_buffer_len = 32768; - -/* Number of concurrent outstanding requests */ -size_t num_requests = 64; - /* PID of ssh transport process */ static pid_t sshpid = -1; /* This is set to 0 if the progressmeter is not desired. */ int showprogress = 1; +/* When this option is set, we always recursively download/upload directories */ +int global_rflag = 0; + +/* When this option is set, the file transfers will always preserve times */ +int global_pflag = 0; + /* SIGINT received during command processing */ volatile sig_atomic_t interrupted = 0; /* I wish qsort() took a separate ctx for the comparison function...*/ int sort_flag; +/* Context used for commandline completion */ +struct complete_ctx { + struct sftp_conn *conn; + char **remote_pathp; +}; + int remote_glob(struct sftp_conn *, const char *, int, int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ @@ -98,16 +110,17 @@ extern char *__progname; #define WHITESPACE " \t\r\n" /* ls flags */ -#define LS_LONG_VIEW 0x01 /* Full view ala ls -l */ -#define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */ -#define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */ -#define LS_NAME_SORT 0x08 /* Sort by name (default) */ -#define LS_TIME_SORT 0x10 /* Sort by mtime */ -#define LS_SIZE_SORT 0x20 /* Sort by file size */ -#define LS_REVERSE_SORT 0x40 /* Reverse sort order */ -#define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */ - -#define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW) +#define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */ +#define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */ +#define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */ +#define LS_NAME_SORT 0x0008 /* Sort by name (default) */ +#define LS_TIME_SORT 0x0010 /* Sort by mtime */ +#define LS_SIZE_SORT 0x0020 /* Sort by file size */ +#define LS_REVERSE_SORT 0x0040 /* Reverse sort order */ +#define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */ +#define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */ + +#define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS) #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT) /* Commands for interactive mode */ @@ -139,46 +152,52 @@ extern char *__progname; struct CMD { const char *c; const int n; + const int t; }; +/* Type of completion */ +#define NOARGS 0 +#define REMOTE 1 +#define LOCAL 2 + static const struct CMD cmds[] = { - { "bye", I_QUIT }, - { "cd", I_CHDIR }, - { "chdir", I_CHDIR }, - { "chgrp", I_CHGRP }, - { "chmod", I_CHMOD }, - { "chown", I_CHOWN }, - { "df", I_DF }, - { "dir", I_LS }, - { "exit", I_QUIT }, - { "get", I_GET }, - { "mget", I_GET }, - { "help", I_HELP }, - { "lcd", I_LCHDIR }, - { "lchdir", I_LCHDIR }, - { "lls", I_LLS }, - { "lmkdir", I_LMKDIR }, - { "ln", I_SYMLINK }, - { "lpwd", I_LPWD }, - { "ls", I_LS }, - { "lumask", I_LUMASK }, - { "mkdir", I_MKDIR }, - { "progress", I_PROGRESS }, - { "put", I_PUT }, - { "mput", I_PUT }, - { "pwd", I_PWD }, - { "quit", I_QUIT }, - { "rename", I_RENAME }, - { "rm", I_RM }, - { "rmdir", I_RMDIR }, - { "symlink", I_SYMLINK }, - { "version", I_VERSION }, - { "!", I_SHELL }, - { "?", I_HELP }, - { NULL, -1} + { "bye", I_QUIT, NOARGS }, + { "cd", I_CHDIR, REMOTE }, + { "chdir", I_CHDIR, REMOTE }, + { "chgrp", I_CHGRP, REMOTE }, + { "chmod", I_CHMOD, REMOTE }, + { "chown", I_CHOWN, REMOTE }, + { "df", I_DF, REMOTE }, + { "dir", I_LS, REMOTE }, + { "exit", I_QUIT, NOARGS }, + { "get", I_GET, REMOTE }, + { "help", I_HELP, NOARGS }, + { "lcd", I_LCHDIR, LOCAL }, + { "lchdir", I_LCHDIR, LOCAL }, + { "lls", I_LLS, LOCAL }, + { "lmkdir", I_LMKDIR, LOCAL }, + { "ln", I_SYMLINK, REMOTE }, + { "lpwd", I_LPWD, LOCAL }, + { "ls", I_LS, REMOTE }, + { "lumask", I_LUMASK, NOARGS }, + { "mkdir", I_MKDIR, REMOTE }, + { "mget", I_GET, REMOTE }, + { "mput", I_PUT, LOCAL }, + { "progress", I_PROGRESS, NOARGS }, + { "put", I_PUT, LOCAL }, + { "pwd", I_PWD, REMOTE }, + { "quit", I_QUIT, NOARGS }, + { "rename", I_RENAME, REMOTE }, + { "rm", I_RM, REMOTE }, + { "rmdir", I_RMDIR, REMOTE }, + { "symlink", I_SYMLINK, REMOTE }, + { "version", I_VERSION, NOARGS }, + { "!", I_SHELL, NOARGS }, + { "?", I_HELP, NOARGS }, + { NULL, -1, -1 } }; -int interactive_loop(int fd_in, int fd_out, char *file1, char *file2); +int interactive_loop(struct sftp_conn *, char *file1, char *file2); /* ARGSUSED */ static void @@ -216,18 +235,18 @@ help(void) "df [-hi] [path] Display statistics for current directory or\n" " filesystem containing 'path'\n" "exit Quit sftp\n" - "get [-P] remote-path [local-path] Download file\n" + "get [-Ppr] remote [local] Download file\n" "help Display this help text\n" "lcd path Change local directory to 'path'\n" "lls [ls-options [path]] Display local directory listing\n" "lmkdir path Create local directory\n" "ln oldpath newpath Symlink remote file\n" "lpwd Print local working directory\n" - "ls [-1aflnrSt] [path] Display remote directory listing\n" + "ls [-1afhlnrSt] [path] Display remote directory listing\n" "lumask umask Set local umask to 'umask'\n" "mkdir path Create remote directory\n" "progress Toggle display of progress meter\n" - "put [-P] local-path [remote-path] Upload file\n" + "put [-Ppr] local [remote] Upload file\n" "pwd Display remote working directory\n" "quit Quit sftp\n" "rename oldpath newpath Rename remote file\n" @@ -313,21 +332,6 @@ path_strip(char *path, char *strip) return (xstrdup(path)); } -static char * -path_append(char *p1, char *p2) -{ - char *ret; - size_t len = strlen(p1) + strlen(p2) + 2; - - ret = xmalloc(len); - strlcpy(ret, p1, len); - if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/') - strlcat(ret, "/", len); - strlcat(ret, p2, len); - - return(ret); -} - static char * make_absolute(char *p, char *pwd) { @@ -343,27 +347,8 @@ make_absolute(char *p, char *pwd) } static int -infer_path(const char *p, char **ifp) -{ - char *cp; - - cp = strrchr(p, '/'); - if (cp == NULL) { - *ifp = xstrdup(p); - return(0); - } - - if (!cp[1]) { - error("Invalid path"); - return(-1); - } - - *ifp = xstrdup(cp + 1); - return(0); -} - -static int -parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag) +parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag, + int *rflag) { extern int opterr, optind, optopt, optreset; int ch; @@ -371,13 +356,17 @@ parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag) optind = optreset = 1; opterr = 0; - *pflag = 0; - while ((ch = getopt(argc, argv, "Pp")) != -1) { + *rflag = *pflag = 0; + while ((ch = getopt(argc, argv, "PpRr")) != -1) { switch (ch) { case 'p': case 'P': *pflag = 1; break; + case 'r': + case 'R': + *rflag = 1; + break; default: error("%s: Invalid flag -%c", cmd, optopt); return -1; @@ -397,7 +386,7 @@ parse_ls_flags(char **argv, int argc, int *lflag) opterr = 0; *lflag = LS_NAME_SORT; - while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) { + while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) { switch (ch) { case '1': *lflag &= ~VIEW_FLAGS; @@ -413,12 +402,15 @@ parse_ls_flags(char **argv, int argc, int *lflag) case 'f': *lflag &= ~SORT_FLAGS; break; + case 'h': + *lflag |= LS_SI_UNITS; + break; case 'l': - *lflag &= ~VIEW_FLAGS; + *lflag &= ~LS_SHORT_VIEW; *lflag |= LS_LONG_VIEW; break; case 'n': - *lflag &= ~VIEW_FLAGS; + *lflag &= ~LS_SHORT_VIEW; *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; break; case 'r': @@ -489,62 +481,79 @@ remote_is_dir(struct sftp_conn *conn, char *path) return(S_ISDIR(a->perm)); } +/* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */ +static int +pathname_is_dir(char *pathname) +{ + size_t l = strlen(pathname); + + return l > 0 && pathname[l - 1] == '/'; +} + static int -process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) +process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, + int pflag, int rflag) { char *abs_src = NULL; char *abs_dst = NULL; - char *tmp; glob_t g; - int err = 0; - int i; + char *filename, *tmp=NULL; + int i, err = 0; abs_src = xstrdup(src); abs_src = make_absolute(abs_src, pwd); - memset(&g, 0, sizeof(g)); + debug3("Looking up %s", abs_src); - if (remote_glob(conn, abs_src, 0, NULL, &g)) { + if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) { error("File \"%s\" not found.", abs_src); err = -1; goto out; } - /* If multiple matches, dst must be a directory or unspecified */ - if (g.gl_matchc > 1 && dst && !is_dir(dst)) { - error("Multiple files match, but \"%s\" is not a directory", - dst); + /* + * If multiple matches then dst must be a directory or + * unspecified. + */ + if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) { + error("Multiple source paths, but destination " + "\"%s\" is not a directory", dst); err = -1; goto out; } for (i = 0; g.gl_pathv[i] && !interrupted; i++) { - if (infer_path(g.gl_pathv[i], &tmp)) { + tmp = xstrdup(g.gl_pathv[i]); + if ((filename = basename(tmp)) == NULL) { + error("basename %s: %s", tmp, strerror(errno)); + xfree(tmp); err = -1; goto out; } if (g.gl_matchc == 1 && dst) { - /* If directory specified, append filename */ - xfree(tmp); if (is_dir(dst)) { - if (infer_path(g.gl_pathv[0], &tmp)) { - err = 1; - goto out; - } - abs_dst = path_append(dst, tmp); - xfree(tmp); - } else + abs_dst = path_append(dst, filename); + } else { abs_dst = xstrdup(dst); + } } else if (dst) { - abs_dst = path_append(dst, tmp); - xfree(tmp); - } else - abs_dst = tmp; + abs_dst = path_append(dst, filename); + } else { + abs_dst = xstrdup(filename); + } + xfree(tmp); printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); - if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1) - err = -1; + if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { + if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, + pflag || global_pflag, 1) == -1) + err = -1; + } else { + if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, + pflag || global_pflag) == -1) + err = -1; + } xfree(abs_dst); abs_dst = NULL; } @@ -556,14 +565,15 @@ out: } static int -process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) +process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, + int pflag, int rflag) { char *tmp_dst = NULL; char *abs_dst = NULL; - char *tmp; + char *tmp = NULL, *filename = NULL; glob_t g; int err = 0; - int i; + int i, dst_is_dir = 1; struct stat sb; if (dst) { @@ -573,16 +583,20 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) memset(&g, 0, sizeof(g)); debug3("Looking up %s", src); - if (glob(src, GLOB_NOCHECK, NULL, &g)) { + if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) { error("File \"%s\" not found.", src); err = -1; goto out; } + /* If we aren't fetching to pwd then stash this status for later */ + if (tmp_dst != NULL) + dst_is_dir = remote_is_dir(conn, tmp_dst); + /* If multiple matches, dst may be directory or unspecified */ - if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) { - error("Multiple files match, but \"%s\" is not a directory", - tmp_dst); + if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) { + error("Multiple paths match, but destination " + "\"%s\" is not a directory", tmp_dst); err = -1; goto out; } @@ -593,38 +607,38 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) error("stat %s: %s", g.gl_pathv[i], strerror(errno)); continue; } - - if (!S_ISREG(sb.st_mode)) { - error("skipping non-regular file %s", - g.gl_pathv[i]); - continue; - } - if (infer_path(g.gl_pathv[i], &tmp)) { + + tmp = xstrdup(g.gl_pathv[i]); + if ((filename = basename(tmp)) == NULL) { + error("basename %s: %s", tmp, strerror(errno)); + xfree(tmp); err = -1; goto out; } if (g.gl_matchc == 1 && tmp_dst) { /* If directory specified, append filename */ - if (remote_is_dir(conn, tmp_dst)) { - if (infer_path(g.gl_pathv[0], &tmp)) { - err = 1; - goto out; - } - abs_dst = path_append(tmp_dst, tmp); - xfree(tmp); - } else + if (dst_is_dir) + abs_dst = path_append(tmp_dst, filename); + else abs_dst = xstrdup(tmp_dst); - } else if (tmp_dst) { - abs_dst = path_append(tmp_dst, tmp); - xfree(tmp); - } else - abs_dst = make_absolute(tmp, pwd); + abs_dst = path_append(tmp_dst, filename); + } else { + abs_dst = make_absolute(xstrdup(filename), pwd); + } + xfree(tmp); printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); - if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1) - err = -1; + if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { + if (upload_dir(conn, g.gl_pathv[i], abs_dst, + pflag || global_pflag, 1) == -1) + err = -1; + } else { + if (do_upload(conn, g.gl_pathv[i], abs_dst, + pflag || global_pflag) == -1) + err = -1; + } } out: @@ -708,13 +722,14 @@ do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag) xfree(tmp); if (lflag & LS_LONG_VIEW) { - if (lflag & LS_NUMERIC_VIEW) { + if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) { char *lname; struct stat sb; memset(&sb, 0, sizeof(sb)); attrib_to_stat(&d[n]->a, &sb); - lname = ls_file(fname, &sb, 1); + lname = ls_file(fname, &sb, 1, + (lflag & LS_SI_UNITS)); printf("%s\n", lname); xfree(lname); } else @@ -816,7 +831,7 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, a = do_lstat(conn, g.gl_pathv[i], 1); if (a != NULL) attrib_to_stat(a, &sb); - lname = ls_file(fname, &sb, 1); + lname = ls_file(fname, &sb, 1, (lflag & LS_SI_UNITS)); printf("%s\n", lname); xfree(lname); } else { @@ -848,19 +863,19 @@ do_df(struct sftp_conn *conn, char *path, int hflag, int iflag) char s_avail[FMT_SCALED_STRSIZE]; char s_root[FMT_SCALED_STRSIZE]; char s_total[FMT_SCALED_STRSIZE]; + unsigned long long ffree; if (do_statvfs(conn, path, &st, 1) == -1) return -1; if (iflag) { + ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0; printf(" Inodes Used Avail " "(root) %%Capacity\n"); printf("%11llu %11llu %11llu %11llu %3llu%%\n", (unsigned long long)st.f_files, (unsigned long long)(st.f_files - st.f_ffree), (unsigned long long)st.f_favail, - (unsigned long long)st.f_ffree, - (unsigned long long)(100 * (st.f_files - st.f_ffree) / - st.f_files)); + (unsigned long long)st.f_ffree, ffree); } else if (hflag) { strlcpy(s_used, "error", sizeof(s_used)); strlcpy(s_avail, "error", sizeof(s_avail)); @@ -934,12 +949,23 @@ undo_glob_escape(char *s) * Split a string into an argument vector using sh(1)-style quoting, * comment and escaping rules, but with some tweaks to handle glob(3) * wildcards. + * The "sloppy" flag allows for recovery from missing terminating quote, for + * use in parsing incomplete commandlines during tab autocompletion. + * * Returns NULL on error or a NULL-terminated array of arguments. + * + * If "lastquote" is not NULL, the quoting character used for the last + * argument is placed in *lastquote ("\0", "'" or "\""). + * + * If "terminated" is not NULL, *terminated will be set to 1 when the + * last argument's quote has been properly terminated or 0 otherwise. + * This parameter is only of use if "sloppy" is set. */ #define MAXARGS 128 #define MAXARGLEN 8192 static char ** -makeargv(const char *arg, int *argcp) +makeargv(const char *arg, int *argcp, int sloppy, char *lastquote, + u_int *terminated) { int argc, quot; size_t i, j; @@ -953,6 +979,10 @@ makeargv(const char *arg, int *argcp) error("string too long"); return NULL; } + if (terminated != NULL) + *terminated = 1; + if (lastquote != NULL) + *lastquote = '\0'; state = MA_START; i = j = 0; for (;;) { @@ -969,6 +999,8 @@ makeargv(const char *arg, int *argcp) if (state == MA_START) { argv[argc] = argvs + j; state = q; + if (lastquote != NULL) + *lastquote = arg[i]; } else if (state == MA_UNQUOTED) state = q; else if (state == q) @@ -1005,6 +1037,8 @@ makeargv(const char *arg, int *argcp) if (state == MA_START) { argv[argc] = argvs + j; state = MA_UNQUOTED; + if (lastquote != NULL) + *lastquote = '\0'; } if (arg[i + 1] == '?' || arg[i + 1] == '[' || arg[i + 1] == '*' || arg[i + 1] == '\\') { @@ -1030,6 +1064,12 @@ makeargv(const char *arg, int *argcp) goto string_done; } else if (arg[i] == '\0') { if (state == MA_SQUOTE || state == MA_DQUOTE) { + if (sloppy) { + state = MA_UNQUOTED; + if (terminated != NULL) + *terminated = 0; + goto string_done; + } error("Unterminated quoted argument"); return NULL; } @@ -1043,6 +1083,8 @@ makeargv(const char *arg, int *argcp) if (state == MA_START) { argv[argc] = argvs + j; state = MA_UNQUOTED; + if (lastquote != NULL) + *lastquote = '\0'; } if ((state == MA_SQUOTE || state == MA_DQUOTE) && (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) { @@ -1065,8 +1107,8 @@ makeargv(const char *arg, int *argcp) } static int -parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag, - unsigned long *n_arg, char **path1, char **path2) +parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, + int *hflag, unsigned long *n_arg, char **path1, char **path2) { const char *cmd, *cp = *cpp; char *cp2, **argv; @@ -1077,18 +1119,19 @@ parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag, /* Skip leading whitespace */ cp = cp + strspn(cp, WHITESPACE); - /* Ignore blank lines and lines which begin with comment '#' char */ - if (*cp == '\0' || *cp == '#') - return (0); - /* Check for leading '-' (disable error processing) */ *iflag = 0; if (*cp == '-') { *iflag = 1; cp++; + cp = cp + strspn(cp, WHITESPACE); } - if ((argv = makeargv(cp, &argc)) == NULL) + /* Ignore blank lines and lines which begin with comment '#' char */ + if (*cp == '\0' || *cp == '#') + return (0); + + if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL) return -1; /* Figure out which command we have */ @@ -1109,13 +1152,13 @@ parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag, } /* Get arguments and parse flags */ - *lflag = *pflag = *hflag = *n_arg = 0; + *lflag = *pflag = *rflag = *hflag = *n_arg = 0; *path1 = *path2 = NULL; optidx = 1; switch (cmdnum) { case I_GET: case I_PUT: - if ((optidx = parse_getput_flags(cmd, argv, argc, pflag)) == -1) + if ((optidx = parse_getput_flags(cmd, argv, argc, pflag, rflag)) == -1) return -1; /* Get first pathname (mandatory) */ if (argc - optidx < 1) { @@ -1235,7 +1278,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, int err_abort) { char *path1, *path2, *tmp; - int pflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i; + int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i; unsigned long n_arg = 0; Attrib a, *aa; char path_buf[MAXPATHLEN]; @@ -1243,7 +1286,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, glob_t g; path1 = path2 = NULL; - cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &hflag, &n_arg, + cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg, &path1, &path2); if (iflag != 0) @@ -1261,10 +1304,10 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, err = -1; break; case I_GET: - err = process_get(conn, path1, path2, *pwd, pflag); + err = process_get(conn, path1, path2, *pwd, pflag, rflag); break; case I_PUT: - err = process_put(conn, path1, path2, *pwd, pflag); + err = process_put(conn, path1, path2, *pwd, pflag, rflag); break; case I_RENAME: path1 = make_absolute(path1, *pwd); @@ -1290,7 +1333,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, attrib_clear(&a); a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; a.perm = 0777; - err = do_mkdir(conn, path1, &a); + err = do_mkdir(conn, path1, &a, 1); break; case I_RMDIR: path1 = make_absolute(path1, *pwd); @@ -1325,7 +1368,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, break; case I_LS: if (!path1) { - do_globbed_ls(conn, *pwd, *pwd, lflag); + do_ls_dir(conn, *pwd, *pwd, lflag); break; } @@ -1468,21 +1511,352 @@ prompt(EditLine *el) { return ("sftp> "); } -#endif + +/* Display entries in 'list' after skipping the first 'len' chars */ +static void +complete_display(char **list, u_int len) +{ + u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen; + struct winsize ws; + char *tmp; + + /* Count entries for sort and find longest */ + for (y = 0; list[y]; y++) + m = MAX(m, strlen(list[y])); + + if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) + width = ws.ws_col; + + m = m > len ? m - len : 0; + columns = width / (m + 2); + columns = MAX(columns, 1); + colspace = width / columns; + colspace = MIN(colspace, width); + + printf("\n"); + m = 1; + for (y = 0; list[y]; y++) { + llen = strlen(list[y]); + tmp = llen > len ? list[y] + len : ""; + printf("%-*s", colspace, tmp); + if (m >= columns) { + printf("\n"); + m = 1; + } else + m++; + } + printf("\n"); +} + +/* + * Given a "list" of words that begin with a common prefix of "word", + * attempt to find an autocompletion to extends "word" by the next + * characters common to all entries in "list". + */ +static char * +complete_ambiguous(const char *word, char **list, size_t count) +{ + if (word == NULL) + return NULL; + + if (count > 0) { + u_int y, matchlen = strlen(list[0]); + + /* Find length of common stem */ + for (y = 1; list[y]; y++) { + u_int x; + + for (x = 0; x < matchlen; x++) + if (list[0][x] != list[y][x]) + break; + + matchlen = x; + } + + if (matchlen > strlen(word)) { + char *tmp = xstrdup(list[0]); + + tmp[matchlen] = '\0'; + return tmp; + } + } + + return xstrdup(word); +} + +/* Autocomplete a sftp command */ +static int +complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote, + int terminated) +{ + u_int y, count = 0, cmdlen, tmplen; + char *tmp, **list, argterm[3]; + const LineInfo *lf; + + list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *)); + + /* No command specified: display all available commands */ + if (cmd == NULL) { + for (y = 0; cmds[y].c; y++) + list[count++] = xstrdup(cmds[y].c); + + list[count] = NULL; + complete_display(list, 0); + + for (y = 0; list[y] != NULL; y++) + xfree(list[y]); + xfree(list); + return count; + } + + /* Prepare subset of commands that start with "cmd" */ + cmdlen = strlen(cmd); + for (y = 0; cmds[y].c; y++) { + if (!strncasecmp(cmd, cmds[y].c, cmdlen)) + list[count++] = xstrdup(cmds[y].c); + } + list[count] = NULL; + + if (count == 0) + return 0; + + /* Complete ambigious command */ + tmp = complete_ambiguous(cmd, list, count); + if (count > 1) + complete_display(list, 0); + + for (y = 0; list[y]; y++) + xfree(list[y]); + xfree(list); + + if (tmp != NULL) { + tmplen = strlen(tmp); + cmdlen = strlen(cmd); + /* If cmd may be extended then do so */ + if (tmplen > cmdlen) + if (el_insertstr(el, tmp + cmdlen) == -1) + fatal("el_insertstr failed."); + lf = el_line(el); + /* Terminate argument cleanly */ + if (count == 1) { + y = 0; + if (!terminated) + argterm[y++] = quote; + if (lastarg || *(lf->cursor) != ' ') + argterm[y++] = ' '; + argterm[y] = '\0'; + if (y > 0 && el_insertstr(el, argterm) == -1) + fatal("el_insertstr failed."); + } + xfree(tmp); + } + + return count; +} + +/* + * Determine whether a particular sftp command's arguments (if any) + * represent local or remote files. + */ +static int +complete_is_remote(char *cmd) { + int i; + + if (cmd == NULL) + return -1; + + for (i = 0; cmds[i].c; i++) { + if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) + return cmds[i].t; + } + + return -1; +} + +/* Autocomplete a filename "file" */ +static int +complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path, + char *file, int remote, int lastarg, char quote, int terminated) +{ + glob_t g; + char *tmp, *tmp2, ins[3]; + u_int i, hadglob, pwdlen, len, tmplen, filelen; + const LineInfo *lf; + + /* Glob from "file" location */ + if (file == NULL) + tmp = xstrdup("*"); + else + xasprintf(&tmp, "%s*", file); + + memset(&g, 0, sizeof(g)); + if (remote != LOCAL) { + tmp = make_absolute(tmp, remote_path); + remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); + } else + glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); + + /* Determine length of pwd so we can trim completion display */ + for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) { + /* Terminate counting on first unescaped glob metacharacter */ + if (tmp[tmplen] == '*' || tmp[tmplen] == '?') { + if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0') + hadglob = 1; + break; + } + if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0') + tmplen++; + if (tmp[tmplen] == '/') + pwdlen = tmplen + 1; /* track last seen '/' */ + } + xfree(tmp); + + if (g.gl_matchc == 0) + goto out; + + if (g.gl_matchc > 1) + complete_display(g.gl_pathv, pwdlen); + + tmp = NULL; + /* Don't try to extend globs */ + if (file == NULL || hadglob) + goto out; + + tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc); + tmp = path_strip(tmp2, remote_path); + xfree(tmp2); + + if (tmp == NULL) + goto out; + + tmplen = strlen(tmp); + filelen = strlen(file); + + if (tmplen > filelen) { + tmp2 = tmp + filelen; + len = strlen(tmp2); + /* quote argument on way out */ + for (i = 0; i < len; i++) { + ins[0] = '\\'; + ins[1] = tmp2[i]; + ins[2] = '\0'; + switch (tmp2[i]) { + case '\'': + case '"': + case '\\': + case '\t': + case ' ': + if (quote == '\0' || tmp2[i] == quote) { + if (el_insertstr(el, ins) == -1) + fatal("el_insertstr " + "failed."); + break; + } + /* FALLTHROUGH */ + default: + if (el_insertstr(el, ins + 1) == -1) + fatal("el_insertstr failed."); + break; + } + } + } + + lf = el_line(el); + if (g.gl_matchc == 1) { + i = 0; + if (!terminated) + ins[i++] = quote; + if (*(lf->cursor - 1) != '/' && + (lastarg || *(lf->cursor) != ' ')) + ins[i++] = ' '; + ins[i] = '\0'; + if (i > 0 && el_insertstr(el, ins) == -1) + fatal("el_insertstr failed."); + } + xfree(tmp); + + out: + globfree(&g); + return g.gl_matchc; +} + +/* tab-completion hook function, called via libedit */ +static unsigned char +complete(EditLine *el, int ch) +{ + char **argv, *line, quote; + u_int argc, carg, cursor, len, terminated, ret = CC_ERROR; + const LineInfo *lf; + struct complete_ctx *complete_ctx; + + lf = el_line(el); + if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0) + fatal("%s: el_get failed", __func__); + + /* Figure out which argument the cursor points to */ + cursor = lf->cursor - lf->buffer; + line = (char *)xmalloc(cursor + 1); + memcpy(line, lf->buffer, cursor); + line[cursor] = '\0'; + argv = makeargv(line, &carg, 1, "e, &terminated); + xfree(line); + + /* Get all the arguments on the line */ + len = lf->lastchar - lf->buffer; + line = (char *)xmalloc(len + 1); + memcpy(line, lf->buffer, len); + line[len] = '\0'; + argv = makeargv(line, &argc, 1, NULL, NULL); + + /* Ensure cursor is at EOL or a argument boundary */ + if (line[cursor] != ' ' && line[cursor] != '\0' && + line[cursor] != '\n') { + xfree(line); + return ret; + } + + if (carg == 0) { + /* Show all available commands */ + complete_cmd_parse(el, NULL, argc == carg, '\0', 1); + ret = CC_REDISPLAY; + } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') { + /* Handle the command parsing */ + if (complete_cmd_parse(el, argv[0], argc == carg, + quote, terminated) != 0) + ret = CC_REDISPLAY; + } else if (carg >= 1) { + /* Handle file parsing */ + int remote = complete_is_remote(argv[0]); + char *filematch = NULL; + + if (carg > 1 && line[cursor-1] != ' ') + filematch = argv[carg - 1]; + + if (remote != 0 && + complete_match(el, complete_ctx->conn, + *complete_ctx->remote_pathp, filematch, + remote, carg == argc, quote, terminated) != 0) + ret = CC_REDISPLAY; + } + + xfree(line); + return ret; +} +#endif /* USE_LIBEDIT */ int -interactive_loop(int fd_in, int fd_out, char *file1, char *file2) +interactive_loop(struct sftp_conn *conn, char *file1, char *file2) { - char *pwd; + char *remote_path; char *dir = NULL; char cmd[2048]; - struct sftp_conn *conn; int err, interactive; EditLine *el = NULL; #ifdef USE_LIBEDIT History *hl = NULL; HistEvent hev; extern char *__progname; + struct complete_ctx complete_ctx; if (!batchmode && isatty(STDIN_FILENO)) { if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) @@ -1497,27 +1871,32 @@ interactive_loop(int fd_in, int fd_out, char *file1, char *file2) el_set(el, EL_TERMINAL, NULL); el_set(el, EL_SIGNAL, 1); el_source(el, NULL); + + /* Tab Completion */ + el_set(el, EL_ADDFN, "ftp-complete", + "Context senstive argument completion", complete); + complete_ctx.conn = conn; + complete_ctx.remote_pathp = &remote_path; + el_set(el, EL_CLIENTDATA, (void*)&complete_ctx); + el_set(el, EL_BIND, "^I", "ftp-complete", NULL); } #endif /* USE_LIBEDIT */ - conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests); - if (conn == NULL) - fatal("Couldn't initialise connection to server"); - - pwd = do_realpath(conn, "."); - if (pwd == NULL) + remote_path = do_realpath(conn, "."); + if (remote_path == NULL) fatal("Need cwd"); if (file1 != NULL) { dir = xstrdup(file1); - dir = make_absolute(dir, pwd); + dir = make_absolute(dir, remote_path); if (remote_is_dir(conn, dir) && file2 == NULL) { printf("Changing to: %s\n", dir); snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); - if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) { + if (parse_dispatch_command(conn, cmd, + &remote_path, 1) != 0) { xfree(dir); - xfree(pwd); + xfree(remote_path); xfree(conn); return (-1); } @@ -1528,9 +1907,10 @@ interactive_loop(int fd_in, int fd_out, char *file1, char *file2) snprintf(cmd, sizeof cmd, "get %s %s", dir, file2); - err = parse_dispatch_command(conn, cmd, &pwd, 1); + err = parse_dispatch_command(conn, cmd, + &remote_path, 1); xfree(dir); - xfree(pwd); + xfree(remote_path); xfree(conn); return (err); } @@ -1571,7 +1951,8 @@ interactive_loop(int fd_in, int fd_out, char *file1, char *file2) const char *line; int count = 0; - if ((line = el_gets(el, &count)) == NULL || count <= 0) { + if ((line = el_gets(el, &count)) == NULL || + count <= 0) { printf("\n"); break; } @@ -1591,11 +1972,12 @@ interactive_loop(int fd_in, int fd_out, char *file1, char *file2) interrupted = 0; signal(SIGINT, cmd_interrupt); - err = parse_dispatch_command(conn, cmd, &pwd, batchmode); + err = parse_dispatch_command(conn, cmd, &remote_path, + batchmode); if (err != 0) break; } - xfree(pwd); + xfree(remote_path); xfree(conn); #ifdef USE_LIBEDIT @@ -1647,9 +2029,11 @@ connect_to_server(char *path, char **args, int *in, int *out) * The underlying ssh is in the same process group, so we must * ignore SIGINT if we want to gracefully abort commands, * otherwise the signal will make it to the ssh process and - * kill it too + * kill it too. Contrawise, since sftp sends SIGTERMs to the + * underlying ssh, it must *not* ignore that signal. */ signal(SIGINT, SIG_IGN); + signal(SIGTERM, SIG_DFL); execvp(path, args); fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); _exit(1); @@ -1668,12 +2052,16 @@ usage(void) extern char *__progname; fprintf(stderr, - "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n" - " [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n" - " [-S program] [-s subsystem | sftp_server] host\n" + "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n" + " [-D sftp_server_path] [-F ssh_config] " + "[-i identity_file]\n" + " [-o ssh_option] [-P port] [-R num_requests] " + "[-S program]\n" + " [-s subsystem | sftp_server] host\n" " %s [user@]host[:file ...]\n" " %s [user@]host[:dir[/]]\n" - " %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname); + " %s -b batchfile [user@]host\n", + __progname, __progname, __progname, __progname); exit(1); } @@ -1681,7 +2069,7 @@ int main(int argc, char **argv) { int in, out, ch, err; - char *host, *userhost, *cp, *file2 = NULL; + char *host = NULL, *userhost, *cp, *file2 = NULL; int debug_level = 0, sshver = 2; char *file1 = NULL, *sftp_server = NULL; char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; @@ -1689,6 +2077,9 @@ main(int argc, char **argv) arglist args; extern int optind; extern char *optarg; + struct sftp_conn *conn; + size_t copy_buffer_len = DEFAULT_COPY_BUFLEN; + size_t num_requests = DEFAULT_NUM_REQUESTS; /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); @@ -1705,10 +2096,29 @@ main(int argc, char **argv) ll = SYSLOG_LEVEL_INFO; infile = stdin; - while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) { + while ((ch = getopt(argc, argv, + "1246hpqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) { switch (ch) { + /* Passed through to ssh(1) */ + case '4': + case '6': case 'C': - addargs(&args, "-C"); + addargs(&args, "-%c", ch); + break; + /* Passed through to ssh(1) with argument */ + case 'F': + case 'c': + case 'i': + case 'o': + addargs(&args, "-%c", ch); + addargs(&args, "%s", optarg); + break; + case 'q': + showprogress = 0; + addargs(&args, "-%c", ch); + break; + case 'P': + addargs(&args, "-oPort %s", optarg); break; case 'v': if (debug_level < 3) { @@ -1717,21 +2127,18 @@ main(int argc, char **argv) } debug_level++; break; - case 'F': - case 'o': - addargs(&args, "-%c%s", ch, optarg); - break; case '1': sshver = 1; if (sftp_server == NULL) sftp_server = _PATH_SFTP_SERVER; break; - case 's': - sftp_server = optarg; + case '2': + sshver = 2; break; - case 'S': - ssh_program = optarg; - replacearg(&args, 0, "%s", ssh_program); + case 'B': + copy_buffer_len = strtol(optarg, &cp, 10); + if (copy_buffer_len == 0 || *cp != '\0') + fatal("Invalid buffer size \"%s\"", optarg); break; case 'b': if (batchmode) @@ -1745,13 +2152,14 @@ main(int argc, char **argv) batchmode = 1; addargs(&args, "-obatchmode yes"); break; - case 'P': + case 'p': + global_pflag = 1; + break; + case 'D': sftp_direct = optarg; break; - case 'B': - copy_buffer_len = strtol(optarg, &cp, 10); - if (copy_buffer_len == 0 || *cp != '\0') - fatal("Invalid buffer size \"%s\"", optarg); + case 'r': + global_rflag = 1; break; case 'R': num_requests = strtol(optarg, &cp, 10); @@ -1759,6 +2167,13 @@ main(int argc, char **argv) fatal("Invalid number of requests \"%s\"", optarg); break; + case 's': + sftp_server = optarg; + break; + case 'S': + ssh_program = optarg; + replacearg(&args, 0, "%s", ssh_program); + break; case 'h': default: usage(); @@ -1785,7 +2200,8 @@ main(int argc, char **argv) fprintf(stderr, "Missing username\n"); usage(); } - addargs(&args, "-l%s", userhost); + addargs(&args, "-l"); + addargs(&args, "%s", userhost); } if ((cp = colon(host)) != NULL) { @@ -1805,24 +2221,32 @@ main(int argc, char **argv) if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) addargs(&args, "-s"); + addargs(&args, "--"); addargs(&args, "%s", host); addargs(&args, "%s", (sftp_server != NULL ? sftp_server : "sftp")); - if (!batchmode) - fprintf(stderr, "Connecting to %s...\n", host); connect_to_server(ssh_program, args.list, &in, &out); } else { args.list = NULL; addargs(&args, "sftp-server"); - if (!batchmode) - fprintf(stderr, "Attaching to %s...\n", sftp_direct); connect_to_server(sftp_direct, args.list, &in, &out); } freeargs(&args); - err = interactive_loop(in, out, file1, file2); + conn = do_init(in, out, copy_buffer_len, num_requests); + if (conn == NULL) + fatal("Couldn't initialise connection to server"); + + if (!batchmode) { + if (sftp_direct == NULL) + fprintf(stderr, "Connected to %s.\n", host); + else + fprintf(stderr, "Attached to %s.\n", sftp_direct); + } + + err = interactive_loop(conn, file1, file2); #if !defined(USE_PIPES) shutdown(in, SHUT_RDWR); diff --git a/crypto/openssh/ssh-add.1 b/crypto/openssh/ssh-add.1 index 005041b689..d7cc53101f 100644 --- a/crypto/openssh/ssh-add.1 +++ b/crypto/openssh/ssh-add.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-add.1,v 1.46 2007/06/12 13:41:03 jmc Exp $ +.\" $OpenBSD: ssh-add.1,v 1.52 2010/03/05 10:28:21 djm Exp $ .\" .\" -*- nroff -*- .\" @@ -37,7 +37,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: June 12 2007 $ +.Dd $Mdocdate: March 5 2010 $ .Dt SSH-ADD 1 .Os .Sh NAME @@ -49,9 +49,9 @@ .Op Fl t Ar life .Op Ar .Nm ssh-add -.Fl s Ar reader +.Fl s Ar pkcs11 .Nm ssh-add -.Fl e Ar reader +.Fl e Ar pkcs11 .Sh DESCRIPTION .Nm adds RSA or DSA identities to the authentication agent, @@ -61,7 +61,14 @@ When run without arguments, it adds the files .Pa ~/.ssh/id_dsa and .Pa ~/.ssh/identity . +After loading a private key, +.Nm +will try to load corresponding certificate information from the +filename obtained by appending +.Pa -cert.pub +to the name of the private key file. Alternative file names can be given on the command line. +.Pp If any file requires a passphrase, .Nm asks for the passphrase from the user. @@ -101,17 +108,17 @@ If no public key is found at a given path, will append .Pa .pub and retry. -.It Fl e Ar reader -Remove key in smartcard -.Ar reader . +.It Fl e Ar pkcs11 +Remove keys provided by the PKCS#11 shared library +.Ar pkcs11 . .It Fl L Lists public key parameters of all identities currently represented by the agent. .It Fl l Lists fingerprints of all identities currently represented by the agent. -.It Fl s Ar reader -Add key in smartcard -.Ar reader . +.It Fl s Ar pkcs11 +Add keys provided by the PKCS#11 shared library +.Ar pkcs11 . .It Fl t Ar life Set a maximum lifetime when adding identities to an agent. The lifetime may be specified in seconds or in a time format @@ -148,8 +155,9 @@ may be necessary to redirect the input from .Pa /dev/null to make this work.) .It Ev SSH_AUTH_SOCK -Identifies the path of a unix-domain socket used to communicate with the -agent. +Identifies the path of a +.Ux Ns -domain +socket used to communicate with the agent. .El .Sh FILES .Bl -tag -width Ds diff --git a/crypto/openssh/ssh-add.c b/crypto/openssh/ssh-add.c index 7a43282f2d..fb641ec489 100644 --- a/crypto/openssh/ssh-add.c +++ b/crypto/openssh/ssh-add.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-add.c,v 1.90 2007/09/09 11:38:01 sobrado Exp $ */ +/* $OpenBSD: ssh-add.c,v 1.96 2010/05/14 00:47:22 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -138,9 +138,9 @@ delete_all(AuthenticationConnection *ac) static int add_file(AuthenticationConnection *ac, const char *filename) { - Key *private; + Key *private, *cert; char *comment = NULL; - char msg[1024]; + char msg[1024], *certpath; int fd, perms_ok, ret = -1; if ((fd = open(filename, O_RDONLY)) < 0) { @@ -194,14 +194,46 @@ add_file(AuthenticationConnection *ac, const char *filename) "Lifetime set to %d seconds\n", lifetime); if (confirm != 0) fprintf(stderr, - "The user has to confirm each use of the key\n"); - } else if (ssh_add_identity(ac, private, comment)) { - fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); - ret = 0; + "The user must confirm each use of the key\n"); } else { fprintf(stderr, "Could not add identity: %s\n", filename); } + + /* Now try to add the certificate flavour too */ + xasprintf(&certpath, "%s-cert.pub", filename); + if ((cert = key_load_public(certpath, NULL)) == NULL) + goto out; + + if (!key_equal_public(cert, private)) { + error("Certificate %s does not match private key %s", + certpath, filename); + key_free(cert); + goto out; + } + + /* Graft with private bits */ + if (key_to_certified(private, key_cert_is_legacy(cert)) != 0) { + error("%s: key_to_certified failed", __func__); + key_free(cert); + goto out; + } + key_cert_copy(cert, private); + key_free(cert); + + if (!ssh_add_identity_constrained(ac, private, comment, + lifetime, confirm)) { + error("Certificate %s (%s) add failed", certpath, + private->cert->key_id); + } + fprintf(stderr, "Certificate added: %s (%s)\n", certpath, + private->cert->key_id); + if (lifetime != 0) + fprintf(stderr, "Lifetime set to %d seconds\n", lifetime); + if (confirm != 0) + fprintf(stderr, "The user must confirm each use of the key\n"); + out: + xfree(certpath); xfree(comment); key_free(private); @@ -214,7 +246,7 @@ update_card(AuthenticationConnection *ac, int add, const char *id) char *pin; int ret = -1; - pin = read_passphrase("Enter passphrase for smartcard: ", RP_ALLOW_STDIN); + pin = read_passphrase("Enter passphrase for PKCS#11: ", RP_ALLOW_STDIN); if (pin == NULL) return -1; @@ -320,10 +352,8 @@ usage(void) fprintf(stderr, " -X Unlock agent.\n"); fprintf(stderr, " -t life Set lifetime (in seconds) when adding identities.\n"); fprintf(stderr, " -c Require confirmation to sign using identities\n"); -#ifdef SMARTCARD - fprintf(stderr, " -s reader Add key in smartcard reader.\n"); - fprintf(stderr, " -e reader Remove key in smartcard reader.\n"); -#endif + fprintf(stderr, " -s pkcs11 Add keys from PKCS#11 provider.\n"); + fprintf(stderr, " -e pkcs11 Remove keys provided by PKCS#11 provider.\n"); } int @@ -332,7 +362,7 @@ main(int argc, char **argv) extern char *optarg; extern int optind; AuthenticationConnection *ac = NULL; - char *sc_reader_id = NULL; + char *pkcs11provider = NULL; int i, ch, deleting = 0, ret = 0; /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ @@ -374,11 +404,11 @@ main(int argc, char **argv) ret = 1; goto done; case 's': - sc_reader_id = optarg; + pkcs11provider = optarg; break; case 'e': deleting = 1; - sc_reader_id = optarg; + pkcs11provider = optarg; break; case 't': if ((lifetime = convtime(optarg)) == -1) { @@ -395,8 +425,8 @@ main(int argc, char **argv) } argc -= optind; argv += optind; - if (sc_reader_id != NULL) { - if (update_card(ac, !deleting, sc_reader_id) == -1) + if (pkcs11provider != NULL) { + if (update_card(ac, !deleting, pkcs11provider) == -1) ret = 1; goto done; } diff --git a/crypto/openssh/ssh-agent.1 b/crypto/openssh/ssh-agent.1 index 533cd6f6bd..f65e8e6255 100644 --- a/crypto/openssh/ssh-agent.1 +++ b/crypto/openssh/ssh-agent.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-agent.1,v 1.47 2009/03/26 08:38:39 sobrado Exp $ +.\" $OpenBSD: ssh-agent.1,v 1.50 2010/01/17 21:49:09 tedu Exp $ .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -34,7 +34,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: March 26 2009 $ +.Dd $Mdocdate: January 17 2010 $ .Dt SSH-AGENT 1 .Os .Sh NAME @@ -67,7 +67,9 @@ machines using The options are as follows: .Bl -tag -width Ds .It Fl a Ar bind_address -Bind the agent to the unix-domain socket +Bind the agent to the +.Ux Ns -domain +socket .Ar bind_address . The default is .Pa /tmp/ssh-XXXXXXXXXX/agent.\*(Ltppid\*(Gt . @@ -117,8 +119,9 @@ and .Pa ~/.ssh/identity . If the identity has a passphrase, .Xr ssh-add 1 -asks for the passphrase (using a small X11 application if running -under X11, or from the terminal if running without X). +asks for the passphrase on the terminal if it has one or from a small X11 +program if running under X11. +If neither of these is the case then the authentication will fail. It then sends the identity to the agent. Several identities can be stored in the agent; the agent can automatically use any of these identities. @@ -162,8 +165,9 @@ Instead, operations that require a private key will be performed by the agent, and the result will be returned to the requester. This way, private keys are not exposed to clients using the agent. .Pp -A unix-domain socket is created -and the name of this socket is stored in the +A +.Ux Ns -domain +socket is created and the name of this socket is stored in the .Ev SSH_AUTH_SOCK environment variable. @@ -186,8 +190,8 @@ Contains the protocol version 2 DSA authentication identity of the user. .It Pa ~/.ssh/id_rsa Contains the protocol version 2 RSA authentication identity of the user. .It Pa /tmp/ssh-XXXXXXXXXX/agent.\*(Ltppid\*(Gt -Unix-domain sockets used to contain the connection to the -authentication agent. +.Ux Ns -domain +sockets used to contain the connection to the authentication agent. These sockets should only be readable by the owner. The sockets should get automatically removed when the agent exits. .El diff --git a/crypto/openssh/ssh-agent.c b/crypto/openssh/ssh-agent.c index f77dea3a63..2c0e286960 100644 --- a/crypto/openssh/ssh-agent.c +++ b/crypto/openssh/ssh-agent.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-agent.c,v 1.161 2009/03/23 19:38:04 tobias Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.166 2010/04/16 01:47:26 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -76,8 +76,8 @@ #include "log.h" #include "misc.h" -#ifdef SMARTCARD -#include "scard.h" +#ifdef ENABLE_PKCS11 +#include "ssh-pkcs11.h" #endif #if defined(HAVE_SYS_PRCTL_H) @@ -105,6 +105,7 @@ typedef struct identity { TAILQ_ENTRY(identity) next; Key *key; char *comment; + char *provider; u_int death; u_int confirm; } Identity; @@ -171,6 +172,8 @@ static void free_identity(Identity *id) { key_free(id->key); + if (id->provider != NULL) + xfree(id->provider); xfree(id->comment); xfree(id); } @@ -465,6 +468,8 @@ process_add_identity(SocketEntry *e, int version) int type, success = 0, death = 0, confirm = 0; char *type_name, *comment; Key *k = NULL; + u_char *cert; + u_int len; switch (version) { case 1: @@ -495,6 +500,15 @@ process_add_identity(SocketEntry *e, int version) buffer_get_bignum2(&e->request, k->dsa->pub_key); buffer_get_bignum2(&e->request, k->dsa->priv_key); break; + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + cert = buffer_get_string(&e->request, &len); + if ((k = key_from_blob(cert, len)) == NULL) + fatal("Certificate parse failed"); + xfree(cert); + key_add_private(k); + buffer_get_bignum2(&e->request, k->dsa->priv_key); + break; case KEY_RSA: k = key_new_private(type); buffer_get_bignum2(&e->request, k->rsa->n); @@ -507,6 +521,18 @@ process_add_identity(SocketEntry *e, int version) /* Generate additional parameters */ rsa_generate_additional_parameters(k->rsa); break; + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + cert = buffer_get_string(&e->request, &len); + if ((k = key_from_blob(cert, len)) == NULL) + fatal("Certificate parse failed"); + xfree(cert); + key_add_private(k); + buffer_get_bignum2(&e->request, k->rsa->d); + buffer_get_bignum2(&e->request, k->rsa->iqmp); + buffer_get_bignum2(&e->request, k->rsa->p); + buffer_get_bignum2(&e->request, k->rsa->q); + break; default: buffer_clear(&e->request); goto send; @@ -516,6 +542,8 @@ process_add_identity(SocketEntry *e, int version) /* enable blinding */ switch (k->type) { case KEY_RSA: + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: case KEY_RSA1: if (RSA_blinding_on(k->rsa, NULL) != 1) { error("process_add_identity: RSA_blinding_on failed"); @@ -549,7 +577,7 @@ process_add_identity(SocketEntry *e, int version) if (lifetime && !death) death = time(NULL) + lifetime; if ((id = lookup_identity(k, version)) == NULL) { - id = xmalloc(sizeof(Identity)); + id = xcalloc(1, sizeof(Identity)); id->key = k; TAILQ_INSERT_TAIL(&tab->idlist, id, next); /* Increment the number of identities. */ @@ -609,17 +637,17 @@ no_identities(SocketEntry *e, u_int type) buffer_free(&msg); } -#ifdef SMARTCARD +#ifdef ENABLE_PKCS11 static void process_add_smartcard_key(SocketEntry *e) { - char *sc_reader_id = NULL, *pin; - int i, type, version, success = 0, death = 0, confirm = 0; - Key **keys, *k; + char *provider = NULL, *pin; + int i, type, version, count = 0, success = 0, death = 0, confirm = 0; + Key **keys = NULL, *k; Identity *id; Idtab *tab; - sc_reader_id = buffer_get_string(&e->request, NULL); + provider = buffer_get_string(&e->request, NULL); pin = buffer_get_string(&e->request, NULL); while (buffer_len(&e->request)) { @@ -633,30 +661,22 @@ process_add_smartcard_key(SocketEntry *e) default: error("process_add_smartcard_key: " "Unknown constraint type %d", type); - xfree(sc_reader_id); - xfree(pin); goto send; } } if (lifetime && !death) death = time(NULL) + lifetime; - keys = sc_get_keys(sc_reader_id, pin); - xfree(sc_reader_id); - xfree(pin); - - if (keys == NULL || keys[0] == NULL) { - error("sc_get_keys failed"); - goto send; - } - for (i = 0; keys[i] != NULL; i++) { + count = pkcs11_add_provider(provider, pin, &keys); + for (i = 0; i < count; i++) { k = keys[i]; version = k->type == KEY_RSA1 ? 1 : 2; tab = idtab_lookup(version); if (lookup_identity(k, version) == NULL) { - id = xmalloc(sizeof(Identity)); + id = xcalloc(1, sizeof(Identity)); id->key = k; - id->comment = sc_get_key_label(k); + id->provider = xstrdup(provider); + id->comment = xstrdup(provider); /* XXX */ id->death = death; id->confirm = confirm; TAILQ_INSERT_TAIL(&tab->idlist, id, next); @@ -667,8 +687,13 @@ process_add_smartcard_key(SocketEntry *e) } keys[i] = NULL; } - xfree(keys); send: + if (pin) + xfree(pin); + if (provider) + xfree(provider); + if (keys) + xfree(keys); buffer_put_int(&e->output, 1); buffer_put_char(&e->output, success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); @@ -677,42 +702,37 @@ send: static void process_remove_smartcard_key(SocketEntry *e) { - char *sc_reader_id = NULL, *pin; - int i, version, success = 0; - Key **keys, *k = NULL; - Identity *id; + char *provider = NULL, *pin = NULL; + int version, success = 0; + Identity *id, *nxt; Idtab *tab; - sc_reader_id = buffer_get_string(&e->request, NULL); + provider = buffer_get_string(&e->request, NULL); pin = buffer_get_string(&e->request, NULL); - keys = sc_get_keys(sc_reader_id, pin); - xfree(sc_reader_id); xfree(pin); - if (keys == NULL || keys[0] == NULL) { - error("sc_get_keys failed"); - goto send; - } - for (i = 0; keys[i] != NULL; i++) { - k = keys[i]; - version = k->type == KEY_RSA1 ? 1 : 2; - if ((id = lookup_identity(k, version)) != NULL) { - tab = idtab_lookup(version); - TAILQ_REMOVE(&tab->idlist, id, next); - tab->nentries--; - free_identity(id); - success = 1; + for (version = 1; version < 3; version++) { + tab = idtab_lookup(version); + for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) { + nxt = TAILQ_NEXT(id, next); + if (!strcmp(provider, id->provider)) { + TAILQ_REMOVE(&tab->idlist, id, next); + free_identity(id); + tab->nentries--; + } } - key_free(k); - keys[i] = NULL; } - xfree(keys); -send: + if (pkcs11_del_provider(provider) == 0) + success = 1; + else + error("process_remove_smartcard_key:" + " pkcs11_del_provider failed"); + xfree(provider); buffer_put_int(&e->output, 1); buffer_put_char(&e->output, success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); } -#endif /* SMARTCARD */ +#endif /* ENABLE_PKCS11 */ /* dispatch incoming messages */ @@ -797,7 +817,7 @@ process_message(SocketEntry *e) case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: process_remove_all_identities(e, 2); break; -#ifdef SMARTCARD +#ifdef ENABLE_PKCS11 case SSH_AGENTC_ADD_SMARTCARD_KEY: case SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED: process_add_smartcard_key(e); @@ -805,7 +825,7 @@ process_message(SocketEntry *e) case SSH_AGENTC_REMOVE_SMARTCARD_KEY: process_remove_smartcard_key(e); break; -#endif /* SMARTCARD */ +#endif /* ENABLE_PKCS11 */ default: /* Unknown message. Respond with failure. */ error("Unknown message %d", type); @@ -919,11 +939,11 @@ after_select(fd_set *readset, fd_set *writeset) socklen_t slen; char buf[1024]; int len, sock; - u_int i; + u_int i, orig_alloc; uid_t euid; gid_t egid; - for (i = 0; i < sockets_alloc; i++) + for (i = 0, orig_alloc = sockets_alloc; i < orig_alloc; i++) switch (sockets[i].type) { case AUTH_UNUSED: break; @@ -956,16 +976,13 @@ after_select(fd_set *readset, fd_set *writeset) case AUTH_CONNECTION: if (buffer_len(&sockets[i].output) > 0 && FD_ISSET(sockets[i].fd, writeset)) { - do { - len = write(sockets[i].fd, - buffer_ptr(&sockets[i].output), - buffer_len(&sockets[i].output)); - if (len == -1 && (errno == EAGAIN || - errno == EINTR || - errno == EWOULDBLOCK)) - continue; - break; - } while (1); + len = write(sockets[i].fd, + buffer_ptr(&sockets[i].output), + buffer_len(&sockets[i].output)); + if (len == -1 && (errno == EAGAIN || + errno == EWOULDBLOCK || + errno == EINTR)) + continue; if (len <= 0) { close_socket(&sockets[i]); break; @@ -973,14 +990,11 @@ after_select(fd_set *readset, fd_set *writeset) buffer_consume(&sockets[i].output, len); } if (FD_ISSET(sockets[i].fd, readset)) { - do { - len = read(sockets[i].fd, buf, sizeof(buf)); - if (len == -1 && (errno == EAGAIN || - errno == EINTR || - errno == EWOULDBLOCK)) - continue; - break; - } while (1); + len = read(sockets[i].fd, buf, sizeof(buf)); + if (len == -1 && (errno == EAGAIN || + errno == EWOULDBLOCK || + errno == EINTR)) + continue; if (len <= 0) { close_socket(&sockets[i]); break; @@ -1015,6 +1029,9 @@ static void cleanup_handler(int sig) { cleanup_socket(); +#ifdef ENABLE_PKCS11 + pkcs11_terminate(); +#endif _exit(2); } @@ -1261,6 +1278,10 @@ main(int ac, char **av) #endif skip: + +#ifdef ENABLE_PKCS11 + pkcs11_init(0); +#endif new_socket(AUTH_SOCKET, sock); if (ac > 0) parent_alive_interval = 10; diff --git a/crypto/openssh/ssh-dss.c b/crypto/openssh/ssh-dss.c index 51a06e98fe..175e4d0301 100644 --- a/crypto/openssh/ssh-dss.c +++ b/crypto/openssh/ssh-dss.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-dss.c,v 1.24 2006/11/06 21:25:28 markus Exp $ */ +/* $OpenBSD: ssh-dss.c,v 1.26 2010/04/16 01:47:26 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -53,7 +53,8 @@ ssh_dss_sign(const Key *key, u_char **sigp, u_int *lenp, u_int rlen, slen, len, dlen; Buffer b; - if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) { + if (key == NULL || key->dsa == NULL || (key->type != KEY_DSA && + key->type != KEY_DSA_CERT && key->type != KEY_DSA_CERT_V00)) { error("ssh_dss_sign: no DSA key"); return -1; } @@ -116,7 +117,8 @@ ssh_dss_verify(const Key *key, const u_char *signature, u_int signaturelen, int rlen, ret; Buffer b; - if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) { + if (key == NULL || key->dsa == NULL || (key->type != KEY_DSA && + key->type != KEY_DSA_CERT && key->type != KEY_DSA_CERT_V00)) { error("ssh_dss_verify: no DSA key"); return -1; } diff --git a/crypto/openssh/ssh-keygen.1 b/crypto/openssh/ssh-keygen.1 index 3596cc1747..9acd8f8c92 100644 --- a/crypto/openssh/ssh-keygen.1 +++ b/crypto/openssh/ssh-keygen.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-keygen.1,v 1.79 2008/07/24 23:55:30 sthen Exp $ +.\" $OpenBSD: ssh-keygen.1,v 1.98 2010/08/04 06:07:11 djm Exp $ .\" .\" -*- nroff -*- .\" @@ -37,22 +37,21 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: July 24 2008 $ +.Dd $Mdocdate: August 4 2010 $ .Dt SSH-KEYGEN 1 .Os .Sh NAME .Nm ssh-keygen .Nd authentication key generation, management and conversion .Sh SYNOPSIS -.Nm ssh-keygen .Bk -words +.Nm ssh-keygen .Op Fl q .Op Fl b Ar bits .Fl t Ar type .Op Fl N Ar new_passphrase .Op Fl C Ar comment .Op Fl f Ar output_keyfile -.Ek .Nm ssh-keygen .Fl p .Op Fl P Ar old_passphrase @@ -60,9 +59,11 @@ .Op Fl f Ar keyfile .Nm ssh-keygen .Fl i +.Op Fl m Ar key_format .Op Fl f Ar input_keyfile .Nm ssh-keygen .Fl e +.Op Fl m Ar key_format .Op Fl f Ar input_keyfile .Nm ssh-keygen .Fl y @@ -79,7 +80,7 @@ .Fl B .Op Fl f Ar input_keyfile .Nm ssh-keygen -.Fl D Ar reader +.Fl D Ar pkcs11 .Nm ssh-keygen .Fl F Ar hostname .Op Fl f Ar known_hosts_file @@ -91,9 +92,6 @@ .Fl R Ar hostname .Op Fl f Ar known_hosts_file .Nm ssh-keygen -.Fl U Ar reader -.Op Fl f Ar input_keyfile -.Nm ssh-keygen .Fl r Ar hostname .Op Fl f Ar input_keyfile .Op Fl g @@ -109,6 +107,19 @@ .Op Fl v .Op Fl a Ar num_trials .Op Fl W Ar generator +.Nm ssh-keygen +.Fl s Ar ca_key +.Fl I Ar certificate_identity +.Op Fl h +.Op Fl n Ar principals +.Op Fl O Ar option +.Op Fl V Ar validity_interval +.Op Fl z Ar serial_number +.Ar +.Nm ssh-keygen +.Fl L +.Op Fl f Ar input_keyfile +.Ek .Sh DESCRIPTION .Nm generates, manages and converts authentication keys for @@ -201,16 +212,23 @@ Requests changing the comment in the private and public key files. This operation is only supported for RSA1 keys. The program will prompt for the file containing the private keys, for the passphrase if the key has one, and for the new comment. -.It Fl D Ar reader -Download the RSA public key stored in the smartcard in -.Ar reader . +.It Fl D Ar pkcs11 +Download the RSA public keys provided by the PKCS#11 shared library +.Ar pkcs11 . +When used in combination with +.Fl s , +this option indicates that a CA key resides in a PKCS#11 token (see the +.Sx CERTIFICATES +section for details). .It Fl e This option will read a private or public OpenSSH key file and -print the key in -RFC 4716 SSH Public Key File Format -to stdout. -This option allows exporting keys for use by several commercial -SSH implementations. +print to stdout the key in one of the formats specified by the +.Fl m +option. +The default export format is +.Dq RFC4716 . +This option allows exporting OpenSSH keys for use by other programs, including +several commercial SSH implementations. .It Fl F Ar hostname Search for the specified .Ar hostname @@ -248,15 +266,29 @@ but they do not reveal identifying information should the file's contents be disclosed. This option will not modify existing hashed hostnames and is therefore safe to use on files that mix hashed and non-hashed names. +.It Fl h +When signing a key, create a host certificate instead of a user +certificate. +Please see the +.Sx CERTIFICATES +section for details. +.It Fl I Ar certificate_identity +Specify the key identity when signing a public key. +Please see the +.Sx CERTIFICATES +section for details. .It Fl i This option will read an unencrypted private (or public) key file -in SSH2-compatible format and print an OpenSSH compatible private +in the format specified by the +.Fl m +option and print an OpenSSH compatible private (or public) key to stdout. -.Nm -also reads the -RFC 4716 SSH Public Key File Format. -This option allows importing keys from several commercial -SSH implementations. +This option allows importing keys from other software, including several +commercial SSH implementations. +The default import format is +.Dq RFC4716 . +.It Fl L +Prints the contents of a certificate. .It Fl l Show fingerprint of specified public key file. Private RSA1 keys are also supported. @@ -269,8 +301,88 @@ an ASCII art representation of the key is supplied with the fingerprint. .It Fl M Ar memory Specify the amount of memory to use (in megabytes) when generating candidate moduli for DH-GEX. +.It Fl m Ar key_format +Specify a key format for the +.Fl i +(import) or +.Fl e +(export) conversion options. +The supported key formats are: +.Dq RFC4716 +(RFC 4716/SSH2 public or private key), +.Dq PKCS8 +(PEM PKCS8 public key) +or +.Dq PEM +(PEM public key). +The default conversion format is +.Dq RFC4716 . .It Fl N Ar new_passphrase Provides the new passphrase. +.It Fl n Ar principals +Specify one or more principals (user or host names) to be included in +a certificate when signing a key. +Multiple principals may be specified, separated by commas. +Please see the +.Sx CERTIFICATES +section for details. +.It Fl O Ar option +Specify a certificate option when signing a key. +This option may be specified multiple times. +Please see the +.Sx CERTIFICATES +section for details. +The options that are valid for user certificates are: +.Bl -tag -width Ds +.It Ic clear +Clear all enabled permissions. +This is useful for clearing the default set of permissions so permissions may +be added individually. +.It Ic force-command Ns = Ns Ar command +Forces the execution of +.Ar command +instead of any shell or command specified by the user when +the certificate is used for authentication. +.It Ic no-agent-forwarding +Disable +.Xr ssh-agent 1 +forwarding (permitted by default). +.It Ic no-port-forwarding +Disable port forwarding (permitted by default). +.It Ic no-pty +Disable PTY allocation (permitted by default). +.It Ic no-user-rc +Disable execution of +.Pa ~/.ssh/rc +by +.Xr sshd 8 +(permitted by default). +.It Ic no-x11-forwarding +Disable X11 forwarding (permitted by default). +.It Ic permit-agent-forwarding +Allows +.Xr ssh-agent 1 +forwarding. +.It Ic permit-port-forwarding +Allows port forwarding. +.It Ic permit-pty +Allows PTY allocation. +.It Ic permit-user-rc +Allows execution of +.Pa ~/.ssh/rc +by +.Xr sshd 8 . +.It Ic permit-x11-forwarding +Allows X11 forwarding. +.It Ic source-address Ns = Ns Ar address_list +Restrict the source addresses from which the certificate is considered valid. +The +.Ar address_list +is a comma-separated list of one or more address/netmask pairs in CIDR +format. +.El +.Pp +At present, no options are valid for host keys. .It Fl P Ar passphrase Provides the (old) passphrase. .It Fl p @@ -300,6 +412,11 @@ Print the SSHFP fingerprint resource record named for the specified public key file. .It Fl S Ar start Specify start point (in hex) when generating candidate moduli for DH-GEX. +.It Fl s Ar ca_key +Certify (sign) a public key using the specified CA key. +Please see the +.Sx CERTIFICATES +section for details. .It Fl T Ar output_file Test DH group exchange candidate primes (generated using the .Fl G @@ -313,9 +430,29 @@ for protocol version 1 and or .Dq dsa for protocol version 2. -.It Fl U Ar reader -Upload an existing RSA private key into the smartcard in -.Ar reader . +.It Fl V Ar validity_interval +Specify a validity interval when signing a certificate. +A validity interval may consist of a single time, indicating that the +certificate is valid beginning now and expiring at that time, or may consist +of two times separated by a colon to indicate an explicit time interval. +The start time may be specified as a date in YYYYMMDD format, a time +in YYYYMMDDHHMMSS format or a relative time (to the current time) consisting +of a minus sign followed by a relative time in the format described in the +.Sx TIME FORMATS +section of +.Xr sshd_config 5 . +The end time may be specified as a YYYYMMDD date, a YYYYMMDDHHMMSS time or +a relative time starting with a plus character. +.Pp +For example: +.Dq +52w1d +(valid from now to 52 weeks and one day from now), +.Dq -4w:+4w +(valid from four weeks ago to four weeks from now), +.Dq 20100101123000:20110101123000 +(valid from 12:30 PM, January 1st, 2010 to 12:30 PM, January 1st, 2011), +.Dq -1d:20110101 +(valid from yesterday to midnight, January 1st, 2011). .It Fl v Verbose mode. Causes @@ -331,6 +468,10 @@ Specify desired generator when testing candidate moduli for DH-GEX. .It Fl y This option will read a private OpenSSH format file and print an OpenSSH public key to stdout. +.It Fl z Ar serial_number +Specifies a serial number to be embedded in the certificate to distinguish +this certificate from others from the same CA. +The default serial number is zero. .El .Sh MODULI GENERATION .Nm @@ -386,6 +527,83 @@ Screened DH groups may be installed in .Pa /etc/moduli . It is important that this file contains moduli of a range of bit lengths and that both ends of a connection share common moduli. +.Sh CERTIFICATES +.Nm +supports signing of keys to produce certificates that may be used for +user or host authentication. +Certificates consist of a public key, some identity information, zero or +more principal (user or host) names and a set of options that +are signed by a Certification Authority (CA) key. +Clients or servers may then trust only the CA key and verify its signature +on a certificate rather than trusting many user/host keys. +Note that OpenSSH certificates are a different, and much simpler, format to +the X.509 certificates used in +.Xr ssl 8 . +.Pp +.Nm +supports two types of certificates: user and host. +User certificates authenticate users to servers, whereas host certificates +authenticate server hosts to users. +To generate a user certificate: +.Pp +.Dl $ ssh-keygen -s /path/to/ca_key -I key_id /path/to/user_key.pub +.Pp +The resultant certificate will be placed in +.Pa /path/to/user_key-cert.pub . +A host certificate requires the +.Fl h +option: +.Pp +.Dl $ ssh-keygen -s /path/to/ca_key -I key_id -h /path/to/host_key.pub +.Pp +The host certificate will be output to +.Pa /path/to/host_key-cert.pub . +.Pp +It is possible to sign using a CA key stored in a PKCS#11 token by +providing the token library using +.Fl D +and identifying the CA key by providing its public half as an argument +to +.Fl s : +.Pp +.Dl $ ssh-keygen -s ca_key.pub -D libpkcs11.so -I key_id host_key.pub +.Pp +In all cases, +.Ar key_id +is a "key identifier" that is logged by the server when the certificate +is used for authentication. +.Pp +Certificates may be limited to be valid for a set of principal (user/host) +names. +By default, generated certificates are valid for all users or hosts. +To generate a certificate for a specified set of principals: +.Pp +.Dl $ ssh-keygen -s ca_key -I key_id -n user1,user2 user_key.pub +.Dl "$ ssh-keygen -s ca_key -I key_id -h -n host.domain user_key.pub" +.Pp +Additional limitations on the validity and use of user certificates may +be specified through certificate options. +A certificate option may disable features of the SSH session, may be +valid only when presented from particular source addresses or may +force the use of a specific command. +For a list of valid certificate options, see the documentation for the +.Fl O +option above. +.Pp +Finally, certificates may be defined with a validity lifetime. +The +.Fl V +option allows specification of certificate start and end times. +A certificate that is presented at a time outside this range will not be +considered valid. +By default, certificates have a maximum validity interval. +.Pp +For certificates to be used for user or host authentication, the CA +public key must be trusted by +.Xr sshd 8 +or +.Xr ssh 1 . +Please refer to those manual pages for details. .Sh FILES .Bl -tag -width Ds .It Pa ~/.ssh/identity @@ -393,7 +611,7 @@ Contains the protocol version 1 RSA authentication identity of the user. This file should not be readable by anyone but the user. It is possible to specify a passphrase when generating the key; that passphrase will be -used to encrypt the private part of this file using 3DES. +used to encrypt the private part of this file using 128-bit AES. This file is not automatically accessed by .Nm but it is offered as the default file for the private key. @@ -411,7 +629,7 @@ Contains the protocol version 2 DSA authentication identity of the user. This file should not be readable by anyone but the user. It is possible to specify a passphrase when generating the key; that passphrase will be -used to encrypt the private part of this file using 3DES. +used to encrypt the private part of this file using 128-bit AES. This file is not automatically accessed by .Nm but it is offered as the default file for the private key. @@ -429,7 +647,7 @@ Contains the protocol version 2 RSA authentication identity of the user. This file should not be readable by anyone but the user. It is possible to specify a passphrase when generating the key; that passphrase will be -used to encrypt the private part of this file using 3DES. +used to encrypt the private part of this file using 128-bit AES. This file is not automatically accessed by .Nm but it is offered as the default file for the private key. diff --git a/crypto/openssh/ssh-keygen.c b/crypto/openssh/ssh-keygen.c index da5db98455..d90b1dfdd3 100644 --- a/crypto/openssh/ssh-keygen.c +++ b/crypto/openssh/ssh-keygen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.174 2009/06/22 05:39:28 dtucker Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.197 2010/08/04 06:07:11 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland @@ -48,9 +48,10 @@ #include "match.h" #include "hostfile.h" #include "dns.h" +#include "ssh2.h" -#ifdef SMARTCARD -#include "scard.h" +#ifdef ENABLE_PKCS11 +#include "ssh-pkcs11.h" #endif /* Number of bits in the RSA/DSA key. This value can be set on the command line. */ @@ -81,6 +82,9 @@ int find_host = 0; /* Flag indicating that we want to delete a host from a known_hosts file */ int delete_host = 0; +/* Flag indicating that we want to show the contents of a certificate */ +int show_cert = 0; + /* Flag indicating that we just want to see the key fingerprint */ int print_fingerprint = 0; int print_bubblebabble = 0; @@ -98,14 +102,53 @@ char *identity_new_passphrase = NULL; /* This is set to the new comment if given on the command line. */ char *identity_comment = NULL; -/* Dump public key file in format used by real and the original SSH 2 */ -int convert_to_ssh2 = 0; -int convert_from_ssh2 = 0; +/* Path to CA key when certifying keys. */ +char *ca_key_path = NULL; + +/* Certificate serial number */ +long long cert_serial = 0; + +/* Key type when certifying */ +u_int cert_key_type = SSH2_CERT_TYPE_USER; + +/* "key ID" of signed key */ +char *cert_key_id = NULL; + +/* Comma-separated list of principal names for certifying keys */ +char *cert_principals = NULL; + +/* Validity period for certificates */ +u_int64_t cert_valid_from = 0; +u_int64_t cert_valid_to = ~0ULL; + +/* Certificate options */ +#define CERTOPT_X_FWD (1) +#define CERTOPT_AGENT_FWD (1<<1) +#define CERTOPT_PORT_FWD (1<<2) +#define CERTOPT_PTY (1<<3) +#define CERTOPT_USER_RC (1<<4) +#define CERTOPT_DEFAULT (CERTOPT_X_FWD|CERTOPT_AGENT_FWD| \ + CERTOPT_PORT_FWD|CERTOPT_PTY|CERTOPT_USER_RC) +u_int32_t certflags_flags = CERTOPT_DEFAULT; +char *certflags_command = NULL; +char *certflags_src_addr = NULL; + +/* Conversion to/from various formats */ +int convert_to = 0; +int convert_from = 0; +enum { + FMT_RFC4716, + FMT_PKCS8, + FMT_PEM +} convert_format = FMT_RFC4716; int print_public = 0; int print_generic = 0; char *key_type_name = NULL; +/* Load key from this PKCS#11 provider */ +char *pkcs11provider = NULL; + /* argv0 */ extern char *__progname; @@ -128,9 +171,13 @@ ask_filename(struct passwd *pw, const char *prompt) case KEY_RSA1: name = _PATH_SSH_CLIENT_IDENTITY; break; + case KEY_DSA_CERT: + case KEY_DSA_CERT_V00: case KEY_DSA: name = _PATH_SSH_CLIENT_ID_DSA; break; + case KEY_RSA_CERT: + case KEY_RSA_CERT_V00: case KEY_RSA: name = _PATH_SSH_CLIENT_ID_RSA; break; @@ -176,19 +223,79 @@ load_identity(char *filename) #define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb static void -do_convert_to_ssh2(struct passwd *pw) +do_convert_to_ssh2(struct passwd *pw, Key *k) { - Key *k; u_int len; u_char *blob; + char comment[61]; + + if (key_to_blob(k, &blob, &len) <= 0) { + fprintf(stderr, "key_to_blob failed\n"); + exit(1); + } + /* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */ + snprintf(comment, sizeof(comment), + "%u-bit %s, converted by %s@%s from OpenSSH", + key_size(k), key_type(k), + pw->pw_name, hostname); + + fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); + fprintf(stdout, "Comment: \"%s\"\n", comment); + dump_base64(stdout, blob, len); + fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END); + key_free(k); + xfree(blob); + exit(0); +} + +static void +do_convert_to_pkcs8(Key *k) +{ + switch (key_type_plain(k->type)) { + case KEY_RSA: + if (!PEM_write_RSA_PUBKEY(stdout, k->rsa)) + fatal("PEM_write_RSA_PUBKEY failed"); + break; + case KEY_DSA: + if (!PEM_write_DSA_PUBKEY(stdout, k->dsa)) + fatal("PEM_write_DSA_PUBKEY failed"); + break; + default: + fatal("%s: unsupported key type %s", __func__, key_type(k)); + } + exit(0); +} + +static void +do_convert_to_pem(Key *k) +{ + switch (key_type_plain(k->type)) { + case KEY_RSA: + if (!PEM_write_RSAPublicKey(stdout, k->rsa)) + fatal("PEM_write_RSAPublicKey failed"); + break; +#if notyet /* OpenSSH 0.9.8 lacks this function */ + case KEY_DSA: + if (!PEM_write_DSAPublicKey(stdout, k->dsa)) + fatal("PEM_write_DSAPublicKey failed"); + break; +#endif + default: + fatal("%s: unsupported key type %s", __func__, key_type(k)); + } + exit(0); +} + +static void +do_convert_to(struct passwd *pw) +{ + Key *k; struct stat st; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); - if (stat(identity_file, &st) < 0) { - perror(identity_file); - exit(1); - } + if (stat(identity_file, &st) < 0) + fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); if ((k = key_load_public(identity_file, NULL)) == NULL) { if ((k = load_identity(identity_file)) == NULL) { fprintf(stderr, "load failed\n"); @@ -199,19 +306,20 @@ do_convert_to_ssh2(struct passwd *pw) fprintf(stderr, "version 1 keys are not supported\n"); exit(1); } - if (key_to_blob(k, &blob, &len) <= 0) { - fprintf(stderr, "key_to_blob failed\n"); - exit(1); + + switch (convert_format) { + case FMT_RFC4716: + do_convert_to_ssh2(pw, k); + break; + case FMT_PKCS8: + do_convert_to_pkcs8(k); + break; + case FMT_PEM: + do_convert_to_pem(k); + break; + default: + fatal("%s: unknown key format %d", __func__, convert_format); } - fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); - fprintf(stdout, - "Comment: \"%u-bit %s, converted from OpenSSH by %s@%s\"\n", - key_size(k), key_type(k), - pw->pw_name, hostname); - dump_base64(stdout, blob, len); - fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END); - key_free(k); - xfree(blob); exit(0); } @@ -353,29 +461,18 @@ get_line(FILE *fp, char *line, size_t len) } static void -do_convert_from_ssh2(struct passwd *pw) +do_convert_from_ssh2(struct passwd *pw, Key **k, int *private) { - Key *k; int blen; u_int len; char line[1024]; u_char blob[8096]; char encoded[8096]; - struct stat st; - int escaped = 0, private = 0, ok; + int escaped = 0; FILE *fp; - if (!have_identity) - ask_filename(pw, "Enter file in which the key is"); - if (stat(identity_file, &st) < 0) { - perror(identity_file); - exit(1); - } - fp = fopen(identity_file, "r"); - if (fp == NULL) { - perror(identity_file); - exit(1); - } + if ((fp = fopen(identity_file, "r")) == NULL) + fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); encoded[0] = '\0'; while ((blen = get_line(fp, line, sizeof(line))) != -1) { if (line[blen - 1] == '\\') @@ -383,7 +480,7 @@ do_convert_from_ssh2(struct passwd *pw) if (strncmp(line, "----", 4) == 0 || strstr(line, ": ") != NULL) { if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL) - private = 1; + *private = 1; if (strstr(line, " END ") != NULL) { break; } @@ -408,60 +505,138 @@ do_convert_from_ssh2(struct passwd *pw) fprintf(stderr, "uudecode failed.\n"); exit(1); } - k = private ? + *k = *private ? do_convert_private_ssh2_from_blob(blob, blen) : key_from_blob(blob, blen); - if (k == NULL) { + if (*k == NULL) { fprintf(stderr, "decode blob failed.\n"); exit(1); } - ok = private ? - (k->type == KEY_DSA ? - PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL) : - PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, NULL, 0, NULL, NULL)) : - key_write(k, stdout); - if (!ok) { - fprintf(stderr, "key write failed\n"); - exit(1); + fclose(fp); +} + +static void +do_convert_from_pkcs8(Key **k, int *private) +{ + EVP_PKEY *pubkey; + FILE *fp; + + if ((fp = fopen(identity_file, "r")) == NULL) + fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); + if ((pubkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) { + fatal("%s: %s is not a recognised public key format", __func__, + identity_file); } - key_free(k); - if (!private) - fprintf(stdout, "\n"); fclose(fp); - exit(0); + switch (EVP_PKEY_type(pubkey->type)) { + case EVP_PKEY_RSA: + *k = key_new(KEY_UNSPEC); + (*k)->type = KEY_RSA; + (*k)->rsa = EVP_PKEY_get1_RSA(pubkey); + break; + case EVP_PKEY_DSA: + *k = key_new(KEY_UNSPEC); + (*k)->type = KEY_DSA; + (*k)->dsa = EVP_PKEY_get1_DSA(pubkey); + break; + default: + fatal("%s: unsupported pubkey type %d", __func__, + EVP_PKEY_type(pubkey->type)); + } + EVP_PKEY_free(pubkey); + return; } static void -do_print_public(struct passwd *pw) +do_convert_from_pem(Key **k, int *private) { - Key *prv; + FILE *fp; + RSA *rsa; +#ifdef notyet + DSA *dsa; +#endif + + if ((fp = fopen(identity_file, "r")) == NULL) + fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); + if ((rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL)) != NULL) { + *k = key_new(KEY_UNSPEC); + (*k)->type = KEY_RSA; + (*k)->rsa = rsa; + fclose(fp); + return; + } +#if notyet /* OpenSSH 0.9.8 lacks this function */ + rewind(fp); + if ((dsa = PEM_read_DSAPublicKey(fp, NULL, NULL, NULL)) != NULL) { + *k = key_new(KEY_UNSPEC); + (*k)->type = KEY_DSA; + (*k)->dsa = dsa; + fclose(fp); + return; + } +#endif + fatal("%s: unrecognised raw private key format", __func__); +} + +static void +do_convert_from(struct passwd *pw) +{ + Key *k = NULL; + int private = 0, ok = 0; struct stat st; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); - if (stat(identity_file, &st) < 0) { - perror(identity_file); - exit(1); + if (stat(identity_file, &st) < 0) + fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); + + switch (convert_format) { + case FMT_RFC4716: + do_convert_from_ssh2(pw, &k, &private); + break; + case FMT_PKCS8: + do_convert_from_pkcs8(&k, &private); + break; + case FMT_PEM: + do_convert_from_pem(&k, &private); + break; + default: + fatal("%s: unknown key format %d", __func__, convert_format); } - prv = load_identity(identity_file); - if (prv == NULL) { - fprintf(stderr, "load failed\n"); + + if (!private) + ok = key_write(k, stdout); + if (ok) + fprintf(stdout, "\n"); + else { + switch (k->type) { + case KEY_DSA: + ok = PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, + NULL, 0, NULL, NULL); + break; + case KEY_RSA: + ok = PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, + NULL, 0, NULL, NULL); + break; + default: + fatal("%s: unsupported key type %s", __func__, + key_type(k)); + } + } + + if (!ok) { + fprintf(stderr, "key write failed\n"); exit(1); } - if (!key_write(prv, stdout)) - fprintf(stderr, "key_write failed"); - key_free(prv); - fprintf(stdout, "\n"); + key_free(k); exit(0); } -#ifdef SMARTCARD static void -do_upload(struct passwd *pw, const char *sc_reader_id) +do_print_public(struct passwd *pw) { - Key *prv = NULL; + Key *prv; struct stat st; - int ret; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); @@ -471,35 +646,39 @@ do_upload(struct passwd *pw, const char *sc_reader_id) } prv = load_identity(identity_file); if (prv == NULL) { - error("load failed"); + fprintf(stderr, "load failed\n"); exit(1); } - ret = sc_put_key(prv, sc_reader_id); + if (!key_write(prv, stdout)) + fprintf(stderr, "key_write failed"); key_free(prv); - if (ret < 0) - exit(1); - logit("loading key done"); + fprintf(stdout, "\n"); exit(0); } static void -do_download(struct passwd *pw, const char *sc_reader_id) +do_download(struct passwd *pw) { +#ifdef ENABLE_PKCS11 Key **keys = NULL; - int i; + int i, nkeys; - keys = sc_get_keys(sc_reader_id, NULL); - if (keys == NULL) - fatal("cannot read public key from smartcard"); - for (i = 0; keys[i]; i++) { + pkcs11_init(0); + nkeys = pkcs11_add_provider(pkcs11provider, NULL, &keys); + if (nkeys <= 0) + fatal("cannot read public key from pkcs11"); + for (i = 0; i < nkeys; i++) { key_write(keys[i], stdout); key_free(keys[i]); fprintf(stdout, "\n"); } xfree(keys); + pkcs11_terminate(); exit(0); +#else + fatal("no pkcs11 support"); +#endif /* ENABLE_PKCS11 */ } -#endif /* SMARTCARD */ static void do_fingerprint(struct passwd *pw) @@ -524,7 +703,7 @@ do_fingerprint(struct passwd *pw) public = key_load_public(identity_file, &comment); if (public != NULL) { fp = key_fingerprint(public, fptype, rep); - ra = key_fingerprint(public, fptype, SSH_FP_RANDOMART); + ra = key_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART); printf("%u %s %s (%s)\n", key_size(public), fp, comment, key_type(public)); if (log_level >= SYSLOG_LEVEL_VERBOSE) @@ -540,67 +719,68 @@ do_fingerprint(struct passwd *pw) comment = NULL; } - f = fopen(identity_file, "r"); - if (f != NULL) { - while (fgets(line, sizeof(line), f)) { - if ((cp = strchr(line, '\n')) == NULL) { - error("line %d too long: %.40s...", - num + 1, line); - skip = 1; - continue; - } - num++; - if (skip) { - skip = 0; - continue; - } - *cp = '\0'; + if ((f = fopen(identity_file, "r")) == NULL) + fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); - /* Skip leading whitespace, empty and comment lines. */ - for (cp = line; *cp == ' ' || *cp == '\t'; cp++) - ; - if (!*cp || *cp == '\n' || *cp == '#') - continue; - i = strtol(cp, &ep, 10); - if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) { - int quoted = 0; - comment = cp; - for (; *cp && (quoted || (*cp != ' ' && - *cp != '\t')); cp++) { - if (*cp == '\\' && cp[1] == '"') - cp++; /* Skip both */ - else if (*cp == '"') - quoted = !quoted; - } - if (!*cp) - continue; - *cp++ = '\0'; + while (fgets(line, sizeof(line), f)) { + if ((cp = strchr(line, '\n')) == NULL) { + error("line %d too long: %.40s...", + num + 1, line); + skip = 1; + continue; + } + num++; + if (skip) { + skip = 0; + continue; + } + *cp = '\0'; + + /* Skip leading whitespace, empty and comment lines. */ + for (cp = line; *cp == ' ' || *cp == '\t'; cp++) + ; + if (!*cp || *cp == '\n' || *cp == '#') + continue; + i = strtol(cp, &ep, 10); + if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) { + int quoted = 0; + comment = cp; + for (; *cp && (quoted || (*cp != ' ' && + *cp != '\t')); cp++) { + if (*cp == '\\' && cp[1] == '"') + cp++; /* Skip both */ + else if (*cp == '"') + quoted = !quoted; } - ep = cp; - public = key_new(KEY_RSA1); + if (!*cp) + continue; + *cp++ = '\0'; + } + ep = cp; + public = key_new(KEY_RSA1); + if (key_read(public, &cp) != 1) { + cp = ep; + key_free(public); + public = key_new(KEY_UNSPEC); if (key_read(public, &cp) != 1) { - cp = ep; key_free(public); - public = key_new(KEY_UNSPEC); - if (key_read(public, &cp) != 1) { - key_free(public); - continue; - } + continue; } - comment = *cp ? cp : comment; - fp = key_fingerprint(public, fptype, rep); - ra = key_fingerprint(public, fptype, SSH_FP_RANDOMART); - printf("%u %s %s (%s)\n", key_size(public), fp, - comment ? comment : "no comment", key_type(public)); - if (log_level >= SYSLOG_LEVEL_VERBOSE) - printf("%s\n", ra); - xfree(ra); - xfree(fp); - key_free(public); - invalid = 0; } - fclose(f); + comment = *cp ? cp : comment; + fp = key_fingerprint(public, fptype, rep); + ra = key_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART); + printf("%u %s %s (%s)\n", key_size(public), fp, + comment ? comment : "no comment", key_type(public)); + if (log_level >= SYSLOG_LEVEL_VERBOSE) + printf("%s\n", ra); + xfree(ra); + xfree(fp); + key_free(public); + invalid = 0; } + fclose(f); + if (invalid) { printf("%s is not a public key file.\n", identity_file); exit(1); @@ -609,7 +789,7 @@ do_fingerprint(struct passwd *pw) } static void -print_host(FILE *f, const char *name, Key *public, int hash) +printhost(FILE *f, const char *name, Key *public, int ca, int hash) { if (print_fingerprint) { enum fp_rep rep; @@ -619,7 +799,7 @@ print_host(FILE *f, const char *name, Key *public, int hash) fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5; rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX; fp = key_fingerprint(public, fptype, rep); - ra = key_fingerprint(public, fptype, SSH_FP_RANDOMART); + ra = key_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART); printf("%u %s %s (%s)\n", key_size(public), fp, name, key_type(public)); if (log_level >= SYSLOG_LEVEL_VERBOSE) @@ -629,7 +809,7 @@ print_host(FILE *f, const char *name, Key *public, int hash) } else { if (hash && (name = host_hash(name, NULL, 0)) == NULL) fatal("hash_host failed"); - fprintf(f, "%s ", name); + fprintf(f, "%s%s%s ", ca ? CA_MARKER : "", ca ? " " : "", name); if (!key_write(public, f)) fatal("key_write failed"); fprintf(f, "\n"); @@ -640,10 +820,11 @@ static void do_known_hosts(struct passwd *pw, const char *name) { FILE *in, *out = stdout; - Key *public; + Key *pub; char *cp, *cp2, *kp, *kp2; char line[16*1024], tmp[MAXPATHLEN], old[MAXPATHLEN]; int c, skip = 0, inplace = 0, num = 0, invalid = 0, has_unhashed = 0; + int ca; if (!have_identity) { cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); @@ -654,7 +835,7 @@ do_known_hosts(struct passwd *pw, const char *name) have_identity = 1; } if ((in = fopen(identity_file, "r")) == NULL) - fatal("fopen: %s", strerror(errno)); + fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); /* * Find hosts goes to stdout, hash and deletions happen in-place @@ -699,9 +880,19 @@ do_known_hosts(struct passwd *pw, const char *name) fprintf(out, "%s\n", cp); continue; } + /* Check whether this is a CA key */ + if (strncasecmp(cp, CA_MARKER, sizeof(CA_MARKER) - 1) == 0 && + (cp[sizeof(CA_MARKER) - 1] == ' ' || + cp[sizeof(CA_MARKER) - 1] == '\t')) { + ca = 1; + cp += sizeof(CA_MARKER); + } else + ca = 0; + /* Find the end of the host name portion. */ for (kp = cp; *kp && *kp != ' ' && *kp != '\t'; kp++) ; + if (*kp == '\0' || *(kp + 1) == '\0') { error("line %d missing key: %.40s...", num, line); @@ -711,15 +902,15 @@ do_known_hosts(struct passwd *pw, const char *name) *kp++ = '\0'; kp2 = kp; - public = key_new(KEY_RSA1); - if (key_read(public, &kp) != 1) { + pub = key_new(KEY_RSA1); + if (key_read(pub, &kp) != 1) { kp = kp2; - key_free(public); - public = key_new(KEY_UNSPEC); - if (key_read(public, &kp) != 1) { + key_free(pub); + pub = key_new(KEY_UNSPEC); + if (key_read(pub, &kp) != 1) { error("line %d invalid key: %.40s...", num, line); - key_free(public); + key_free(pub); invalid = 1; continue; } @@ -737,43 +928,52 @@ do_known_hosts(struct passwd *pw, const char *name) c = (strcmp(cp2, cp) == 0); if (find_host && c) { printf("# Host %s found: " - "line %d type %s\n", name, - num, key_type(public)); - print_host(out, cp, public, 0); + "line %d type %s%s\n", name, + num, key_type(pub), + ca ? " (CA key)" : ""); + printhost(out, cp, pub, ca, 0); } - if (delete_host && !c) - print_host(out, cp, public, 0); + if (delete_host && !c && !ca) + printhost(out, cp, pub, ca, 0); } else if (hash_hosts) - print_host(out, cp, public, 0); + printhost(out, cp, pub, ca, 0); } else { if (find_host || delete_host) { c = (match_hostname(name, cp, strlen(cp)) == 1); if (find_host && c) { printf("# Host %s found: " - "line %d type %s\n", name, - num, key_type(public)); - print_host(out, name, public, - hash_hosts); + "line %d type %s%s\n", name, + num, key_type(pub), + ca ? " (CA key)" : ""); + printhost(out, name, pub, + ca, hash_hosts && !ca); } - if (delete_host && !c) - print_host(out, cp, public, 0); + if (delete_host && !c && !ca) + printhost(out, cp, pub, ca, 0); } else if (hash_hosts) { for (cp2 = strsep(&cp, ","); cp2 != NULL && *cp2 != '\0'; cp2 = strsep(&cp, ",")) { - if (strcspn(cp2, "*?!") != strlen(cp2)) + if (ca) { + fprintf(stderr, "Warning: " + "ignoring CA key for host: " + "%.64s\n", cp2); + printhost(out, cp2, pub, ca, 0); + } else if (strcspn(cp2, "*?!") != + strlen(cp2)) { fprintf(stderr, "Warning: " "ignoring host name with " "metacharacters: %.64s\n", cp2); - else - print_host(out, cp2, public, 1); + printhost(out, cp2, pub, ca, 0); + } else + printhost(out, cp2, pub, ca, 1); } has_unhashed = 1; } } - key_free(public); + key_free(pub); } fclose(in); @@ -1030,6 +1230,483 @@ do_change_comment(struct passwd *pw) exit(0); } +static const char * +fmt_validity(u_int64_t valid_from, u_int64_t valid_to) +{ + char from[32], to[32]; + static char ret[64]; + time_t tt; + struct tm *tm; + + *from = *to = '\0'; + if (valid_from == 0 && valid_to == 0xffffffffffffffffULL) + return "forever"; + + if (valid_from != 0) { + /* XXX revisit INT_MAX in 2038 :) */ + tt = valid_from > INT_MAX ? INT_MAX : valid_from; + tm = localtime(&tt); + strftime(from, sizeof(from), "%Y-%m-%dT%H:%M:%S", tm); + } + if (valid_to != 0xffffffffffffffffULL) { + /* XXX revisit INT_MAX in 2038 :) */ + tt = valid_to > INT_MAX ? INT_MAX : valid_to; + tm = localtime(&tt); + strftime(to, sizeof(to), "%Y-%m-%dT%H:%M:%S", tm); + } + + if (valid_from == 0) { + snprintf(ret, sizeof(ret), "before %s", to); + return ret; + } + if (valid_to == 0xffffffffffffffffULL) { + snprintf(ret, sizeof(ret), "after %s", from); + return ret; + } + + snprintf(ret, sizeof(ret), "from %s to %s", from, to); + return ret; +} + +static void +add_flag_option(Buffer *c, const char *name) +{ + debug3("%s: %s", __func__, name); + buffer_put_cstring(c, name); + buffer_put_string(c, NULL, 0); +} + +static void +add_string_option(Buffer *c, const char *name, const char *value) +{ + Buffer b; + + debug3("%s: %s=%s", __func__, name, value); + buffer_init(&b); + buffer_put_cstring(&b, value); + + buffer_put_cstring(c, name); + buffer_put_string(c, buffer_ptr(&b), buffer_len(&b)); + + buffer_free(&b); +} + +#define OPTIONS_CRITICAL 1 +#define OPTIONS_EXTENSIONS 2 +static void +prepare_options_buf(Buffer *c, int which) +{ + buffer_clear(c); + if ((which & OPTIONS_CRITICAL) != 0 && + certflags_command != NULL) + add_string_option(c, "force-command", certflags_command); + if ((which & OPTIONS_EXTENSIONS) != 0 && + (certflags_flags & CERTOPT_AGENT_FWD) != 0) + add_flag_option(c, "permit-agent-forwarding"); + if ((which & OPTIONS_EXTENSIONS) != 0 && + (certflags_flags & CERTOPT_PORT_FWD) != 0) + add_flag_option(c, "permit-port-forwarding"); + if ((which & OPTIONS_EXTENSIONS) != 0 && + (certflags_flags & CERTOPT_PTY) != 0) + add_flag_option(c, "permit-pty"); + if ((which & OPTIONS_EXTENSIONS) != 0 && + (certflags_flags & CERTOPT_USER_RC) != 0) + add_flag_option(c, "permit-user-rc"); + if ((which & OPTIONS_EXTENSIONS) != 0 && + (certflags_flags & CERTOPT_X_FWD) != 0) + add_flag_option(c, "permit-X11-forwarding"); + if ((which & OPTIONS_CRITICAL) != 0 && + certflags_src_addr != NULL) + add_string_option(c, "source-address", certflags_src_addr); +} + +static Key * +load_pkcs11_key(char *path) +{ +#ifdef ENABLE_PKCS11 + Key **keys = NULL, *public, *private = NULL; + int i, nkeys; + + if ((public = key_load_public(path, NULL)) == NULL) + fatal("Couldn't load CA public key \"%s\"", path); + + nkeys = pkcs11_add_provider(pkcs11provider, identity_passphrase, &keys); + debug3("%s: %d keys", __func__, nkeys); + if (nkeys <= 0) + fatal("cannot read public key from pkcs11"); + for (i = 0; i < nkeys; i++) { + if (key_equal_public(public, keys[i])) { + private = keys[i]; + continue; + } + key_free(keys[i]); + } + xfree(keys); + key_free(public); + return private; +#else + fatal("no pkcs11 support"); +#endif /* ENABLE_PKCS11 */ +} + +static void +do_ca_sign(struct passwd *pw, int argc, char **argv) +{ + int i, fd; + u_int n; + Key *ca, *public; + char *otmp, *tmp, *cp, *out, *comment, **plist = NULL; + FILE *f; + int v00 = 0; /* legacy keys */ + + if (key_type_name != NULL) { + switch (key_type_from_name(key_type_name)) { + case KEY_RSA_CERT_V00: + case KEY_DSA_CERT_V00: + v00 = 1; + break; + case KEY_UNSPEC: + if (strcasecmp(key_type_name, "v00") == 0) { + v00 = 1; + break; + } else if (strcasecmp(key_type_name, "v01") == 0) + break; + /* FALLTHROUGH */ + default: + fprintf(stderr, "unknown key type %s\n", key_type_name); + exit(1); + } + } + + pkcs11_init(1); + tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); + if (pkcs11provider != NULL) { + if ((ca = load_pkcs11_key(tmp)) == NULL) + fatal("No PKCS#11 key matching %s found", ca_key_path); + } else if ((ca = load_identity(tmp)) == NULL) + fatal("Couldn't load CA key \"%s\"", tmp); + xfree(tmp); + + for (i = 0; i < argc; i++) { + /* Split list of principals */ + n = 0; + if (cert_principals != NULL) { + otmp = tmp = xstrdup(cert_principals); + plist = NULL; + for (; (cp = strsep(&tmp, ",")) != NULL; n++) { + plist = xrealloc(plist, n + 1, sizeof(*plist)); + if (*(plist[n] = xstrdup(cp)) == '\0') + fatal("Empty principal name"); + } + xfree(otmp); + } + + tmp = tilde_expand_filename(argv[i], pw->pw_uid); + if ((public = key_load_public(tmp, &comment)) == NULL) + fatal("%s: unable to open \"%s\"", __func__, tmp); + if (public->type != KEY_RSA && public->type != KEY_DSA) + fatal("%s: key \"%s\" type %s cannot be certified", + __func__, tmp, key_type(public)); + + /* Prepare certificate to sign */ + if (key_to_certified(public, v00) != 0) + fatal("Could not upgrade key %s to certificate", tmp); + public->cert->type = cert_key_type; + public->cert->serial = (u_int64_t)cert_serial; + public->cert->key_id = xstrdup(cert_key_id); + public->cert->nprincipals = n; + public->cert->principals = plist; + public->cert->valid_after = cert_valid_from; + public->cert->valid_before = cert_valid_to; + if (v00) { + prepare_options_buf(&public->cert->critical, + OPTIONS_CRITICAL|OPTIONS_EXTENSIONS); + } else { + prepare_options_buf(&public->cert->critical, + OPTIONS_CRITICAL); + prepare_options_buf(&public->cert->extensions, + OPTIONS_EXTENSIONS); + } + public->cert->signature_key = key_from_private(ca); + + if (key_certify(public, ca) != 0) + fatal("Couldn't not certify key %s", tmp); + + if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0) + *cp = '\0'; + xasprintf(&out, "%s-cert.pub", tmp); + xfree(tmp); + + if ((fd = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) + fatal("Could not open \"%s\" for writing: %s", out, + strerror(errno)); + if ((f = fdopen(fd, "w")) == NULL) + fatal("%s: fdopen: %s", __func__, strerror(errno)); + if (!key_write(public, f)) + fatal("Could not write certified key to %s", out); + fprintf(f, " %s\n", comment); + fclose(f); + + if (!quiet) { + logit("Signed %s key %s: id \"%s\" serial %llu%s%s " + "valid %s", key_cert_type(public), + out, public->cert->key_id, public->cert->serial, + cert_principals != NULL ? " for " : "", + cert_principals != NULL ? cert_principals : "", + fmt_validity(cert_valid_from, cert_valid_to)); + } + + key_free(public); + xfree(out); + } + pkcs11_terminate(); + exit(0); +} + +static u_int64_t +parse_relative_time(const char *s, time_t now) +{ + int64_t mul, secs; + + mul = *s == '-' ? -1 : 1; + + if ((secs = convtime(s + 1)) == -1) + fatal("Invalid relative certificate time %s", s); + if (mul == -1 && secs > now) + fatal("Certificate time %s cannot be represented", s); + return now + (u_int64_t)(secs * mul); +} + +static u_int64_t +parse_absolute_time(const char *s) +{ + struct tm tm; + time_t tt; + char buf[32], *fmt; + + /* + * POSIX strptime says "The application shall ensure that there + * is white-space or other non-alphanumeric characters between + * any two conversion specifications" so arrange things this way. + */ + switch (strlen(s)) { + case 8: + fmt = "%Y-%m-%d"; + snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2s", s, s + 4, s + 6); + break; + case 14: + fmt = "%Y-%m-%dT%H:%M:%S"; + snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s:%.2s", + s, s + 4, s + 6, s + 8, s + 10, s + 12); + break; + default: + fatal("Invalid certificate time format %s", s); + } + + bzero(&tm, sizeof(tm)); + if (strptime(buf, fmt, &tm) == NULL) + fatal("Invalid certificate time %s", s); + if ((tt = mktime(&tm)) < 0) + fatal("Certificate time %s cannot be represented", s); + return (u_int64_t)tt; +} + +static void +parse_cert_times(char *timespec) +{ + char *from, *to; + time_t now = time(NULL); + int64_t secs; + + /* +timespec relative to now */ + if (*timespec == '+' && strchr(timespec, ':') == NULL) { + if ((secs = convtime(timespec + 1)) == -1) + fatal("Invalid relative certificate life %s", timespec); + cert_valid_to = now + secs; + /* + * Backdate certificate one minute to avoid problems on hosts + * with poorly-synchronised clocks. + */ + cert_valid_from = ((now - 59)/ 60) * 60; + return; + } + + /* + * from:to, where + * from := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS + * to := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS + */ + from = xstrdup(timespec); + to = strchr(from, ':'); + if (to == NULL || from == to || *(to + 1) == '\0') + fatal("Invalid certificate life specification %s", timespec); + *to++ = '\0'; + + if (*from == '-' || *from == '+') + cert_valid_from = parse_relative_time(from, now); + else + cert_valid_from = parse_absolute_time(from); + + if (*to == '-' || *to == '+') + cert_valid_to = parse_relative_time(to, cert_valid_from); + else + cert_valid_to = parse_absolute_time(to); + + if (cert_valid_to <= cert_valid_from) + fatal("Empty certificate validity interval"); + xfree(from); +} + +static void +add_cert_option(char *opt) +{ + char *val; + + if (strcmp(opt, "clear") == 0) + certflags_flags = 0; + else if (strcasecmp(opt, "no-x11-forwarding") == 0) + certflags_flags &= ~CERTOPT_X_FWD; + else if (strcasecmp(opt, "permit-x11-forwarding") == 0) + certflags_flags |= CERTOPT_X_FWD; + else if (strcasecmp(opt, "no-agent-forwarding") == 0) + certflags_flags &= ~CERTOPT_AGENT_FWD; + else if (strcasecmp(opt, "permit-agent-forwarding") == 0) + certflags_flags |= CERTOPT_AGENT_FWD; + else if (strcasecmp(opt, "no-port-forwarding") == 0) + certflags_flags &= ~CERTOPT_PORT_FWD; + else if (strcasecmp(opt, "permit-port-forwarding") == 0) + certflags_flags |= CERTOPT_PORT_FWD; + else if (strcasecmp(opt, "no-pty") == 0) + certflags_flags &= ~CERTOPT_PTY; + else if (strcasecmp(opt, "permit-pty") == 0) + certflags_flags |= CERTOPT_PTY; + else if (strcasecmp(opt, "no-user-rc") == 0) + certflags_flags &= ~CERTOPT_USER_RC; + else if (strcasecmp(opt, "permit-user-rc") == 0) + certflags_flags |= CERTOPT_USER_RC; + else if (strncasecmp(opt, "force-command=", 14) == 0) { + val = opt + 14; + if (*val == '\0') + fatal("Empty force-command option"); + if (certflags_command != NULL) + fatal("force-command already specified"); + certflags_command = xstrdup(val); + } else if (strncasecmp(opt, "source-address=", 15) == 0) { + val = opt + 15; + if (*val == '\0') + fatal("Empty source-address option"); + if (certflags_src_addr != NULL) + fatal("source-address already specified"); + if (addr_match_cidr_list(NULL, val) != 0) + fatal("Invalid source-address list"); + certflags_src_addr = xstrdup(val); + } else + fatal("Unsupported certificate option \"%s\"", opt); +} + +static void +show_options(const Buffer *optbuf, int v00, int in_critical) +{ + u_char *name, *data; + u_int dlen; + Buffer options, option; + + buffer_init(&options); + buffer_append(&options, buffer_ptr(optbuf), buffer_len(optbuf)); + + buffer_init(&option); + while (buffer_len(&options) != 0) { + name = buffer_get_string(&options, NULL); + data = buffer_get_string_ptr(&options, &dlen); + buffer_append(&option, data, dlen); + printf(" %s", name); + if ((v00 || !in_critical) && + (strcmp(name, "permit-X11-forwarding") == 0 || + strcmp(name, "permit-agent-forwarding") == 0 || + strcmp(name, "permit-port-forwarding") == 0 || + strcmp(name, "permit-pty") == 0 || + strcmp(name, "permit-user-rc") == 0)) + printf("\n"); + else if ((v00 || in_critical) && + (strcmp(name, "force-command") == 0 || + strcmp(name, "source-address") == 0)) { + data = buffer_get_string(&option, NULL); + printf(" %s\n", data); + xfree(data); + } else { + printf(" UNKNOWN OPTION (len %u)\n", + buffer_len(&option)); + buffer_clear(&option); + } + xfree(name); + if (buffer_len(&option) != 0) + fatal("Option corrupt: extra data at end"); + } + buffer_free(&option); + buffer_free(&options); +} + +static void +do_show_cert(struct passwd *pw) +{ + Key *key; + struct stat st; + char *key_fp, *ca_fp; + u_int i, v00; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) < 0) + fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); + if ((key = key_load_public(identity_file, NULL)) == NULL) + fatal("%s is not a public key", identity_file); + if (!key_is_cert(key)) + fatal("%s is not a certificate", identity_file); + v00 = key->type == KEY_RSA_CERT_V00 || key->type == KEY_DSA_CERT_V00; + + key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + ca_fp = key_fingerprint(key->cert->signature_key, + SSH_FP_MD5, SSH_FP_HEX); + + printf("%s:\n", identity_file); + printf(" Type: %s %s certificate\n", key_ssh_name(key), + key_cert_type(key)); + printf(" Public key: %s %s\n", key_type(key), key_fp); + printf(" Signing CA: %s %s\n", + key_type(key->cert->signature_key), ca_fp); + printf(" Key ID: \"%s\"\n", key->cert->key_id); + if (!v00) + printf(" Serial: %llu\n", key->cert->serial); + printf(" Valid: %s\n", + fmt_validity(key->cert->valid_after, key->cert->valid_before)); + printf(" Principals: "); + if (key->cert->nprincipals == 0) + printf("(none)\n"); + else { + for (i = 0; i < key->cert->nprincipals; i++) + printf("\n %s", + key->cert->principals[i]); + printf("\n"); + } + printf(" Critical Options: "); + if (buffer_len(&key->cert->critical) == 0) + printf("(none)\n"); + else { + printf("\n"); + show_options(&key->cert->critical, v00, 1); + } + if (!v00) { + printf(" Extensions: "); + if (buffer_len(&key->cert->extensions) == 0) + printf("(none)\n"); + else { + printf("\n"); + show_options(&key->cert->extensions, v00, 0); + } + } + exit(0); +} + static void usage(void) { @@ -1040,33 +1717,39 @@ usage(void) fprintf(stderr, " -b bits Number of bits in the key to create.\n"); fprintf(stderr, " -C comment Provide new comment.\n"); fprintf(stderr, " -c Change comment in private and public key files.\n"); -#ifdef SMARTCARD - fprintf(stderr, " -D reader Download public key from smartcard.\n"); -#endif /* SMARTCARD */ - fprintf(stderr, " -e Convert OpenSSH to RFC 4716 key file.\n"); +#ifdef ENABLE_PKCS11 + fprintf(stderr, " -D pkcs11 Download public key from pkcs11 token.\n"); +#endif + fprintf(stderr, " -e Export OpenSSH to foreign format key file.\n"); fprintf(stderr, " -F hostname Find hostname in known hosts file.\n"); fprintf(stderr, " -f filename Filename of the key file.\n"); fprintf(stderr, " -G file Generate candidates for DH-GEX moduli.\n"); fprintf(stderr, " -g Use generic DNS resource record format.\n"); fprintf(stderr, " -H Hash names in known_hosts file.\n"); - fprintf(stderr, " -i Convert RFC 4716 to OpenSSH key file.\n"); + fprintf(stderr, " -h Generate host certificate instead of a user certificate.\n"); + fprintf(stderr, " -I key_id Key identifier to include in certificate.\n"); + fprintf(stderr, " -i Import foreign format to OpenSSH key file.\n"); + fprintf(stderr, " -L Print the contents of a certificate.\n"); fprintf(stderr, " -l Show fingerprint of key file.\n"); fprintf(stderr, " -M memory Amount of memory (MB) to use for generating DH-GEX moduli.\n"); + fprintf(stderr, " -m key_fmt Conversion format for -e/-i (PEM|PKCS8|RFC4716).\n"); fprintf(stderr, " -N phrase Provide new passphrase.\n"); + fprintf(stderr, " -n name,... User/host principal names to include in certificate\n"); + fprintf(stderr, " -O option Specify a certificate option.\n"); fprintf(stderr, " -P phrase Provide old passphrase.\n"); fprintf(stderr, " -p Change passphrase of private key file.\n"); fprintf(stderr, " -q Quiet.\n"); fprintf(stderr, " -R hostname Remove host from known_hosts file.\n"); fprintf(stderr, " -r hostname Print DNS resource record.\n"); fprintf(stderr, " -S start Start point (hex) for generating DH-GEX moduli.\n"); + fprintf(stderr, " -s ca_key Certify keys with CA key.\n"); fprintf(stderr, " -T file Screen candidates for DH-GEX moduli.\n"); fprintf(stderr, " -t type Specify type of key to create.\n"); -#ifdef SMARTCARD - fprintf(stderr, " -U reader Upload private key to smartcard.\n"); -#endif /* SMARTCARD */ + fprintf(stderr, " -V from:to Specify certificate validity interval.\n"); fprintf(stderr, " -v Verbose.\n"); fprintf(stderr, " -W gen Generator to use for generating DH-GEX moduli.\n"); fprintf(stderr, " -y Read private key file and print public key.\n"); + fprintf(stderr, " -z serial Specify a serial number.\n"); exit(1); } @@ -1078,12 +1761,12 @@ int main(int argc, char **argv) { char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2; - char out_file[MAXPATHLEN], *reader_id = NULL; - char *rr_hostname = NULL; + char out_file[MAXPATHLEN], *rr_hostname = NULL; Key *private, *public; struct passwd *pw; struct stat st; - int opt, type, fd, download = 0; + int opt, type, fd; + u_int maxbits; u_int32_t memory = 0, generator_wanted = 0, trials = 100; int do_gen_candidates = 0, do_screen_candidates = 0; BIGNUM *start = NULL; @@ -1115,8 +1798,8 @@ main(int argc, char **argv) exit(1); } - while ((opt = getopt(argc, argv, - "degiqpclBHvxXyF:b:f:t:U:D:P:N:C:r:g:R:T:G:M:S:a:W:")) != -1) { + while ((opt = getopt(argc, argv, "degiqpclBHLhvxXyF:b:f:t:D:I:P:m:N:n:" + "O:C:r:g:R:T:G:M:S:s:a:V:W:z:")) != -1) { switch (opt) { case 'b': bits = (u_int32_t)strtonum(optarg, 768, 32768, &errstr); @@ -1131,16 +1814,40 @@ main(int argc, char **argv) case 'H': hash_hosts = 1; break; + case 'I': + cert_key_id = optarg; + break; case 'R': delete_host = 1; rr_hostname = optarg; break; + case 'L': + show_cert = 1; + break; case 'l': print_fingerprint = 1; break; case 'B': print_bubblebabble = 1; break; + case 'm': + if (strcasecmp(optarg, "RFC4716") == 0 || + strcasecmp(optarg, "ssh2") == 0) { + convert_format = FMT_RFC4716; + break; + } + if (strcasecmp(optarg, "PKCS8") == 0) { + convert_format = FMT_PKCS8; + break; + } + if (strcasecmp(optarg, "PEM") == 0) { + convert_format = FMT_PEM; + break; + } + fatal("Unsupported conversion format \"%s\"", optarg); + case 'n': + cert_principals = optarg; + break; case 'p': change_passphrase = 1; break; @@ -1162,6 +1869,9 @@ main(int argc, char **argv) case 'N': identity_new_passphrase = optarg; break; + case 'O': + add_cert_option(optarg); + break; case 'C': identity_comment = optarg; break; @@ -1171,12 +1881,16 @@ main(int argc, char **argv) case 'e': case 'x': /* export key */ - convert_to_ssh2 = 1; + convert_to = 1; + break; + case 'h': + cert_key_type = SSH2_CERT_TYPE_HOST; + certflags_flags = 0; break; case 'i': case 'X': /* import key */ - convert_from_ssh2 = 1; + convert_from = 1; break; case 'y': print_public = 1; @@ -1184,14 +1898,14 @@ main(int argc, char **argv) case 'd': key_type_name = "dsa"; break; + case 's': + ca_key_path = optarg; + break; case 't': key_type_name = optarg; break; case 'D': - download = 1; - /*FALLTHROUGH*/ - case 'U': - reader_id = optarg; + pkcs11provider = optarg; break; case 'v': if (log_level == SYSLOG_LEVEL_INFO) @@ -1220,9 +1934,8 @@ main(int argc, char **argv) break; case 'M': memory = (u_int32_t)strtonum(optarg, 1, UINT_MAX, &errstr); - if (errstr) { + if (errstr) fatal("Memory limit is %s: %s", errstr, optarg); - } break; case 'G': do_gen_candidates = 1; @@ -1241,6 +1954,14 @@ main(int argc, char **argv) if (BN_hex2bn(&start, optarg) == 0) fatal("Invalid start point."); break; + case 'V': + parse_cert_times(optarg); + break; + case 'z': + cert_serial = strtonum(optarg, 0, LLONG_MAX, &errstr); + if (errstr) + fatal("Invalid serial number: %s", errstr); + break; case '?': default: usage(); @@ -1250,7 +1971,15 @@ main(int argc, char **argv) /* reinit */ log_init(argv[0], log_level, SYSLOG_FACILITY_USER, 1); - if (optind < argc) { + argv += optind; + argc -= optind; + + if (ca_key_path != NULL) { + if (argc < 1) { + printf("Too few arguments.\n"); + usage(); + } + } else if (argc > 0) { printf("Too many arguments.\n"); usage(); } @@ -1262,6 +1991,13 @@ main(int argc, char **argv) printf("Cannot use -l with -D or -R.\n"); usage(); } + if (ca_key_path != NULL) { + if (cert_key_id == NULL) + fatal("Must specify key id (-I) when certifying"); + do_ca_sign(pw, argc, argv); + } + if (show_cert) + do_show_cert(pw); if (delete_host || hash_hosts || find_host) do_known_hosts(pw, rr_hostname); if (print_fingerprint || print_bubblebabble) @@ -1270,10 +2006,10 @@ main(int argc, char **argv) do_change_passphrase(pw); if (change_comment) do_change_comment(pw); - if (convert_to_ssh2) - do_convert_to_ssh2(pw); - if (convert_from_ssh2) - do_convert_from_ssh2(pw); + if (convert_to) + do_convert_to(pw); + if (convert_from) + do_convert_from(pw); if (print_public) do_print_public(pw); if (rr_hostname != NULL) { @@ -1299,16 +2035,8 @@ main(int argc, char **argv) exit(0); } } - if (reader_id != NULL) { -#ifdef SMARTCARD - if (download) - do_download(pw, reader_id); - else - do_upload(pw, reader_id); -#else /* SMARTCARD */ - fatal("no support for smartcards."); -#endif /* SMARTCARD */ - } + if (pkcs11provider != NULL) + do_download(pw); if (do_gen_candidates) { FILE *out = fopen(out_file, "w"); @@ -1360,6 +2088,12 @@ main(int argc, char **argv) } if (bits == 0) bits = (type == KEY_DSA) ? DEFAULT_BITS_DSA : DEFAULT_BITS; + maxbits = (type == KEY_DSA) ? + OPENSSL_DSA_MAX_MODULUS_BITS : OPENSSL_RSA_MAX_MODULUS_BITS; + if (bits > maxbits) { + fprintf(stderr, "key bits exceeds maximum %d\n", maxbits); + exit(1); + } if (type == KEY_DSA && bits != 1024) fatal("DSA keys must be 1024 bits"); if (!quiet) @@ -1375,13 +2109,19 @@ main(int argc, char **argv) ask_filename(pw, "Enter file in which to save the key"); /* Create ~/.ssh directory if it doesn't already exist. */ - snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, _PATH_SSH_USER_DIR); - if (strstr(identity_file, dotsshdir) != NULL && - stat(dotsshdir, &st) < 0) { - if (mkdir(dotsshdir, 0700) < 0) - error("Could not create directory '%s'.", dotsshdir); - else if (!quiet) - printf("Created directory '%s'.\n", dotsshdir); + snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", + pw->pw_dir, _PATH_SSH_USER_DIR); + if (strstr(identity_file, dotsshdir) != NULL) { + if (stat(dotsshdir, &st) < 0) { + if (errno != ENOENT) { + error("Could not stat %s: %s", dotsshdir, + strerror(errno)); + } else if (mkdir(dotsshdir, 0700) < 0) { + error("Could not create directory '%s': %s", + dotsshdir, strerror(errno)); + } else if (!quiet) + printf("Created directory '%s'.\n", dotsshdir); + } } /* If the file already exists, ask the user to confirm. */ if (stat(identity_file, &st) >= 0) { diff --git a/crypto/openssh/ssh-keyscan.1 b/crypto/openssh/ssh-keyscan.1 index 4a58645665..78255ff790 100644 --- a/crypto/openssh/ssh-keyscan.1 +++ b/crypto/openssh/ssh-keyscan.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-keyscan.1,v 1.26 2008/12/29 01:12:36 stevesk Exp $ +.\" $OpenBSD: ssh-keyscan.1,v 1.28 2010/01/09 23:04:13 dtucker Exp $ .\" .\" Copyright 1995, 1996 by David Mazieres . .\" @@ -6,7 +6,7 @@ .\" permitted provided that due credit is given to the author and the .\" OpenBSD project by leaving this copyright notice intact. .\" -.Dd $Mdocdate: December 29 2008 $ +.Dd $Mdocdate: January 9 2010 $ .Dt SSH-KEYSCAN 1 .Os .Sh NAME diff --git a/crypto/openssh/ssh-keyscan.c b/crypto/openssh/ssh-keyscan.c index 9a91be499e..b6cf427cd6 100644 --- a/crypto/openssh/ssh-keyscan.c +++ b/crypto/openssh/ssh-keyscan.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keyscan.c,v 1.78 2009/01/22 10:02:34 djm Exp $ */ +/* $OpenBSD: ssh-keyscan.c,v 1.82 2010/06/22 04:54:30 djm Exp $ */ /* * Copyright 1995, 1996 by David Mazieres . * @@ -104,122 +104,6 @@ typedef struct Connection { TAILQ_HEAD(conlist, Connection) tq; /* Timeout Queue */ con *fdcon; -/* - * This is just a wrapper around fgets() to make it usable. - */ - -/* Stress-test. Increase this later. */ -#define LINEBUF_SIZE 16 - -typedef struct { - char *buf; - u_int size; - int lineno; - const char *filename; - FILE *stream; - void (*errfun) (const char *,...); -} Linebuf; - -static Linebuf * -Linebuf_alloc(const char *filename, void (*errfun) (const char *,...)) -{ - Linebuf *lb; - - if (!(lb = malloc(sizeof(*lb)))) { - if (errfun) - (*errfun) ("linebuf (%s): malloc failed\n", - filename ? filename : "(stdin)"); - return (NULL); - } - if (filename) { - lb->filename = filename; - if (!(lb->stream = fopen(filename, "r"))) { - xfree(lb); - if (errfun) - (*errfun) ("%s: %s\n", filename, strerror(errno)); - return (NULL); - } - } else { - lb->filename = "(stdin)"; - lb->stream = stdin; - } - - if (!(lb->buf = malloc((lb->size = LINEBUF_SIZE)))) { - if (errfun) - (*errfun) ("linebuf (%s): malloc failed\n", lb->filename); - xfree(lb); - return (NULL); - } - lb->errfun = errfun; - lb->lineno = 0; - return (lb); -} - -static void -Linebuf_free(Linebuf * lb) -{ - fclose(lb->stream); - xfree(lb->buf); - xfree(lb); -} - -#if 0 -static void -Linebuf_restart(Linebuf * lb) -{ - clearerr(lb->stream); - rewind(lb->stream); - lb->lineno = 0; -} - -static int -Linebuf_lineno(Linebuf * lb) -{ - return (lb->lineno); -} -#endif - -static char * -Linebuf_getline(Linebuf * lb) -{ - size_t n = 0; - void *p; - - lb->lineno++; - for (;;) { - /* Read a line */ - if (!fgets(&lb->buf[n], lb->size - n, lb->stream)) { - if (ferror(lb->stream) && lb->errfun) - (*lb->errfun)("%s: %s\n", lb->filename, - strerror(errno)); - return (NULL); - } - n = strlen(lb->buf); - - /* Return it or an error if it fits */ - if (n > 0 && lb->buf[n - 1] == '\n') { - lb->buf[n - 1] = '\0'; - return (lb->buf); - } - if (n != lb->size - 1) { - if (lb->errfun) - (*lb->errfun)("%s: skipping incomplete last line\n", - lb->filename); - return (NULL); - } - /* Double the buffer if we need more space */ - lb->size *= 2; - if ((p = realloc(lb->buf, lb->size)) == NULL) { - lb->size /= 2; - if (lb->errfun) - (*lb->errfun)("linebuf (%s): realloc failed\n", - lb->filename); - return (NULL); - } - lb->buf = p; - } -} - static int fdlim_get(int hard) { @@ -724,8 +608,10 @@ int main(int argc, char **argv) { int debug_flag = 0, log_level = SYSLOG_LEVEL_INFO; - int opt, fopt_count = 0; - char *tname; + int opt, fopt_count = 0, j; + char *tname, *cp, line[NI_MAXHOST]; + FILE *fp; + u_long linenum; extern int optind; extern char *optarg; @@ -826,19 +712,40 @@ main(int argc, char **argv) read_wait_nfdset = howmany(maxfd, NFDBITS); read_wait = xcalloc(read_wait_nfdset, sizeof(fd_mask)); - if (fopt_count) { - Linebuf *lb; - char *line; - int j; + for (j = 0; j < fopt_count; j++) { + if (argv[j] == NULL) + fp = stdin; + else if ((fp = fopen(argv[j], "r")) == NULL) + fatal("%s: %s: %s", __progname, argv[j], + strerror(errno)); + linenum = 0; + + while (read_keyfile_line(fp, + argv[j] == NULL ? "(stdin)" : argv[j], line, sizeof(line), + &linenum) != -1) { + /* Chomp off trailing whitespace and comments */ + if ((cp = strchr(line, '#')) == NULL) + cp = line + strlen(line) - 1; + while (cp >= line) { + if (*cp == ' ' || *cp == '\t' || + *cp == '\n' || *cp == '#') + *cp-- = '\0'; + else + break; + } - for (j = 0; j < fopt_count; j++) { - lb = Linebuf_alloc(argv[j], error); - if (!lb) + /* Skip empty lines */ + if (*line == '\0') continue; - while ((line = Linebuf_getline(lb)) != NULL) - do_host(line); - Linebuf_free(lb); + + do_host(line); } + + if (ferror(fp)) + fatal("%s: %s: %s", __progname, argv[j], + strerror(errno)); + + fclose(fp); } while (optind < argc) diff --git a/crypto/openssh/ssh-keysign.8 b/crypto/openssh/ssh-keysign.8 index 3ba54b935f..46c0ee9cd9 100644 --- a/crypto/openssh/ssh-keysign.8 +++ b/crypto/openssh/ssh-keysign.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-keysign.8,v 1.9 2007/05/31 19:20:16 jmc Exp $ +.\" $OpenBSD: ssh-keysign.8,v 1.10 2010/08/04 05:42:47 djm Exp $ .\" .\" Copyright (c) 2002 Markus Friedl. All rights reserved. .\" @@ -22,7 +22,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: May 31 2007 $ +.Dd $Mdocdate: August 4 2010 $ .Dt SSH-KEYSIGN 8 .Os .Sh NAME @@ -68,6 +68,9 @@ accessible to others. Since they are readable only by root, .Nm must be set-uid root if host-based authentication is used. +.It Pa /etc/ssh/ssh_host_dsa_key-cert.pub, /etc/ssh/ssh_host_rsa_key-cert.pub +If these files exist they are assumed to contain public certificate +information corresponding with the private keys above. .El .Sh SEE ALSO .Xr ssh 1 , diff --git a/crypto/openssh/ssh-keysign.c b/crypto/openssh/ssh-keysign.c index c4bc7e56e5..0c7077050f 100644 --- a/crypto/openssh/ssh-keysign.c +++ b/crypto/openssh/ssh-keysign.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keysign.c,v 1.29 2006/08/03 03:34:42 deraadt Exp $ */ +/* $OpenBSD: ssh-keysign.c,v 1.32 2010/08/04 06:08:40 djm Exp $ */ /* * Copyright (c) 2002 Markus Friedl. All rights reserved. * @@ -222,7 +222,7 @@ main(int argc, char **argv) if ((fd == STDIN_FILENO) || (fd == STDOUT_FILENO)) fatal("bad fd"); if ((host = get_local_name(fd)) == NULL) - fatal("cannot get sockname for fd"); + fatal("cannot get local name for fd"); data = buffer_get_string(&b, &dlen); if (valid_request(pw, host, &key, data, dlen) < 0) @@ -232,7 +232,7 @@ main(int argc, char **argv) found = 0; for (i = 0; i < 2; i++) { if (keys[i] != NULL && - key_equal(key, keys[i])) { + key_equal_public(key, keys[i])) { found = 1; break; } diff --git a/crypto/openssh/ssh-pkcs11-client.c b/crypto/openssh/ssh-pkcs11-client.c new file mode 100644 index 0000000000..650c373427 --- /dev/null +++ b/crypto/openssh/ssh-pkcs11-client.c @@ -0,0 +1,238 @@ +/* $OpenBSD: ssh-pkcs11-client.c,v 1.2 2010/02/24 06:12:53 djm Exp $ */ +/* + * Copyright (c) 2010 Markus Friedl. All rights reserved. + * + * 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" + +#ifdef ENABLE_PKCS11 + +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif +#include + +#include +#include +#include +#include + +#include "pathnames.h" +#include "xmalloc.h" +#include "buffer.h" +#include "log.h" +#include "misc.h" +#include "key.h" +#include "authfd.h" +#include "atomicio.h" +#include "ssh-pkcs11.h" + +/* borrows code from sftp-server and ssh-agent */ + +int fd = -1; +pid_t pid = -1; + +static void +send_msg(Buffer *m) +{ + u_char buf[4]; + int mlen = buffer_len(m); + + put_u32(buf, mlen); + if (atomicio(vwrite, fd, buf, 4) != 4 || + atomicio(vwrite, fd, buffer_ptr(m), + buffer_len(m)) != buffer_len(m)) + error("write to helper failed"); + buffer_consume(m, mlen); +} + +static int +recv_msg(Buffer *m) +{ + u_int l, len; + u_char buf[1024]; + + if ((len = atomicio(read, fd, buf, 4)) != 4) { + error("read from helper failed: %u", len); + return (0); /* XXX */ + } + len = get_u32(buf); + if (len > 256 * 1024) + fatal("response too long: %u", len); + /* read len bytes into m */ + buffer_clear(m); + while (len > 0) { + l = len; + if (l > sizeof(buf)) + l = sizeof(buf); + if (atomicio(read, fd, buf, l) != l) { + error("response from helper failed."); + return (0); /* XXX */ + } + buffer_append(m, buf, l); + len -= l; + } + return (buffer_get_char(m)); +} + +int +pkcs11_init(int interactive) +{ + return (0); +} + +void +pkcs11_terminate(void) +{ + close(fd); +} + +static int +pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, + int padding) +{ + Key key; + u_char *blob, *signature = NULL; + u_int blen, slen = 0; + int ret = -1; + Buffer msg; + + if (padding != RSA_PKCS1_PADDING) + return (-1); + key.type = KEY_RSA; + key.rsa = rsa; + if (key_to_blob(&key, &blob, &blen) == 0) + return -1; + buffer_init(&msg); + buffer_put_char(&msg, SSH2_AGENTC_SIGN_REQUEST); + buffer_put_string(&msg, blob, blen); + buffer_put_string(&msg, from, flen); + buffer_put_int(&msg, 0); + xfree(blob); + send_msg(&msg); + + if (recv_msg(&msg) == SSH2_AGENT_SIGN_RESPONSE) { + signature = buffer_get_string(&msg, &slen); + if (slen <= (u_int)RSA_size(rsa)) { + memcpy(to, signature, slen); + ret = slen; + } + xfree(signature); + } + return (ret); +} + +/* redirect the private key encrypt operation to the ssh-pkcs11-helper */ +static int +wrap_key(RSA *rsa) +{ + static RSA_METHOD helper_rsa; + + memcpy(&helper_rsa, RSA_get_default_method(), sizeof(helper_rsa)); + helper_rsa.name = "ssh-pkcs11-helper"; + helper_rsa.rsa_priv_enc = pkcs11_rsa_private_encrypt; + RSA_set_method(rsa, &helper_rsa); + return (0); +} + +static int +pkcs11_start_helper(void) +{ + int pair[2]; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) { + error("socketpair: %s", strerror(errno)); + return (-1); + } + if ((pid = fork()) == -1) { + error("fork: %s", strerror(errno)); + return (-1); + } else if (pid == 0) { + if ((dup2(pair[1], STDIN_FILENO) == -1) || + (dup2(pair[1], STDOUT_FILENO) == -1)) { + fprintf(stderr, "dup2: %s\n", strerror(errno)); + _exit(1); + } + close(pair[0]); + close(pair[1]); + execlp(_PATH_SSH_PKCS11_HELPER, _PATH_SSH_PKCS11_HELPER, + (char *) 0); + fprintf(stderr, "exec: %s: %s\n", _PATH_SSH_PKCS11_HELPER, + strerror(errno)); + _exit(1); + } + close(pair[1]); + fd = pair[0]; + return (0); +} + +int +pkcs11_add_provider(char *name, char *pin, Key ***keysp) +{ + Key *k; + int i, nkeys; + u_char *blob; + u_int blen; + Buffer msg; + + if (fd < 0 && pkcs11_start_helper() < 0) + return (-1); + + buffer_init(&msg); + buffer_put_char(&msg, SSH_AGENTC_ADD_SMARTCARD_KEY); + buffer_put_cstring(&msg, name); + buffer_put_cstring(&msg, pin); + send_msg(&msg); + buffer_clear(&msg); + + if (recv_msg(&msg) == SSH2_AGENT_IDENTITIES_ANSWER) { + nkeys = buffer_get_int(&msg); + *keysp = xcalloc(nkeys, sizeof(Key *)); + for (i = 0; i < nkeys; i++) { + blob = buffer_get_string(&msg, &blen); + xfree(buffer_get_string(&msg, NULL)); + k = key_from_blob(blob, blen); + wrap_key(k->rsa); + (*keysp)[i] = k; + xfree(blob); + } + } else { + nkeys = -1; + } + buffer_free(&msg); + return (nkeys); +} + +int +pkcs11_del_provider(char *name) +{ + int ret = -1; + Buffer msg; + + buffer_init(&msg); + buffer_put_char(&msg, SSH_AGENTC_REMOVE_SMARTCARD_KEY); + buffer_put_cstring(&msg, name); + buffer_put_cstring(&msg, ""); + send_msg(&msg); + buffer_clear(&msg); + + if (recv_msg(&msg) == SSH_AGENT_SUCCESS) + ret = 0; + buffer_free(&msg); + return (ret); +} + +#endif /* ENABLE_PKCS11 */ diff --git a/crypto/openssh/ssh-pkcs11-helper.8 b/crypto/openssh/ssh-pkcs11-helper.8 new file mode 100644 index 0000000000..9bdaadc015 --- /dev/null +++ b/crypto/openssh/ssh-pkcs11-helper.8 @@ -0,0 +1,43 @@ +.\" $OpenBSD: ssh-pkcs11-helper.8,v 1.3 2010/02/10 23:20:38 markus Exp $ +.\" +.\" Copyright (c) 2010 Markus Friedl. All rights reserved. +.\" +.\" 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: February 10 2010 $ +.Dt SSH-PKCS11-HELPER 8 +.Os +.Sh NAME +.Nm ssh-pkcs11-helper +.Nd ssh-agent helper program for PKCS#11 support +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +.Nm +is used by +.Xr ssh-agent 1 +to access keys provided by a PKCS#11 token. +.Pp +.Nm +is not intended to be invoked by the user, but from +.Xr ssh-agent 1 . +.Sh SEE ALSO +.Xr ssh 1 , +.Xr ssh-add 1 , +.Xr ssh-agent 1 +.Sh HISTORY +.Nm +first appeared in +.Ox 4.7 . +.Sh AUTHORS +.An Markus Friedl Aq markus@openbsd.org diff --git a/crypto/openssh/ssh-pkcs11-helper.c b/crypto/openssh/ssh-pkcs11-helper.c new file mode 100644 index 0000000000..8e3f57acef --- /dev/null +++ b/crypto/openssh/ssh-pkcs11-helper.c @@ -0,0 +1,372 @@ +/* $OpenBSD: ssh-pkcs11-helper.c,v 1.3 2010/02/24 06:12:53 djm Exp $ */ +/* + * Copyright (c) 2010 Markus Friedl. All rights reserved. + * + * 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 +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#include "openbsd-compat/sys-queue.h" + +#include +#include +#include +#include + +#include "xmalloc.h" +#include "buffer.h" +#include "log.h" +#include "misc.h" +#include "key.h" +#include "authfd.h" +#include "ssh-pkcs11.h" + +#ifdef ENABLE_PKCS11 + +/* borrows code from sftp-server and ssh-agent */ + +struct pkcs11_keyinfo { + Key *key; + char *providername; + TAILQ_ENTRY(pkcs11_keyinfo) next; +}; + +TAILQ_HEAD(, pkcs11_keyinfo) pkcs11_keylist; + +#define MAX_MSG_LENGTH 10240 /*XXX*/ + +/* helper */ +#define get_int() buffer_get_int(&iqueue); +#define get_string(lenp) buffer_get_string(&iqueue, lenp); + +/* input and output queue */ +Buffer iqueue; +Buffer oqueue; + +static void +add_key(Key *k, char *name) +{ + struct pkcs11_keyinfo *ki; + + ki = xcalloc(1, sizeof(*ki)); + ki->providername = xstrdup(name); + ki->key = k; + TAILQ_INSERT_TAIL(&pkcs11_keylist, ki, next); +} + +static void +del_keys_by_name(char *name) +{ + struct pkcs11_keyinfo *ki, *nxt; + + for (ki = TAILQ_FIRST(&pkcs11_keylist); ki; ki = nxt) { + nxt = TAILQ_NEXT(ki, next); + if (!strcmp(ki->providername, name)) { + TAILQ_REMOVE(&pkcs11_keylist, ki, next); + xfree(ki->providername); + key_free(ki->key); + free(ki); + } + } +} + +/* lookup matching 'private' key */ +static Key * +lookup_key(Key *k) +{ + struct pkcs11_keyinfo *ki; + + TAILQ_FOREACH(ki, &pkcs11_keylist, next) { + debug("check %p %s", ki, ki->providername); + if (key_equal(k, ki->key)) + return (ki->key); + } + return (NULL); +} + +static void +send_msg(Buffer *m) +{ + int mlen = buffer_len(m); + + buffer_put_int(&oqueue, mlen); + buffer_append(&oqueue, buffer_ptr(m), mlen); + buffer_consume(m, mlen); +} + +static void +process_add(void) +{ + char *name, *pin; + Key **keys; + int i, nkeys; + u_char *blob; + u_int blen; + Buffer msg; + + buffer_init(&msg); + name = get_string(NULL); + pin = get_string(NULL); + if ((nkeys = pkcs11_add_provider(name, pin, &keys)) > 0) { + buffer_put_char(&msg, SSH2_AGENT_IDENTITIES_ANSWER); + buffer_put_int(&msg, nkeys); + for (i = 0; i < nkeys; i++) { + key_to_blob(keys[i], &blob, &blen); + buffer_put_string(&msg, blob, blen); + buffer_put_cstring(&msg, name); + xfree(blob); + add_key(keys[i], name); + } + xfree(keys); + } else { + buffer_put_char(&msg, SSH_AGENT_FAILURE); + } + xfree(pin); + xfree(name); + send_msg(&msg); + buffer_free(&msg); +} + +static void +process_del(void) +{ + char *name, *pin; + Buffer msg; + + buffer_init(&msg); + name = get_string(NULL); + pin = get_string(NULL); + del_keys_by_name(name); + if (pkcs11_del_provider(name) == 0) + buffer_put_char(&msg, SSH_AGENT_SUCCESS); + else + buffer_put_char(&msg, SSH_AGENT_FAILURE); + xfree(pin); + xfree(name); + send_msg(&msg); + buffer_free(&msg); +} + +static void +process_sign(void) +{ + u_char *blob, *data, *signature = NULL; + u_int blen, dlen, slen = 0; + int ok = -1, flags, ret; + Key *key, *found; + Buffer msg; + + blob = get_string(&blen); + data = get_string(&dlen); + flags = get_int(); /* XXX ignore */ + + if ((key = key_from_blob(blob, blen)) != NULL) { + if ((found = lookup_key(key)) != NULL) { + slen = RSA_size(key->rsa); + signature = xmalloc(slen); + if ((ret = RSA_private_encrypt(dlen, data, signature, + found->rsa, RSA_PKCS1_PADDING)) != -1) { + slen = ret; + ok = 0; + } + } + key_free(key); + } + buffer_init(&msg); + if (ok == 0) { + buffer_put_char(&msg, SSH2_AGENT_SIGN_RESPONSE); + buffer_put_string(&msg, signature, slen); + } else { + buffer_put_char(&msg, SSH_AGENT_FAILURE); + } + xfree(data); + xfree(blob); + if (signature != NULL) + xfree(signature); + send_msg(&msg); + buffer_free(&msg); +} + +static void +process(void) +{ + u_int msg_len; + u_int buf_len; + u_int consumed; + u_int type; + u_char *cp; + + buf_len = buffer_len(&iqueue); + if (buf_len < 5) + return; /* Incomplete message. */ + cp = buffer_ptr(&iqueue); + msg_len = get_u32(cp); + if (msg_len > MAX_MSG_LENGTH) { + error("bad message len %d", msg_len); + cleanup_exit(11); + } + if (buf_len < msg_len + 4) + return; + buffer_consume(&iqueue, 4); + buf_len -= 4; + type = buffer_get_char(&iqueue); + switch (type) { + case SSH_AGENTC_ADD_SMARTCARD_KEY: + debug("process_add"); + process_add(); + break; + case SSH_AGENTC_REMOVE_SMARTCARD_KEY: + debug("process_del"); + process_del(); + break; + case SSH2_AGENTC_SIGN_REQUEST: + debug("process_sign"); + process_sign(); + break; + default: + error("Unknown message %d", type); + break; + } + /* discard the remaining bytes from the current packet */ + if (buf_len < buffer_len(&iqueue)) { + error("iqueue grew unexpectedly"); + cleanup_exit(255); + } + consumed = buf_len - buffer_len(&iqueue); + if (msg_len < consumed) { + error("msg_len %d < consumed %d", msg_len, consumed); + cleanup_exit(255); + } + if (msg_len > consumed) + buffer_consume(&iqueue, msg_len - consumed); +} + +void +cleanup_exit(int i) +{ + /* XXX */ + _exit(i); +} + +int +main(int argc, char **argv) +{ + fd_set *rset, *wset; + int in, out, max, log_stderr = 0; + ssize_t len, olen, set_size; + SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; + LogLevel log_level = SYSLOG_LEVEL_ERROR; + char buf[4*4096]; + + extern char *optarg; + extern char *__progname; + + TAILQ_INIT(&pkcs11_keylist); + pkcs11_init(0); + + init_rng(); + seed_rng(); + __progname = ssh_get_progname(argv[0]); + + log_init(__progname, log_level, log_facility, log_stderr); + + in = STDIN_FILENO; + out = STDOUT_FILENO; + + max = 0; + if (in > max) + max = in; + if (out > max) + max = out; + + buffer_init(&iqueue); + buffer_init(&oqueue); + + set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); + rset = (fd_set *)xmalloc(set_size); + wset = (fd_set *)xmalloc(set_size); + + for (;;) { + memset(rset, 0, set_size); + memset(wset, 0, set_size); + + /* + * Ensure that we can read a full buffer and handle + * the worst-case length packet it can generate, + * otherwise apply backpressure by stopping reads. + */ + if (buffer_check_alloc(&iqueue, sizeof(buf)) && + buffer_check_alloc(&oqueue, MAX_MSG_LENGTH)) + FD_SET(in, rset); + + olen = buffer_len(&oqueue); + if (olen > 0) + FD_SET(out, wset); + + if (select(max+1, rset, wset, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + error("select: %s", strerror(errno)); + cleanup_exit(2); + } + + /* copy stdin to iqueue */ + if (FD_ISSET(in, rset)) { + len = read(in, buf, sizeof buf); + if (len == 0) { + debug("read eof"); + cleanup_exit(0); + } else if (len < 0) { + error("read: %s", strerror(errno)); + cleanup_exit(1); + } else { + buffer_append(&iqueue, buf, len); + } + } + /* send oqueue to stdout */ + if (FD_ISSET(out, wset)) { + len = write(out, buffer_ptr(&oqueue), olen); + if (len < 0) { + error("write: %s", strerror(errno)); + cleanup_exit(1); + } else { + buffer_consume(&oqueue, len); + } + } + + /* + * Process requests from client if we can fit the results + * into the output buffer, otherwise stop processing input + * and let the output queue drain. + */ + if (buffer_check_alloc(&oqueue, MAX_MSG_LENGTH)) + process(); + } +} +#else /* ENABLE_PKCS11 */ +int +main(int argc, char **argv) +{ + extern char *__progname; + + __progname = ssh_get_progname(argv[0]); + log_init(__progname, SYSLOG_LEVEL_ERROR, SYSLOG_FACILITY_AUTH, 0); + fatal("PKCS#11 support disabled at compile time"); +} +#endif /* ENABLE_PKCS11 */ diff --git a/crypto/openssh/ssh-pkcs11.c b/crypto/openssh/ssh-pkcs11.c new file mode 100644 index 0000000000..286c232c72 --- /dev/null +++ b/crypto/openssh/ssh-pkcs11.c @@ -0,0 +1,593 @@ +/* $OpenBSD: ssh-pkcs11.c,v 1.6 2010/06/08 21:32:19 markus Exp $ */ +/* + * Copyright (c) 2010 Markus Friedl. All rights reserved. + * + * 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" + +#ifdef ENABLE_PKCS11 + +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif +#include +#include + +#include +#include + +#include "openbsd-compat/sys-queue.h" + +#define CRYPTOKI_COMPAT +#include "pkcs11.h" + +#include "log.h" +#include "misc.h" +#include "key.h" +#include "ssh-pkcs11.h" +#include "xmalloc.h" + +struct pkcs11_slotinfo { + CK_TOKEN_INFO token; + CK_SESSION_HANDLE session; + int logged_in; +}; + +struct pkcs11_provider { + char *name; + void *handle; + CK_FUNCTION_LIST *function_list; + CK_INFO info; + CK_ULONG nslots; + CK_SLOT_ID *slotlist; + struct pkcs11_slotinfo *slotinfo; + int valid; + int refcount; + TAILQ_ENTRY(pkcs11_provider) next; +}; + +TAILQ_HEAD(, pkcs11_provider) pkcs11_providers; + +struct pkcs11_key { + struct pkcs11_provider *provider; + CK_ULONG slotidx; + int (*orig_finish)(RSA *rsa); + RSA_METHOD rsa_method; + char *keyid; + int keyid_len; +}; + +int pkcs11_interactive = 0; + +int +pkcs11_init(int interactive) +{ + pkcs11_interactive = interactive; + TAILQ_INIT(&pkcs11_providers); + return (0); +} + +/* + * finalize a provider shared libarary, it's no longer usable. + * however, there might still be keys referencing this provider, + * so the actuall freeing of memory is handled by pkcs11_provider_unref(). + * this is called when a provider gets unregistered. + */ +static void +pkcs11_provider_finalize(struct pkcs11_provider *p) +{ + CK_RV rv; + CK_ULONG i; + + debug("pkcs11_provider_finalize: %p refcount %d valid %d", + p, p->refcount, p->valid); + if (!p->valid) + return; + for (i = 0; i < p->nslots; i++) { + if (p->slotinfo[i].session && + (rv = p->function_list->C_CloseSession( + p->slotinfo[i].session)) != CKR_OK) + error("C_CloseSession failed: %lu", rv); + } + if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK) + error("C_Finalize failed: %lu", rv); + p->valid = 0; + p->function_list = NULL; + dlclose(p->handle); +} + +/* + * remove a reference to the provider. + * called when a key gets destroyed or when the provider is unregistered. + */ +static void +pkcs11_provider_unref(struct pkcs11_provider *p) +{ + debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount); + if (--p->refcount <= 0) { + if (p->valid) + error("pkcs11_provider_unref: %p still valid", p); + xfree(p->slotlist); + xfree(p->slotinfo); + xfree(p); + } +} + +/* unregister all providers, keys might still point to the providers */ +void +pkcs11_terminate(void) +{ + struct pkcs11_provider *p; + + while ((p = TAILQ_FIRST(&pkcs11_providers)) != NULL) { + TAILQ_REMOVE(&pkcs11_providers, p, next); + pkcs11_provider_finalize(p); + pkcs11_provider_unref(p); + } +} + +/* lookup provider by name */ +static struct pkcs11_provider * +pkcs11_provider_lookup(char *provider_id) +{ + struct pkcs11_provider *p; + + TAILQ_FOREACH(p, &pkcs11_providers, next) { + debug("check %p %s", p, p->name); + if (!strcmp(provider_id, p->name)) + return (p); + } + return (NULL); +} + +/* unregister provider by name */ +int +pkcs11_del_provider(char *provider_id) +{ + struct pkcs11_provider *p; + + if ((p = pkcs11_provider_lookup(provider_id)) != NULL) { + TAILQ_REMOVE(&pkcs11_providers, p, next); + pkcs11_provider_finalize(p); + pkcs11_provider_unref(p); + return (0); + } + return (-1); +} + +/* openssl callback for freeing an RSA key */ +static int +pkcs11_rsa_finish(RSA *rsa) +{ + struct pkcs11_key *k11; + int rv = -1; + + if ((k11 = RSA_get_app_data(rsa)) != NULL) { + if (k11->orig_finish) + rv = k11->orig_finish(rsa); + if (k11->provider) + pkcs11_provider_unref(k11->provider); + if (k11->keyid) + xfree(k11->keyid); + xfree(k11); + } + return (rv); +} + +/* find a single 'obj' for given attributes */ +static int +pkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr, + CK_ULONG nattr, CK_OBJECT_HANDLE *obj) +{ + CK_FUNCTION_LIST *f; + CK_SESSION_HANDLE session; + CK_ULONG nfound = 0; + CK_RV rv; + int ret = -1; + + f = p->function_list; + session = p->slotinfo[slotidx].session; + if ((rv = f->C_FindObjectsInit(session, attr, nattr)) != CKR_OK) { + error("C_FindObjectsInit failed (nattr %lu): %lu", nattr, rv); + return (-1); + } + if ((rv = f->C_FindObjects(session, obj, 1, &nfound)) != CKR_OK || + nfound != 1) { + debug("C_FindObjects failed (nfound %lu nattr %lu): %lu", + nfound, nattr, rv); + } else + ret = 0; + if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK) + error("C_FindObjectsFinal failed: %lu", rv); + return (ret); +} + +/* openssl callback doing the actual signing operation */ +static int +pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, + int padding) +{ + struct pkcs11_key *k11; + struct pkcs11_slotinfo *si; + CK_FUNCTION_LIST *f; + CK_OBJECT_HANDLE obj; + CK_ULONG tlen = 0; + CK_RV rv; + CK_OBJECT_CLASS private_key_class = CKO_PRIVATE_KEY; + CK_BBOOL true_val = CK_TRUE; + CK_MECHANISM mech = { + CKM_RSA_PKCS, NULL_PTR, 0 + }; + CK_ATTRIBUTE key_filter[] = { + {CKA_CLASS, NULL, sizeof(private_key_class) }, + {CKA_ID, NULL, 0}, + {CKA_SIGN, NULL, sizeof(true_val) } + }; + char *pin, prompt[1024]; + int rval = -1; + + /* some compilers complain about non-constant initializer so we + use NULL in CK_ATTRIBUTE above and set the values here */ + key_filter[0].pValue = &private_key_class; + key_filter[2].pValue = &true_val; + + if ((k11 = RSA_get_app_data(rsa)) == NULL) { + error("RSA_get_app_data failed for rsa %p", rsa); + return (-1); + } + if (!k11->provider || !k11->provider->valid) { + error("no pkcs11 (valid) provider for rsa %p", rsa); + return (-1); + } + f = k11->provider->function_list; + si = &k11->provider->slotinfo[k11->slotidx]; + if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { + if (!pkcs11_interactive) { + error("need pin"); + return (-1); + } + snprintf(prompt, sizeof(prompt), "Enter PIN for '%s': ", + si->token.label); + pin = read_passphrase(prompt, RP_ALLOW_EOF); + if (pin == NULL) + return (-1); /* bail out */ + if ((rv = f->C_Login(si->session, CKU_USER, pin, strlen(pin))) + != CKR_OK) { + xfree(pin); + error("C_Login failed: %lu", rv); + return (-1); + } + xfree(pin); + si->logged_in = 1; + } + key_filter[1].pValue = k11->keyid; + key_filter[1].ulValueLen = k11->keyid_len; + /* try to find object w/CKA_SIGN first, retry w/o */ + if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 && + pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) { + error("cannot find private key"); + } else if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) { + error("C_SignInit failed: %lu", rv); + } else { + /* XXX handle CKR_BUFFER_TOO_SMALL */ + tlen = RSA_size(rsa); + rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen); + if (rv == CKR_OK) + rval = tlen; + else + error("C_Sign failed: %lu", rv); + } + return (rval); +} + +static int +pkcs11_rsa_private_decrypt(int flen, const u_char *from, u_char *to, RSA *rsa, + int padding) +{ + return (-1); +} + +/* redirect private key operations for rsa key to pkcs11 token */ +static int +pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, + CK_ATTRIBUTE *keyid_attrib, RSA *rsa) +{ + struct pkcs11_key *k11; + const RSA_METHOD *def = RSA_get_default_method(); + + k11 = xcalloc(1, sizeof(*k11)); + k11->provider = provider; + provider->refcount++; /* provider referenced by RSA key */ + k11->slotidx = slotidx; + /* identify key object on smartcard */ + k11->keyid_len = keyid_attrib->ulValueLen; + k11->keyid = xmalloc(k11->keyid_len); + memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); + k11->orig_finish = def->finish; + memcpy(&k11->rsa_method, def, sizeof(k11->rsa_method)); + k11->rsa_method.name = "pkcs11"; + k11->rsa_method.rsa_priv_enc = pkcs11_rsa_private_encrypt; + k11->rsa_method.rsa_priv_dec = pkcs11_rsa_private_decrypt; + k11->rsa_method.finish = pkcs11_rsa_finish; + RSA_set_method(rsa, &k11->rsa_method); + RSA_set_app_data(rsa, k11); + return (0); +} + +/* remove trailing spaces */ +static void +rmspace(char *buf, size_t len) +{ + size_t i; + + if (!len) + return; + for (i = len - 1; i > 0; i--) + if (i == len - 1 || buf[i] == ' ') + buf[i] = '\0'; + else + break; +} + +/* + * open a pkcs11 session and login if required. + * if pin == NULL we delay login until key use + */ +static int +pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin) +{ + CK_RV rv; + CK_FUNCTION_LIST *f; + CK_SESSION_HANDLE session; + int login_required; + + f = p->function_list; + login_required = p->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED; + if (pin && login_required && !strlen(pin)) { + error("pin required"); + return (-1); + } + if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION| + CKF_SERIAL_SESSION, NULL, NULL, &session)) + != CKR_OK) { + error("C_OpenSession failed: %lu", rv); + return (-1); + } + if (login_required && pin) { + if ((rv = f->C_Login(session, CKU_USER, pin, strlen(pin))) + != CKR_OK) { + error("C_Login failed: %lu", rv); + if ((rv = f->C_CloseSession(session)) != CKR_OK) + error("C_CloseSession failed: %lu", rv); + return (-1); + } + p->slotinfo[slotidx].logged_in = 1; + } + p->slotinfo[slotidx].session = session; + return (0); +} + +/* + * lookup public keys for token in slot identified by slotidx, + * add 'wrapped' public keys to the 'keysp' array and increment nkeys. + * keysp points to an (possibly empty) array with *nkeys keys. + */ +static int +pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, Key ***keysp, + int *nkeys) +{ + Key *key; + RSA *rsa; + int i; + CK_RV rv; + CK_OBJECT_HANDLE obj; + CK_ULONG nfound; + CK_SESSION_HANDLE session; + CK_FUNCTION_LIST *f; + CK_OBJECT_CLASS pubkey_class = CKO_PUBLIC_KEY; + CK_ATTRIBUTE pubkey_filter[] = { + { CKA_CLASS, NULL, sizeof(pubkey_class) } + }; + CK_ATTRIBUTE attribs[] = { + { CKA_ID, NULL, 0 }, + { CKA_MODULUS, NULL, 0 }, + { CKA_PUBLIC_EXPONENT, NULL, 0 } + }; + + /* some compilers complain about non-constant initializer so we + use NULL in CK_ATTRIBUTE above and set the value here */ + pubkey_filter[0].pValue = &pubkey_class; + + f = p->function_list; + session = p->slotinfo[slotidx].session; + /* setup a filter the looks for public keys */ + if ((rv = f->C_FindObjectsInit(session, pubkey_filter, 1)) != CKR_OK) { + error("C_FindObjectsInit failed: %lu", rv); + return (-1); + } + while (1) { + /* XXX 3 attributes in attribs[] */ + for (i = 0; i < 3; i++) { + attribs[i].pValue = NULL; + attribs[i].ulValueLen = 0; + } + if ((rv = f->C_FindObjects(session, &obj, 1, &nfound)) != CKR_OK + || nfound == 0) + break; + /* found a key, so figure out size of the attributes */ + if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) + != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + continue; + } + /* check that none of the attributes are zero length */ + if (attribs[0].ulValueLen == 0 || + attribs[1].ulValueLen == 0 || + attribs[2].ulValueLen == 0) { + continue; + } + /* allocate buffers for attributes */ + for (i = 0; i < 3; i++) + attribs[i].pValue = xmalloc(attribs[i].ulValueLen); + /* retrieve ID, modulus and public exponent of RSA key */ + if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) + != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + } else if ((rsa = RSA_new()) == NULL) { + error("RSA_new failed"); + } else { + rsa->n = BN_bin2bn(attribs[1].pValue, + attribs[1].ulValueLen, NULL); + rsa->e = BN_bin2bn(attribs[2].pValue, + attribs[2].ulValueLen, NULL); + if (rsa->n && rsa->e && + pkcs11_rsa_wrap(p, slotidx, &attribs[0], rsa) == 0) { + key = key_new(KEY_UNSPEC); + key->rsa = rsa; + key->type = KEY_RSA; + key->flags |= KEY_FLAG_EXT; + /* expand key array and add key */ + *keysp = xrealloc(*keysp, *nkeys + 1, + sizeof(Key *)); + (*keysp)[*nkeys] = key; + *nkeys = *nkeys + 1; + debug("have %d keys", *nkeys); + } else { + RSA_free(rsa); + } + } + for (i = 0; i < 3; i++) + xfree(attribs[i].pValue); + } + if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK) + error("C_FindObjectsFinal failed: %lu", rv); + return (0); +} + +/* register a new provider, fails if provider already exists */ +int +pkcs11_add_provider(char *provider_id, char *pin, Key ***keyp) +{ + int nkeys, need_finalize = 0; + struct pkcs11_provider *p = NULL; + void *handle = NULL; + CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **); + CK_RV rv; + CK_FUNCTION_LIST *f = NULL; + CK_TOKEN_INFO *token; + CK_ULONG i; + + *keyp = NULL; + if (pkcs11_provider_lookup(provider_id) != NULL) { + error("provider already registered: %s", provider_id); + goto fail; + } + /* open shared pkcs11-libarary */ + if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) { + error("dlopen %s failed: %s", provider_id, dlerror()); + goto fail; + } + if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) { + error("dlsym(C_GetFunctionList) failed: %s", dlerror()); + goto fail; + } + p = xcalloc(1, sizeof(*p)); + p->name = xstrdup(provider_id); + p->handle = handle; + /* setup the pkcs11 callbacks */ + if ((rv = (*getfunctionlist)(&f)) != CKR_OK) { + error("C_GetFunctionList failed: %lu", rv); + goto fail; + } + p->function_list = f; + if ((rv = f->C_Initialize(NULL)) != CKR_OK) { + error("C_Initialize failed: %lu", rv); + goto fail; + } + need_finalize = 1; + if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) { + error("C_GetInfo failed: %lu", rv); + goto fail; + } + rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID)); + rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription)); + debug("manufacturerID <%s> cryptokiVersion %d.%d" + " libraryDescription <%s> libraryVersion %d.%d", + p->info.manufacturerID, + p->info.cryptokiVersion.major, + p->info.cryptokiVersion.minor, + p->info.libraryDescription, + p->info.libraryVersion.major, + p->info.libraryVersion.minor); + if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) { + error("C_GetSlotList failed: %lu", rv); + goto fail; + } + if (p->nslots == 0) { + error("no slots"); + goto fail; + } + p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID)); + if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots)) + != CKR_OK) { + error("C_GetSlotList failed: %lu", rv); + goto fail; + } + p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo)); + p->valid = 1; + nkeys = 0; + for (i = 0; i < p->nslots; i++) { + token = &p->slotinfo[i].token; + if ((rv = f->C_GetTokenInfo(p->slotlist[i], token)) + != CKR_OK) { + error("C_GetTokenInfo failed: %lu", rv); + continue; + } + rmspace(token->label, sizeof(token->label)); + rmspace(token->manufacturerID, sizeof(token->manufacturerID)); + rmspace(token->model, sizeof(token->model)); + rmspace(token->serialNumber, sizeof(token->serialNumber)); + debug("label <%s> manufacturerID <%s> model <%s> serial <%s>" + " flags 0x%lx", + token->label, token->manufacturerID, token->model, + token->serialNumber, token->flags); + /* open session, login with pin and retrieve public keys */ + if (pkcs11_open_session(p, i, pin) == 0) + pkcs11_fetch_keys(p, i, keyp, &nkeys); + } + if (nkeys > 0) { + TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); + p->refcount++; /* add to provider list */ + return (nkeys); + } + error("no keys"); + /* don't add the provider, since it does not have any keys */ +fail: + if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) + error("C_Finalize failed: %lu", rv); + if (p) { + if (p->slotlist) + xfree(p->slotlist); + if (p->slotinfo) + xfree(p->slotinfo); + xfree(p); + } + if (handle) + dlclose(handle); + return (-1); +} + +#endif /* ENABLE_PKCS11 */ diff --git a/crypto/openssh/platform.h b/crypto/openssh/ssh-pkcs11.h similarity index 74% copy from crypto/openssh/platform.h copy to crypto/openssh/ssh-pkcs11.h index cf93bc57c0..59f456adf0 100644 --- a/crypto/openssh/platform.h +++ b/crypto/openssh/ssh-pkcs11.h @@ -1,7 +1,6 @@ -/* $Id: platform.h,v 1.1 2006/08/30 17:24:41 djm Exp $ */ - +/* $OpenBSD: ssh-pkcs11.h,v 1.2 2010/02/24 06:12:53 djm Exp $ */ /* - * Copyright (c) 2006 Darren Tucker. All rights reserved. + * Copyright (c) 2010 Markus Friedl. All rights reserved. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -15,9 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - -#include - -void platform_pre_fork(void); -void platform_post_fork_parent(pid_t child_pid); -void platform_post_fork_child(void); +int pkcs11_init(int); +void pkcs11_terminate(void); +int pkcs11_add_provider(char *, char *, Key ***); +int pkcs11_del_provider(char *); diff --git a/crypto/openssh/ssh-rsa.c b/crypto/openssh/ssh-rsa.c index 0e16ff85f1..c471ff323b 100644 --- a/crypto/openssh/ssh-rsa.c +++ b/crypto/openssh/ssh-rsa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-rsa.c,v 1.39 2006/08/03 03:34:42 deraadt Exp $ */ +/* $OpenBSD: ssh-rsa.c,v 1.44 2010/07/16 14:07:35 djm Exp $ */ /* * Copyright (c) 2000, 2003 Markus Friedl * @@ -30,6 +30,7 @@ #include "buffer.h" #include "key.h" #include "compat.h" +#include "misc.h" #include "ssh.h" static int openssh_RSA_verify(int, u_char *, u_int, u_char *, u_int, RSA *); @@ -46,7 +47,8 @@ ssh_rsa_sign(const Key *key, u_char **sigp, u_int *lenp, int ok, nid; Buffer b; - if (key == NULL || key->type != KEY_RSA || key->rsa == NULL) { + if (key == NULL || key->rsa == NULL || (key->type != KEY_RSA && + key->type != KEY_RSA_CERT && key->type != KEY_RSA_CERT_V00)) { error("ssh_rsa_sign: no RSA key"); return -1; } @@ -113,7 +115,8 @@ ssh_rsa_verify(const Key *key, const u_char *signature, u_int signaturelen, u_int len, dlen, modlen; int rlen, ret, nid; - if (key == NULL || key->type != KEY_RSA || key->rsa == NULL) { + if (key == NULL || key->rsa == NULL || (key->type != KEY_RSA && + key->type != KEY_RSA_CERT && key->type != KEY_RSA_CERT_V00)) { error("ssh_rsa_verify: no RSA key"); return -1; } @@ -208,7 +211,7 @@ openssh_RSA_verify(int type, u_char *hash, u_int hashlen, u_char *sigbuf, u_int siglen, RSA *rsa) { u_int ret, rsasize, oidlen = 0, hlen = 0; - int len; + int len, oidmatch, hashmatch; const u_char *oid = NULL; u_char *decrypted = NULL; @@ -247,11 +250,13 @@ openssh_RSA_verify(int type, u_char *hash, u_int hashlen, error("bad decrypted len: %d != %d + %d", len, hlen, oidlen); goto done; } - if (memcmp(decrypted, oid, oidlen) != 0) { + oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0; + hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0; + if (!oidmatch) { error("oid mismatch"); goto done; } - if (memcmp(decrypted + oidlen, hash, hlen) != 0) { + if (!hashmatch) { error("hash mismatch"); goto done; } diff --git a/crypto/openssh/ssh.1 b/crypto/openssh/ssh.1 index 6c6271ee4f..02d28a00b9 100644 --- a/crypto/openssh/ssh.1 +++ b/crypto/openssh/ssh.1 @@ -34,8 +34,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh.1,v 1.283 2009/03/19 15:15:09 jmc Exp $ -.Dd $Mdocdate: March 19 2009 $ +.\" $OpenBSD: ssh.1,v 1.308 2010/08/04 05:37:01 djm Exp $ +.Dd $Mdocdate: August 4 2010 $ .Dt SSH 1 .Os .Sh NAME @@ -43,43 +43,25 @@ .Nd OpenSSH SSH client (remote login program) .Sh SYNOPSIS .Nm ssh +.Bk -words .Op Fl 1246AaCfgKkMNnqsTtVvXxYy .Op Fl b Ar bind_address .Op Fl c Ar cipher_spec -.Oo Fl D\ \& -.Sm off -.Oo Ar bind_address : Oc -.Ar port -.Sm on -.Oc +.Op Fl D Oo Ar bind_address : Oc Ns Ar port .Op Fl e Ar escape_char .Op Fl F Ar configfile -.Bk -words +.Op Fl I Ar pkcs11 .Op Fl i Ar identity_file -.Ek -.Oo Fl L\ \& -.Sm off -.Oo Ar bind_address : Oc -.Ar port : host : hostport -.Sm on -.Oc -.Bk -words +.Op Fl L Oo Ar bind_address : Oc Ns Ar port : Ns Ar host : Ns Ar hostport .Op Fl l Ar login_name -.Ek .Op Fl m Ar mac_spec .Op Fl O Ar ctl_cmd .Op Fl o Ar option .Op Fl p Ar port -.Oo Fl R\ \& -.Sm off -.Oo Ar bind_address : Oc -.Ar port : host : hostport -.Sm on -.Oc +.Op Fl R Oo Ar bind_address : Oc Ns Ar port : Ns Ar host : Ns Ar hostport .Op Fl S Ar ctl_path -.Bk -words -.Oo Fl w Ar local_tun Ns -.Op : Ns Ar remote_tun Oc +.Op Fl W Ar host : Ns Ar port +.Op Fl w Ar local_tun Ns Op : Ns Ar remote_tun .Oo Ar user Ns @ Oc Ns Ar hostname .Op Ar command .Ek @@ -132,8 +114,9 @@ This can also be specified on a per-host basis in a configuration file. .Pp Agent forwarding should be enabled with caution. Users with the ability to bypass file permissions on the remote host -(for the agent's Unix-domain socket) -can access the local agent through the forwarded connection. +(for the agent's +.Ux Ns -domain +socket) can access the local agent through the forwarded connection. An attacker cannot obtain key material from the agent, however they can perform operations on the keys that enable them to authenticate using the identities loaded into the agent. @@ -193,7 +176,9 @@ is a comma-separated list of ciphers listed in order of preference. See the .Cm Ciphers -keyword for more information. +keyword in +.Xr ssh_config 5 +for more information. .It Fl D Xo .Sm off .Oo Ar bind_address : Oc @@ -217,14 +202,7 @@ will act as a SOCKS server. Only root can forward privileged ports. Dynamic port forwardings can also be specified in the configuration file. .Pp -IPv6 addresses can be specified with an alternative syntax: -.Sm off -.Xo -.Op Ar bind_address No / -.Ar port -.Xc -.Sm on -or by enclosing the address in square brackets. +IPv6 addresses can be specified by enclosing the address in square brackets. Only the superuser can forward privileged ports. By default, the local port is bound in accordance with the .Cm GatewayPorts @@ -284,13 +262,11 @@ will wait for all remote port forwards to be successfully established before placing itself in the background. .It Fl g Allows remote hosts to connect to local forwarded ports. -.It Fl I Ar smartcard_device -Specify the device +.It Fl I Ar pkcs11 +Specify the PKCS#11 shared library .Nm -should use to communicate with a smartcard used for storing the user's +should use to communicate with a PKCS#11 token providing the user's private RSA key. -This option is only available if support for smartcard devices -is compiled in (default is no support). .It Fl i Ar identity_file Selects a file from which the identity (private key) for RSA or DSA authentication is read. @@ -307,6 +283,11 @@ It is possible to have multiple .Fl i options (and multiple identities specified in configuration files). +.Nm +will also try to load certificate information from the filename obtained +by appending +.Pa -cert.pub +to identity filenames. .It Fl K Enables GSSAPI-based authentication and forwarding (delegation) of GSSAPI credentials to the server. @@ -332,15 +313,7 @@ port .Ar hostport from the remote machine. Port forwardings can also be specified in the configuration file. -IPv6 addresses can be specified with an alternative syntax: -.Sm off -.Xo -.Op Ar bind_address No / -.Ar port No / Ar host No / -.Ar hostport -.Xc -.Sm on -or by enclosing the address in square brackets. +IPv6 addresses can be specified by enclosing the address in square brackets. Only the superuser can forward privileged ports. By default, the local port is bound in accordance with the .Cm GatewayPorts @@ -417,7 +390,9 @@ option is specified, the argument is interpreted and passed to the master process. Valid commands are: .Dq check -(check that the master process is running) and +(check that the master process is running), +.Dq forward +(request forwardings without command execution) and .Dq exit (request the master to exit). .It Fl o Ar option @@ -469,6 +444,7 @@ For full details of the options listed below, and their possible values, see .It NumberOfPasswordPrompts .It PasswordAuthentication .It PermitLocalCommand +.It PKCS11Provider .It Port .It PreferredAuthentications .It Protocol @@ -481,7 +457,6 @@ For full details of the options listed below, and their possible values, see .It SendEnv .It ServerAliveInterval .It ServerAliveCountMax -.It SmartcardDevice .It StrictHostKeyChecking .It TCPKeepAlive .It Tunnel @@ -521,15 +496,7 @@ from the local machine. Port forwardings can also be specified in the configuration file. Privileged ports can be forwarded only when logging in as root on the remote machine. -IPv6 addresses can be specified by enclosing the address in square braces or -using an alternative syntax: -.Sm off -.Xo -.Op Ar bind_address No / -.Ar host No / Ar port No / -.Ar hostport -.Xc . -.Sm on +IPv6 addresses can be specified by enclosing the address in square braces. .Pp By default, the listening socket on the server will be bound to the loopback interface only. @@ -553,8 +520,14 @@ argument is .Ql 0 , the listen port will be dynamically allocated on the server and reported to the client at run time. +When used together with +.Ic -O forward +the allocated port will be printed to the standard output. .It Fl S Ar ctl_path -Specifies the location of a control socket for connection sharing. +Specifies the location of a control socket for connection sharing, +or the string +.Dq none +to disable connection sharing. Refer to the description of .Cm ControlPath and @@ -593,6 +566,19 @@ Multiple .Fl v options increase the verbosity. The maximum is 3. +.It Fl W Ar host : Ns Ar port +Requests that standard input and output on the client be forwarded to +.Ar host +on +.Ar port +over the secure channel. +Implies +.Fl N , +.Fl T , +.Cm ExitOnForwardFailure +and +.Cm ClearAllForwardings +and works with Protocol version 2 only. .It Fl w Xo .Ar local_tun Ns Op : Ns Ar remote_tun .Xc @@ -666,20 +652,18 @@ exits with the exit status of the remote command or with 255 if an error occurred. .Sh AUTHENTICATION The OpenSSH SSH client supports SSH protocols 1 and 2. -Protocol 2 is the default, with -.Nm -falling back to protocol 1 if it detects protocol 2 is unsupported. -These settings may be altered using the +The default is to use protocol 2 only, +though this can be changed via the .Cm Protocol option in -.Xr ssh_config 5 , -or enforced using the +.Xr ssh_config 5 +or the .Fl 1 and .Fl 2 options (see above). Both protocols support similar authentication methods, -but protocol 2 is preferred since +but protocol 2 is the default since it provides additional mechanisms for confidentiality (the traffic is encrypted using AES, 3DES, Blowfish, CAST128, or Arcfour) and integrity (hmac-md5, hmac-sha1, umac-64, hmac-ripemd160). @@ -788,8 +772,20 @@ file, and has one key per line, though the lines can be very long. After this, the user can log in without giving the password. .Pp -The most convenient way to use public key authentication may be with an -authentication agent. +A variation on public key authentication +is available in the form of certificate authentication: +instead of a set of public/private keys, +signed certificates are used. +This has the advantage that a single trusted certification authority +can be used in place of many public/private keys. +See the +.Sx CERTIFICATES +section of +.Xr ssh-keygen 1 +for more information. +.Pp +The most convenient way to use public key or certificate authentication +may be with an authentication agent. See .Xr ssh-agent 1 for more information. diff --git a/crypto/openssh/ssh.c b/crypto/openssh/ssh.c index adfe60e4b2..4419f7642e 100644 --- a/crypto/openssh/ssh.c +++ b/crypto/openssh/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.326 2009/07/02 02:11:47 dtucker Exp $ */ +/* $OpenBSD: ssh.c,v 1.346 2010/08/12 21:49:44 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -79,6 +79,7 @@ #include "ssh.h" #include "ssh1.h" #include "ssh2.h" +#include "canohost.h" #include "compat.h" #include "cipher.h" #include "packet.h" @@ -100,10 +101,11 @@ #include "match.h" #include "msg.h" #include "uidswap.h" +#include "roaming.h" #include "version.h" -#ifdef SMARTCARD -#include "scard.h" +#ifdef ENABLE_PKCS11 +#include "ssh-pkcs11.h" #endif extern char *__progname; @@ -125,6 +127,15 @@ int no_shell_flag = 0; */ int stdin_null_flag = 0; +/* + * Flag indicating that the current process should be backgrounded and + * a new slave launched in the foreground for ControlPersist. + */ +int need_controlpersist_detach = 0; + +/* Copies of flags for ControlPersist foreground slave */ +int ostdin_null_flag, ono_shell_flag, ono_tty_flag, otty_flag; + /* * Flag indicating that ssh should fork after authentication. This is useful * so that the passphrase can be entered manually, and then ssh goes to the @@ -132,6 +143,10 @@ int stdin_null_flag = 0; */ int fork_after_authentication_flag = 0; +/* forward stdio to remote host and port */ +char *stdio_forward_host = NULL; +int stdio_forward_port = 0; + /* * General data structure for command line options and options configurable * in configuration files. See readconf.h. @@ -182,10 +197,12 @@ usage(void) fprintf(stderr, "usage: ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]\n" " [-D [bind_address:]port] [-e escape_char] [-F configfile]\n" -" [-i identity_file] [-L [bind_address:]port:host:hostport]\n" +" [-I pkcs11] [-i identity_file]\n" +" [-L [bind_address:]port:host:hostport]\n" " [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]\n" " [-R [bind_address:]port:host:hostport] [-S ctl_path]\n" -" [-w local_tun[:remote_tun]] [user@]hostname [command]\n" +" [-W host:port] [-w local_tun[:remote_tun]]\n" +" [user@]hostname [command]\n" ); exit(255); } @@ -220,6 +237,12 @@ main(int ac, char **av) __progname = ssh_get_progname(av[0]); init_rng(); + /* + * Discard other fds that are hanging around. These can cause problem + * with backgrounded ssh processes started by ControlPersist. + */ + closefrom(STDERR_FILENO + 1); + /* * Save the original real uid. It will be needed later (uid-swapping * may clobber the real uid). @@ -275,7 +298,7 @@ main(int ac, char **av) again: while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx" - "ACD:F:I:KL:MNO:PR:S:TVw:XYy")) != -1) { + "ACD:F:I:KL:MNO:PR:S:TVw:W:XYy")) != -1) { switch (opt) { case '1': options.protocol = SSH_PROTO_1; @@ -313,8 +336,15 @@ main(int ac, char **av) options.gateway_ports = 1; break; case 'O': + if (stdio_forward_host != NULL) + fatal("Cannot specify multiplexing " + "command with -W"); + else if (muxclient_command != 0) + fatal("Multiplexing command already specified"); if (strcmp(optarg, "check") == 0) muxclient_command = SSHMUX_COMMAND_ALIVE_CHECK; + else if (strcmp(optarg, "forward") == 0) + muxclient_command = SSHMUX_COMMAND_FORWARD; else if (strcmp(optarg, "exit") == 0) muxclient_command = SSHMUX_COMMAND_TERMINATE; else @@ -351,10 +381,10 @@ main(int ac, char **av) xstrdup(optarg); break; case 'I': -#ifdef SMARTCARD - options.smartcard_device = xstrdup(optarg); +#ifdef ENABLE_PKCS11 + options.pkcs11_provider = xstrdup(optarg); #else - fprintf(stderr, "no support for smartcards.\n"); + fprintf(stderr, "no support for PKCS#11.\n"); #endif break; case 't': @@ -388,6 +418,26 @@ main(int ac, char **av) exit(255); } break; + case 'W': + if (stdio_forward_host != NULL) + fatal("stdio forward already specified"); + if (muxclient_command != 0) + fatal("Cannot specify stdio forward with -O"); + if (parse_forward(&fwd, optarg, 1, 0)) { + stdio_forward_host = fwd.listen_host; + stdio_forward_port = fwd.listen_port; + xfree(fwd.connect_host); + } else { + fprintf(stderr, + "Bad stdio forwarding specification '%s'\n", + optarg); + exit(255); + } + no_tty_flag = 1; + no_shell_flag = 1; + options.clear_forwardings = 1; + options.exit_on_forward_failure = 1; + break; case 'q': options.log_level = SYSLOG_LEVEL_QUIET; break; @@ -527,7 +577,7 @@ main(int ac, char **av) ac -= optind; av += optind; - if (ac > 0 && !host && **av != '-') { + if (ac > 0 && !host) { if (strrchr(*av, '@')) { p = xstrdup(*av); cp = strrchr(p, '@'); @@ -588,7 +638,7 @@ main(int ac, char **av) tty_flag = 1; /* Force no tty */ - if (no_tty_flag) + if (no_tty_flag || muxclient_command != 0) tty_flag = 0; /* Do not allocate a tty if stdin is not a tty. */ if ((!isatty(fileno(stdin)) || stdin_null_flag) && !force_tty_flag) { @@ -644,6 +694,11 @@ main(int ac, char **av) options.port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT; } + if (options.hostname != NULL) { + host = percent_expand(options.hostname, + "h", host, (char *)NULL); + } + if (options.local_command != NULL) { char thishost[NI_MAXHOST]; @@ -653,16 +708,12 @@ main(int ac, char **av) debug3("expanding LocalCommand: %s", options.local_command); cp = options.local_command; options.local_command = percent_expand(cp, "d", pw->pw_dir, - "h", options.hostname? options.hostname : host, - "l", thishost, "n", host, "r", options.user, "p", buf, - "u", pw->pw_name, (char *)NULL); + "h", host, "l", thishost, "n", host, "r", options.user, + "p", buf, "u", pw->pw_name, (char *)NULL); debug3("expanded LocalCommand: %s", options.local_command); xfree(cp); } - if (options.hostname != NULL) - host = options.hostname; - /* force lowercase for hostkey matching */ if (options.host_key_alias != NULL) { for (p = options.host_key_alias; *p; p++) @@ -729,26 +780,34 @@ main(int ac, char **av) sensitive_data.external_keysign = 0; if (options.rhosts_rsa_authentication || options.hostbased_authentication) { - sensitive_data.nkeys = 3; + sensitive_data.nkeys = 5; sensitive_data.keys = xcalloc(sensitive_data.nkeys, sizeof(Key)); PRIV_START; sensitive_data.keys[0] = key_load_private_type(KEY_RSA1, _PATH_HOST_KEY_FILE, "", NULL, NULL); - sensitive_data.keys[1] = key_load_private_type(KEY_DSA, + sensitive_data.keys[1] = key_load_private_cert(KEY_DSA, + _PATH_HOST_DSA_KEY_FILE, "", NULL); + sensitive_data.keys[2] = key_load_private_cert(KEY_RSA, + _PATH_HOST_RSA_KEY_FILE, "", NULL); + sensitive_data.keys[3] = key_load_private_type(KEY_DSA, _PATH_HOST_DSA_KEY_FILE, "", NULL, NULL); - sensitive_data.keys[2] = key_load_private_type(KEY_RSA, + sensitive_data.keys[4] = key_load_private_type(KEY_RSA, _PATH_HOST_RSA_KEY_FILE, "", NULL, NULL); PRIV_END; if (options.hostbased_authentication == 1 && sensitive_data.keys[0] == NULL && - sensitive_data.keys[1] == NULL && - sensitive_data.keys[2] == NULL) { - sensitive_data.keys[1] = key_load_public( + sensitive_data.keys[3] == NULL && + sensitive_data.keys[4] == NULL) { + sensitive_data.keys[1] = key_load_cert( + _PATH_HOST_DSA_KEY_FILE); + sensitive_data.keys[2] = key_load_cert( + _PATH_HOST_RSA_KEY_FILE); + sensitive_data.keys[3] = key_load_public( _PATH_HOST_DSA_KEY_FILE, NULL); - sensitive_data.keys[2] = key_load_public( + sensitive_data.keys[4] = key_load_public( _PATH_HOST_RSA_KEY_FILE, NULL); sensitive_data.external_keysign = 1; } @@ -795,6 +854,13 @@ main(int ac, char **av) ssh_login(&sensitive_data, host, (struct sockaddr *)&hostaddr, pw, timeout_ms); + if (packet_connection_is_on_socket()) { + verbose("Authenticated to %s ([%s]:%d).", host, + get_remote_ipaddr(), get_remote_port()); + } else { + verbose("Authenticated to %s (via proxy).", host); + } + /* We no longer need the private host keys. Clear them now. */ if (sensitive_data.nkeys != 0) { for (i = 0; i < sensitive_data.nkeys; i++) { @@ -834,6 +900,61 @@ main(int ac, char **av) return exit_status; } +static void +control_persist_detach(void) +{ + pid_t pid; + int devnull; + + debug("%s: backgrounding master process", __func__); + + /* + * master (current process) into the background, and make the + * foreground process a client of the backgrounded master. + */ + switch ((pid = fork())) { + case -1: + fatal("%s: fork: %s", __func__, strerror(errno)); + case 0: + /* Child: master process continues mainloop */ + break; + default: + /* Parent: set up mux slave to connect to backgrounded master */ + debug2("%s: background process is %ld", __func__, (long)pid); + stdin_null_flag = ostdin_null_flag; + no_shell_flag = ono_shell_flag; + no_tty_flag = ono_tty_flag; + tty_flag = otty_flag; + close(muxserver_sock); + muxserver_sock = -1; + muxclient(options.control_path); + /* muxclient() doesn't return on success. */ + fatal("Failed to connect to new control master"); + } + if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { + error("%s: open(\"/dev/null\"): %s", __func__, + strerror(errno)); + } else { + if (dup2(devnull, STDIN_FILENO) == -1 || + dup2(devnull, STDOUT_FILENO) == -1) + error("%s: dup2: %s", __func__, strerror(errno)); + if (devnull > STDERR_FILENO) + close(devnull); + } +} + +/* Do fork() after authentication. Used by "ssh -f" */ +static void +fork_postauth(void) +{ + if (need_controlpersist_detach) + control_persist_detach(); + debug("forking to background"); + fork_after_authentication_flag = 0; + if (daemon(1, 1) < 0) + fatal("daemon() failed: %.200s", strerror(errno)); +} + /* Callback for remote forward global requests */ static void ssh_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) @@ -845,9 +966,10 @@ ssh_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", rfwd->listen_port, rfwd->connect_host, rfwd->connect_port); if (type == SSH2_MSG_REQUEST_SUCCESS && rfwd->listen_port == 0) { + rfwd->allocated_port = packet_get_int(); logit("Allocated port %u for remote forward to %s:%d", - packet_get_int(), - rfwd->connect_host, rfwd->connect_port); + rfwd->allocated_port, + rfwd->connect_host, rfwd->connect_port); } if (type == SSH2_MSG_REQUEST_FAILURE) { @@ -860,21 +982,54 @@ ssh_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) } if (++remote_forward_confirms_received == options.num_remote_forwards) { debug("All remote forwarding requests processed"); - if (fork_after_authentication_flag) { - fork_after_authentication_flag = 0; - if (daemon(1, 1) < 0) - fatal("daemon() failed: %.200s", - strerror(errno)); - } + if (fork_after_authentication_flag) + fork_postauth(); } } +static void +client_cleanup_stdio_fwd(int id, void *arg) +{ + debug("stdio forwarding: done"); + cleanup_exit(0); +} + +static int +client_setup_stdio_fwd(const char *host_to_connect, u_short port_to_connect) +{ + Channel *c; + int in, out; + + debug3("client_setup_stdio_fwd %s:%d", host_to_connect, + port_to_connect); + + in = dup(STDIN_FILENO); + out = dup(STDOUT_FILENO); + if (in < 0 || out < 0) + fatal("channel_connect_stdio_fwd: dup() in/out failed"); + + if ((c = channel_connect_stdio_fwd(host_to_connect, port_to_connect, + in, out)) == NULL) + return 0; + channel_register_cleanup(c->self, client_cleanup_stdio_fwd, 0); + return 1; +} + static void ssh_init_forwarding(void) { int success = 0; int i; + if (stdio_forward_host != NULL) { + if (!compat20) { + fatal("stdio forwarding require Protocol 2"); + } + if (!client_setup_stdio_fwd(stdio_forward_host, + stdio_forward_port)) + fatal("Failed to connect in stdio forward mode."); + } + /* Initiate local TCP/IP port forwardings. */ for (i = 0; i < options.num_local_forwards; i++) { debug("Local connections to %.200s:%d forwarded to remote " @@ -1024,7 +1179,9 @@ ssh_session(void) char *proto, *data; /* Get reasonable local authentication information. */ client_x11_get_proto(display, options.xauth_location, - options.forward_x11_trusted, &proto, &data); + options.forward_x11_trusted, + options.forward_x11_timeout, + &proto, &data); /* Request forwarding with authentication spoofing. */ debug("Requesting X11 forwarding with authentication " "spoofing."); @@ -1070,12 +1227,13 @@ ssh_session(void) * If requested and we are not interested in replies to remote * forwarding requests, then let ssh continue in the background. */ - if (fork_after_authentication_flag && - (!options.exit_on_forward_failure || - options.num_remote_forwards == 0)) { - fork_after_authentication_flag = 0; - if (daemon(1, 1) < 0) - fatal("daemon() failed: %.200s", strerror(errno)); + if (fork_after_authentication_flag) { + if (options.exit_on_forward_failure && + options.num_remote_forwards > 0) { + debug("deferring postauth fork until remote forward " + "confirmation received"); + } else + fork_postauth(); } /* @@ -1106,18 +1264,22 @@ ssh_session(void) /* request pty/x11/agent/tcpfwd/shell for channel */ static void -ssh_session2_setup(int id, void *arg) +ssh_session2_setup(int id, int success, void *arg) { extern char **environ; const char *display; int interactive = tty_flag; + if (!success) + return; /* No need for error message, channels code sens one */ + display = getenv("DISPLAY"); if (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); + options.forward_x11_trusted, + options.forward_x11_timeout, &proto, &data); /* Request forwarding with authentication spoofing. */ debug("Requesting X11 forwarding with authentication " "spoofing."); @@ -1194,6 +1356,31 @@ ssh_session2(void) /* XXX should be pre-session */ ssh_init_forwarding(); + /* Start listening for multiplex clients */ + muxserver_listen(); + + /* + * If we are in control persist mode, then prepare to background + * ourselves and have a foreground client attach as a control + * slave. NB. we must save copies of the flags that we override for + * the backgrounding, since we defer attachment of the slave until + * after the connection is fully established (in particular, + * async rfwd replies have been received for ExitOnForwardFailure). + */ + if (options.control_persist && muxserver_sock != -1) { + ostdin_null_flag = stdin_null_flag; + ono_shell_flag = no_shell_flag; + ono_tty_flag = no_tty_flag; + otty_flag = tty_flag; + stdin_null_flag = 1; + no_shell_flag = 1; + no_tty_flag = 1; + tty_flag = 0; + if (!fork_after_authentication_flag) + need_controlpersist_detach = 1; + fork_after_authentication_flag = 1; + } + if (!no_shell_flag || (datafellows & SSH_BUG_DUMMYCHAN)) id = ssh_session2_open(); @@ -1212,16 +1399,22 @@ ssh_session2(void) options.permit_local_command) ssh_local_cmd(options.local_command); - /* Start listening for multiplex clients */ - muxserver_listen(); - - /* If requested, let ssh continue in the background. */ + /* + * If requested and we are not interested in replies to remote + * forwarding requests, then let ssh continue in the background. + */ if (fork_after_authentication_flag) { - fork_after_authentication_flag = 0; - if (daemon(1, 1) < 0) - fatal("daemon() failed: %.200s", strerror(errno)); + if (options.exit_on_forward_failure && + options.num_remote_forwards > 0) { + debug("deferring postauth fork until remote forward " + "confirmation received"); + } else + fork_postauth(); } + if (options.use_roaming) + request_roaming(); + return client_loop(tty_flag, tty_flag ? options.escape_char : SSH_ESCAPECHAR_NONE, id); } @@ -1234,31 +1427,37 @@ load_public_identity_files(void) int i = 0; Key *public; struct passwd *pw; -#ifdef SMARTCARD + u_int n_ids; + char *identity_files[SSH_MAX_IDENTITY_FILES]; + Key *identity_keys[SSH_MAX_IDENTITY_FILES]; +#ifdef ENABLE_PKCS11 Key **keys; + int nkeys; +#endif /* PKCS11 */ - if (options.smartcard_device != NULL && + n_ids = 0; + bzero(identity_files, sizeof(identity_files)); + bzero(identity_keys, sizeof(identity_keys)); + +#ifdef ENABLE_PKCS11 + if (options.pkcs11_provider != NULL && options.num_identity_files < SSH_MAX_IDENTITY_FILES && - (keys = sc_get_keys(options.smartcard_device, NULL)) != NULL) { - int count = 0; - for (i = 0; keys[i] != NULL; i++) { - count++; - memmove(&options.identity_files[1], - &options.identity_files[0], - sizeof(char *) * (SSH_MAX_IDENTITY_FILES - 1)); - memmove(&options.identity_keys[1], - &options.identity_keys[0], - sizeof(Key *) * (SSH_MAX_IDENTITY_FILES - 1)); - options.num_identity_files++; - options.identity_keys[0] = keys[i]; - options.identity_files[0] = sc_get_key_label(keys[i]); + (pkcs11_init(!options.batch_mode) == 0) && + (nkeys = pkcs11_add_provider(options.pkcs11_provider, NULL, + &keys)) > 0) { + for (i = 0; i < nkeys; i++) { + if (n_ids >= SSH_MAX_IDENTITY_FILES) { + key_free(keys[i]); + continue; + } + identity_keys[n_ids] = keys[i]; + identity_files[n_ids] = + xstrdup(options.pkcs11_provider); /* XXX */ + n_ids++; } - if (options.num_identity_files > SSH_MAX_IDENTITY_FILES) - options.num_identity_files = SSH_MAX_IDENTITY_FILES; - i = count; xfree(keys); } -#endif /* SMARTCARD */ +#endif /* ENABLE_PKCS11 */ if ((pw = getpwuid(original_real_uid)) == NULL) fatal("load_public_identity_files: getpwuid failed"); pwname = xstrdup(pw->pw_name); @@ -1266,7 +1465,11 @@ load_public_identity_files(void) if (gethostname(thishost, sizeof(thishost)) == -1) fatal("load_public_identity_files: gethostname: %s", strerror(errno)); - for (; i < options.num_identity_files; i++) { + for (i = 0; i < options.num_identity_files; i++) { + if (n_ids >= SSH_MAX_IDENTITY_FILES) { + xfree(options.identity_files[i]); + continue; + } cp = tilde_expand_filename(options.identity_files[i], original_real_uid); filename = percent_expand(cp, "d", pwdir, @@ -1277,9 +1480,37 @@ load_public_identity_files(void) debug("identity file %s type %d", filename, public ? public->type : -1); xfree(options.identity_files[i]); - options.identity_files[i] = filename; - options.identity_keys[i] = public; + identity_files[n_ids] = filename; + identity_keys[n_ids] = public; + + if (++n_ids >= SSH_MAX_IDENTITY_FILES) + continue; + + /* Try to add the certificate variant too */ + xasprintf(&cp, "%s-cert", filename); + public = key_load_public(cp, NULL); + debug("identity file %s type %d", cp, + public ? public->type : -1); + if (public == NULL) { + xfree(cp); + continue; + } + if (!key_is_cert(public)) { + debug("%s: key %s type %s is not a certificate", + __func__, cp, key_type(public)); + key_free(public); + xfree(cp); + continue; + } + identity_keys[n_ids] = public; + /* point to the original path, most likely the private key */ + identity_files[n_ids] = xstrdup(filename); + n_ids++; } + options.num_identity_files = n_ids; + memcpy(options.identity_files, identity_files, sizeof(identity_files)); + memcpy(options.identity_keys, identity_keys, sizeof(identity_keys)); + bzero(pwname, strlen(pwname)); xfree(pwname); bzero(pwdir, strlen(pwdir)); diff --git a/crypto/openssh/ssh.h b/crypto/openssh/ssh.h index 186cfff964..c94633bdce 100644 --- a/crypto/openssh/ssh.h +++ b/crypto/openssh/ssh.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.h,v 1.78 2006/08/03 03:34:42 deraadt Exp $ */ +/* $OpenBSD: ssh.h,v 1.79 2010/06/25 07:14:46 djm Exp $ */ /* * Author: Tatu Ylonen @@ -18,9 +18,6 @@ /* Default port number. */ #define SSH_DEFAULT_PORT 22 -/* Maximum number of TCP/IP ports forwarded per direction. */ -#define SSH_MAX_FORWARDS_PER_DIRECTION 100 - /* * Maximum number of RSA authentication identity files that can be specified * in configuration files or on the command line. diff --git a/crypto/openssh/ssh2.h b/crypto/openssh/ssh2.h index 1c33dc268b..3ffaf686b8 100644 --- a/crypto/openssh/ssh2.h +++ b/crypto/openssh/ssh2.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh2.h,v 1.11 2008/11/04 08:22:13 djm Exp $ */ +/* $OpenBSD: ssh2.h,v 1.13 2010/02/26 20:29:54 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -166,3 +166,13 @@ #define SSH2_EXTENDED_DATA_STDERR 1 +/* kex messages for resume@appgate.com */ +#define SSH2_MSG_KEX_ROAMING_RESUME 30 +#define SSH2_MSG_KEX_ROAMING_AUTH_REQUIRED 31 +#define SSH2_MSG_KEX_ROAMING_AUTH 32 +#define SSH2_MSG_KEX_ROAMING_AUTH_OK 33 +#define SSH2_MSG_KEX_ROAMING_AUTH_FAIL 34 + +/* Certificate types for OpenSSH certificate keys extension */ +#define SSH2_CERT_TYPE_USER 1 +#define SSH2_CERT_TYPE_HOST 2 diff --git a/crypto/openssh/ssh_config b/crypto/openssh/ssh_config index f28d595834..18936740f6 100644 --- a/crypto/openssh/ssh_config +++ b/crypto/openssh/ssh_config @@ -1,4 +1,4 @@ -# $OpenBSD: ssh_config,v 1.25 2009/02/17 01:28:32 djm Exp $ +# $OpenBSD: ssh_config,v 1.26 2010/01/11 01:39:46 dtucker Exp $ # This is the ssh client system-wide configuration file. See # ssh_config(5) for more information. This file provides defaults for @@ -44,3 +44,4 @@ # TunnelDevice any:any # PermitLocalCommand no # VisualHostKey no +# ProxyCommand ssh -q -W %h:%p gateway.example.com diff --git a/crypto/openssh/ssh_config.5 b/crypto/openssh/ssh_config.5 index ea9a20b23e..ddb806ec00 100644 --- a/crypto/openssh/ssh_config.5 +++ b/crypto/openssh/ssh_config.5 @@ -34,8 +34,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh_config.5,v 1.119 2009/02/22 23:50:57 djm Exp $ -.Dd $Mdocdate: February 22 2009 $ +.\" $OpenBSD: ssh_config.5,v 1.138 2010/08/04 05:37:01 djm Exp $ +.Dd $Mdocdate: August 4 2010 $ .Dt SSH_CONFIG 5 .Os .Sh NAME @@ -319,6 +319,28 @@ It is recommended that any used for opportunistic connection sharing include at least %h, %p, and %r. This ensures that shared connections are uniquely identified. +.It Cm ControlPersist +When used in conjunction with +.Cm ControlMaster , +specifies that the master connection should remain open +in the background (waiting for future client connections) +after the initial client connection has been closed. +If set to +.Dq no , +then the master connection will not be placed into the background, +and will close as soon as the initial client connection is closed. +If set to +.Dq yes , +then the master connection will remain in the background indefinitely +(until killed or closed via a mechanism such as the +.Xr ssh 1 +.Dq Fl O No exit +option). +If set to a time in seconds, or a time in any of the formats documented in +.Xr sshd_config 5 , +then the backgrounded master connection will automatically terminate +after it has remained idle (with no client connections) for the +specified time. .It Cm DynamicForward Specifies that a TCP port on the local machine be forwarded over the secure channel, and the application @@ -329,9 +351,7 @@ The argument must be .Sm off .Oo Ar bind_address : Oc Ar port . .Sm on -IPv6 addresses can be specified by enclosing addresses in square brackets or -by using an alternative syntax: -.Oo Ar bind_address Ns / Oc Ns Ar port . +IPv6 addresses can be specified by enclosing addresses in square brackets. By default, the local port is bound in accordance with the .Cm GatewayPorts setting. @@ -432,6 +452,17 @@ An attacker may then be able to perform activities such as keystroke monitoring if the .Cm ForwardX11Trusted option is also enabled. +.It Cm ForwardX11Timeout +Specify a timeout for untrusted X11 forwarding +using the format described in the +.Sx TIME FORMATS +section of +.Xr sshd_config 5 . +X11 connections received by +.Xr ssh 1 +after this time will be refused. +The default is to disable untrusted X11 forwarding after twenty minutes has +elapsed. .It Cm ForwardX11Trusted If this option is set to .Dq yes , @@ -526,6 +557,10 @@ or for multiple servers running on a single host. .It Cm HostName Specifies the real host name to log into. This can be used to specify nicknames or abbreviations for hosts. +If the hostname contains the character sequence +.Ql %h , +then this will be replaced with the host name specified on the commandline +(this is useful for manipulating unqualified names). The default is the name given on the command line. Numeric IP addresses are also permitted (both on the command line and in .Cm HostName @@ -559,6 +594,12 @@ and for protocol version 2. Additionally, any identities represented by the authentication agent will be used for authentication. +.Xr ssh 1 +will try to load certificate information from the filename obtained by +appending +.Pa -cert.pub +to the path of a specified +.Cm IdentityFile . .Pp The file name may use the tilde syntax to refer to a user's home directory or one of the following @@ -616,6 +657,13 @@ The following escape character substitutions will be performed: (remote user name) or .Ql %u (local user name). +.Pp +The command is run synchronously and does not have access to the +session of the +.Xr ssh 1 +that spawned it. +It should not be used for interactive commands. +.Pp This directive is ignored unless .Cm PermitLocalCommand has been enabled. @@ -628,11 +676,7 @@ The first argument must be .Sm on and the second argument must be .Ar host : Ns Ar hostport . -IPv6 addresses can be specified by enclosing addresses in square brackets or -by using an alternative syntax: -.Oo Ar bind_address Ns / Oc Ns Ar port -and -.Ar host Ns / Ns Ar hostport . +IPv6 addresses can be specified by enclosing addresses in square brackets. Multiple forwardings may be specified, and additional forwardings can be given on the command line. Only the superuser can forward privileged ports. @@ -704,6 +748,12 @@ or .Dq no . The default is .Dq no . +.It Cm PKCS11Provider +Specifies which PKCS#11 provider to use. +The argument to this keyword is the PKCS#11 shared libary +.Xr ssh 1 +should use to communicate with a PKCS#11 token providing the user's +private RSA key. .It Cm Port Specifies the port number to connect on the remote host. The default is 22. @@ -713,14 +763,12 @@ authentication methods. This allows a client to prefer one method (e.g.\& .Cm keyboard-interactive ) over another method (e.g.\& -.Cm password ) -The default for this option is: -.Do gssapi-with-mic , -hostbased, -publickey, -keyboard-interactive, -password -.Dc . +.Cm password ) . +The default is: +.Bd -literal -offset indent +gssapi-with-mic,hostbased,publickey, +keyboard-interactive,password +.Ed .It Cm Protocol Specifies the protocol versions .Xr ssh 1 @@ -730,22 +778,26 @@ The possible values are and .Sq 2 . Multiple versions must be comma-separated. -The default is -.Dq 2,1 . -This means that ssh -tries version 2 and falls back to version 1 +When this option is set to +.Dq 2,1 +.Nm ssh +will try version 2 and fall back to version 1 if version 2 is not available. +The default is +.Sq 2 . .It Cm ProxyCommand Specifies the command to use to connect to the server. The command string extends to the end of the line, and is executed with the user's shell. -In the command string, +In the command string, any occurrence of .Ql %h will be substituted by the host name to -connect and +connect, .Ql %p -by the port. +by the port, and +.Ql %r +by the remote user name. The command can be basically anything, and should read from its standard input and write to its standard output. It should eventually connect an @@ -804,11 +856,7 @@ The first argument must be .Sm on and the second argument must be .Ar host : Ns Ar hostport . -IPv6 addresses can be specified by enclosing addresses in square brackets -or by using an alternative syntax: -.Oo Ar bind_address Ns / Oc Ns Ar port -and -.Ar host Ns / Ns Ar hostport . +IPv6 addresses can be specified by enclosing addresses in square brackets. Multiple forwardings may be specified, and additional forwardings can be given on the command line. Privileged ports can be forwarded only when @@ -918,13 +966,6 @@ channel to request a response from the server. The default is 0, indicating that these messages will not be sent to the server. This option applies to protocol version 2 only. -.It Cm SmartcardDevice -Specifies which smartcard device to use. -The argument to this keyword is the device -.Xr ssh 1 -should use to communicate with a smartcard used for storing the user's -private RSA key. -By default, no device is specified and smartcard support is not activated. .It Cm StrictHostKeyChecking If this flag is set to .Dq yes , diff --git a/crypto/openssh/sshconnect.c b/crypto/openssh/sshconnect.c index 3e57e859df..f55beffe40 100644 --- a/crypto/openssh/sshconnect.c +++ b/crypto/openssh/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.214 2009/05/28 16:50:16 andreas Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.224 2010/04/16 21:14:27 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -28,6 +28,7 @@ #include #include +#include #include #ifdef HAVE_PATHS_H #include @@ -57,6 +58,7 @@ #include "misc.h" #include "dns.h" #include "roaming.h" +#include "ssh2.h" #include "version.h" char *client_version_string = NULL; @@ -99,8 +101,8 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) * (e.g. Solaris) */ xasprintf(&tmp, "exec %s", proxy_command); - command_string = percent_expand(tmp, "h", host, - "p", strport, (char *)NULL); + command_string = percent_expand(tmp, "h", host, "p", strport, + "r", options.user, (char *)NULL); xfree(tmp); /* Create pipes for communicating with the proxy. */ @@ -191,8 +193,11 @@ ssh_create_socket(int privileged, struct addrinfo *ai) return sock; } sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (sock < 0) + if (sock < 0) { error("socket: %.100s", strerror(errno)); + return -1; + } + fcntl(sock, F_SETFD, FD_CLOEXEC); /* Bind the socket to an alternative local IP address */ if (options.bind_address == NULL) @@ -572,6 +577,23 @@ confirm(const char *prompt) } } +static int +check_host_cert(const char *host, const Key *host_key) +{ + const char *reason; + + if (key_cert_check_authority(host_key, 1, 0, host, &reason) != 0) { + error("%s", reason); + return 0; + } + if (buffer_len(&host_key->cert->critical) != 0) { + error("Certificate for %s contains unsupported " + "critical options(s)", host); + return 0; + } + return 1; +} + /* * check whether the supplied host key is valid, return -1 if the key * is not valid. the user_hostfile will not be updated if 'readonly' is true. @@ -584,13 +606,13 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, Key *host_key, int readonly, const char *user_hostfile, const char *system_hostfile) { - Key *file_key; - const char *type = key_type(host_key); + Key *file_key, *raw_key = NULL; + const char *type; char *ip = NULL, *host = NULL; char hostline[1000], *hostp, *fp, *ra; HostStatus host_status; HostStatus ip_status; - int r, local = 0, host_ip_differ = 0; + int r, want_cert, local = 0, host_ip_differ = 0; int salen; char ntop[NI_MAXHOST]; char msg[1024]; @@ -663,11 +685,15 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, host = put_host_port(hostname, port); } + retry: + want_cert = key_is_cert(host_key); + type = key_type(host_key); + /* * Store the host key from the known host file in here so that we can * compare it with the key for the IP address. */ - file_key = key_new(host_key->type); + file_key = key_new(key_is_cert(host_key) ? KEY_UNSPEC : host_key->type); /* * Check if the host key is present in the user's list of known @@ -683,9 +709,10 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, } /* * Also perform check for the ip address, skip the check if we are - * localhost or the hostname was an ip address to begin with + * localhost, looking for a certificate, or the hostname was an ip + * address to begin with. */ - if (options.check_host_ip) { + if (!want_cert && options.check_host_ip) { Key *ip_key = key_new(host_key->type); ip_file = user_hostfile; @@ -709,11 +736,14 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, switch (host_status) { case HOST_OK: /* The host is known and the key matches. */ - debug("Host '%.200s' is known and matches the %s host key.", - host, type); - debug("Found key in %s:%d", host_file, host_line); + debug("Host '%.200s' is known and matches the %s host %s.", + host, type, want_cert ? "certificate" : "key"); + debug("Found %s in %s:%d", + want_cert ? "CA key" : "key", host_file, host_line); + if (want_cert && !check_host_cert(hostname, host_key)) + goto fail; if (options.check_host_ip && ip_status == HOST_NEW) { - if (readonly) + if (readonly || want_cert) logit("%s host key for IP address " "'%.128s' not in list of known hosts.", type, ip); @@ -745,7 +775,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, break; } } - if (readonly) + if (readonly || want_cert) goto fail; /* The host is new. */ if (options.strict_host_key_checking == 1) { @@ -829,7 +859,37 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, logit("Warning: Permanently added '%.200s' (%s) to the " "list of known hosts.", hostp, type); break; + case HOST_REVOKED: + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("@ WARNING: REVOKED HOST KEY DETECTED! @"); + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("The %s host key for %s is marked as revoked.", type, host); + error("This could mean that a stolen key is being used to"); + error("impersonate this host."); + + /* + * If strict host key checking is in use, the user will have + * to edit the key manually and we can only abort. + */ + if (options.strict_host_key_checking) { + error("%s host key for %.200s was revoked and you have " + "requested strict checking.", type, host); + goto fail; + } + goto continue_unsafe; + case HOST_CHANGED: + if (want_cert) { + /* + * This is only a debug() since it is valid to have + * CAs with wildcard DNS matches that don't match + * all hosts that one might visit. + */ + debug("Host certificate authority does not " + "match %s in %s:%d", CA_MARKER, + host_file, host_line); + goto fail; + } if (readonly == ROQUIET) goto fail; if (options.check_host_ip && host_ip_differ) { @@ -867,6 +927,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, goto fail; } + continue_unsafe: /* * If strict host key checking has not been requested, allow * the connection but without MITM-able authentication or @@ -925,7 +986,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, * XXX Should permit the user to change to use the new id. * This could be done by converting the host key to an * identifying sentence, tell that the host identifies itself - * by that sentence, and ask the user if he/she whishes to + * by that sentence, and ask the user if he/she wishes to * accept the authentication. */ break; @@ -966,6 +1027,20 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, return 0; fail: + if (want_cert && host_status != HOST_REVOKED) { + /* + * No matching certificate. Downgrade cert to raw key and + * search normally. + */ + debug("No matching CA found. Retry with plain key"); + raw_key = key_from_private(host_key); + if (key_drop_cert(raw_key) != 0) + fatal("Couldn't drop certificate"); + host_key = raw_key; + goto retry; + } + if (raw_key != NULL) + key_free(raw_key); xfree(ip); xfree(host); return -1; @@ -978,7 +1053,8 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) struct stat st; int flags = 0; - if (options.verify_host_key_dns && + /* XXX certs are not yet supported for DNS */ + if (!key_is_cert(host_key) && options.verify_host_key_dns && verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) { if (flags & DNS_VERIFY_FOUND) { diff --git a/crypto/openssh/sshconnect2.c b/crypto/openssh/sshconnect2.c index 260c6307a6..4c379ae598 100644 --- a/crypto/openssh/sshconnect2.c +++ b/crypto/openssh/sshconnect2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect2.c,v 1.171 2009/03/05 07:18:19 djm Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.183 2010/04/26 22:28:24 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -152,6 +153,11 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) dispatch_run(DISPATCH_BLOCK, &kex->done, kex); + if (options.use_roaming && !kex->roaming) { + debug("Roaming not allowed by server"); + options.use_roaming = 0; + } + session_id2 = kex->session_id; session_id2_len = kex->session_id_len; @@ -189,7 +195,7 @@ struct Authctxt { const char *host; const char *service; Authmethod *method; - int success; + sig_atomic_t success; char *authlist; /* pubkey */ Idlist keys; @@ -210,6 +216,7 @@ struct Authmethod { }; void input_userauth_success(int, u_int32_t, void *); +void input_userauth_success_unexpected(int, u_int32_t, void *); void input_userauth_failure(int, u_int32_t, void *); void input_userauth_banner(int, u_int32_t, void *); void input_userauth_error(int, u_int32_t, void *); @@ -414,7 +421,7 @@ input_userauth_banner(int type, u_int32_t seq, void *ctxt) if (len > 65536) len = 65536; msg = xmalloc(len * 4 + 1); /* max expansion from strnvis() */ - strnvis(msg, raw, len * 4 + 1, VIS_SAFE|VIS_OCTAL); + strnvis(msg, raw, len * 4 + 1, VIS_SAFE|VIS_OCTAL|VIS_NOSLASH); fprintf(stderr, "%s", msg); xfree(msg); } @@ -427,12 +434,15 @@ void input_userauth_success(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; + if (authctxt == NULL) fatal("input_userauth_success: no authentication context"); if (authctxt->authlist) { xfree(authctxt->authlist); authctxt->authlist = NULL; } + if (authctxt->method != NULL && authctxt->method->cleanup != NULL) + authctxt->method->cleanup(authctxt); if (authctxt->methoddata) { xfree(authctxt->methoddata); authctxt->methoddata = NULL; @@ -440,6 +450,18 @@ input_userauth_success(int type, u_int32_t seq, void *ctxt) authctxt->success = 1; /* break out */ } +void +input_userauth_success_unexpected(int type, u_int32_t seq, void *ctxt) +{ + Authctxt *authctxt = ctxt; + + if (authctxt == NULL) + fatal("%s: no authentication context", __func__); + + fatal("Unexpected authentication success during %s.", + authctxt->method->name); +} + /* ARGSUSED */ void input_userauth_failure(int type, u_int32_t seq, void *ctxt) @@ -782,6 +804,8 @@ userauth_passwd(Authctxt *authctxt) static int attempt = 0; char prompt[150]; char *password; + const char *host = options.host_key_alias ? options.host_key_alias : + authctxt->host; if (attempt++ >= options.number_of_password_prompts) return 0; @@ -790,7 +814,7 @@ userauth_passwd(Authctxt *authctxt) error("Permission denied, please try again."); snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ", - authctxt->server_user, authctxt->host); + authctxt->server_user, host); password = read_passphrase(prompt, 0); packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_cstring(authctxt->server_user); @@ -819,6 +843,8 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) Authctxt *authctxt = ctxt; char *info, *lang, *password = NULL, *retype = NULL; char prompt[150]; + const char *host = options.host_key_alias ? options.host_key_alias : + authctxt->host; debug2("input_userauth_passwd_changereq"); @@ -839,7 +865,7 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) packet_put_char(1); /* additional info */ snprintf(prompt, sizeof(prompt), "Enter %.30s@%.128s's old password: ", - authctxt->server_user, authctxt->host); + authctxt->server_user, host); password = read_passphrase(prompt, 0); packet_put_cstring(password); memset(password, 0, strlen(password)); @@ -848,7 +874,7 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) while (password == NULL) { snprintf(prompt, sizeof(prompt), "Enter %.30s@%.128s's new password: ", - authctxt->server_user, authctxt->host); + authctxt->server_user, host); password = read_passphrase(prompt, RP_ALLOW_EOF); if (password == NULL) { /* bail out */ @@ -856,7 +882,7 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) } snprintf(prompt, sizeof(prompt), "Retype %.30s@%.128s's new password: ", - authctxt->server_user, authctxt->host); + authctxt->server_user, host); retype = read_passphrase(prompt, 0); if (strcmp(password, retype) != 0) { memset(password, 0, strlen(password)); @@ -1114,8 +1140,11 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id) u_int skip = 0; int ret = -1; int have_sig = 1; + char *fp; - debug3("sign_and_send_pubkey"); + fp = key_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX); + debug3("sign_and_send_pubkey: %s %s", key_type(id->key), fp); + xfree(fp); if (key_to_blob(id->key, &blob, &bloblen) == 0) { /* we cannot handle this key */ @@ -1224,7 +1253,7 @@ load_identity_file(char *filename) { Key *private; char prompt[300], *passphrase; - int perm_ok, quit, i; + int perm_ok = 0, quit, i; struct stat st; if (stat(filename, &st) < 0) { @@ -1285,6 +1314,8 @@ pubkey_prepare(Authctxt *authctxt) key = options.identity_keys[i]; if (key && key->type == KEY_RSA1) continue; + if (key && key->cert && key->cert->type != SSH2_CERT_TYPE_USER) + continue; options.identity_keys[i] = NULL; id = xcalloc(1, sizeof(*id)); id->key = key; @@ -1370,7 +1401,8 @@ userauth_pubkey(Authctxt *authctxt) * private key instead */ if (id->key && id->key->type != KEY_RSA1) { - debug("Offering public key: %s", id->filename); + debug("Offering %s public key: %s", key_type(id->key), + id->filename); sent = send_pubkey_test(authctxt, id); } else if (id->key == NULL) { debug("Trying private key: %s", id->filename); @@ -1488,7 +1520,7 @@ ssh_keysign(Key *key, u_char **sigp, u_int *lenp, debug2("ssh_keysign called"); if (stat(_PATH_SSH_KEY_SIGN, &st) < 0) { - error("ssh_keysign: no installed: %s", strerror(errno)); + error("ssh_keysign: not installed: %s", strerror(errno)); return -1; } if (fflush(stdout) != 0) @@ -1506,6 +1538,8 @@ ssh_keysign(Key *key, u_char **sigp, u_int *lenp, return -1; } if (pid == 0) { + /* keep the socket on exec */ + fcntl(packet_get_connection_in(), F_SETFD, 0); permanently_drop_suid(getuid()); close(from[0]); if (dup2(from[1], STDOUT_FILENO) < 0) @@ -1558,10 +1592,10 @@ userauth_hostbased(Authctxt *authctxt) Sensitive *sensitive = authctxt->sensitive; Buffer b; u_char *signature, *blob; - char *chost, *pkalg, *p, myname[NI_MAXHOST]; + char *chost, *pkalg, *p; const char *service; u_int blen, slen; - int ok, i, len, found = 0; + int ok, i, found = 0; /* check for a useful key */ for (i = 0; i < sensitive->nkeys; i++) { @@ -1582,23 +1616,13 @@ userauth_hostbased(Authctxt *authctxt) return 0; } /* figure out a name for the client host */ - p = NULL; - if (packet_connection_is_on_socket()) - p = get_local_name(packet_get_connection_in()); - if (p == NULL) { - if (gethostname(myname, sizeof(myname)) == -1) { - verbose("userauth_hostbased: gethostname: %s", - strerror(errno)); - } else - p = xstrdup(myname); - } + p = get_local_name(packet_get_connection_in()); if (p == NULL) { error("userauth_hostbased: cannot get local ipaddr/name"); key_free(private); xfree(blob); return 0; } - len = strlen(p) + 2; xasprintf(&chost, "%s.", p); debug2("userauth_hostbased: chost %s", chost); xfree(p); @@ -1709,6 +1733,8 @@ userauth_jpake(Authctxt *authctxt) /* Expect step 1 packet from peer */ dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1, input_userauth_jpake_server_step1); + dispatch_set(SSH2_MSG_USERAUTH_SUCCESS, + &input_userauth_success_unexpected); return 1; } @@ -1721,6 +1747,7 @@ userauth_jpake_cleanup(Authctxt *authctxt) jpake_free(authctxt->methoddata); authctxt->methoddata = NULL; } + dispatch_set(SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success); } #endif /* JPAKE */ diff --git a/crypto/openssh/sshd.8 b/crypto/openssh/sshd.8 index 111d491d9a..d3685b92b3 100644 --- a/crypto/openssh/sshd.8 +++ b/crypto/openssh/sshd.8 @@ -34,8 +34,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd.8,v 1.248 2009/03/26 08:38:39 sobrado Exp $ -.Dd $Mdocdate: March 26 2009 $ +.\" $OpenBSD: sshd.8,v 1.257 2010/08/04 05:37:01 djm Exp $ +.Dd $Mdocdate: August 4 2010 $ .Dt SSHD 8 .Os .Sh NAME @@ -47,6 +47,7 @@ .Op Fl 46DdeiqTt .Op Fl b Ar bits .Op Fl C Ar connection_spec +.Op Fl c Ar host_certificate_file .Op Fl f Ar config_file .Op Fl g Ar login_grace_time .Op Fl h Ar host_key_file @@ -119,6 +120,15 @@ and All are required and may be supplied in any order, either with multiple .Fl C options or as a comma-separated list. +.It Fl c Ar host_certificate_file +Specifies a path to a certificate file to identify +.Nm +during key exchange. +The certificate file must match a host key file specified using the +.Fl h +option or the +.Cm HostKey +configuration directive. .It Fl D When this option is specified, .Nm @@ -127,8 +137,8 @@ This allows easy monitoring of .Nm sshd . .It Fl d Debug mode. -The server sends verbose debug output to the system -log, and does not put itself in the background. +The server sends verbose debug output to standard error, +and does not put itself in the background. The server also will not fork and will only process one connection. This option is only intended for debugging for the server. Multiple @@ -260,7 +270,7 @@ or .El .Sh AUTHENTICATION The OpenSSH SSH daemon supports SSH protocols 1 and 2. -Both protocols are supported by default, +The default is to use protocol 2 only, though this can be changed via the .Cm Protocol option in @@ -498,6 +508,13 @@ No spaces are permitted, except within double quotes. The following option specifications are supported (note that option keywords are case-insensitive): .Bl -tag -width Ds +.It Cm cert-authority +Specifies that the listed key is a certification authority (CA) that is +trusted to validate signed certificates for user authentication. +.Pp +Certificates may encode access restrictions similar to these key options. +If both certificate restrictions and key options are present, the most +restrictive union of the two is applied. .It Cm command="command" Specifies that the command is executed whenever this key is used for authentication. @@ -517,6 +534,10 @@ The command originally supplied by the client is available in the .Ev SSH_ORIGINAL_COMMAND environment variable. Note that this option applies to shell, command or subsystem execution. +Also note that this command may be superseded by either a +.Xr sshd_config 5 +.Cm ForceCommand +directive or a command embedded in a certificate. .It Cm environment="NAME=value" Specifies that the string is to be added to the environment when logging in using this key. @@ -574,13 +595,23 @@ Limit local .Li ``ssh -L'' port forwarding such that it may only connect to the specified host and port. -IPv6 addresses can be specified with an alternative syntax: -.Ar host Ns / Ns Ar port . +IPv6 addresses can be specified by enclosing the address in square brackets. Multiple .Cm permitopen options may be applied separated by commas. No pattern matching is performed on the specified hostnames, they must be literal domains or addresses. +.It Cm principals="principals" +On a +.Cm cert-authority +line, specifies allowed principals for certificate authentication as a +comma-separated list. +At least one name from the list must appear in the certificate's +list of principals for the certificate to be accepted. +This option is ignored for keys that are not marked as trusted certificate +signers using the +.Cm cert-authority +option. .It Cm tunnel="n" Force a .Xr tun 4 @@ -613,10 +644,19 @@ be prepared by the administrator (optional), and the per-user file is maintained automatically: whenever the user connects from an unknown host, its key is added to the per-user file. .Pp -Each line in these files contains the following fields: hostnames, -bits, exponent, modulus, comment. +Each line in these files contains the following fields: markers (optional), +hostnames, bits, exponent, modulus, comment. The fields are separated by spaces. .Pp +The marker is optional, but if it is present then it must be one of +.Dq @cert-authority , +to indicate that the line contains a certification authority (CA) key, +or +.Dq @revoked , +to indicate that the key contained on the line is revoked and must not ever +be accepted. +Only one marker should be used on a key line. +.Pp Hostnames is a comma-separated list of patterns .Pf ( Ql * and @@ -656,8 +696,25 @@ Lines starting with and empty lines are ignored as comments. .Pp When performing host authentication, authentication is accepted if any -matching line has the proper key. -It is thus permissible (but not +matching line has the proper key; either one that matches exactly or, +if the server has presented a certificate for authentication, the key +of the certification authority that signed the certificate. +For a key to be trusted as a certification authority, it must use the +.Dq @cert-authority +marker described above. +.Pp +The known hosts file also provides a facility to mark keys as revoked, +for example when it is known that the associated private key has been +stolen. +Revoked keys are specified by including the +.Dq @revoked +marker at the beginning of the key line, and are never accepted for +authentication or as certification authorities, but instead will +produce a warning from +.Xr ssh 1 +when they are encountered. +.Pp +It is permissible (but not recommended) to have several lines or different host keys for the same names. This will inevitably happen when short forms of host names @@ -668,10 +725,16 @@ accepted if valid information can be found from either file. .Pp Note that the lines in these files are typically hundreds of characters long, and you definitely don't want to type in the host keys by hand. -Rather, generate them by a script +Rather, generate them by a script, +.Xr ssh-keyscan 1 or by taking .Pa /etc/ssh/ssh_host_key.pub and adding the host names at the front. +.Xr ssh-keygen 1 +also offers some basic automated editing for +.Pa ~/.ssh/known_hosts +including removing hosts matching a host name and converting all host +names to their hashed representations. .Pp An example ssh_known_hosts file: .Bd -literal -offset 3n @@ -681,6 +744,10 @@ cvs.example.net,192.0.2.10 ssh-rsa AAAA1234.....= # A hashed hostname |1|JfKTdBh7rNbXkVAQCRp4OQoPfmI=|USECr3SWf1JUPsms5AqfD5QfxkM= ssh-rsa AAAA1234.....= +# A revoked key +@revoked * ssh-rsa AAAAB5W... +# A CA key, accepted for any host in *.mydomain.com or *.mydomain.org +@cert-authority *.mydomain.org,*.mydomain.com ssh-rsa AAAAB5W... .Ed .Sh FILES .Bl -tag -width Ds -compact diff --git a/crypto/openssh/sshd.c b/crypto/openssh/sshd.c index 13a455d1fe..a7d3ee5c41 100644 --- a/crypto/openssh/sshd.c +++ b/crypto/openssh/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.367 2009/05/28 16:50:16 andreas Exp $ */ +/* $OpenBSD: sshd.c,v 1.375 2010/04/16 01:47:26 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -205,6 +205,7 @@ struct { Key *server_key; /* ephemeral server key */ Key *ssh1_host_key; /* ssh1 host key */ Key **host_keys; /* all private host keys */ + Key **host_certificates; /* all public host certificates */ int have_ssh1_key; int have_ssh2_key; u_char ssh1_cookie[SSH_SESSION_KEY_LENGTH]; @@ -309,6 +310,7 @@ sighup_restart(void) close_listen_socks(); close_startup_pipes(); alarm(0); /* alarm timer persists across exec */ + signal(SIGHUP, SIG_IGN); /* will be restored after exec */ execv(saved_argv[0], saved_argv); logit("RESTART FAILED: av[0]='%.100s', error: %.100s.", saved_argv[0], strerror(errno)); @@ -544,6 +546,10 @@ destroy_sensitive_data(void) key_free(sensitive_data.host_keys[i]); sensitive_data.host_keys[i] = NULL; } + if (sensitive_data.host_certificates[i]) { + key_free(sensitive_data.host_certificates[i]); + sensitive_data.host_certificates[i] = NULL; + } } sensitive_data.ssh1_host_key = NULL; memset(sensitive_data.ssh1_cookie, 0, SSH_SESSION_KEY_LENGTH); @@ -570,6 +576,7 @@ demote_sensitive_data(void) if (tmp->type == KEY_RSA1) sensitive_data.ssh1_host_key = tmp; } + /* Certs do not need demotion */ } /* We do not clear ssh1_host key and cookie. XXX - Okay Niels? */ @@ -716,10 +723,11 @@ list_hostkey_types(void) const char *p; char *ret; int i; + Key *key; buffer_init(&b); for (i = 0; i < options.num_host_key_files; i++) { - Key *key = sensitive_data.host_keys[i]; + key = sensitive_data.host_keys[i]; if (key == NULL) continue; switch (key->type) { @@ -731,6 +739,21 @@ list_hostkey_types(void) buffer_append(&b, p, strlen(p)); break; } + /* If the private key has a cert peer, then list that too */ + key = sensitive_data.host_certificates[i]; + if (key == NULL) + continue; + switch (key->type) { + case KEY_RSA_CERT_V00: + case KEY_DSA_CERT_V00: + case KEY_RSA_CERT: + case KEY_DSA_CERT: + if (buffer_len(&b) > 0) + buffer_append(&b, ",", 1); + p = key_ssh_name(key); + buffer_append(&b, p, strlen(p)); + break; + } } buffer_append(&b, "\0", 1); ret = xstrdup(buffer_ptr(&b)); @@ -739,19 +762,43 @@ list_hostkey_types(void) return ret; } -Key * -get_hostkey_by_type(int type) +static Key * +get_hostkey_by_type(int type, int need_private) { int i; + Key *key; for (i = 0; i < options.num_host_key_files; i++) { - Key *key = sensitive_data.host_keys[i]; + switch (type) { + case KEY_RSA_CERT_V00: + case KEY_DSA_CERT_V00: + case KEY_RSA_CERT: + case KEY_DSA_CERT: + key = sensitive_data.host_certificates[i]; + break; + default: + key = sensitive_data.host_keys[i]; + break; + } if (key != NULL && key->type == type) - return key; + return need_private ? + sensitive_data.host_keys[i] : key; } return NULL; } +Key * +get_hostkey_public_by_type(int type) +{ + return get_hostkey_by_type(type, 0); +} + +Key * +get_hostkey_private_by_type(int type) +{ + return get_hostkey_by_type(type, 1); +} + Key * get_hostkey_by_index(int ind) { @@ -766,8 +813,13 @@ get_hostkey_index(Key *key) int i; for (i = 0; i < options.num_host_key_files; i++) { - if (key == sensitive_data.host_keys[i]) - return (i); + if (key_is_cert(key)) { + if (key == sensitive_data.host_certificates[i]) + return (i); + } else { + if (key == sensitive_data.host_keys[i]) + return (i); + } } return (-1); } @@ -806,9 +858,9 @@ usage(void) fprintf(stderr, "%s, %s\n", SSH_RELEASE, SSLeay_version(SSLEAY_VERSION)); fprintf(stderr, -"usage: sshd [-46DdeiqTt] [-b bits] [-C connection_spec] [-f config_file]\n" -" [-g login_grace_time] [-h host_key_file] [-k key_gen_time]\n" -" [-o option] [-p port] [-u len]\n" +"usage: sshd [-46DdeiqTt] [-b bits] [-C connection_spec] [-c host_cert_file]\n" +" [-f config_file] [-g login_grace_time] [-h host_key_file]\n" +" [-k key_gen_time] [-o option] [-p port] [-u len]\n" ); exit(1); } @@ -979,15 +1031,9 @@ server_listen(void) &on, sizeof(on)) == -1) error("setsockopt SO_REUSEADDR: %s", strerror(errno)); -#ifdef IPV6_V6ONLY /* Only communicate in IPv6 over AF_INET6 sockets. */ - if (ai->ai_family == AF_INET6) { - if (setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, - &on, sizeof(on)) == -1) - error("setsockopt IPV6_V6ONLY: %s", - strerror(errno)); - } -#endif + if (ai->ai_family == AF_INET6) + sock_set_v6only(listen_sock); debug("Bind to port %s on %s.", strport, ntop); @@ -1241,7 +1287,7 @@ main(int ac, char **av) { extern char *optarg; extern int optind; - int opt, i, on = 1; + int opt, i, j, on = 1; int sock_in = -1, sock_out = -1, newsock = -1; const char *remote_ip; char *test_user = NULL, *test_host = NULL, *test_addr = NULL; @@ -1294,6 +1340,14 @@ main(int ac, char **av) case 'f': config_file_name = optarg; break; + case 'c': + if (options.num_host_cert_files >= MAX_HOSTCERTS) { + fprintf(stderr, "too many host certificates.\n"); + exit(1); + } + options.host_cert_files[options.num_host_cert_files++] = + derelativise_path(optarg); + break; case 'd': if (debug_flag == 0) { debug_flag = 1; @@ -1356,7 +1410,8 @@ main(int ac, char **av) fprintf(stderr, "too many host keys.\n"); exit(1); } - options.host_key_files[options.num_host_key_files++] = optarg; + options.host_key_files[options.num_host_key_files++] = + derelativise_path(optarg); break; case 't': test_flag = 1; @@ -1540,6 +1595,46 @@ main(int ac, char **av) exit(1); } + /* + * Load certificates. They are stored in an array at identical + * indices to the public keys that they relate to. + */ + sensitive_data.host_certificates = xcalloc(options.num_host_key_files, + sizeof(Key *)); + for (i = 0; i < options.num_host_key_files; i++) + sensitive_data.host_certificates[i] = NULL; + + for (i = 0; i < options.num_host_cert_files; i++) { + key = key_load_public(options.host_cert_files[i], NULL); + if (key == NULL) { + error("Could not load host certificate: %s", + options.host_cert_files[i]); + continue; + } + if (!key_is_cert(key)) { + error("Certificate file is not a certificate: %s", + options.host_cert_files[i]); + key_free(key); + continue; + } + /* Find matching private key */ + for (j = 0; j < options.num_host_key_files; j++) { + if (key_equal_public(key, + sensitive_data.host_keys[j])) { + sensitive_data.host_certificates[j] = key; + break; + } + } + if (j >= options.num_host_key_files) { + error("No matching private key for certificate: %s", + options.host_cert_files[i]); + key_free(key); + continue; + } + sensitive_data.host_certificates[j] = key; + debug("host certificate: #%d type %d %s", j, key->type, + key_type(key)); + } /* Check certain values for sanity. */ if (options.protocol & SSH_PROTO_1) { if (options.server_key_bits < 512 || @@ -1662,6 +1757,7 @@ main(int ac, char **av) if (inetd_flag) { server_accept_inetd(&sock_in, &sock_out); } else { + platform_pre_listen(); server_listen(); if (options.protocol & SSH_PROTO_1) @@ -1751,6 +1847,10 @@ main(int ac, char **av) sock_in, sock_out, newsock, startup_pipe, config_s[0]); } + /* Executed child processes don't need these. */ + fcntl(sock_out, F_SETFD, FD_CLOEXEC); + fcntl(sock_in, F_SETFD, FD_CLOEXEC); + /* * Disable the key regeneration alarm. We will not regenerate the * key since we are no longer in a position to give it to anyone. We @@ -1848,6 +1948,7 @@ main(int ac, char **av) /* prepare buffer to collect messages to display to user after login */ buffer_init(&loginmsg); + auth_debug_reset(); if (use_privsep) if (privsep_preauth(authctxt) == 1) @@ -2204,7 +2305,8 @@ do_ssh2_kex(void) kex->server = 1; kex->client_version_string=client_version_string; kex->server_version_string=server_version_string; - kex->load_host_key=&get_hostkey_by_type; + kex->load_host_public_key=&get_hostkey_public_by_type; + kex->load_host_private_key=&get_hostkey_private_by_type; kex->host_key_index=&get_hostkey_index; xxx_kex = kex; diff --git a/crypto/openssh/sshd_config b/crypto/openssh/sshd_config index 1b53a0efb9..72fbae37be 100644 --- a/crypto/openssh/sshd_config +++ b/crypto/openssh/sshd_config @@ -1,4 +1,4 @@ -# $OpenBSD: sshd_config,v 1.80 2008/07/02 02:24:18 djm Exp $ +# $OpenBSD: sshd_config,v 1.81 2009/10/08 14:03:41 markus Exp $ # This is the sshd server system-wide configuration file. See # sshd_config(5) for more information. @@ -15,10 +15,8 @@ #ListenAddress 0.0.0.0 #ListenAddress :: -# Disable legacy (protocol version 1) support in the server for new -# installations. In future the default will change to require explicit -# activation of protocol 1 -Protocol 2 +# The default requires explicit activation of protocol 1 +#Protocol 2 # HostKey for protocol version 1 #HostKey /etc/ssh/ssh_host_key diff --git a/crypto/openssh/sshd_config.5 b/crypto/openssh/sshd_config.5 index 588aed56e1..596a728f80 100644 --- a/crypto/openssh/sshd_config.5 +++ b/crypto/openssh/sshd_config.5 @@ -34,8 +34,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd_config.5,v 1.106 2009/04/21 15:13:17 stevesk Exp $ -.Dd $Mdocdate: April 21 2009 $ +.\" $OpenBSD: sshd_config.5,v 1.125 2010/06/30 07:28:34 jmc Exp $ +.Dd $Mdocdate: June 30 2010 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -155,6 +155,10 @@ for more information on patterns. .It Cm AuthorizedKeysFile Specifies the file that contains the public keys that can be used for user authentication. +The format is described in the +.Sx AUTHORIZED_KEYS FILE FORMAT +section of +.Xr sshd 8 . .Cm AuthorizedKeysFile may contain tokens of the form %T which are substituted during connection setup. @@ -167,6 +171,47 @@ is taken to be an absolute path or one relative to the user's home directory. The default is .Dq .ssh/authorized_keys . +.It Cm AuthorizedPrincipalsFile +Specifies a file that lists principal names that are accepted for +certificate authentication. +When using certificates signed by a key listed in +.Cm TrustedUserCAKeys , +this file lists names, one of which must appear in the certificate for it +to be accepted for authentication. +Names are listed one per line preceded by key options (as described +in +.Sx AUTHORIZED_KEYS FILE FORMAT +in +.Xr sshd 8 ) . +Empty lines and comments starting with +.Ql # +are ignored. +.Pp +.Cm AuthorizedPrincipalsFile +may contain tokens of the form %T which are substituted during connection +setup. +The following tokens are defined: %% is replaced by a literal '%', +%h is replaced by the home directory of the user being authenticated, and +%u is replaced by the username of that user. +After expansion, +.Cm AuthorizedPrincipalsFile +is taken to be an absolute path or one relative to the user's home +directory. +.Pp +The default is not to use a principals file \(en in this case, the username +of the user must appear in a certificate's principals list for it to be +accepted. +Note that +.Cm AuthorizedPrincipalsFile +is only used when authentication proceeds using a CA listed in +.Cm TrustedUserCAKeys +and is not consulted for certification authorities trusted via +.Pa ~/.ssh/authorized_keys , +though the +.Cm principals= +key option offers a similar facility (see +.Xr sshd 8 +for details). .It Cm Banner The contents of the specified file are sent to the remote user before authentication is allowed. @@ -182,16 +227,16 @@ PAM or though authentication styles supported in The default is .Dq yes . .It Cm ChrootDirectory -Specifies a path to +Specifies the pathname of a directory to .Xr chroot 2 to after authentication. -This path, and all its components, must be root-owned directories that are +All components of the pathname must be root-owned directories that are not writable by any other user or group. After the chroot, .Xr sshd 8 changes the working directory to the user's home directory. .Pp -The path may contain the following tokens that are expanded at runtime once +The pathname may contain the following tokens that are expanded at runtime once the connecting user has been authenticated: %% is replaced by a literal '%', %h is replaced by the home directory of the user being authenticated, and %u is replaced by the username of that user. @@ -411,6 +456,14 @@ uses the name supplied by the client rather than attempting to resolve the name from the TCP connection itself. The default is .Dq no . +.It Cm HostCertificate +Specifies a file containing a public host certificate. +The certificate's public key must match a private host key already specified +by +.Cm HostKey . +The default behaviour of +.Xr sshd 8 +is not to load any certificates. .It Cm HostKey Specifies a file containing a private host key used by SSH. @@ -600,12 +653,15 @@ keyword. Available keywords are .Cm AllowAgentForwarding , .Cm AllowTcpForwarding , +.Cm AuthorizedKeysFile , +.Cm AuthorizedPrincipalsFile , .Cm Banner , .Cm ChrootDirectory , .Cm ForceCommand , .Cm GatewayPorts , .Cm GSSAPIAuthentication , .Cm HostbasedAuthentication , +.Cm HostbasedUsesNameFromPacketOnly , .Cm KbdInteractiveAuthentication , .Cm KerberosAuthentication , .Cm MaxAuthTries , @@ -614,6 +670,8 @@ Available keywords are .Cm PermitEmptyPasswords , .Cm PermitOpen , .Cm PermitRootLogin , +.Cm PermitTunnel , +.Cm PubkeyAuthentication , .Cm RhostsRSAAuthentication , .Cm RSAAuthentication , .Cm X11DisplayOffset , @@ -792,7 +850,7 @@ and .Sq 2 . Multiple versions must be comma-separated. The default is -.Dq 2,1 . +.Sq 2 . Note that the order of the protocol list does not indicate preference, because the client selects among multiple protocol versions offered by the server. @@ -805,6 +863,11 @@ Specifies whether public key authentication is allowed. The default is .Dq yes . Note that this option applies to protocol version 2 only. +.It Cm RevokedKeys +Specifies a list of revoked public keys. +Keys listed in this file will be refused for public key authentication. +Note that if this file is not readable, then public key authentication will +be refused for all users. .It Cm RhostsRSAAuthentication Specifies whether rhosts or /etc/hosts.equiv authentication together with successful RSA host authentication is allowed. @@ -828,6 +891,9 @@ This is normally desirable because novices sometimes accidentally leave their directory or files world-writable. The default is .Dq yes . +Note that this does not apply to +.Cm ChrootDirectory , +whose permissions and ownership are checked unconditionally. .It Cm Subsystem Configures an external subsystem (e.g. file transfer daemon). Arguments should be a subsystem name and a command (with optional arguments) @@ -877,6 +943,22 @@ This avoids infinitely hanging sessions. .Pp To disable TCP keepalive messages, the value should be set to .Dq no . +.It Cm TrustedUserCAKeys +Specifies a file containing public keys of certificate authorities that are +trusted to sign user certificates for authentication. +Keys are listed one per line; empty lines and comments starting with +.Ql # +are allowed. +If a certificate is presented for authentication and has its signing CA key +listed in this file, then it may be used for authentication for any user +listed in the certificate's principals list. +Note that certificates that lack a list of principals will not be permitted +for authentication using +.Cm TrustedUserCAKeys . +For more details on certificates, see the +.Sx CERTIFICATES +section in +.Xr ssh-keygen 1 . .It Cm UseDNS Specifies whether .Xr sshd 8 diff --git a/crypto/openssh/sshpty.h b/crypto/openssh/sshpty.h index ac90035846..cfa322480f 100644 --- a/crypto/openssh/sshpty.h +++ b/crypto/openssh/sshpty.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sshpty.h,v 1.11 2008/05/19 15:45:07 djm Exp $ */ +/* $OpenBSD: sshpty.h,v 1.12 2010/01/09 05:04:24 djm Exp $ */ /* * Author: Tatu Ylonen @@ -17,8 +17,8 @@ #include struct termios *get_saved_tio(void); -void leave_raw_mode(void); -void enter_raw_mode(void); +void leave_raw_mode(int); +void enter_raw_mode(int); int pty_allocate(int *, int *, char *, size_t); void pty_release(const char *); diff --git a/crypto/openssh/sshtty.c b/crypto/openssh/sshtty.c index 21ade4e515..d214ce3bb1 100644 --- a/crypto/openssh/sshtty.c +++ b/crypto/openssh/sshtty.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshtty.c,v 1.13 2008/05/19 15:45:07 djm Exp $ */ +/* $OpenBSD: sshtty.c,v 1.14 2010/01/09 05:04:24 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -54,23 +54,25 @@ get_saved_tio(void) } void -leave_raw_mode(void) +leave_raw_mode(int quiet) { if (!_in_raw_mode) return; - if (tcsetattr(fileno(stdin), TCSADRAIN, &_saved_tio) == -1) - perror("tcsetattr"); - else + if (tcsetattr(fileno(stdin), TCSADRAIN, &_saved_tio) == -1) { + if (!quiet) + perror("tcsetattr"); + } else _in_raw_mode = 0; } void -enter_raw_mode(void) +enter_raw_mode(int quiet) { struct termios tio; if (tcgetattr(fileno(stdin), &tio) == -1) { - perror("tcgetattr"); + if (!quiet) + perror("tcgetattr"); return; } _saved_tio = tio; @@ -86,8 +88,9 @@ enter_raw_mode(void) tio.c_oflag &= ~OPOST; tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; - if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) == -1) - perror("tcsetattr"); - else + if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) == -1) { + if (!quiet) + perror("tcsetattr"); + } else _in_raw_mode = 1; } -- 2.41.0