Import OpenSSH-5.8p1.
authorPeter Avalos <pavalos@dragonflybsd.org>
Sat, 9 Apr 2011 05:59:12 +0000 (22:59 -0700)
committerPeter Avalos <pavalos@dragonflybsd.org>
Sat, 9 Apr 2011 05:59:12 +0000 (22:59 -0700)
 * Fix vulnerability in legacy certificate signing introduced in
   OpenSSH-5.6.

 * Implement Elliptic Curve Cryptography modes for key exchange (ECDH)
   and host/user keys (ECDSA) as specified by RFC5656. ECDH and ECDSA
   offer better performance than plain DH and DSA at the same equivalent
   symmetric key length, as well as much shorter keys.

 * sftp(1)/sftp-server(8): add a protocol extension to support a hard
   link operation. It is available through the "ln" command in the
   client. The old "ln" behaviour of creating a symlink is available
   using its "-s" option or through the preexisting "symlink" command

 * scp(1): Add a new -3 option to scp: Copies between two remote hosts
   are transferred through the local host.  Without this option the
   data is copied directly between the two remote hosts.

 * ssh(1): automatically order the hostkeys requested by the client
   based on which hostkeys are already recorded in known_hosts. This
   avoids hostkey warnings when connecting to servers with new ECDSA
   keys, since these are now preferred when learning hostkeys for the
   first time.

 * ssh(1)/sshd(8): add a new IPQoS option to specify arbitrary
   TOS/DSCP/QoS values instead of hardcoding lowdelay/throughput.

 * ssh(1): "atomically" create the listening mux socket by binding it on
   a temporary name and then linking it into position after listen() has
   succeeded. This allows the mux clients to determine that the server
   socket is either ready or stale without races. stale server sockets
   are now automatically removed.

 * ssh(1)/sshd(8): add a KexAlgorithms knob to the client and server
   configuration to allow selection of which key exchange methods are
   used by ssh(1) and sshd(8) and their order of preference.

 * sftp(1)/scp(1): factor out bandwidth limiting code from scp(1) into
   a generic bandwidth limiter that can be attached using the atomicio
   callback mechanism and use it to add a bandwidth limit option to
   sftp(1).

BugFixes:

 * ssh(1)/ssh-agent(1): honour $TMPDIR for client xauth and ssh-agent
   temporary directories.

 * ssh(1): avoid NULL deref on receiving a channel request on an unknown
   or invalid channel;

 * sshd(8): remove a debug() that pollutes stderr on client connecting
   to a server in debug mode

 * scp(1): pass through ssh command-line flags and options when doing
   remote-remote transfers, e.g. to enable agent forwarding which is
   particularly useful in this case;

 * sftp-server(8): umask should be parsed as octal

 * sftp(1): escape '[' in filename tab-completion

 * ssh(1): Typo in confirmation message.

 * sshd(8): prevent free() of string in .rodata when overriding
   AuthorizedKeys in a Match block

 * sshd(8): Use default shell /bin/sh if $SHELL is ""

 * ssh(1): kill proxy command on fatal() (we already killed it on
   clean exit);

 * ssh(1): install a SIGCHLD handler to reap expiried child process;

 * sshd(8): Use correct uid_t/pid_t types instead of int.

114 files changed:
crypto/openssh/LICENCE
crypto/openssh/PROTOCOL
crypto/openssh/PROTOCOL.agent
crypto/openssh/PROTOCOL.certkeys
crypto/openssh/PROTOCOL.mux
crypto/openssh/README
crypto/openssh/README.DELETED
crypto/openssh/atomicio.c
crypto/openssh/atomicio.h
crypto/openssh/audit-bsm.c
crypto/openssh/audit.c
crypto/openssh/audit.h
crypto/openssh/auth-options.c
crypto/openssh/auth-rsa.c
crypto/openssh/auth.c
crypto/openssh/auth1.c
crypto/openssh/auth2-jpake.c
crypto/openssh/auth2-pubkey.c
crypto/openssh/auth2.c
crypto/openssh/authfd.c
crypto/openssh/authfile.c
crypto/openssh/bufaux.c
crypto/openssh/bufec.c [new file with mode: 0644]
crypto/openssh/buffer.h
crypto/openssh/canohost.c
crypto/openssh/channels.c
crypto/openssh/cipher-3des1.c
crypto/openssh/cipher-acss.c
crypto/openssh/cipher-aes.c
crypto/openssh/cipher-bf1.c
crypto/openssh/cipher-ctr.c
crypto/openssh/clientloop.c
crypto/openssh/compress.c
crypto/openssh/defines.h
crypto/openssh/dns.c
crypto/openssh/entropy.c
crypto/openssh/hostfile.c
crypto/openssh/hostfile.h
crypto/openssh/includes.h
crypto/openssh/jpake.c
crypto/openssh/kex.c
crypto/openssh/kex.h
crypto/openssh/kexdhc.c
crypto/openssh/kexdhs.c
crypto/openssh/kexecdh.c [new file with mode: 0644]
crypto/openssh/kexecdhc.c [copied from crypto/openssh/kexdhc.c with 60% similarity]
crypto/openssh/kexecdhs.c [copied from crypto/openssh/kexdhs.c with 57% similarity]
crypto/openssh/kexgexc.c
crypto/openssh/kexgexs.c
crypto/openssh/key.c
crypto/openssh/key.h
crypto/openssh/loginrec.c
crypto/openssh/loginrec.h
crypto/openssh/misc.c
crypto/openssh/misc.h
crypto/openssh/moduli.c
crypto/openssh/monitor.c
crypto/openssh/monitor_wrap.c
crypto/openssh/mux.c
crypto/openssh/myproposal.h
crypto/openssh/openbsd-compat/bsd-misc.c
crypto/openssh/openbsd-compat/bsd-misc.h
crypto/openssh/openbsd-compat/glob.h
crypto/openssh/openbsd-compat/openbsd-compat.h
crypto/openssh/openbsd-compat/openssl-compat.h
crypto/openssh/openbsd-compat/port-linux.h
crypto/openssh/openbsd-compat/port-solaris.h
crypto/openssh/openbsd-compat/timingsafe_bcmp.c [copied from crypto/openssh/platform.h with 61% similarity]
crypto/openssh/packet.c
crypto/openssh/packet.h
crypto/openssh/pathnames.h
crypto/openssh/platform.c
crypto/openssh/platform.h
crypto/openssh/readconf.c
crypto/openssh/readconf.h
crypto/openssh/readpass.c
crypto/openssh/schnorr.c
crypto/openssh/scp.1
crypto/openssh/scp.c
crypto/openssh/servconf.c
crypto/openssh/servconf.h
crypto/openssh/session.c
crypto/openssh/sftp-client.c
crypto/openssh/sftp-client.h
crypto/openssh/sftp-server.c
crypto/openssh/sftp.1
crypto/openssh/sftp.c
crypto/openssh/ssh-add.1
crypto/openssh/ssh-add.c
crypto/openssh/ssh-agent.1
crypto/openssh/ssh-agent.c
crypto/openssh/ssh-dss.c
crypto/openssh/ssh-ecdsa.c [new file with mode: 0644]
crypto/openssh/ssh-keygen.1
crypto/openssh/ssh-keygen.c
crypto/openssh/ssh-keyscan.1
crypto/openssh/ssh-keyscan.c
crypto/openssh/ssh-keysign.8
crypto/openssh/ssh-keysign.c
crypto/openssh/ssh-rsa.c
crypto/openssh/ssh.1
crypto/openssh/ssh.c
crypto/openssh/ssh2.h
crypto/openssh/ssh_config.5
crypto/openssh/sshconnect.c
crypto/openssh/sshconnect.h
crypto/openssh/sshconnect2.c
crypto/openssh/sshd.8
crypto/openssh/sshd.c
crypto/openssh/sshd_config
crypto/openssh/sshd_config.5
crypto/openssh/sshlogin.c
crypto/openssh/uuencode.c
crypto/openssh/uuencode.h

index 3964b1d..120d6fd 100644 (file)
@@ -206,6 +206,7 @@ OpenSSH contains no GPL code.
        Sun Microsystems
        The SCO Group
        Daniel Walsh
+       Red Hat, Inc
 
      * Redistribution and use in source and binary forms, with or without
      * modification, are permitted provided that the following conditions
index 5fc31ea..c281960 100644 (file)
@@ -12,7 +12,9 @@ are individually implemented as extensions described below.
 The protocol used by OpenSSH's ssh-agent is described in the file
 PROTOCOL.agent
 
-1. transport: Protocol 2 MAC algorithm "umac-64@openssh.com"
+1. Transport protocol changes
+
+1.1. transport: Protocol 2 MAC algorithm "umac-64@openssh.com"
 
 This is a new transport-layer MAC method using the UMAC algorithm
 (rfc4418). This method is identical to the "umac-64" method documented
@@ -20,7 +22,7 @@ in:
 
 http://www.openssh.com/txt/draft-miller-secsh-umac-01.txt
 
-2. transport: Protocol 2 compression algorithm "zlib@openssh.com"
+1.2. transport: Protocol 2 compression algorithm "zlib@openssh.com"
 
 This transport-layer compression method uses the zlib compression
 algorithm (identical to the "zlib" method in rfc4253), but delays the
@@ -31,14 +33,27 @@ The method is documented in:
 
 http://www.openssh.com/txt/draft-miller-secsh-compression-delayed-00.txt
 
-3. transport: New public key algorithms "ssh-rsa-cert-v00@openssh.com" and
-   "ssh-dsa-cert-v00@openssh.com"
+1.3. transport: New public key algorithms "ssh-rsa-cert-v00@openssh.com",
+     "ssh-dsa-cert-v00@openssh.com",
+     "ecdsa-sha2-nistp256-cert-v01@openssh.com",
+     "ecdsa-sha2-nistp384-cert-v01@openssh.com" and
+     "ecdsa-sha2-nistp521-cert-v01@openssh.com"
 
-OpenSSH introduces two new public key algorithms to support certificate
+OpenSSH introduces 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"
+1.4. transport: Elliptic Curve cryptography
+
+OpenSSH supports ECC key exchange and public key authentication as
+specified in RFC5656. Only the ecdsa-sha2-nistp256, ecdsa-sha2-nistp384
+and ecdsa-sha2-nistp521 curves over GF(p) are supported. Elliptic
+curve points encoded using point compression are NOT accepted or
+generated.
+
+2. Connection protocol changes
+
+2.1. 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
@@ -77,8 +92,8 @@ message is only sent to OpenSSH peers (identified by banner).
 Other SSH implementations may be whitelisted to receive this message
 upon request.
 
-5. connection: disallow additional sessions extension
-   "no-more-sessions@openssh.com"
+2.2. connection: disallow additional sessions extension
+     "no-more-sessions@openssh.com"
 
 Most SSH connections will only ever request a single session, but a
 attacker may abuse a running ssh client to surreptitiously open
@@ -105,7 +120,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.
 
-6. connection: Tunnel forward extension "tun@openssh.com"
+2.3. 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
@@ -166,7 +181,9 @@ The contents of the "data" field for layer 2 packets is:
 The "frame" field contains an IEEE 802.3 Ethernet frame, including
 header.
 
-7. sftp: Reversal of arguments to SSH_FXP_SYMLINK
+3. SFTP protocol changes
+
+3.1. 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,
@@ -179,7 +196,7 @@ SSH_FXP_SYMLINK as follows:
        string          targetpath
        string          linkpath
 
-8. sftp: Server extension announcement in SSH_FXP_VERSION
+3.2. 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
@@ -200,7 +217,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.
 
-9. sftp: Extension request "posix-rename@openssh.com"
+3.3. 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
@@ -217,7 +234,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".
 
-10. sftp: Extension requests "statvfs@openssh.com" and
+3.4. sftp: Extension requests "statvfs@openssh.com" and
          "fstatvfs@openssh.com"
 
 These requests correspond to the statvfs and fstatvfs POSIX system
@@ -258,4 +275,20 @@ 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.15 2010/02/26 20:29:54 djm Exp $
+10. sftp: Extension request "hardlink@openssh.com"
+
+This request is for creating a hard link to a regular file. This
+request is implemented as a SSH_FXP_EXTENDED request with the
+following format:
+
+       uint32          id
+       string          "hardlink@openssh.com"
+       string          oldpath
+       string          newpath
+
+On receiving this request the server will perform the operation
+link(oldpath, newpath) and will respond with a SSH_FXP_STATUS message.
+This extension is advertised in the SSH_FXP_VERSION hello with version
+"1".
+
+$OpenBSD: PROTOCOL,v 1.17 2010/12/04 00:18:01 djm Exp $
index b34fcd3..de94d03 100644 (file)
@@ -159,8 +159,8 @@ successfully added or a SSH_AGENT_FAILURE if an error occurred.
 
 2.2.3 Add protocol 2 key
 
-The OpenSSH agent supports DSA and RSA keys for protocol 2. DSA keys may
-be added using the following request
+The OpenSSH agent supports DSA, ECDSA and RSA keys for protocol 2. DSA
+keys may be added using the following request
 
        byte                    SSH2_AGENTC_ADD_IDENTITY or
                                SSH2_AGENTC_ADD_ID_CONSTRAINED
@@ -182,6 +182,30 @@ DSA certificates may be added with:
        string                  key_comment
        constraint[]            key_constraints
 
+ECDSA keys may be added using the following request
+
+       byte                    SSH2_AGENTC_ADD_IDENTITY or
+                               SSH2_AGENTC_ADD_ID_CONSTRAINED
+       string                  "ecdsa-sha2-nistp256" |
+                               "ecdsa-sha2-nistp384" |
+                               "ecdsa-sha2-nistp521"
+       string                  ecdsa_curve_name
+       string                  ecdsa_public_key
+       mpint                   ecdsa_private
+       string                  key_comment
+       constraint[]            key_constraints
+
+ECDSA certificates may be added with:
+       byte                    SSH2_AGENTC_ADD_IDENTITY or
+                               SSH2_AGENTC_ADD_ID_CONSTRAINED
+       string                  "ecdsa-sha2-nistp256-cert-v01@openssh.com" |
+                               "ecdsa-sha2-nistp384-cert-v01@openssh.com" |
+                               "ecdsa-sha2-nistp521-cert-v01@openssh.com"
+       string                  certificate
+       mpint                   ecdsa_private_key
+       string                  key_comment
+       constraint[]            key_constraints
+
 RSA keys may be added with this request:
 
        byte                    SSH2_AGENTC_ADD_IDENTITY or
@@ -214,7 +238,7 @@ order to the protocol 1 add keys message. As with the corresponding
 protocol 1 "add key" request, the private key is overspecified to avoid
 redundant processing.
 
-For both DSA and RSA key add requests, "key_constraints" may only be
+For DSA, ECDSA and RSA key add requests, "key_constraints" may only be
 present if the request type is SSH2_AGENTC_ADD_ID_CONSTRAINED.
 
 The agent will reply with a SSH_AGENT_SUCCESS if the key has been
@@ -294,8 +318,7 @@ Protocol 2 keys may be removed with the following request:
        string                  key_blob
 
 Where "key_blob" is encoded as per RFC 4253 section 6.6 "Public Key
-Algorithms" for either of the supported key types: "ssh-dss" or
-"ssh-rsa".
+Algorithms" for any of the supported protocol 2 key types.
 
 The agent will delete any private key matching the specified public key
 and return SSH_AGENT_SUCCESS. If no such key was found, the agent will
@@ -364,8 +387,7 @@ Followed by zero or more consecutive keys, encoded as:
        string                  key_comment
 
 Where "key_blob" is encoded as per RFC 4253 section 6.6 "Public Key
-Algorithms" for either of the supported key types: "ssh-dss" or
-"ssh-rsa".
+Algorithms" for any of the supported protocol 2 key types.
 
 2.6 Private key operations
 
@@ -429,9 +451,9 @@ a protocol 2 key:
        uint32                  flags
 
 Where "key_blob" is encoded as per RFC 4253 section 6.6 "Public Key
-Algorithms" for either of the supported key types: "ssh-dss" or
-"ssh-rsa". "flags" is a bit-mask, but at present only one possible value
-is defined (see below for its meaning):
+Algorithms" for any of the supported protocol 2 key types. "flags" is
+a bit-mask, but at present only one possible value is defined (see below
+for its meaning):
 
        SSH_AGENT_OLD_SIGNATURE         1
 
@@ -535,4 +557,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.5 2010/02/26 20:29:54 djm Exp $
+$OpenBSD: PROTOCOL.agent,v 1.6 2010/08/31 11:54:45 djm Exp $
index 1d1be13..2f97649 100644 (file)
@@ -5,31 +5,37 @@ 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.
+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.
+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.
+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.
+Certified keys are represented using new key types:
+
+    ssh-rsa-cert-v01@openssh.com
+    ssh-dss-cert-v01@openssh.com
+    ecdsa-sha2-nistp256-cert-v01@openssh.com
+    ecdsa-sha2-nistp384-cert-v01@openssh.com
+    ecdsa-sha2-nistp521-cert-v01@openssh.com
+
+These include certification information along with the public key
+that is used to sign challenges. ssh-keygen performs the CA signing
+operation.
 
 Protocol extensions
 -------------------
@@ -47,10 +53,9 @@ 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.
+The certificate 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
@@ -93,6 +98,26 @@ DSA certificate
     string    signature key
     string    signature
 
+ECDSA certificate
+
+    string    "ecdsa-sha2-nistp256@openssh.com" |
+              "ecdsa-sha2-nistp384@openssh.com" |
+              "ecdsa-sha2-nistp521@openssh.com"
+    string    nonce
+    string    curve
+    string    public_key
+    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.
@@ -101,6 +126,9 @@ 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.
 
+curve and public key are respectively the ECDSA "[identifier]" and "Q"
+defined in section 3.1 of RFC5656.
+
 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
@@ -123,7 +151,8 @@ 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
+
+    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
@@ -137,15 +166,17 @@ 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"
+The valid key types for CA keys are ssh-rsa, ssh-dss and the ECDSA types
+ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, ecdsa-sha2-nistp521. "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.
+be signed by a DSS or ECDSA 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).
+(RFC4253 section 6.6 for ssh-rsa and ssh-dss, RFC5656 for the ECDSA
+types).
 
 Critical options
 ----------------
@@ -222,4 +253,4 @@ permit-user-rc          empty         Flag indicating that execution of
                                       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 $
+$OpenBSD: PROTOCOL.certkeys,v 1.8 2010/08/31 11:54:45 djm Exp $
index 1d8c463..2a5817b 100644 (file)
@@ -28,7 +28,7 @@ defined.
 To open a new multiplexed session, a client may send the following
 request:
 
-       uint32  MUX_C_MSG_NEW_SESSION
+       uint32  MUX_C_NEW_SESSION
        uint32  request id
        string  reserved
        bool    want tty flag
@@ -99,7 +99,7 @@ The server will reply with one of MUX_S_OK or MUX_S_PERMISSION_DENIED.
 
 A client may request the master to establish a port forward:
 
-       uint32  MUX_C_OPEN_FORWARD
+       uint32  MUX_C_OPEN_FWD
        uint32  request id
        uint32  forwarding type
        string  listen host
@@ -118,24 +118,23 @@ For dynamically allocated listen port the server replies with
        uint32  client request id
        uint32  allocated remote listen port
 
-5. Requesting closure of port forwards
+6. Requesting closure of port forwards
 
-A client may request the master to establish a port forward:
+Note: currently unimplemented (server will always reply with MUX_S_FAILURE).
+
+A client may request the master to close a port forward:
 
-       uint32  MUX_C_OPEN_FORWARD
+       uint32  MUX_C_CLOSE_FWD
        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
+7. Requesting stdio forwarding
 
 A client may request the master to establish a stdio forwarding:
 
@@ -153,7 +152,7 @@ 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
+8. Status messages
 
 The MUX_S_OK message is empty:
 
@@ -170,14 +169,15 @@ The MUX_S_PERMISSION_DENIED and MUX_S_FAILURE include a reason:
        uint32  client request id
        string  reason
 
-7. Protocol numbers
+9. 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_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
@@ -200,4 +200,4 @@ 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 $
+$OpenBSD: PROTOCOL.mux,v 1.4 2011/01/31 21:42:15 djm Exp $
index 4eaa545..4f69506 100644 (file)
@@ -1,4 +1,4 @@
-See http://www.openssh.com/txt/release-5.6 for the release notes.
+See http://www.openssh.com/txt/release-5.8 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.74 2010/08/08 16:32:06 djm Exp $
+$Id: README,v 1.75.4.1 2011/02/04 00:57:50 djm Exp $
index 8b7cee9..ccf7151 100644 (file)
@@ -7,6 +7,7 @@ README.privsep
 TODO
 WARNING.RNG
 aclocal.m4
+audit-linux.c
 buildpkg.sh.in
 config.guess
 config.h.in
@@ -39,6 +40,7 @@ openbsd-compat/bsd-poll.c
 openbsd-compat/bsd-snprintf.c
 openbsd-compat/bsd-statvfs.c
 openbsd-compat/bsd-waitpid.c
+openbsd-compat/charclass.h
 openbsd-compat/daemon.c
 openbsd-compat/dirname.c
 openbsd-compat/fake-rfc2553.c
index a6b2d12..601b3c3 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: atomicio.c,v 1.25 2007/06/25 12:02:27 dtucker Exp $ */
+/* $OpenBSD: atomicio.c,v 1.26 2010/09/22 22:58:51 djm Exp $ */
 /*
  * Copyright (c) 2006 Damien Miller. All rights reserved.
  * Copyright (c) 2005 Anil Madhavapeddy. All rights reserved.
@@ -48,7 +48,8 @@
  * ensure all of data on socket comes through. f==read || f==vwrite
  */
 size_t
-atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n)
+atomicio6(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n,
+    int (*cb)(void *, size_t), void *cb_arg)
 {
        char *s = _s;
        size_t pos = 0;
@@ -73,17 +74,28 @@ atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n)
                        return pos;
                default:
                        pos += (size_t)res;
+                       if (cb != NULL && cb(cb_arg, (size_t)res) == -1) {
+                               errno = EINTR;
+                               return pos;
+                       }
                }
        }
-       return (pos);
+       return pos;
+}
+
+size_t
+atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n)
+{
+       return atomicio6(f, fd, _s, n, NULL, NULL);
 }
 
 /*
  * ensure all of data on socket comes through. f==readv || f==writev
  */
 size_t
-atomiciov(ssize_t (*f) (int, const struct iovec *, int), int fd,
-    const struct iovec *_iov, int iovcnt)
+atomiciov6(ssize_t (*f) (int, const struct iovec *, int), int fd,
+    const struct iovec *_iov, int iovcnt,
+    int (*cb)(void *, size_t), void *cb_arg)
 {
        size_t pos = 0, rem;
        ssize_t res;
@@ -137,6 +149,17 @@ atomiciov(ssize_t (*f) (int, const struct iovec *, int), int fd,
                        iov[0].iov_base = ((char *)iov[0].iov_base) + rem;
                        iov[0].iov_len -= rem;
                }
+               if (cb != NULL && cb(cb_arg, (size_t)res) == -1) {
+                       errno = EINTR;
+                       return pos;
+               }
        }
        return pos;
 }
+
+size_t
+atomiciov(ssize_t (*f) (int, const struct iovec *, int), int fd,
+    const struct iovec *_iov, int iovcnt)
+{
+       return atomiciov6(f, fd, _iov, iovcnt, NULL, NULL);
+}
index 2fcd25d..0d728ac 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: atomicio.h,v 1.10 2006/08/03 03:34:41 deraadt Exp $ */
+/* $OpenBSD: atomicio.h,v 1.11 2010/09/22 22:58:51 djm Exp $ */
 
 /*
  * Copyright (c) 2006 Damien Miller.  All rights reserved.
@@ -32,6 +32,9 @@
 /*
  * Ensure all of data on socket comes through. f==read || f==vwrite
  */
+size_t
+atomicio6(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n,
+    int (*cb)(void *, size_t), void *);
 size_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t);
 
 #define vwrite (ssize_t (*)(int, void *, size_t))write
@@ -39,6 +42,9 @@ size_t        atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t);
 /*
  * ensure all of data on socket comes through. f==readv || f==writev
  */
+size_t
+atomiciov6(ssize_t (*f) (int, const struct iovec *, int), int fd,
+    const struct iovec *_iov, int iovcnt, int (*cb)(void *, size_t), void *);
 size_t atomiciov(ssize_t (*)(int, const struct iovec *, int),
     int, const struct iovec *, int);
 
index 2c417bc..f196d4f 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: audit-bsm.c,v 1.6 2008/02/25 10:05:04 dtucker Exp $ */
+/* $Id: audit-bsm.c,v 1.7 2011/01/17 10:15:29 dtucker Exp $ */
 
 /*
  * TODO
@@ -305,13 +305,13 @@ audit_run_command(const char *command)
 }
 
 void
-audit_session_open(const char *ttyn)
+audit_session_open(struct logininfo *li)
 {
        /* not implemented */
 }
 
 void
-audit_session_close(const char *ttyn)
+audit_session_close(struct logininfo *li)
 {
        /* not implemented */
 }
index dbea34c..ced57fa 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: audit.c,v 1.5 2006/09/01 05:38:36 djm Exp $ */
+/* $Id: audit.c,v 1.6 2011/01/17 10:15:30 dtucker Exp $ */
 
 /*
  * Copyright (c) 2004, 2005 Darren Tucker.  All rights reserved.
@@ -147,9 +147,9 @@ audit_event(ssh_audit_event_t event)
  * within a single connection.
  */
 void
-audit_session_open(const char *ttyn)
+audit_session_open(struct logininfo *li)
 {
-       const char *t = ttyn ? ttyn : "(no tty)";
+       const char *t = li->line ? li->line : "(no tty)";
 
        debug("audit session open euid %d user %s tty name %s", geteuid(),
            audit_username(), t);
@@ -163,9 +163,9 @@ audit_session_open(const char *ttyn)
  * within a single connection.
  */
 void
-audit_session_close(const char *ttyn)
+audit_session_close(struct logininfo *li)
 {
-       const char *t = ttyn ? ttyn : "(no tty)";
+       const char *t = li->line ? li->line : "(no tty)";
 
        debug("audit session close euid %d user %s tty name %s", geteuid(),
            audit_username(), t);
index 695f723..92ede5b 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: audit.h,v 1.3 2006/08/05 14:05:10 dtucker Exp $ */
+/* $Id: audit.h,v 1.4 2011/01/17 10:15:30 dtucker Exp $ */
 
 /*
  * Copyright (c) 2004, 2005 Darren Tucker.  All rights reserved.
@@ -26,6 +26,9 @@
 
 #ifndef _SSH_AUDIT_H
 # define _SSH_AUDIT_H
+
+#include "loginrec.h"
+
 enum ssh_audit_event_type {
        SSH_LOGIN_EXCEED_MAXTRIES,
        SSH_LOGIN_ROOT_DENIED,
@@ -46,8 +49,8 @@ typedef enum ssh_audit_event_type ssh_audit_event_t;
 
 void   audit_connection_from(const char *, int);
 void   audit_event(ssh_audit_event_t);
-void   audit_session_open(const char *);
-void   audit_session_close(const char *);
+void   audit_session_open(struct logininfo *);
+void   audit_session_close(struct logininfo *);
 void   audit_run_command(const char *);
 ssh_audit_event_t audit_classify_auth(const char *);
 
index a704024..eae45cf 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth-options.c,v 1.52 2010/05/20 23:46:02 djm Exp $ */
+/* $OpenBSD: auth-options.c,v 1.54 2010/12/24 21:41:48 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -172,7 +172,7 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum)
                                goto bad_option;
                        }
                        forced_command[i] = '\0';
-                       auth_debug_add("Forced command: %.900s", forced_command);
+                       auth_debug_add("Forced command.");
                        opts++;
                        goto next_option;
                }
@@ -444,7 +444,7 @@ parse_option_list(u_char *optblob, size_t optblob_len, struct passwd *pw,
        buffer_append(&c, optblob, optblob_len);
 
        while (buffer_len(&c) > 0) {
-               if ((name = buffer_get_string_ret(&c, &nlen)) == NULL ||
+               if ((name = buffer_get_cstring_ret(&c, &nlen)) == NULL ||
                    (data_blob = buffer_get_string_ret(&c, &dlen)) == NULL) {
                        error("Certificate options corrupt");
                        goto out;
@@ -479,7 +479,7 @@ parse_option_list(u_char *optblob, size_t optblob_len, struct passwd *pw,
                }
                if (!found && (which & OPTIONS_CRITICAL) != 0) {
                        if (strcmp(name, "force-command") == 0) {
-                               if ((command = buffer_get_string_ret(&data,
+                               if ((command = buffer_get_cstring_ret(&data,
                                    &clen)) == NULL) {
                                        error("Certificate constraint \"%s\" "
                                            "corrupt", name);
@@ -500,7 +500,7 @@ parse_option_list(u_char *optblob, size_t optblob_len, struct passwd *pw,
                                found = 1;
                        }
                        if (strcmp(name, "source-address") == 0) {
-                               if ((allowed = buffer_get_string_ret(&data,
+                               if ((allowed = buffer_get_cstring_ret(&data,
                                    &clen)) == NULL) {
                                        error("Certificate constraint "
                                            "\"%s\" corrupt", name);
index 56702d1..4edaab0 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth-rsa.c,v 1.78 2010/07/13 23:13:16 djm Exp $ */
+/* $OpenBSD: auth-rsa.c,v 1.79 2010/12/03 23:55:27 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -94,9 +94,6 @@ 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",
@@ -249,6 +246,10 @@ auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey)
                            "actual %d vs. announced %d.",
                            file, linenum, BN_num_bits(key->rsa->n), bits);
 
+               /* Never accept a revoked key */
+               if (auth_key_is_revoked(key))
+                       break;
+
                /* We have found the desired key. */
                /*
                 * If our options do not allow this key to be used,
index dba1e65..33680b9 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth.c,v 1.89 2010/08/04 05:42:47 djm Exp $ */
+/* $OpenBSD: auth.c,v 1.91 2010/11/29 23:45:51 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -379,16 +379,15 @@ HostStatus
 check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
     const char *sysfile, const char *userfile)
 {
-       Key *found;
        char *user_hostfile;
        struct stat st;
        HostStatus host_status;
+       struct hostkeys *hostkeys;
+       const struct hostkey_entry *found;
 
-       /* Check if we know the host and its host key. */
-       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) {
+       hostkeys = init_hostkeys();
+       load_hostkeys(hostkeys, host, sysfile);
+       if (userfile != NULL) {
                user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
                if (options.strict_modes &&
                    (stat(user_hostfile, &st) == 0) &&
@@ -401,16 +400,23 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
                            user_hostfile);
                } else {
                        temporarily_use_uid(pw);
-                       host_status = check_host_in_hostfile(user_hostfile,
-                           host, key, found, NULL);
+                       load_hostkeys(hostkeys, host, user_hostfile);
                        restore_uid();
                }
                xfree(user_hostfile);
        }
-       key_free(found);
+       host_status = check_key_in_hostkeys(hostkeys, key, &found);
+       if (host_status == HOST_REVOKED)
+               error("WARNING: revoked key for %s attempted authentication",
+                   found->host);
+       else if (host_status == HOST_OK)
+               debug("%s: key for %s found at %s:%ld", __func__,
+                   found->host, found->file, found->line);
+       else
+               debug("%s: key for host %s not found", __func__, host);
+
+       free_hostkeys(hostkeys);
 
-       debug2("check_key_in_hostfiles: key %s for %s", host_status == HOST_OK ?
-           "ok" : "not found", host);
        return host_status;
 }
 
@@ -518,7 +524,7 @@ auth_openfile(const char *file, struct passwd *pw, int strict_modes,
                close(fd);
                return NULL;
        }
-       if (options.strict_modes &&
+       if (strict_modes &&
            secure_filename(f, file, pw, line, sizeof(line)) != 0) {
                fclose(f);
                logit("Authentication refused: %s", line);
index bf442db..cc85aec 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth1.c,v 1.74 2010/06/25 08:46:17 djm Exp $ */
+/* $OpenBSD: auth1.c,v 1.75 2010/08/31 09:58:37 djm Exp $ */
 /*
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
  *                    All rights reserved
@@ -167,7 +167,7 @@ auth1_process_rhosts_rsa(Authctxt *authctxt, char *info, size_t infolen)
         * trust the client; root on the client machine can
         * claim to be any user.
         */
-       client_user = packet_get_string(&ulen);
+       client_user = packet_get_cstring(&ulen);
 
        /* Get the client host key. */
        client_host_key = key_new(KEY_RSA1);
@@ -389,7 +389,7 @@ do_authentication(Authctxt *authctxt)
        packet_read_expect(SSH_CMSG_USER);
 
        /* Get the user name. */
-       user = packet_get_string(&ulen);
+       user = packet_get_cstring(&ulen);
        packet_check_eom();
 
        if ((style = strchr(user, ':')) != NULL)
index 5de5506..a460e82 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-jpake.c,v 1.3 2009/03/05 07:18:19 djm Exp $ */
+/* $OpenBSD: auth2-jpake.c,v 1.4 2010/08/31 11:54:45 djm Exp $ */
 /*
  * Copyright (c) 2008 Damien Miller.  All rights reserved.
  *
@@ -162,6 +162,11 @@ derive_rawsalt(const char *username, u_char *rawsalt, u_int len)
                        fatal("%s: DSA key missing priv_key", __func__);
                buffer_put_bignum2(&b, k->dsa->priv_key);
                break;
+       case KEY_ECDSA:
+               if (EC_KEY_get0_private_key(k->ecdsa) == NULL)
+                       fatal("%s: ECDSA key missing priv_key", __func__);
+               buffer_put_bignum2(&b, EC_KEY_get0_private_key(k->ecdsa));
+               break;
        default:
                fatal("%s: unknown key type %d", __func__, k->type);
        }
index 35cf79c..7d21413 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-pubkey.c,v 1.26 2010/06/29 23:16:46 djm Exp $ */
+/* $OpenBSD: auth2-pubkey.c,v 1.27 2010/11/20 05:12:38 deraadt Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -232,7 +232,7 @@ match_principals_file(char *file, struct passwd *pw, struct KeyCert *cert)
                if ((ep = strrchr(cp, ' ')) != NULL ||
                    (ep = strrchr(cp, '\t')) != NULL) {
                        for (; *ep == ' ' || *ep == '\t'; ep++)
-                               ;;
+                               ;
                        line_opts = cp;
                        cp = ep;
                }
index 5d54685..95820f9 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2.c,v 1.121 2009/06/22 05:39:28 dtucker Exp $ */
+/* $OpenBSD: auth2.c,v 1.122 2010/08/31 09:58:37 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -182,7 +182,7 @@ input_service_request(int type, u_int32_t seq, void *ctxt)
        Authctxt *authctxt = ctxt;
        u_int len;
        int acceptit = 0;
-       char *service = packet_get_string(&len);
+       char *service = packet_get_cstring(&len);
        packet_check_eom();
 
        if (authctxt == NULL)
@@ -221,9 +221,9 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
        if (authctxt == NULL)
                fatal("input_userauth_request: no authctxt");
 
-       user = packet_get_string(NULL);
-       service = packet_get_string(NULL);
-       method = packet_get_string(NULL);
+       user = packet_get_cstring(NULL);
+       service = packet_get_cstring(NULL);
+       method = packet_get_cstring(NULL);
        debug("userauth-request for user %s service %s method %s", user, service, method);
        debug("attempt %d failures %d", authctxt->attempt, authctxt->failures);
 
index 739722f..c11c3f5 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: authfd.c,v 1.83 2010/04/16 01:47:26 djm Exp $ */
+/* $OpenBSD: authfd.c,v 1.84 2010/08/31 11:54:45 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -509,6 +509,21 @@ ssh_encode_identity_ssh2(Buffer *b, Key *key, const char *comment)
                    buffer_len(&key->cert->certblob));
                buffer_put_bignum2(b, key->dsa->priv_key);
                break;
+#ifdef OPENSSL_HAS_ECC
+       case KEY_ECDSA:
+               buffer_put_cstring(b, key_curve_nid_to_name(key->ecdsa_nid));
+               buffer_put_ecpoint(b, EC_KEY_get0_group(key->ecdsa),
+                   EC_KEY_get0_public_key(key->ecdsa));
+               buffer_put_bignum2(b, EC_KEY_get0_private_key(key->ecdsa));
+               break;
+       case KEY_ECDSA_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, EC_KEY_get0_private_key(key->ecdsa));
+               break;
+#endif
        }
        buffer_put_cstring(b, comment);
 }
@@ -541,6 +556,8 @@ ssh_add_identity_constrained(AuthenticationConnection *auth, Key *key,
        case KEY_DSA:
        case KEY_DSA_CERT:
        case KEY_DSA_CERT_V00:
+       case KEY_ECDSA:
+       case KEY_ECDSA_CERT:
                type = constrained ?
                    SSH2_AGENTC_ADD_ID_CONSTRAINED :
                    SSH2_AGENTC_ADD_IDENTITY;
@@ -589,7 +606,8 @@ ssh_remove_identity(AuthenticationConnection *auth, Key *key)
                buffer_put_bignum(&msg, key->rsa->e);
                buffer_put_bignum(&msg, key->rsa->n);
        } else if (key_type_plain(key->type) == KEY_DSA ||
-           key_type_plain(key->type) == KEY_RSA) {
+           key_type_plain(key->type) == KEY_RSA ||
+           key_type_plain(key->type) == KEY_ECDSA) {
                key_to_blob(key, &blob, &blen);
                buffer_put_char(&msg, SSH2_AGENTC_REMOVE_IDENTITY);
                buffer_put_string(&msg, blob, blen);
index 2bd8878..f2aec26 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: authfile.c,v 1.82 2010/08/04 05:49:22 djm Exp $ */
+/* $OpenBSD: authfile.c,v 1.87 2010/11/29 18:57:04 markus Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -74,19 +74,18 @@ static const char authfile_id_string[] =
     "SSH PRIVATE KEY FILE FORMAT 1.1\n";
 
 /*
- * Saves the authentication (private) key in a file, encrypting it with
- * passphrase.  The identification of the file (lowest 64 bits of n) will
+ * Serialises the authentication (private) key to a blob, encrypting it with
+ * passphrase.  The identification of the blob (lowest 64 bits of n) will
  * precede the key to provide identification of the key without needing a
  * passphrase.
  */
-
 static int
-key_save_private_rsa1(Key *key, const char *filename, const char *passphrase,
+key_private_rsa1_to_blob(Key *key, Buffer *blob, const char *passphrase,
     const char *comment)
 {
        Buffer buffer, encrypted;
        u_char buf[100], *cp;
-       int fd, i, cipher_num;
+       int i, cipher_num;
        CipherContext ciphercontext;
        Cipher *cipher;
        u_int32_t rnd;
@@ -157,156 +156,222 @@ key_save_private_rsa1(Key *key, const char *filename, const char *passphrase,
        memset(buf, 0, sizeof(buf));
        buffer_free(&buffer);
 
-       fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
-       if (fd < 0) {
-               error("open %s failed: %s.", filename, strerror(errno));
-               buffer_free(&encrypted);
-               return 0;
-       }
-       if (atomicio(vwrite, fd, buffer_ptr(&encrypted),
-           buffer_len(&encrypted)) != buffer_len(&encrypted)) {
-               error("write to key file %s failed: %s", filename,
-                   strerror(errno));
-               buffer_free(&encrypted);
-               close(fd);
-               unlink(filename);
-               return 0;
-       }
-       close(fd);
+       buffer_append(blob, buffer_ptr(&encrypted), buffer_len(&encrypted));
        buffer_free(&encrypted);
+
        return 1;
 }
 
-/* save SSH v2 key in OpenSSL PEM format */
+/* convert SSH v2 key in OpenSSL PEM format */
 static int
-key_save_private_pem(Key *key, const char *filename, const char *_passphrase,
+key_private_pem_to_blob(Key *key, Buffer *blob, const char *_passphrase,
     const char *comment)
 {
-       FILE *fp;
-       int fd;
        int success = 0;
-       int len = strlen(_passphrase);
+       int blen, 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
+       const u_char *bptr;
+       BIO *bio;
 
        if (len > 0 && len <= 4) {
                error("passphrase too short: have %d bytes, need > 4", len);
                return 0;
        }
-       fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
-       if (fd < 0) {
-               error("open %s failed: %s.", filename, strerror(errno));
-               return 0;
-       }
-       fp = fdopen(fd, "w");
-       if (fp == NULL) {
-               error("fdopen %s failed: %s.", filename, strerror(errno));
-               close(fd);
+       if ((bio = BIO_new(BIO_s_mem())) == NULL) {
+               error("%s: BIO_new failed", __func__);
                return 0;
        }
        switch (key->type) {
        case KEY_DSA:
-               success = PEM_write_DSAPrivateKey(fp, key->dsa,
+               success = PEM_write_bio_DSAPrivateKey(bio, key->dsa,
                    cipher, passphrase, len, NULL, NULL);
                break;
+#ifdef OPENSSL_HAS_ECC
+       case KEY_ECDSA:
+               success = PEM_write_bio_ECPrivateKey(bio, key->ecdsa,
+                   cipher, passphrase, len, NULL, NULL);
+               break;
+#endif
        case KEY_RSA:
-               success = PEM_write_RSAPrivateKey(fp, key->rsa,
+               success = PEM_write_bio_RSAPrivateKey(bio, key->rsa,
                    cipher, passphrase, len, NULL, NULL);
                break;
        }
-       fclose(fp);
+       if (success) {
+               if ((blen = BIO_get_mem_data(bio, &bptr)) <= 0)
+                       success = 0;
+               else
+                       buffer_append(blob, bptr, blen);
+       }
+       BIO_free(bio);
        return success;
 }
 
-int
-key_save_private(Key *key, const char *filename, const char *passphrase,
+/* Save a key blob to a file */
+static int
+key_save_private_blob(Buffer *keybuf, const char *filename)
+{
+       int fd;
+
+       if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) {
+               error("open %s failed: %s.", filename, strerror(errno));
+               return 0;
+       }
+       if (atomicio(vwrite, fd, buffer_ptr(keybuf),
+           buffer_len(keybuf)) != buffer_len(keybuf)) {
+               error("write to key file %s failed: %s", filename,
+                   strerror(errno));
+               close(fd);
+               unlink(filename);
+               return 0;
+       }
+       close(fd);
+       return 1;
+}
+
+/* Serialise "key" to buffer "blob" */
+static int
+key_private_to_blob(Key *key, Buffer *blob, const char *passphrase,
     const char *comment)
 {
        switch (key->type) {
        case KEY_RSA1:
-               return key_save_private_rsa1(key, filename, passphrase,
-                   comment);
+               return key_private_rsa1_to_blob(key, blob, passphrase, comment);
        case KEY_DSA:
+       case KEY_ECDSA:
        case KEY_RSA:
-               return key_save_private_pem(key, filename, passphrase,
-                   comment);
+               return key_private_pem_to_blob(key, blob, passphrase, comment);
        default:
-               break;
+               error("%s: cannot save key type %d", __func__, key->type);
+               return 0;
        }
-       error("key_save_private: cannot save key type %d", key->type);
-       return 0;
+}
+
+int
+key_save_private(Key *key, const char *filename, const char *passphrase,
+    const char *comment)
+{
+       Buffer keyblob;
+       int success = 0;
+
+       buffer_init(&keyblob);
+       if (!key_private_to_blob(key, &keyblob, passphrase, comment))
+               goto out;
+       if (!key_save_private_blob(&keyblob, filename))
+               goto out;
+       success = 1;
+ out:
+       buffer_free(&keyblob);
+       return success;
 }
 
 /*
- * Loads the public part of the ssh v1 key file.  Returns NULL if an error was
- * encountered (the file does not exist or is not readable), and the key
- * otherwise.
+ * Parse the public, unencrypted portion of a RSA1 key.
  */
-
 static Key *
-key_load_public_rsa1(int fd, const char *filename, char **commentp)
+key_parse_public_rsa1(Buffer *blob, char **commentp)
 {
-       Buffer buffer;
        Key *pub;
-       struct stat st;
-       char *cp;
-       u_int i;
+
+       /* Check that it is at least big enough to contain the ID string. */
+       if (buffer_len(blob) < sizeof(authfile_id_string)) {
+               debug3("Truncated RSA1 identifier");
+               return NULL;
+       }
+
+       /*
+        * Make sure it begins with the id string.  Consume the id string
+        * from the buffer.
+        */
+       if (memcmp(buffer_ptr(blob), authfile_id_string,
+           sizeof(authfile_id_string)) != 0) {
+               debug3("Incorrect RSA1 identifier");
+               return NULL;
+       }
+       buffer_consume(blob, sizeof(authfile_id_string));
+
+       /* Skip cipher type and reserved data. */
+       (void) buffer_get_char(blob);   /* cipher type */
+       (void) buffer_get_int(blob);            /* reserved */
+
+       /* Read the public key from the buffer. */
+       (void) buffer_get_int(blob);
+       pub = key_new(KEY_RSA1);
+       buffer_get_bignum(blob, pub->rsa->n);
+       buffer_get_bignum(blob, pub->rsa->e);
+       if (commentp)
+               *commentp = buffer_get_string(blob, NULL);
+       /* The encrypted private part is not parsed by this function. */
+       buffer_clear(blob);
+
+       return pub;
+}
+
+/* Load the contents of a key file into a buffer */
+static int
+key_load_file(int fd, const char *filename, Buffer *blob)
+{
        size_t len;
+       u_char *cp;
+       struct stat st;
 
        if (fstat(fd, &st) < 0) {
-               error("fstat for key file %.200s failed: %.100s",
-                   filename, strerror(errno));
-               return NULL;
+               error("%s: fstat of key file %.200s%sfailed: %.100s", __func__,
+                   filename == NULL ? "" : filename,
+                   filename == NULL ? "" : " ",
+                   strerror(errno));
+               close(fd);
+               return 0;
        }
        if (st.st_size > 1*1024*1024) {
-               error("key file %.200s too large", filename);
-               return NULL;
+               error("%s: key file %.200s%stoo large", __func__,
+                   filename == NULL ? "" : filename,
+                   filename == NULL ? "" : " ");
+               close(fd);
+               return 0;
        }
        len = (size_t)st.st_size;               /* truncated */
 
-       buffer_init(&buffer);
-       cp = buffer_append_space(&buffer, len);
+       buffer_init(blob);
+       cp = buffer_append_space(blob, len);
 
        if (atomicio(read, fd, cp, len) != len) {
-               debug("Read from key file %.200s failed: %.100s", filename,
+               debug("%s: read from key file %.200s%sfailed: %.100s", __func__,
+                   filename == NULL ? "" : filename,
+                   filename == NULL ? "" : " ",
                    strerror(errno));
-               buffer_free(&buffer);
-               return NULL;
+               buffer_clear(blob);
+               close(fd);
+               return 0;
        }
+       return 1;
+}
 
-       /* Check that it is at least big enough to contain the ID string. */
-       if (len < sizeof(authfile_id_string)) {
-               debug3("Not a RSA1 key file %.200s.", filename);
+/*
+ * Loads the public part of the ssh v1 key file.  Returns NULL if an error was
+ * encountered (the file does not exist or is not readable), and the key
+ * otherwise.
+ */
+static Key *
+key_load_public_rsa1(int fd, const char *filename, char **commentp)
+{
+       Buffer buffer;
+       Key *pub;
+
+       buffer_init(&buffer);
+       if (!key_load_file(fd, filename, &buffer)) {
                buffer_free(&buffer);
                return NULL;
        }
-       /*
-        * Make sure it begins with the id string.  Consume the id string
-        * from the buffer.
-        */
-       for (i = 0; i < sizeof(authfile_id_string); i++)
-               if (buffer_get_char(&buffer) != authfile_id_string[i]) {
-                       debug3("Not a RSA1 key file %.200s.", filename);
-                       buffer_free(&buffer);
-                       return NULL;
-               }
-       /* Skip cipher type and reserved data. */
-       (void) buffer_get_char(&buffer);        /* cipher type */
-       (void) buffer_get_int(&buffer);         /* reserved */
-
-       /* Read the public key from the buffer. */
-       (void) buffer_get_int(&buffer);
-       pub = key_new(KEY_RSA1);
-       buffer_get_bignum(&buffer, pub->rsa->n);
-       buffer_get_bignum(&buffer, pub->rsa->e);
-       if (commentp)
-               *commentp = buffer_get_string(&buffer, NULL);
-       /* The encrypted private part is not parsed by this function. */
 
+       pub = key_parse_public_rsa1(&buffer, commentp);
+       if (pub == NULL)
+               debug3("Could not load \"%s\" as a RSA1 public key", filename);
        buffer_free(&buffer);
        return pub;
 }
@@ -329,113 +394,73 @@ key_load_public_type(int type, const char *filename, char **commentp)
        return NULL;
 }
 
-/*
- * Loads the private key from the file.  Returns 0 if an error is encountered
- * (file does not exist or is not readable, or passphrase is bad). This
- * initializes the private key.
- * Assumes we are called under uid of the owner of the file.
- */
-
 static Key *
-key_load_private_rsa1(int fd, const char *filename, const char *passphrase,
-    char **commentp)
+key_parse_private_rsa1(Buffer *blob, const char *passphrase, char **commentp)
 {
-       u_int i;
        int check1, check2, cipher_type;
-       size_t len;
-       Buffer buffer, decrypted;
+       Buffer decrypted;
        u_char *cp;
        CipherContext ciphercontext;
        Cipher *cipher;
        Key *prv = NULL;
-       struct stat st;
-
-       if (fstat(fd, &st) < 0) {
-               error("fstat for key file %.200s failed: %.100s",
-                   filename, strerror(errno));
-               close(fd);
-               return NULL;
-       }
-       if (st.st_size > 1*1024*1024) {
-               error("key file %.200s too large", filename);
-               close(fd);
-               return (NULL);
-       }
-       len = (size_t)st.st_size;               /* truncated */
-
-       buffer_init(&buffer);
-       cp = buffer_append_space(&buffer, len);
-
-       if (atomicio(read, fd, cp, len) != len) {
-               debug("Read from key file %.200s failed: %.100s", filename,
-                   strerror(errno));
-               buffer_free(&buffer);
-               close(fd);
-               return NULL;
-       }
 
        /* Check that it is at least big enough to contain the ID string. */
-       if (len < sizeof(authfile_id_string)) {
-               debug3("Not a RSA1 key file %.200s.", filename);
-               buffer_free(&buffer);
-               close(fd);
+       if (buffer_len(blob) < sizeof(authfile_id_string)) {
+               debug3("Truncated RSA1 identifier");
                return NULL;
        }
+
        /*
         * Make sure it begins with the id string.  Consume the id string
         * from the buffer.
         */
-       for (i = 0; i < sizeof(authfile_id_string); i++)
-               if (buffer_get_char(&buffer) != authfile_id_string[i]) {
-                       debug3("Not a RSA1 key file %.200s.", filename);
-                       buffer_free(&buffer);
-                       close(fd);
-                       return NULL;
-               }
+       if (memcmp(buffer_ptr(blob), authfile_id_string,
+           sizeof(authfile_id_string)) != 0) {
+               debug3("Incorrect RSA1 identifier");
+               return NULL;
+       }
+       buffer_consume(blob, sizeof(authfile_id_string));
 
        /* Read cipher type. */
-       cipher_type = buffer_get_char(&buffer);
-       (void) buffer_get_int(&buffer); /* Reserved data. */
+       cipher_type = buffer_get_char(blob);
+       (void) buffer_get_int(blob);    /* Reserved data. */
 
        /* Read the public key from the buffer. */
-       (void) buffer_get_int(&buffer);
+       (void) buffer_get_int(blob);
        prv = key_new_private(KEY_RSA1);
 
-       buffer_get_bignum(&buffer, prv->rsa->n);
-       buffer_get_bignum(&buffer, prv->rsa->e);
+       buffer_get_bignum(blob, prv->rsa->n);
+       buffer_get_bignum(blob, prv->rsa->e);
        if (commentp)
-               *commentp = buffer_get_string(&buffer, NULL);
+               *commentp = buffer_get_string(blob, NULL);
        else
-               xfree(buffer_get_string(&buffer, NULL));
+               (void)buffer_get_string_ptr(blob, NULL);
 
        /* Check that it is a supported cipher. */
        cipher = cipher_by_number(cipher_type);
        if (cipher == NULL) {
-               debug("Unsupported cipher %d used in key file %.200s.",
-                   cipher_type, filename);
-               buffer_free(&buffer);
+               debug("Unsupported RSA1 cipher %d", cipher_type);
                goto fail;
        }
        /* Initialize space for decrypted data. */
        buffer_init(&decrypted);
-       cp = buffer_append_space(&decrypted, buffer_len(&buffer));
+       cp = buffer_append_space(&decrypted, buffer_len(blob));
 
        /* Rest of the buffer is encrypted.  Decrypt it using the passphrase. */
        cipher_set_key_string(&ciphercontext, cipher, passphrase,
            CIPHER_DECRYPT);
        cipher_crypt(&ciphercontext, cp,
-           buffer_ptr(&buffer), buffer_len(&buffer));
+           buffer_ptr(blob), buffer_len(blob));
        cipher_cleanup(&ciphercontext);
        memset(&ciphercontext, 0, sizeof(ciphercontext));
-       buffer_free(&buffer);
+       buffer_clear(blob);
 
        check1 = buffer_get_char(&decrypted);
        check2 = buffer_get_char(&decrypted);
        if (check1 != buffer_get_char(&decrypted) ||
            check2 != buffer_get_char(&decrypted)) {
                if (strcmp(passphrase, "") != 0)
-                       debug("Bad passphrase supplied for key file %.200s.",
-                           filename);
+                       debug("Bad passphrase supplied for RSA1 key");
                /* Bad passphrase. */
                buffer_free(&decrypted);
                goto fail;
@@ -454,38 +479,37 @@ key_load_private_rsa1(int fd, const char *filename, const char *passphrase,
 
        /* enable blinding */
        if (RSA_blinding_on(prv->rsa, NULL) != 1) {
-               error("key_load_private_rsa1: RSA_blinding_on failed");
+               error("%s: RSA_blinding_on failed", __func__);
                goto fail;
        }
-       close(fd);
        return prv;
 
 fail:
        if (commentp)
                xfree(*commentp);
-       close(fd);
        key_free(prv);
        return NULL;
 }
 
-Key *
-key_load_private_pem(int fd, int type, const char *passphrase,
+static Key *
+key_parse_private_pem(Buffer *blob, int type, const char *passphrase,
     char **commentp)
 {
-       FILE *fp;
        EVP_PKEY *pk = NULL;
        Key *prv = NULL;
        char *name = "<no key>";
+       BIO *bio;
 
-       fp = fdopen(fd, "r");
-       if (fp == NULL) {
-               error("fdopen failed: %s", strerror(errno));
-               close(fd);
+       if ((bio = BIO_new_mem_buf(buffer_ptr(blob),
+           buffer_len(blob))) == NULL) {
+               error("%s: BIO_new_mem_buf failed", __func__);
                return NULL;
        }
-       pk = PEM_read_PrivateKey(fp, NULL, NULL, (char *)passphrase);
+       
+       pk = PEM_read_bio_PrivateKey(bio, NULL, NULL, (char *)passphrase);
+       BIO_free(bio);
        if (pk == NULL) {
-               debug("PEM_read_PrivateKey failed");
+               debug("%s: PEM_read_PrivateKey failed", __func__);
                (void)ERR_get_error();
        } else if (pk->type == EVP_PKEY_RSA &&
            (type == KEY_UNSPEC||type==KEY_RSA)) {
@@ -497,7 +521,7 @@ key_load_private_pem(int fd, int type, const char *passphrase,
                RSA_print_fp(stderr, prv->rsa, 8);
 #endif
                if (RSA_blinding_on(prv->rsa, NULL) != 1) {
-                       error("key_load_private_pem: RSA_blinding_on failed");
+                       error("%s: RSA_blinding_on failed", __func__);
                        key_free(prv);
                        prv = NULL;
                }
@@ -510,11 +534,31 @@ key_load_private_pem(int fd, int type, const char *passphrase,
 #ifdef DEBUG_PK
                DSA_print_fp(stderr, prv->dsa, 8);
 #endif
+#ifdef OPENSSL_HAS_ECC
+       } else if (pk->type == EVP_PKEY_EC &&
+           (type == KEY_UNSPEC||type==KEY_ECDSA)) {
+               prv = key_new(KEY_UNSPEC);
+               prv->ecdsa = EVP_PKEY_get1_EC_KEY(pk);
+               prv->type = KEY_ECDSA;
+               if ((prv->ecdsa_nid = key_ecdsa_key_to_nid(prv->ecdsa)) == -1 ||
+                   key_curve_nid_to_name(prv->ecdsa_nid) == NULL ||
+                   key_ec_validate_public(EC_KEY_get0_group(prv->ecdsa),
+                   EC_KEY_get0_public_key(prv->ecdsa)) != 0 ||
+                   key_ec_validate_private(prv->ecdsa) != 0) {
+                       error("%s: bad ECDSA key", __func__);
+                       key_free(prv);
+                       prv = NULL;
+               }
+               name = "ecdsa w/o comment";
+#ifdef DEBUG_PK
+               if (prv != NULL && prv->ecdsa != NULL)
+                       key_dump_ec_key(prv->ecdsa);
+#endif
+#endif /* OPENSSL_HAS_ECC */
        } else {
-               error("PEM_read_PrivateKey: mismatch or "
-                   "unknown EVP_PKEY save_type %d", pk->save_type);
+               error("%s: PEM_read_PrivateKey: mismatch or "
+                   "unknown EVP_PKEY save_type %d", __func__, pk->save_type);
        }
-       fclose(fp);
        if (pk != NULL)
                EVP_PKEY_free(pk);
        if (prv != NULL && commentp)
@@ -524,6 +568,23 @@ key_load_private_pem(int fd, int type, const char *passphrase,
        return prv;
 }
 
+Key *
+key_load_private_pem(int fd, int type, const char *passphrase,
+    char **commentp)
+{
+       Buffer buffer;
+       Key *prv;
+
+       buffer_init(&buffer);
+       if (!key_load_file(fd, NULL, &buffer)) {
+               buffer_free(&buffer);
+               return NULL;
+       }
+       prv = key_parse_private_pem(&buffer, type, passphrase, commentp);
+       buffer_free(&buffer);
+       return prv;
+}
+
 int
 key_perm_ok(int fd, const char *filename)
 {
@@ -552,11 +613,31 @@ key_perm_ok(int fd, const char *filename)
        return 1;
 }
 
+static Key *
+key_parse_private_type(Buffer *blob, int type, const char *passphrase,
+    char **commentp)
+{
+       switch (type) {
+       case KEY_RSA1:
+               return key_parse_private_rsa1(blob, passphrase, commentp);
+       case KEY_DSA:
+       case KEY_ECDSA:
+       case KEY_RSA:
+       case KEY_UNSPEC:
+               return key_parse_private_pem(blob, type, passphrase, commentp);
+       default:
+               break;
+       }
+       return NULL;
+}
+
 Key *
 key_load_private_type(int type, const char *filename, const char *passphrase,
     char **commentp, int *perm_ok)
 {
        int fd;
+       Key *ret;
+       Buffer buffer;
 
        fd = open(filename, O_RDONLY);
        if (fd < 0) {
@@ -575,21 +656,17 @@ key_load_private_type(int type, const char *filename, const char *passphrase,
        }
        if (perm_ok != NULL)
                *perm_ok = 1;
-       switch (type) {
-       case KEY_RSA1:
-               return key_load_private_rsa1(fd, filename, passphrase,
-                   commentp);
-               /* closes fd */
-       case KEY_DSA:
-       case KEY_RSA:
-       case KEY_UNSPEC:
-               return key_load_private_pem(fd, type, passphrase, commentp);
-               /* closes fd */
-       default:
+
+       buffer_init(&buffer);
+       if (!key_load_file(fd, filename, &buffer)) {
+               buffer_free(&buffer);
                close(fd);
-               break;
+               return NULL;
        }
-       return NULL;
+       close(fd);
+       ret = key_parse_private_type(&buffer, type, passphrase, commentp);
+       buffer_free(&buffer);
+       return ret;
 }
 
 Key *
@@ -597,6 +674,7 @@ key_load_private(const char *filename, const char *passphrase,
     char **commentp)
 {
        Key *pub, *prv;
+       Buffer buffer, pubcopy;
        int fd;
 
        fd = open(filename, O_RDONLY);
@@ -610,20 +688,33 @@ key_load_private(const char *filename, const char *passphrase,
                close(fd);
                return NULL;
        }
-       pub = key_load_public_rsa1(fd, filename, commentp);
-       lseek(fd, (off_t) 0, SEEK_SET);         /* rewind */
+
+       buffer_init(&buffer);
+       if (!key_load_file(fd, filename, &buffer)) {
+               buffer_free(&buffer);
+               close(fd);
+               return NULL;
+       }
+       close(fd);
+
+       buffer_init(&pubcopy);
+       buffer_append(&pubcopy, buffer_ptr(&buffer), buffer_len(&buffer));
+       /* it's a SSH v1 key if the public key part is readable */
+       pub = key_parse_public_rsa1(&pubcopy, commentp);
+       buffer_free(&pubcopy);
        if (pub == NULL) {
-               /* closes fd */
-               prv = key_load_private_pem(fd, KEY_UNSPEC, passphrase, NULL);
+               prv = key_parse_private_type(&buffer, KEY_UNSPEC,
+                   passphrase, NULL);
                /* use the filename as a comment for PEM */
                if (commentp && prv)
                        *commentp = xstrdup(filename);
        } else {
-               /* it's a SSH v1 key if the public key part is readable */
                key_free(pub);
-               /* closes fd */
-               prv = key_load_private_rsa1(fd, filename, passphrase, NULL);
+               /* key_parse_public_rsa1() has already loaded the comment */
+               prv = key_parse_private_type(&buffer, KEY_RSA1, passphrase,
+                   NULL);
        }
+       buffer_free(&buffer);
        return prv;
 }
 
@@ -721,6 +812,7 @@ key_load_private_cert(int type, const char *filename, const char *passphrase,
        switch (type) {
        case KEY_RSA:
        case KEY_DSA:
+       case KEY_ECDSA:
                break;
        default:
                error("%s: unsupported key type", __func__);
index 854fd51..00208ca 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: bufaux.c,v 1.49 2010/03/26 03:13:17 djm Exp $ */
+/* $OpenBSD: bufaux.c,v 1.50 2010/08/31 09:58:37 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -202,6 +202,39 @@ buffer_get_string(Buffer *buffer, u_int *length_ptr)
        return (ret);
 }
 
+char *
+buffer_get_cstring_ret(Buffer *buffer, u_int *length_ptr)
+{
+       u_int length;
+       char *cp, *ret = buffer_get_string_ret(buffer, &length);
+
+       if (ret == NULL)
+               return NULL;
+       if ((cp = memchr(ret, '\0', length)) != NULL) {
+               /* XXX allow \0 at end-of-string for a while, remove later */
+               if (cp == ret + length - 1)
+                       error("buffer_get_cstring_ret: string contains \\0");
+               else {
+                       bzero(ret, length);
+                       xfree(ret);
+                       return NULL;
+               }
+       }
+       if (length_ptr != NULL)
+               *length_ptr = length;
+       return ret;
+}
+
+char *
+buffer_get_cstring(Buffer *buffer, u_int *length_ptr)
+{
+       char *ret;
+
+       if ((ret = buffer_get_cstring_ret(buffer, length_ptr)) == NULL)
+               fatal("buffer_get_cstring: buffer error");
+       return ret;
+}
+
 void *
 buffer_get_string_ptr_ret(Buffer *buffer, u_int *length_ptr)
 {
diff --git a/crypto/openssh/bufec.c b/crypto/openssh/bufec.c
new file mode 100644 (file)
index 0000000..3dcb494
--- /dev/null
@@ -0,0 +1,146 @@
+/* $OpenBSD: bufec.c,v 1.1 2010/08/31 11:54:45 djm Exp $ */
+/*
+ * Copyright (c) 2010 Damien Miller <djm@mindrot.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#ifdef OPENSSL_HAS_ECC
+
+#include <sys/types.h>
+
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+
+#include <string.h>
+#include <stdarg.h>
+
+#include "xmalloc.h"
+#include "buffer.h"
+#include "log.h"
+#include "misc.h"
+
+/*
+ * Maximum supported EC GFp field length is 528 bits. SEC1 uncompressed
+ * encoding represents this as two bitstring points that should each
+ * be no longer than the field length, SEC1 specifies a 1 byte
+ * point type header.
+ * Being paranoid here may insulate us to parsing problems in
+ * EC_POINT_oct2point.
+ */
+#define BUFFER_MAX_ECPOINT_LEN ((528*2 / 8) + 1)
+
+/*
+ * Append an EC_POINT to the buffer as a string containing a SEC1 encoded
+ * uncompressed point. Fortunately OpenSSL handles the gory details for us.
+ */
+int
+buffer_put_ecpoint_ret(Buffer *buffer, const EC_GROUP *curve,
+    const EC_POINT *point)
+{
+       u_char *buf = NULL;
+       size_t len;
+       BN_CTX *bnctx;
+       int ret = -1;
+
+       /* Determine length */
+       if ((bnctx = BN_CTX_new()) == NULL)
+               fatal("%s: BN_CTX_new failed", __func__);
+       len = EC_POINT_point2oct(curve, point, POINT_CONVERSION_UNCOMPRESSED,
+           NULL, 0, bnctx);
+       if (len > BUFFER_MAX_ECPOINT_LEN) {
+               error("%s: giant EC point: len = %lu (max %u)",
+                   __func__, (u_long)len, BUFFER_MAX_ECPOINT_LEN);
+               goto out;
+       }
+       /* Convert */
+       buf = xmalloc(len);
+       if (EC_POINT_point2oct(curve, point, POINT_CONVERSION_UNCOMPRESSED,
+           buf, len, bnctx) != len) {
+               error("%s: EC_POINT_point2oct length mismatch", __func__);
+               goto out;
+       }
+       /* Append */
+       buffer_put_string(buffer, buf, len);
+       ret = 0;
+ out:
+       if (buf != NULL) {
+               bzero(buf, len);
+               xfree(buf);
+       }
+       BN_CTX_free(bnctx);
+       return ret;
+}
+
+void
+buffer_put_ecpoint(Buffer *buffer, const EC_GROUP *curve,
+    const EC_POINT *point)
+{
+       if (buffer_put_ecpoint_ret(buffer, curve, point) == -1)
+               fatal("%s: buffer error", __func__);
+}
+
+int
+buffer_get_ecpoint_ret(Buffer *buffer, const EC_GROUP *curve,
+    EC_POINT *point)
+{
+       u_char *buf;
+       u_int len;
+       BN_CTX *bnctx;
+       int ret = -1;
+
+       if ((buf = buffer_get_string_ret(buffer, &len)) == NULL) {
+               error("%s: invalid point", __func__);
+               return -1;
+       }
+       if ((bnctx = BN_CTX_new()) == NULL)
+               fatal("%s: BN_CTX_new failed", __func__);
+       if (len > BUFFER_MAX_ECPOINT_LEN) {
+               error("%s: EC_POINT too long: %u > max %u", __func__,
+                   len, BUFFER_MAX_ECPOINT_LEN);
+               goto out;
+       }
+       if (len == 0) {
+               error("%s: EC_POINT buffer is empty", __func__);
+               goto out;
+       }
+       if (buf[0] != POINT_CONVERSION_UNCOMPRESSED) {
+               error("%s: EC_POINT is in an incorrect form: "
+                   "0x%02x (want 0x%02x)", __func__, buf[0],
+                   POINT_CONVERSION_UNCOMPRESSED);
+               goto out;
+       }
+       if (EC_POINT_oct2point(curve, point, buf, len, bnctx) != 1) {
+               error("buffer_get_bignum2_ret: BN_bin2bn failed");
+               goto out;
+       }
+       /* EC_POINT_oct2point verifies that the point is on the curve for us */
+       ret = 0;
+ out:
+       BN_CTX_free(bnctx);
+       bzero(buf, len);
+       xfree(buf);
+       return ret;
+}
+
+void
+buffer_get_ecpoint(Buffer *buffer, const EC_GROUP *curve,
+    EC_POINT *point)
+{
+       if (buffer_get_ecpoint_ret(buffer, curve, point) == -1)
+               fatal("%s: buffer error", __func__);
+}
+
+#endif /* OPENSSL_HAS_ECC */
index 4ef4f80..e2a9dd1 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: buffer.h,v 1.19 2010/02/09 03:56:28 djm Exp $ */
+/* $OpenBSD: buffer.h,v 1.21 2010/08/31 11:54:45 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -68,6 +68,7 @@ void    buffer_put_char(Buffer *, int);
 void   *buffer_get_string(Buffer *, u_int *);
 void   *buffer_get_string_ptr(Buffer *, u_int *);
 void    buffer_put_string(Buffer *, const void *, u_int);
+char   *buffer_get_cstring(Buffer *, u_int *);
 void   buffer_put_cstring(Buffer *, const char *);
 
 #define buffer_skip_string(b) \
@@ -81,7 +82,17 @@ 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 *);
+char   *buffer_get_cstring_ret(Buffer *, u_int *);
 void   *buffer_get_string_ptr_ret(Buffer *, u_int *);
 int    buffer_get_char_ret(char *, Buffer *);
 
+#ifdef OPENSSL_HAS_ECC
+#include <openssl/ec.h>
+
+int    buffer_put_ecpoint_ret(Buffer *, const EC_GROUP *, const EC_POINT *);
+void   buffer_put_ecpoint(Buffer *, const EC_GROUP *, const EC_POINT *);
+int    buffer_get_ecpoint_ret(Buffer *, const EC_GROUP *, EC_POINT *);
+void   buffer_get_ecpoint(Buffer *, const EC_GROUP *, EC_POINT *);
+#endif
+
 #endif                         /* BUFFER_H */
index ef94d91..dabd8a3 100644 (file)
@@ -199,7 +199,7 @@ ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len)
        memcpy(&inaddr, ((char *)&a6->sin6_addr) + 12, sizeof(inaddr));
        port = a6->sin6_port;
 
-       memset(addr, 0, sizeof(*a4));
+       bzero(a4, sizeof(*a4));
 
        a4->sin_family = AF_INET;
        *len = sizeof(*a4);
index 1cd5004..6abe2d0 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.c,v 1.309 2010/08/05 13:08:42 djm Exp $ */
+/* $OpenBSD: channels.c,v 1.310 2010/11/24 01:24:14 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -373,9 +373,6 @@ channel_close_fd(int *fdp)
 static void
 channel_close_fds(Channel *c)
 {
-       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->rfd);
        channel_close_fd(&c->wfd);
index 17a13a1..b7aa588 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cipher-3des1.c,v 1.6 2006/08/03 03:34:42 deraadt Exp $ */
+/* $OpenBSD: cipher-3des1.c,v 1.7 2010/10/01 23:05:32 djm Exp $ */
 /*
  * Copyright (c) 2003 Markus Friedl.  All rights reserved.
  *
@@ -103,7 +103,8 @@ ssh1_3des_init(EVP_CIPHER_CTX *ctx, const u_char *key, const u_char *iv,
 }
 
 static int
-ssh1_3des_cbc(EVP_CIPHER_CTX *ctx, u_char *dest, const u_char *src, u_int len)
+ssh1_3des_cbc(EVP_CIPHER_CTX *ctx, u_char *dest, const u_char *src,
+    LIBCRYPTO_EVP_INL_TYPE len)
 {
        struct ssh1_3des_ctx *c;
 
index cb0bf73..e755f92 100644 (file)
@@ -23,6 +23,7 @@
 #if !defined(EVP_CTRL_SET_ACSS_MODE) && (OPENSSL_VERSION_NUMBER >= 0x00907000L)
 
 #include "acss.h"
+#include "openbsd-compat/openssl-compat.h"
 
 #define data(ctx) ((EVP_ACSS_KEY *)(ctx)->cipher_data)
 
@@ -43,7 +44,7 @@ acss_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
 
 static int
 acss_ciph(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in,
-    unsigned int inl)
+    LIBCRYPTO_EVP_INL_TYPE inl)
 {
        acss(&data(ctx)->ks,inl,in,out);
        return 1;
index 3ea5949..bfda6d2 100644 (file)
@@ -72,7 +72,7 @@ ssh_rijndael_init(EVP_CIPHER_CTX *ctx, const u_char *key, const u_char *iv,
 
 static int
 ssh_rijndael_cbc(EVP_CIPHER_CTX *ctx, u_char *dest, const u_char *src,
-    u_int len)
+    LIBCRYPTO_EVP_INL_TYPE len)
 {
        struct ssh_rijndael_ctx *c;
        u_char buf[RIJNDAEL_BLOCKSIZE];
index e0e33b4..309509d 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cipher-bf1.c,v 1.5 2006/08/03 03:34:42 deraadt Exp $ */
+/* $OpenBSD: cipher-bf1.c,v 1.6 2010/10/01 23:05:32 djm Exp $ */
 /*
  * Copyright (c) 2003 Markus Friedl.  All rights reserved.
  *
@@ -76,10 +76,12 @@ static void bf_ssh1_init (EVP_CIPHER_CTX * ctx, const unsigned char *key,
 }
 #endif
 
-static int (*orig_bf)(EVP_CIPHER_CTX *, u_char *, const u_char *, u_int) = NULL;
+static int (*orig_bf)(EVP_CIPHER_CTX *, u_char *,
+    const u_char *, LIBCRYPTO_EVP_INL_TYPE) = NULL;
 
 static int
-bf_ssh1_cipher(EVP_CIPHER_CTX *ctx, u_char *out, const u_char *in, u_int len)
+bf_ssh1_cipher(EVP_CIPHER_CTX *ctx, u_char *out, const u_char *in,
+    LIBCRYPTO_EVP_INL_TYPE len)
 {
        int ret;
 
index 3b86cc1..04975b4 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cipher-ctr.c,v 1.10 2006/08/03 03:34:42 deraadt Exp $ */
+/* $OpenBSD: cipher-ctr.c,v 1.11 2010/10/01 23:05:32 djm Exp $ */
 /*
  * Copyright (c) 2003 Markus Friedl <markus@openbsd.org>
  *
@@ -34,7 +34,7 @@
 #endif
 
 const EVP_CIPHER *evp_aes_128_ctr(void);
-void ssh_aes_ctr_iv(EVP_CIPHER_CTX *, int, u_char *, u_int);
+void ssh_aes_ctr_iv(EVP_CIPHER_CTX *, int, u_char *, size_t);
 
 struct ssh_aes_ctr_ctx
 {
@@ -48,7 +48,7 @@ struct ssh_aes_ctr_ctx
  * (LSB at ctr[len-1], MSB at ctr[0])
  */
 static void
-ssh_ctr_inc(u_char *ctr, u_int len)
+ssh_ctr_inc(u_char *ctr, size_t len)
 {
        int i;
 
@@ -59,10 +59,10 @@ ssh_ctr_inc(u_char *ctr, u_int len)
 
 static int
 ssh_aes_ctr(EVP_CIPHER_CTX *ctx, u_char *dest, const u_char *src,
-    u_int len)
+    LIBCRYPTO_EVP_INL_TYPE len)
 {
        struct ssh_aes_ctr_ctx *c;
-       u_int n = 0;
+       size_t n = 0;
        u_char buf[AES_BLOCK_SIZE];
 
        if (len == 0)
@@ -113,7 +113,7 @@ ssh_aes_ctr_cleanup(EVP_CIPHER_CTX *ctx)
 }
 
 void
-ssh_aes_ctr_iv(EVP_CIPHER_CTX *evp, int doset, u_char * iv, u_int len)
+ssh_aes_ctr_iv(EVP_CIPHER_CTX *evp, int doset, u_char * iv, size_t len)
 {
        struct ssh_aes_ctr_ctx *c;
 
index de79793..f6c1444 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: clientloop.c,v 1.222 2010/07/19 09:15:12 djm Exp $ */
+/* $OpenBSD: clientloop.c,v 1.231 2011/01/16 12:05:59 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -325,7 +325,7 @@ client_x11_get_proto(const char *display, const char *xauth_path,
                if (trusted == 0) {
                        xauthdir = xmalloc(MAXPATHLEN);
                        xauthfile = xmalloc(MAXPATHLEN);
-                       strlcpy(xauthdir, "/tmp/ssh-XXXXXXXXXX", MAXPATHLEN);
+                       mktemp_proto(xauthdir, MAXPATHLEN);
                        if (mkdtemp(xauthdir) != NULL) {
                                do_unlink = 1;
                                snprintf(xauthfile, MAXPATHLEN, "%s/xauthfile",
@@ -544,7 +544,7 @@ static void
 server_alive_check(void)
 {
        if (packet_inc_alive_timeouts() > options.server_alive_count_max) {
-               logit("Timeout, server not responding.");
+               logit("Timeout, server %s not responding.", host);
                cleanup_exit(255);
        }
        packet_start(SSH2_MSG_GLOBAL_REQUEST);
@@ -1590,25 +1590,23 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
        }
 
        /* Output any buffered data for stdout. */
-       while (buffer_len(&stdout_buffer) > 0) {
-               len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
-                   buffer_len(&stdout_buffer));
-               if (len <= 0) {
+       if (buffer_len(&stdout_buffer) > 0) {
+               len = atomicio(vwrite, fileno(stdout),
+                   buffer_ptr(&stdout_buffer), buffer_len(&stdout_buffer));
+               if (len < 0 || (u_int)len != buffer_len(&stdout_buffer))
                        error("Write failed flushing stdout buffer.");
-                       break;
-               }
-               buffer_consume(&stdout_buffer, len);
+               else
+                       buffer_consume(&stdout_buffer, len);
        }
 
        /* Output any buffered data for stderr. */
-       while (buffer_len(&stderr_buffer) > 0) {
-               len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
-                   buffer_len(&stderr_buffer));
-               if (len <= 0) {
+       if (buffer_len(&stderr_buffer) > 0) {
+               len = atomicio(vwrite, fileno(stderr),
+                   buffer_ptr(&stderr_buffer), buffer_len(&stderr_buffer));
+               if (len < 0 || (u_int)len != buffer_len(&stderr_buffer))
                        error("Write failed flushing stderr buffer.");
-                       break;
-               }
-               buffer_consume(&stderr_buffer, len);
+               else
+                       buffer_consume(&stderr_buffer, len);
        }
 
        /* Clear and free any buffers. */
@@ -1622,7 +1620,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
        packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes);
        packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes);
        verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds",
-           obytes, ibytes, total_time);
+           (unsigned long long)obytes, (unsigned long long)ibytes, total_time);
        if (total_time > 0)
                verbose("Bytes per second: sent %.1f, received %.1f",
                    obytes / total_time, ibytes / total_time);
@@ -1933,7 +1931,7 @@ client_input_channel_req(int type, u_int32_t seq, void *ctxt)
                }
                packet_check_eom();
        }
-       if (reply) {
+       if (reply && c != NULL) {
                packet_start(success ?
                    SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
                packet_put_int(c->remote_id);
@@ -1973,6 +1971,9 @@ client_session2_setup(int id, int want_tty, int want_subsystem,
        if ((c = channel_lookup(id)) == NULL)
                fatal("client_session2_setup: channel %d: unknown channel", id);
 
+       packet_set_interactive(want_tty,
+           options.ip_qos_interactive, options.ip_qos_bulk);
+
        if (want_tty) {
                struct winsize ws;
 
@@ -2129,5 +2130,6 @@ cleanup_exit(int i)
        leave_non_blocking();
        if (options.control_path != NULL && muxserver_sock != -1)
                unlink(options.control_path);
+       ssh_kill_proxy_command();
        _exit(i);
 }
index c058d22..24778e5 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: compress.c,v 1.25 2006/08/06 01:13:32 stevesk Exp $ */
+/* $OpenBSD: compress.c,v 1.26 2010/09/08 04:13:31 deraadt Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
 #include <sys/types.h>
 
 #include <stdarg.h>
-#include <zlib.h>
 
 #include "log.h"
 #include "buffer.h"
 #include "compress.h"
 
+#include <zlib.h>
+
 z_stream incoming_stream;
 z_stream outgoing_stream;
 static int compress_init_send_called = 0;
index fe25170..cd27306 100644 (file)
@@ -25,7 +25,7 @@
 #ifndef _DEFINES_H
 #define _DEFINES_H
 
-/* $Id: defines.h,v 1.160 2010/04/09 08:13:27 dtucker Exp $ */
+/* $Id: defines.h,v 1.164 2011/01/17 10:15:31 dtucker Exp $ */
 
 
 /* Constants */
@@ -42,6 +42,9 @@ enum
 # define SHUT_RDWR SHUT_RDWR
 #endif
 
+/*
+ * Definitions for IP type of service (ip_tos)
+ */
 #ifndef IPTOS_LOWDELAY
 # define IPTOS_LOWDELAY          0x10
 # define IPTOS_THROUGHPUT        0x08
@@ -50,6 +53,40 @@ enum
 # define IPTOS_MINCOST           IPTOS_LOWCOST
 #endif /* IPTOS_LOWDELAY */
 
+/*
+ * Definitions for DiffServ Codepoints as per RFC2474
+ */
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#ifndef IPTOS_DSCP_AF11
+# define       IPTOS_DSCP_AF11         0x28
+# define       IPTOS_DSCP_AF12         0x30
+# define       IPTOS_DSCP_AF13         0x38
+# define       IPTOS_DSCP_AF21         0x48
+# define       IPTOS_DSCP_AF22         0x50
+# define       IPTOS_DSCP_AF23         0x58
+# define       IPTOS_DSCP_AF31         0x68
+# define       IPTOS_DSCP_AF32         0x70
+# define       IPTOS_DSCP_AF33         0x78
+# define       IPTOS_DSCP_AF41         0x88
+# define       IPTOS_DSCP_AF42         0x90
+# define       IPTOS_DSCP_AF43         0x98
+# define       IPTOS_DSCP_EF           0xb8
+#endif /* IPTOS_DSCP_AF11 */
+#ifndef IPTOS_DSCP_CS0
+# define       IPTOS_DSCP_CS0          0x00
+# define       IPTOS_DSCP_CS1          0x20
+# define       IPTOS_DSCP_CS2          0x40
+# define       IPTOS_DSCP_CS3          0x60
+# define       IPTOS_DSCP_CS4          0x80
+# define       IPTOS_DSCP_CS5          0xa0
+# define       IPTOS_DSCP_CS6          0xc0
+# define       IPTOS_DSCP_CS7          0xe0
+#endif /* IPTOS_DSCP_CS0 */
+#ifndef IPTOS_DSCP_EF
+# define       IPTOS_DSCP_EF           0xb8
+#endif /* IPTOS_DSCP_EF */
+
 #ifndef MAXPATHLEN
 # ifdef PATH_MAX
 #  define MAXPATHLEN PATH_MAX
@@ -256,6 +293,10 @@ typedef unsigned int size_t;
 # define SIZE_T_MAX UINT_MAX
 #endif /* HAVE_SIZE_T */
 
+#ifndef SIZE_MAX
+#define SIZE_MAX SIZE_T_MAX
+#endif
+
 #ifndef HAVE_SSIZE_T
 typedef int ssize_t;
 # define HAVE_SSIZE_T
@@ -566,6 +607,11 @@ struct winsize {
 # define CUSTOM_SSH_AUDIT_EVENTS
 #endif
 
+#ifdef USE_LINUX_AUDIT
+# define SSH_AUDIT_EVENTS
+# define CUSTOM_SSH_AUDIT_EVENTS
+#endif
+
 #if !defined(HAVE___func__) && defined(HAVE___FUNCTION__)
 #  define __func__ __FUNCTION__
 #elif !defined(HAVE___func__)
index 2e7bb5a..131cb3d 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: dns.c,v 1.26 2010/02/26 20:29:54 djm Exp $ */
+/* $OpenBSD: dns.c,v 1.27 2010/08/31 11:54:45 djm Exp $ */
 
 /*
  * Copyright (c) 2003 Wesley Griffin. All rights reserved.
@@ -86,6 +86,7 @@ dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type,
        case KEY_DSA:
                *algorithm = SSHFP_KEY_DSA;
                break;
+       /* XXX KEY_ECDSA */
        default:
                *algorithm = SSHFP_KEY_RESERVED; /* 0 */
        }
index 8b70539..a821662 100644 (file)
@@ -157,7 +157,7 @@ init_rng(void)
         */
        if ((SSLeay() ^ OPENSSL_VERSION_NUMBER) & ~0xff0L)
                fatal("OpenSSL version mismatch. Built against %lx, you "
-                   "have %lx", OPENSSL_VERSION_NUMBER, SSLeay());
+                   "have %lx", (u_long)OPENSSL_VERSION_NUMBER, SSLeay());
 
 #ifndef OPENSSL_PRNG_ONLY
        original_uid = getuid();
index afab6da..b6f924b 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: hostfile.c,v 1.48 2010/03/04 10:36:03 djm Exp $ */
+/* $OpenBSD: hostfile.c,v 1.50 2010/12/04 13:31:37 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
 #include "key.h"
 #include "hostfile.h"
 #include "log.h"
+#include "misc.h"
+
+struct hostkeys {
+       struct hostkey_entry *entries;
+       u_int num_entries;
+};
 
 static int
 extract_salt(const char *s, u_int l, char *salt, size_t salt_len)
@@ -164,26 +170,28 @@ hostfile_read_key(char **cpp, u_int *bitsp, Key *ret)
 
        /* Return results. */
        *cpp = cp;
-       *bitsp = key_size(ret);
+       if (bitsp != NULL)
+               *bitsp = key_size(ret);
        return 1;
 }
 
 static int
-hostfile_check_key(int bits, const Key *key, const char *host, const char *filename, int linenum)
+hostfile_check_key(int bits, const Key *key, const char *host,
+    const char *filename, u_long linenum)
 {
        if (key == NULL || key->type != KEY_RSA1 || key->rsa == NULL)
                return 1;
        if (bits != BN_num_bits(key->rsa->n)) {
-               logit("Warning: %s, line %d: keysize mismatch for host %s: "
+               logit("Warning: %s, line %lu: keysize mismatch for host %s: "
                    "actual %d vs. announced %d.",
                    filename, linenum, host, BN_num_bits(key->rsa->n), bits);
-               logit("Warning: replace %d with %d in %s, line %d.",
+               logit("Warning: replace %d with %d in %s, line %lu.",
                    bits, BN_num_bits(key->rsa->n), filename, linenum);
        }
        return 1;
 }
 
-static enum { MRK_ERROR, MRK_NONE, MRK_REVOKE, MRK_CA }
+static HostkeyMarker
 check_markers(char **cpp)
 {
        char marker[32], *sp, *cp = *cpp;
@@ -218,49 +226,32 @@ check_markers(char **cpp)
        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
- * has the specified key, HOST_NEW if the host is not known, and HOST_CHANGED
- * if the host is known but used to have a different host key.
- *
- * If no 'key' has been specified and a key of type 'keytype' is known
- * for the specified host, then HOST_FOUND is returned.
- */
+struct hostkeys *
+init_hostkeys(void)
+{
+       struct hostkeys *ret = xcalloc(1, sizeof(*ret));
 
-static HostStatus
-check_host_in_hostfile_by_key_or_type(const char *filename,
-    const char *host, const Key *key, int keytype, Key *found,
-    int want_revocation, int *numret)
+       ret->entries = NULL;
+       return ret;
+}
+
+void
+load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path)
 {
        FILE *f;
        char line[8192];
-       int want, have, linenum = 0, want_cert = key_is_cert(key);
-       u_int kbits;
+       u_long linenum = 0, num_loaded = 0;
        char *cp, *cp2, *hashed_host;
-       HostStatus end_return;
-
-       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");
-       if (!f)
-               return HOST_NEW;
-
-       /*
-        * Return value when the loop terminates.  This is set to
-        * HOST_CHANGED if we have seen a different key for the host and have
-        * not found the proper one.
-        */
-       end_return = HOST_NEW;
-
-       /* Go through the file. */
-       while (fgets(line, sizeof(line), f)) {
+       HostkeyMarker marker;
+       Key *key;
+       int kbits;
+
+       if ((f = fopen(path, "r")) == NULL)
+               return;
+       debug3("%s: loading entries for host \"%.100s\" from file \"%s\"",
+           __func__, host, path);
+       while (read_keyfile_line(f, path, line, sizeof(line), &linenum) == 0) {
                cp = line;
-               linenum++;
 
                /* Skip any leading whitespace, comments and empty lines. */
                for (; *cp == ' ' || *cp == '\t'; cp++)
@@ -268,19 +259,11 @@ 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)
+               if ((marker = check_markers(&cp)) == MRK_ERROR) {
+                       verbose("%s: invalid marker at %s:%lu",
+                           __func__, path, linenum);
                        continue;
+               }
 
                /* Find the end of the host name portion. */
                for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
@@ -292,8 +275,8 @@ check_host_in_hostfile_by_key_or_type(const char *filename,
                                continue;
                        hashed_host = host_hash(host, cp, (u_int) (cp2 - cp));
                        if (hashed_host == NULL) {
-                               debug("Invalid hashed host line %d of %s",
-                                   linenum, filename);
+                               debug("Invalid hashed host line %lu of %s",
+                                   linenum, path);
                                continue;
                        }
                        if (strncmp(hashed_host, cp, (u_int) (cp2 - cp)) != 0)
@@ -303,98 +286,167 @@ 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.
                 */
-               if (!hostfile_read_key(&cp, &kbits, found))
+               key = key_new(KEY_UNSPEC);
+               if (!hostfile_read_key(&cp, &kbits, key)) {
+                       key_free(key);
+                       key = key_new(KEY_RSA1);
+                       if (!hostfile_read_key(&cp, &kbits, key)) {
+                               key_free(key);
+                               continue;
+                       }
+               }
+               if (!hostfile_check_key(kbits, key, host, path, linenum))
                        continue;
 
-               if (numret != NULL)
-                       *numret = linenum;
+               debug3("%s: found %skey type %s in file %s:%lu", __func__,
+                   marker == MRK_NONE ? "" :
+                   (marker == MRK_CA ? "ca " : "revoked "),
+                   key_type(key), path, linenum);
+               hostkeys->entries = xrealloc(hostkeys->entries,
+                   hostkeys->num_entries + 1, sizeof(*hostkeys->entries));
+               hostkeys->entries[hostkeys->num_entries].host = xstrdup(host);
+               hostkeys->entries[hostkeys->num_entries].file = xstrdup(path);
+               hostkeys->entries[hostkeys->num_entries].line = linenum;
+               hostkeys->entries[hostkeys->num_entries].key = key;
+               hostkeys->entries[hostkeys->num_entries].marker = marker;
+               hostkeys->num_entries++;
+               num_loaded++;
+       }
+       debug3("%s: loaded %lu keys", __func__, num_loaded);
+       fclose(f);
+       return;
+}      
 
-               if (key == NULL) {
-                       /* we found a key of the requested type */
-                       if (found->type == keytype) {
-                               fclose(f);
-                               return HOST_FOUND;
-                       }
-                       continue;
-               }
+void
+free_hostkeys(struct hostkeys *hostkeys)
+{
+       u_int i;
+
+       for (i = 0; i < hostkeys->num_entries; i++) {
+               xfree(hostkeys->entries[i].host);
+               xfree(hostkeys->entries[i].file);
+               key_free(hostkeys->entries[i].key);
+               bzero(hostkeys->entries + i, sizeof(*hostkeys->entries));
+       }
+       if (hostkeys->entries != NULL)
+               xfree(hostkeys->entries);
+       hostkeys->entries = NULL;
+       hostkeys->num_entries = 0;
+       xfree(hostkeys);
+}
+
+static int
+check_key_not_revoked(struct hostkeys *hostkeys, Key *k)
+{
+       int is_cert = key_is_cert(k);
+       u_int i;
 
-               if (!hostfile_check_key(kbits, found, host, filename, linenum))
+       for (i = 0; i < hostkeys->num_entries; i++) {
+               if (hostkeys->entries[i].marker != MRK_REVOKE)
                        continue;
+               if (key_equal_public(k, hostkeys->entries[i].key))
+                       return -1;
+               if (is_cert &&
+                   key_equal_public(k->cert->signature_key,
+                   hostkeys->entries[i].key))
+                       return -1;
+       }
+       return 0;
+}
 
-               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);
+/*
+ * Match keys against a specified key, or look one up by key type.
+ *
+ * If looking for a keytype (key == NULL) and one is found then return
+ * HOST_FOUND, otherwise HOST_NEW.
+ *
+ * If looking for a key (key != NULL):
+ *  1. If the key is a cert and a matching CA is found, return HOST_OK
+ *  2. If the key is not a cert and a matching key is found, return HOST_OK
+ *  3. If no key matches but a key with a different type is found, then
+ *     return HOST_CHANGED
+ *  4. If no matching keys are found, then return HOST_NEW.
+ *
+ * Finally, check any found key is not revoked.
+ */
+static HostStatus
+check_hostkeys_by_key_or_type(struct hostkeys *hostkeys,
+    Key *k, int keytype, const struct hostkey_entry **found)
+{
+       u_int i;
+       HostStatus end_return = HOST_NEW;
+       int want_cert = key_is_cert(k);
+       HostkeyMarker want_marker = want_cert ? MRK_CA : MRK_NONE;
+       int proto = (k ? k->type : keytype) == KEY_RSA1 ? 1 : 2;
+
+       if (found != NULL)
+               *found = NULL;
+
+       for (i = 0; i < hostkeys->num_entries; i++) {
+               if (proto == 1 && hostkeys->entries[i].key->type != KEY_RSA1)
+                       continue;
+               if (proto == 2 && hostkeys->entries[i].key->type == KEY_RSA1)
                        continue;
+               if (hostkeys->entries[i].marker != want_marker)
+                       continue;
+               if (k == NULL) {
+                       if (hostkeys->entries[i].key->type != keytype)
+                               continue;
+                       end_return = HOST_FOUND;
+                       if (found != NULL)
+                               *found = hostkeys->entries + i;
+                       k = hostkeys->entries[i].key;
+                       break;
                }
-
-               /* Check if the current key is the same as the given key. */
-               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;
+               if (want_cert) {
+                       if (key_equal_public(k->cert->signature_key,
+                           hostkeys->entries[i].key)) {
+                               /* A matching CA exists */
+                               end_return = HOST_OK;
+                               if (found != NULL)
+                                       *found = hostkeys->entries + i;
+                               break;
+                       }
+               } else {
+                       if (key_equal(k, hostkeys->entries[i].key)) {
+                               end_return = HOST_OK;
+                               if (found != NULL)
+                                       *found = hostkeys->entries + i;
+                               break;
+                       }
+                       /* A non-maching key exists */
+                       end_return = HOST_CHANGED;
+                       if (found != NULL)
+                               *found = hostkeys->entries + i;
                }
-               /*
-                * They do not match.  We will continue to go through the
-                * file; however, we note that we will not return that it is
-                * new.
-                */
-               end_return = HOST_CHANGED;
        }
-       /* Clear variables and close the file. */
-       fclose(f);
-
-       /*
-        * Return either HOST_NEW or HOST_CHANGED, depending on whether we
-        * saw a different key for the host.
-        */
+       if (check_key_not_revoked(hostkeys, k) != 0) {
+               end_return = HOST_REVOKED;
+               if (found != NULL)
+                       *found = NULL;
+       }
        return end_return;
 }
-
+       
 HostStatus
-check_host_in_hostfile(const char *filename, const char *host, const Key *key,
-    Key *found, int *numret)
+check_key_in_hostkeys(struct hostkeys *hostkeys, Key *key,
+    const struct hostkey_entry **found)
 {
        if (key == NULL)
                fatal("no key to look up");
-       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);
+       return check_hostkeys_by_key_or_type(hostkeys, key, 0, found);
 }
 
 int
-lookup_key_in_hostfile_by_type(const char *filename, const char *host,
-    int keytype, Key *found, int *numret)
+lookup_key_in_hostkeys_by_type(struct hostkeys *hostkeys, int keytype,
+    const struct hostkey_entry **found)
 {
-       return (check_host_in_hostfile_by_key_or_type(filename, host, NULL,
-           keytype, found, 0, numret) == HOST_FOUND);
+       return (check_hostkeys_by_key_or_type(hostkeys, NULL, keytype,
+           found) == HOST_FOUND);
 }
 
 /*
index 1d460c1..d84d422 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: hostfile.h,v 1.18 2010/03/04 10:36:03 djm Exp $ */
+/* $OpenBSD: hostfile.h,v 1.19 2010/11/29 23:45:51 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -18,12 +18,30 @@ typedef enum {
        HOST_OK, HOST_NEW, HOST_CHANGED, HOST_REVOKED, HOST_FOUND
 }       HostStatus;
 
+typedef enum {
+       MRK_ERROR, MRK_NONE, MRK_REVOKE, MRK_CA
+}      HostkeyMarker;
+
+struct hostkey_entry {
+       char *host;
+       char *file;
+       u_long line;
+       Key *key;
+       HostkeyMarker marker;
+};
+struct hostkeys;
+
+struct hostkeys *init_hostkeys(void);
+void    load_hostkeys(struct hostkeys *, const char *, const char *);
+void    free_hostkeys(struct hostkeys *);
+
+HostStatus check_key_in_hostkeys(struct hostkeys *, Key *,
+    const struct hostkey_entry **);
+int     lookup_key_in_hostkeys_by_type(struct hostkeys *, int,
+    const struct hostkey_entry **);
+
 int     hostfile_read_key(char **, u_int *, Key *);
-HostStatus check_host_in_hostfile(const char *, const char *,
-           const Key *, Key *, int *);
-int    add_host_to_hostfile(const char *, const char *, const Key *, int);
-int    lookup_key_in_hostfile_by_type(const char *, const char *,
-           int, Key *, int *);
+int     add_host_to_hostfile(const char *, const char *, const Key *, int);
 
 #define HASH_MAGIC     "|1|"
 #define HASH_DELIM     '|'
index 6bb9878..b4c53d9 100644 (file)
@@ -30,7 +30,7 @@
 # include <bstring.h>
 #endif
 #if defined(HAVE_GLOB_H) && defined(GLOB_HAS_ALTDIRFUNC) && \
-    defined(GLOB_HAS_GL_MATCHC) && \
+    defined(GLOB_HAS_GL_MATCHC) && defined(GLOB_HAS_GL_STATV) && \
     defined(HAVE_DECL_GLOB_NOMATCH) &&  HAVE_DECL_GLOB_NOMATCH != 0 && \
     !defined(BROKEN_GLOB)
 # include <glob.h>
index cdf65f5..ac9a4bc 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: jpake.c,v 1.4 2010/07/13 23:13:16 djm Exp $ */
+/* $OpenBSD: jpake.c,v 1.6 2010/09/20 04:54:07 djm Exp $ */
 /*
  * Copyright (c) 2008 Damien Miller.  All rights reserved.
  *
@@ -45,6 +45,7 @@
 #include "packet.h"
 #include "dispatch.h"
 #include "log.h"
+#include "misc.h"
 
 #include "jpake.h"
 #include "schnorr.h"
@@ -257,8 +258,12 @@ jpake_step2(struct modp_group *grp, BIGNUM *s,
        /* Validate peer's step 1 values */
        if (BN_cmp(theirpub1, BN_value_one()) <= 0)
                fatal("%s: theirpub1 <= 1", __func__);
+       if (BN_cmp(theirpub1, grp->p) >= 0)
+               fatal("%s: theirpub1 >= p", __func__);
        if (BN_cmp(theirpub2, BN_value_one()) <= 0)
                fatal("%s: theirpub2 <= 1", __func__);
+       if (BN_cmp(theirpub2, grp->p) >= 0)
+               fatal("%s: theirpub2 >= p", __func__);
 
        if (schnorr_verify_buf(grp->p, grp->q, grp->g, theirpub1,
            theirid, theirid_len, theirpub1_proof, theirpub1_proof_len) != 1)
@@ -363,6 +368,8 @@ jpake_key_confirm(struct modp_group *grp, BIGNUM *s, BIGNUM *step2_val,
        /* Validate step 2 values */
        if (BN_cmp(step2_val, BN_value_one()) <= 0)
                fatal("%s: step2_val <= 1", __func__);
+       if (BN_cmp(step2_val, grp->p) >= 0)
+               fatal("%s: step2_val >= p", __func__);
 
        /*
         * theirpriv2_s_proof is calculated with a different generator:
index 148cfee..c65e28f 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: kex.c,v 1.82 2009/10/24 11:13:54 andreas Exp $ */
+/* $OpenBSD: kex.c,v 1.86 2010/09/22 05:01:29 djm Exp $ */
 /*
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
  *
@@ -62,6 +62,34 @@ extern const EVP_MD *evp_ssh_sha256(void);
 static void kex_kexinit_finish(Kex *);
 static void kex_choose_conf(Kex *);
 
+/* Validate KEX method name list */
+int
+kex_names_valid(const char *names)
+{
+       char *s, *cp, *p;
+
+       if (names == NULL || strcmp(names, "") == 0)
+               return 0;
+       s = cp = xstrdup(names);
+       for ((p = strsep(&cp, ",")); p && *p != '\0';
+           (p = strsep(&cp, ","))) {
+               if (strcmp(p, KEX_DHGEX_SHA256) != 0 &&
+                   strcmp(p, KEX_DHGEX_SHA1) != 0 &&
+                   strcmp(p, KEX_DH14) != 0 &&
+                   strcmp(p, KEX_DH1) != 0 &&
+                   (strncmp(p, KEX_ECDH_SHA2_STEM,
+                   sizeof(KEX_ECDH_SHA2_STEM) - 1) != 0 ||
+                   kex_ecdh_name_to_nid(p) == -1)) {
+                       error("Unsupported KEX algorithm \"%.100s\"", p);
+                       xfree(s);
+                       return 0;
+               }
+       }
+       debug3("kex names ok: [%s]", names);
+       xfree(s);
+       return 1;
+}
+
 /* put algorithm proposal into buffer */
 static void
 kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX])
@@ -98,7 +126,7 @@ kex_buf2prop(Buffer *raw, int *first_kex_follows)
                buffer_get_char(&b);
        /* extract kex init proposal strings */
        for (i = 0; i < PROPOSAL_MAX; i++) {
-               proposal[i] = buffer_get_string(&b,NULL);
+               proposal[i] = buffer_get_cstring(&b,NULL);
                debug2("kex_parse_kexinit: %s", proposal[i]);
        }
        /* first kex follows / reserved */
@@ -325,6 +353,10 @@ choose_kex(Kex *k, char *client, char *server)
        } else if (strcmp(k->name, KEX_DHGEX_SHA256) == 0) {
                k->kex_type = KEX_DH_GEX_SHA256;
                k->evp_md = evp_ssh_sha256();
+       } else if (strncmp(k->name, KEX_ECDH_SHA2_STEM,
+           sizeof(KEX_ECDH_SHA2_STEM) - 1) == 0) {
+               k->kex_type = KEX_ECDH_SHA2;
+               k->evp_md = kex_ecdh_name_to_evpmd(k->name);
 #endif
        } else
                fatal("bad kex alg %s", k->name);
@@ -559,11 +591,11 @@ derive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus,
        memset(&md, 0, sizeof(md));
 }
 
-#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH)
+#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
 void
 dump_digest(char *msg, u_char *digest, int len)
 {
-       u_int i;
+       int i;
 
        fprintf(stderr, "%s\n", msg);
        for (i = 0; i < len; i++) {
index 62fa2ea..7373d3c 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: kex.h,v 1.49 2010/02/26 20:29:54 djm Exp $ */
+/* $OpenBSD: kex.h,v 1.52 2010/09/22 05:01:29 djm Exp $ */
 
 /*
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
@@ -29,6 +29,9 @@
 #include <signal.h>
 #include <openssl/evp.h>
 #include <openssl/hmac.h>
+#ifdef OPENSSL_HAS_ECC
+#include <openssl/ec.h>
+#endif
 
 #define KEX_COOKIE_LEN 16
 
@@ -37,6 +40,8 @@
 #define        KEX_DHGEX_SHA1          "diffie-hellman-group-exchange-sha1"
 #define        KEX_DHGEX_SHA256        "diffie-hellman-group-exchange-sha256"
 #define        KEX_RESUME              "resume@appgate.com"
+/* The following represents the family of ECDH methods */
+#define        KEX_ECDH_SHA2_STEM      "ecdh-sha2-"
 
 #define COMP_NONE      0
 #define COMP_ZLIB      1
@@ -67,6 +72,7 @@ enum kex_exchange {
        KEX_DH_GRP14_SHA1,
        KEX_DH_GEX_SHA1,
        KEX_DH_GEX_SHA256,
+       KEX_ECDH_SHA2,
        KEX_MAX
 };
 
@@ -132,6 +138,8 @@ struct Kex {
        void    (*kex[KEX_MAX])(Kex *);
 };
 
+int     kex_names_valid(const char *);
+
 Kex    *kex_setup(char *[PROPOSAL_MAX]);
 void    kex_finish(Kex *);
 
@@ -145,6 +153,8 @@ void         kexdh_client(Kex *);
 void    kexdh_server(Kex *);
 void    kexgex_client(Kex *);
 void    kexgex_server(Kex *);
+void    kexecdh_client(Kex *);
+void    kexecdh_server(Kex *);
 
 void
 kex_dh_hash(char *, char *, char *, int, char *, int, u_char *, int,
@@ -153,11 +163,22 @@ void
 kexgex_hash(const EVP_MD *, char *, char *, char *, int, char *,
     int, u_char *, int, int, int, int, BIGNUM *, BIGNUM *, BIGNUM *,
     BIGNUM *, BIGNUM *, u_char **, u_int *);
+#ifdef OPENSSL_HAS_ECC
+void
+kex_ecdh_hash(const EVP_MD *, const EC_GROUP *, char *, char *, char *, int,
+    char *, int, u_char *, int, const EC_POINT *, const EC_POINT *,
+    const BIGNUM *, u_char **, u_int *);
+int    kex_ecdh_name_to_nid(const char *);
+const EVP_MD *kex_ecdh_name_to_evpmd(const char *);
+#else
+# define kex_ecdh_name_to_nid(x) (-1)
+# define kex_ecdh_name_to_evpmd(x) (NULL)
+#endif
 
 void
 derive_ssh1_session_id(BIGNUM *, BIGNUM *, u_int8_t[8], u_int8_t[16]);
 
-#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH)
+#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
 void   dump_digest(char *, u_char *, int);
 #endif
 
index d384c80..76ceb5d 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: kexdhc.c,v 1.11 2006/11/06 21:25:28 markus Exp $ */
+/* $OpenBSD: kexdhc.c,v 1.12 2010/11/10 01:33:07 djm Exp $ */
 /*
  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
  *
@@ -27,6 +27,8 @@
 
 #include <sys/types.h>
 
+#include <openssl/dh.h>
+
 #include <stdarg.h>
 #include <stdio.h>
 #include <string.h>
index e722877..f56e887 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: kexdhs.c,v 1.11 2010/02/26 20:29:54 djm Exp $ */
+/* $OpenBSD: kexdhs.c,v 1.12 2010/11/10 01:33:07 djm Exp $ */
 /*
  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
  *
@@ -31,6 +31,8 @@
 #include <string.h>
 #include <signal.h>
 
+#include <openssl/dh.h>
+
 #include "xmalloc.h"
 #include "buffer.h"
 #include "key.h"
diff --git a/crypto/openssh/kexecdh.c b/crypto/openssh/kexecdh.c
new file mode 100644 (file)
index 0000000..f13f69d
--- /dev/null
@@ -0,0 +1,117 @@
+/* $OpenBSD: kexecdh.c,v 1.3 2010/09/22 05:01:29 djm Exp $ */
+/*
+ * Copyright (c) 2001 Markus Friedl.  All rights reserved.
+ * Copyright (c) 2010 Damien Miller.  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"
+
+#ifdef OPENSSL_HAS_ECC
+
+#include <sys/types.h>
+
+#include <signal.h>
+#include <string.h>
+
+#include <openssl/bn.h>
+#include <openssl/evp.h>
+#include <openssl/ec.h>
+#include <openssl/ecdh.h>
+
+#include "buffer.h"
+#include "ssh2.h"
+#include "key.h"
+#include "cipher.h"
+#include "kex.h"
+#include "log.h"
+
+int
+kex_ecdh_name_to_nid(const char *kexname)
+{
+       if (strlen(kexname) < sizeof(KEX_ECDH_SHA2_STEM) - 1)
+               fatal("%s: kexname too short \"%s\"", __func__, kexname);
+       return key_curve_name_to_nid(kexname + sizeof(KEX_ECDH_SHA2_STEM) - 1);
+}
+
+const EVP_MD *
+kex_ecdh_name_to_evpmd(const char *kexname)
+{
+       int nid = kex_ecdh_name_to_nid(kexname);
+
+       if (nid == -1)
+               fatal("%s: unsupported ECDH curve \"%s\"", __func__, kexname);
+       return key_ec_nid_to_evpmd(nid);
+}
+
+void
+kex_ecdh_hash(
+    const EVP_MD *evp_md,
+    const EC_GROUP *ec_group,
+    char *client_version_string,
+    char *server_version_string,
+    char *ckexinit, int ckexinitlen,
+    char *skexinit, int skexinitlen,
+    u_char *serverhostkeyblob, int sbloblen,
+    const EC_POINT *client_dh_pub,
+    const EC_POINT *server_dh_pub,
+    const BIGNUM *shared_secret,
+    u_char **hash, u_int *hashlen)
+{
+       Buffer b;
+       EVP_MD_CTX md;
+       static u_char digest[EVP_MAX_MD_SIZE];
+
+       buffer_init(&b);
+       buffer_put_cstring(&b, client_version_string);
+       buffer_put_cstring(&b, server_version_string);
+
+       /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */
+       buffer_put_int(&b, ckexinitlen+1);
+       buffer_put_char(&b, SSH2_MSG_KEXINIT);
+       buffer_append(&b, ckexinit, ckexinitlen);
+       buffer_put_int(&b, skexinitlen+1);
+       buffer_put_char(&b, SSH2_MSG_KEXINIT);
+       buffer_append(&b, skexinit, skexinitlen);
+
+       buffer_put_string(&b, serverhostkeyblob, sbloblen);
+       buffer_put_ecpoint(&b, ec_group, client_dh_pub);
+       buffer_put_ecpoint(&b, ec_group, server_dh_pub);
+       buffer_put_bignum2(&b, shared_secret);
+
+#ifdef DEBUG_KEX
+       buffer_dump(&b);
+#endif
+       EVP_DigestInit(&md, evp_md);
+       EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
+       EVP_DigestFinal(&md, digest, NULL);
+
+       buffer_free(&b);
+
+#ifdef DEBUG_KEX
+       dump_digest("hash", digest, EVP_MD_size(evp_md));
+#endif
+       *hash = digest;
+       *hashlen = EVP_MD_size(evp_md);
+}
+
+#endif /* OPENSSL_HAS_ECC */
similarity index 60%
copy from crypto/openssh/kexdhc.c
copy to crypto/openssh/kexecdhc.c
index d384c80..115d4bf 100644 (file)
@@ -1,6 +1,7 @@
-/* $OpenBSD: kexdhc.c,v 1.11 2006/11/06 21:25:28 markus Exp $ */
+/* $OpenBSD: kexecdhc.c,v 1.2 2010/09/22 05:01:29 djm Exp $ */
 /*
  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
+ * Copyright (c) 2010 Damien Miller.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -27,7 +28,6 @@
 
 #include <sys/types.h>
 
-#include <stdarg.h>
 #include <stdio.h>
 #include <string.h>
 #include <signal.h>
 #include "dh.h"
 #include "ssh2.h"
 
+#ifdef OPENSSL_HAS_ECC
+
+#include <openssl/ecdh.h>
+
 void
-kexdh_client(Kex *kex)
+kexecdh_client(Kex *kex)
 {
-       BIGNUM *dh_server_pub = NULL, *shared_secret = NULL;
-       DH *dh;
+       EC_KEY *client_key;
+       EC_POINT *server_public;
+       const EC_GROUP *group;
+       BIGNUM *shared_secret;
        Key *server_host_key;
        u_char *server_host_key_blob = NULL, *signature = NULL;
        u_char *kbuf, *hash;
        u_int klen, slen, sbloblen, hashlen;
-       int kout;
-
-       /* generate and send 'e', client DH public key */
-       switch (kex->kex_type) {
-       case KEX_DH_GRP1_SHA1:
-               dh = dh_new_group1();
-               break;
-       case KEX_DH_GRP14_SHA1:
-               dh = dh_new_group14();
-               break;
-       default:
-               fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
-       }
-       dh_gen_key(dh, kex->we_need * 8);
-       packet_start(SSH2_MSG_KEXDH_INIT);
-       packet_put_bignum2(dh->pub_key);
+       int curve_nid;
+
+       if ((curve_nid = kex_ecdh_name_to_nid(kex->name)) == -1)
+               fatal("%s: unsupported ECDH curve \"%s\"", __func__, kex->name);
+       if ((client_key = EC_KEY_new_by_curve_name(curve_nid)) == NULL)
+               fatal("%s: EC_KEY_new_by_curve_name failed", __func__);
+       if (EC_KEY_generate_key(client_key) != 1)
+               fatal("%s: EC_KEY_generate_key failed", __func__);
+       group = EC_KEY_get0_group(client_key);
+
+       packet_start(SSH2_MSG_KEX_ECDH_INIT);
+       packet_put_ecpoint(group, EC_KEY_get0_public_key(client_key));
        packet_send();
+       debug("sending SSH2_MSG_KEX_ECDH_INIT");
 
-       debug("sending SSH2_MSG_KEXDH_INIT");
-#ifdef DEBUG_KEXDH
-       DHparams_print_fp(stderr, dh);
-       fprintf(stderr, "pub= ");
-       BN_print_fp(stderr, dh->pub_key);
-       fprintf(stderr, "\n");
+#ifdef DEBUG_KEXECDH
+       fputs("client private key:\n", stderr);
+       key_dump_ec_key(client_key);
 #endif
 
-       debug("expecting SSH2_MSG_KEXDH_REPLY");
-       packet_read_expect(SSH2_MSG_KEXDH_REPLY);
+       debug("expecting SSH2_MSG_KEX_ECDH_REPLY");
+       packet_read_expect(SSH2_MSG_KEX_ECDH_REPLY);
 
-       /* key, cert */
+       /* hostkey */
        server_host_key_blob = packet_get_string(&sbloblen);
        server_host_key = key_from_blob(server_host_key_blob, sbloblen);
        if (server_host_key == NULL)
@@ -92,54 +92,56 @@ kexdh_client(Kex *kex)
        if (kex->verify_host_key(server_host_key) == -1)
                fatal("server_host_key verification failed");
 
-       /* DH parameter f, server public DH key */
-       if ((dh_server_pub = BN_new()) == NULL)
-               fatal("dh_server_pub == NULL");
-       packet_get_bignum2(dh_server_pub);
+       /* Q_S, server public key */
+       if ((server_public = EC_POINT_new(group)) == NULL)
+               fatal("%s: EC_POINT_new failed", __func__);
+       packet_get_ecpoint(group, server_public);
 
-#ifdef DEBUG_KEXDH
-       fprintf(stderr, "dh_server_pub= ");
-       BN_print_fp(stderr, dh_server_pub);
-       fprintf(stderr, "\n");
-       debug("bits %d", BN_num_bits(dh_server_pub));
+       if (key_ec_validate_public(group, server_public) != 0)
+               fatal("%s: invalid server public key", __func__);
+
+#ifdef DEBUG_KEXECDH
+       fputs("server public key:\n", stderr);
+       key_dump_ec_point(group, server_public);
 #endif
 
        /* signed H */
        signature = packet_get_string(&slen);
        packet_check_eom();
 
-       if (!dh_pub_is_valid(dh, dh_server_pub))
-               packet_disconnect("bad server public DH value");
-
-       klen = DH_size(dh);
+       klen = (EC_GROUP_get_degree(group) + 7) / 8;
        kbuf = xmalloc(klen);
-       if ((kout = DH_compute_key(kbuf, dh_server_pub, dh)) < 0)
-               fatal("DH_compute_key: failed");
-#ifdef DEBUG_KEXDH
-       dump_digest("shared secret", kbuf, kout);
+       if (ECDH_compute_key(kbuf, klen, server_public,
+           client_key, NULL) != (int)klen)
+               fatal("%s: ECDH_compute_key failed", __func__);
+
+#ifdef DEBUG_KEXECDH
+       dump_digest("shared secret", kbuf, klen);
 #endif
        if ((shared_secret = BN_new()) == NULL)
-               fatal("kexdh_client: BN_new failed");
-       if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
-               fatal("kexdh_client: BN_bin2bn failed");
+               fatal("%s: BN_new failed", __func__);
+       if (BN_bin2bn(kbuf, klen, shared_secret) == NULL)
+               fatal("%s: BN_bin2bn failed", __func__);
        memset(kbuf, 0, klen);
        xfree(kbuf);
 
        /* calc and verify H */
-       kex_dh_hash(
+       kex_ecdh_hash(
+           kex->evp_md,
+           group,
            kex->client_version_string,
            kex->server_version_string,
            buffer_ptr(&kex->my), buffer_len(&kex->my),
            buffer_ptr(&kex->peer), buffer_len(&kex->peer),
            server_host_key_blob, sbloblen,
-           dh->pub_key,
-           dh_server_pub,
+           EC_KEY_get0_public_key(client_key),
+           server_public,
            shared_secret,
            &hash, &hashlen
        );
        xfree(server_host_key_blob);
-       BN_clear_free(dh_server_pub);
-       DH_free(dh);
+       EC_POINT_clear_free(server_public);
+       EC_KEY_free(client_key);
 
        if (key_verify(server_host_key, signature, slen, hash, hashlen) != 1)
                fatal("key_verify failed for server_host_key");
@@ -157,3 +159,10 @@ kexdh_client(Kex *kex)
        BN_clear_free(shared_secret);
        kex_finish(kex);
 }
+#else /* OPENSSL_HAS_ECC */
+void
+kexecdh_client(Kex *kex)
+{
+       fatal("ECC support is not enabled");
+}
+#endif /* OPENSSL_HAS_ECC */
similarity index 57%
copy from crypto/openssh/kexdhs.c
copy to crypto/openssh/kexecdhs.c
index e722877..8c515df 100644 (file)
@@ -1,6 +1,7 @@
-/* $OpenBSD: kexdhs.c,v 1.11 2010/02/26 20:29:54 djm Exp $ */
+/* $OpenBSD: kexecdhs.c,v 1.2 2010/09/22 05:01:29 djm Exp $ */
 /*
  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
+ * Copyright (c) 2010 Damien Miller.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -26,8 +27,6 @@
 #include "includes.h"
 
 #include <sys/types.h>
-
-#include <stdarg.h>
 #include <string.h>
 #include <signal.h>
 
 #endif
 #include "monitor_wrap.h"
 
+#ifdef OPENSSL_HAS_ECC
+
+#include <openssl/ecdh.h>
+
 void
-kexdh_server(Kex *kex)
+kexecdh_server(Kex *kex)
 {
-       BIGNUM *shared_secret = NULL, *dh_client_pub = NULL;
-       DH *dh;
-       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;
-
-       /* generate server DH public key */
-       switch (kex->kex_type) {
-       case KEX_DH_GRP1_SHA1:
-               dh = dh_new_group1();
-               break;
-       case KEX_DH_GRP14_SHA1:
-               dh = dh_new_group14();
-               break;
-       default:
-               fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
-       }
-       dh_gen_key(dh, kex->we_need * 8);
-
-       debug("expecting SSH2_MSG_KEXDH_INIT");
-       packet_read_expect(SSH2_MSG_KEXDH_INIT);
+       EC_POINT *client_public;
+       EC_KEY *server_key;
+       const EC_GROUP *group;
+       BIGNUM *shared_secret;
+       Key *server_host_private, *server_host_public;
+       u_char *server_host_key_blob = NULL, *signature = NULL;
+       u_char *kbuf, *hash;
+       u_int klen, slen, sbloblen, hashlen;
+       int curve_nid;
+
+       if ((curve_nid = kex_ecdh_name_to_nid(kex->name)) == -1)
+               fatal("%s: unsupported ECDH curve \"%s\"", __func__, kex->name);
+       if ((server_key = EC_KEY_new_by_curve_name(curve_nid)) == NULL)
+               fatal("%s: EC_KEY_new_by_curve_name failed", __func__);
+       if (EC_KEY_generate_key(server_key) != 1)
+               fatal("%s: EC_KEY_generate_key failed", __func__);
+       group = EC_KEY_get0_group(server_key);
+
+#ifdef DEBUG_KEXECDH
+       fputs("server private key:\n", stderr);
+       key_dump_ec_key(server_key);
+#endif
 
        if (kex->load_host_public_key == NULL ||
            kex->load_host_private_key == NULL)
@@ -82,57 +85,54 @@ kexdh_server(Kex *kex)
                fatal("Missing private key for hostkey type %d",
                    kex->hostkey_type);
 
-       /* key, cert */
-       if ((dh_client_pub = BN_new()) == NULL)
-               fatal("dh_client_pub == NULL");
-       packet_get_bignum2(dh_client_pub);
+       debug("expecting SSH2_MSG_KEX_ECDH_INIT");
+       packet_read_expect(SSH2_MSG_KEX_ECDH_INIT);
+       if ((client_public = EC_POINT_new(group)) == NULL)
+               fatal("%s: EC_POINT_new failed", __func__);
+       packet_get_ecpoint(group, client_public);
        packet_check_eom();
 
-#ifdef DEBUG_KEXDH
-       fprintf(stderr, "dh_client_pub= ");
-       BN_print_fp(stderr, dh_client_pub);
-       fprintf(stderr, "\n");
-       debug("bits %d", BN_num_bits(dh_client_pub));
-#endif
+       if (key_ec_validate_public(group, client_public) != 0)
+               fatal("%s: invalid client public key", __func__);
 
-#ifdef DEBUG_KEXDH
-       DHparams_print_fp(stderr, dh);
-       fprintf(stderr, "pub= ");
-       BN_print_fp(stderr, dh->pub_key);
-       fprintf(stderr, "\n");
+#ifdef DEBUG_KEXECDH
+       fputs("client public key:\n", stderr);
+       key_dump_ec_point(group, client_public);
 #endif
-       if (!dh_pub_is_valid(dh, dh_client_pub))
-               packet_disconnect("bad client public DH value");
 
-       klen = DH_size(dh);
+       /* Calculate shared_secret */
+       klen = (EC_GROUP_get_degree(group) + 7) / 8;
        kbuf = xmalloc(klen);
-       if ((kout = DH_compute_key(kbuf, dh_client_pub, dh)) < 0)
-               fatal("DH_compute_key: failed");
+       if (ECDH_compute_key(kbuf, klen, client_public,
+           server_key, NULL) != (int)klen)
+               fatal("%s: ECDH_compute_key failed", __func__);
+
 #ifdef DEBUG_KEXDH
-       dump_digest("shared secret", kbuf, kout);
+       dump_digest("shared secret", kbuf, klen);
 #endif
        if ((shared_secret = BN_new()) == NULL)
-               fatal("kexdh_server: BN_new failed");
-       if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
-               fatal("kexdh_server: BN_bin2bn failed");
+               fatal("%s: BN_new failed", __func__);
+       if (BN_bin2bn(kbuf, klen, shared_secret) == NULL)
+               fatal("%s: BN_bin2bn failed", __func__);
        memset(kbuf, 0, klen);
        xfree(kbuf);
 
-       key_to_blob(server_host_public, &server_host_key_blob, &sbloblen);
-
        /* calc H */
-       kex_dh_hash(
+       key_to_blob(server_host_public, &server_host_key_blob, &sbloblen);
+       kex_ecdh_hash(
+           kex->evp_md,
+           group,
            kex->client_version_string,
            kex->server_version_string,
            buffer_ptr(&kex->peer), buffer_len(&kex->peer),
            buffer_ptr(&kex->my), buffer_len(&kex->my),
            server_host_key_blob, sbloblen,
-           dh_client_pub,
-           dh->pub_key,
+           client_public,
+           EC_KEY_get0_public_key(server_key),
            shared_secret,
            &hash, &hashlen
        );
-       BN_clear_free(dh_client_pub);
+       EC_POINT_clear_free(client_public);
 
        /* save session id := H */
        if (kex->session_id == NULL) {
@@ -142,25 +142,32 @@ kexdh_server(Kex *kex)
        }
 
        /* sign H */
-       if (PRIVSEP(key_sign(server_host_private, &signature, &slen, hash,
-           hashlen)) < 0)
+       if (PRIVSEP(key_sign(server_host_private, &signature, &slen,
+           hash, hashlen)) < 0)
                fatal("kexdh_server: key_sign failed");
 
        /* destroy_sensitive_data(); */
 
-       /* send server hostkey, DH pubkey 'f' and singed H */
-       packet_start(SSH2_MSG_KEXDH_REPLY);
+       /* send server hostkey, ECDH pubkey 'Q_S' and signed H */
+       packet_start(SSH2_MSG_KEX_ECDH_REPLY);
        packet_put_string(server_host_key_blob, sbloblen);
-       packet_put_bignum2(dh->pub_key);        /* f */
+       packet_put_ecpoint(group, EC_KEY_get0_public_key(server_key));
        packet_put_string(signature, slen);
        packet_send();
 
        xfree(signature);
        xfree(server_host_key_blob);
-       /* have keys, free DH */
-       DH_free(dh);
+       /* have keys, free server key */
+       EC_KEY_free(server_key);
 
        kex_derive_keys(kex, hash, hashlen, shared_secret);
        BN_clear_free(shared_secret);
        kex_finish(kex);
 }
+#else /* OPENSSL_HAS_ECC */
+void
+kexecdh_server(Kex *kex)
+{
+       fatal("ECC support is not enabled");
+}
+#endif /* OPENSSL_HAS_ECC */
index adb973d..79552d7 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: kexgexc.c,v 1.11 2006/11/06 21:25:28 markus Exp $ */
+/* $OpenBSD: kexgexc.c,v 1.12 2010/11/10 01:33:07 djm Exp $ */
 /*
  * Copyright (c) 2000 Niels Provos.  All rights reserved.
  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
@@ -28,6 +28,8 @@
 
 #include <sys/types.h>
 
+#include <openssl/dh.h>
+
 #include <stdarg.h>
 #include <stdio.h>
 #include <string.h>
index f4156af..a5e3df7 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: kexgexs.c,v 1.13 2010/02/26 20:29:54 djm Exp $ */
+/* $OpenBSD: kexgexs.c,v 1.14 2010/11/10 01:33:07 djm Exp $ */
 /*
  * Copyright (c) 2000 Niels Provos.  All rights reserved.
  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
@@ -33,6 +33,8 @@
 #include <string.h>
 #include <signal.h>
 
+#include <openssl/dh.h>
+
 #include "xmalloc.h"
 #include "buffer.h"
 #include "key.h"
index e4aa25c..e3a305e 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: key.c,v 1.90 2010/07/13 23:13:16 djm Exp $ */
+/* $OpenBSD: key.c,v 1.96 2011/02/04 00:44:21 djm Exp $ */
 /*
  * read_bignum():
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -78,6 +78,8 @@ key_new(int type)
        DSA *dsa;
        k = xcalloc(1, sizeof(*k));
        k->type = type;
+       k->ecdsa = NULL;
+       k->ecdsa_nid = -1;
        k->dsa = NULL;
        k->rsa = NULL;
        k->cert = NULL;
@@ -109,6 +111,12 @@ key_new(int type)
                        fatal("key_new: BN_new failed");
                k->dsa = dsa;
                break;
+#ifdef OPENSSL_HAS_ECC
+       case KEY_ECDSA:
+       case KEY_ECDSA_CERT:
+               /* Cannot do anything until we know the group */
+               break;
+#endif
        case KEY_UNSPEC:
                break;
        default:
@@ -149,6 +157,10 @@ key_add_private(Key *k)
                if ((k->dsa->priv_key = BN_new()) == NULL)
                        fatal("key_new_private: BN_new failed");
                break;
+       case KEY_ECDSA:
+       case KEY_ECDSA_CERT:
+               /* Cannot do anything until we know the group */
+               break;
        case KEY_UNSPEC:
                break;
        default:
@@ -204,6 +216,14 @@ key_free(Key *k)
                        DSA_free(k->dsa);
                k->dsa = NULL;
                break;
+#ifdef OPENSSL_HAS_ECC
+       case KEY_ECDSA:
+       case KEY_ECDSA_CERT:
+               if (k->ecdsa != NULL)
+                       EC_KEY_free(k->ecdsa);
+               k->ecdsa = NULL;
+               break;
+#endif
        case KEY_UNSPEC:
                break;
        default:
@@ -241,6 +261,10 @@ cert_compare(struct KeyCert *a, struct KeyCert *b)
 int
 key_equal_public(const Key *a, const Key *b)
 {
+#ifdef OPENSSL_HAS_ECC
+       BN_CTX *bnctx;
+#endif
+
        if (a == NULL || b == NULL ||
            key_type_plain(a->type) != key_type_plain(b->type))
                return 0;
@@ -261,6 +285,26 @@ key_equal_public(const Key *a, const Key *b)
                    BN_cmp(a->dsa->q, b->dsa->q) == 0 &&
                    BN_cmp(a->dsa->g, b->dsa->g) == 0 &&
                    BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0;
+#ifdef OPENSSL_HAS_ECC
+       case KEY_ECDSA_CERT:
+       case KEY_ECDSA:
+               if (a->ecdsa == NULL || b->ecdsa == NULL ||
+                   EC_KEY_get0_public_key(a->ecdsa) == NULL ||
+                   EC_KEY_get0_public_key(b->ecdsa) == NULL)
+                       return 0;
+               if ((bnctx = BN_CTX_new()) == NULL)
+                       fatal("%s: BN_CTX_new failed", __func__);
+               if (EC_GROUP_cmp(EC_KEY_get0_group(a->ecdsa),
+                   EC_KEY_get0_group(b->ecdsa), bnctx) != 0 ||
+                   EC_POINT_cmp(EC_KEY_get0_group(a->ecdsa),
+                   EC_KEY_get0_public_key(a->ecdsa),
+                   EC_KEY_get0_public_key(b->ecdsa), bnctx) != 0) {
+                       BN_CTX_free(bnctx);
+                       return 0;
+               }
+               BN_CTX_free(bnctx);
+               return 1;
+#endif /* OPENSSL_HAS_ECC */
        default:
                fatal("key_equal: bad key type %d", a->type);
        }
@@ -312,12 +356,14 @@ key_fingerprint_raw(Key *k, enum fp_type dgst_type, u_int *dgst_raw_length)
                BN_bn2bin(k->rsa->e, blob + nlen);
                break;
        case KEY_DSA:
+       case KEY_ECDSA:
        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_ECDSA_CERT:
        case KEY_RSA_CERT:
                /* We want a fingerprint of the _key_ not of the cert */
                otype = k->type;
@@ -615,6 +661,9 @@ key_read(Key *ret, char **cpp)
        int len, n, type;
        u_int bits;
        u_char *blob;
+#ifdef OPENSSL_HAS_ECC
+       int curve_nid = -1;
+#endif
 
        cp = *cpp;
 
@@ -644,9 +693,11 @@ key_read(Key *ret, char **cpp)
        case KEY_UNSPEC:
        case KEY_RSA:
        case KEY_DSA:
+       case KEY_ECDSA:
        case KEY_DSA_CERT_V00:
        case KEY_RSA_CERT_V00:
        case KEY_DSA_CERT:
+       case KEY_ECDSA_CERT:
        case KEY_RSA_CERT:
                space = strchr(cp, ' ');
                if (space == NULL) {
@@ -655,6 +706,13 @@ key_read(Key *ret, char **cpp)
                }
                *space = '\0';
                type = key_type_from_name(cp);
+#ifdef OPENSSL_HAS_ECC
+               if (key_type_plain(type) == KEY_ECDSA &&
+                   (curve_nid = key_ecdsa_nid_from_name(cp)) == -1) {
+                       debug("key_read: invalid curve");
+                       return -1;
+               }
+#endif
                *space = ' ';
                if (type == KEY_UNSPEC) {
                        debug3("key_read: missing keytype");
@@ -691,6 +749,14 @@ key_read(Key *ret, char **cpp)
                        key_free(k);
                        return -1;
                }
+#ifdef OPENSSL_HAS_ECC
+               if (key_type_plain(type) == KEY_ECDSA &&
+                   curve_nid != k->ecdsa_nid) {
+                       error("key_read: type mismatch: EC curve mismatch");
+                       key_free(k);
+                       return -1;
+               }
+#endif
 /*XXXX*/
                if (key_is_cert(ret)) {
                        if (!key_is_cert(k)) {
@@ -721,6 +787,19 @@ key_read(Key *ret, char **cpp)
                        DSA_print_fp(stderr, ret->dsa, 8);
 #endif
                }
+#ifdef OPENSSL_HAS_ECC
+               if (key_type_plain(ret->type) == KEY_ECDSA) {
+                       if (ret->ecdsa != NULL)
+                               EC_KEY_free(ret->ecdsa);
+                       ret->ecdsa = k->ecdsa;
+                       ret->ecdsa_nid = k->ecdsa_nid;
+                       k->ecdsa = NULL;
+                       k->ecdsa_nid = -1;
+#ifdef DEBUG_PK
+                       key_dump_ec_key(ret->ecdsa);
+#endif
+               }
+#endif
                success = 1;
 /*XXXX*/
                key_free(k);
@@ -777,6 +856,13 @@ key_write(const Key *key, FILE *f)
                if (key->dsa == NULL)
                        return 0;
                break;
+#ifdef OPENSSL_HAS_ECC
+       case KEY_ECDSA:
+       case KEY_ECDSA_CERT:
+               if (key->ecdsa == NULL)
+                       return 0;
+               break;
+#endif
        case KEY_RSA:
        case KEY_RSA_CERT_V00:
        case KEY_RSA_CERT:
@@ -810,6 +896,10 @@ key_type(const Key *k)
                return "RSA";
        case KEY_DSA:
                return "DSA";
+#ifdef OPENSSL_HAS_ECC
+       case KEY_ECDSA:
+               return "ECDSA";
+#endif
        case KEY_RSA_CERT_V00:
                return "RSA-CERT-V00";
        case KEY_DSA_CERT_V00:
@@ -818,6 +908,10 @@ key_type(const Key *k)
                return "RSA-CERT";
        case KEY_DSA_CERT:
                return "DSA-CERT";
+#ifdef OPENSSL_HAS_ECC
+       case KEY_ECDSA_CERT:
+               return "ECDSA-CERT";
+#endif
        }
        return "unknown";
 }
@@ -835,10 +929,10 @@ key_cert_type(const Key *k)
        }
 }
 
-const char *
-key_ssh_name(const Key *k)
+static const char *
+key_ssh_name_from_type_nid(int type, int nid)
 {
-       switch (k->type) {
+       switch (type) {
        case KEY_RSA:
                return "ssh-rsa";
        case KEY_DSA:
@@ -851,10 +945,49 @@ key_ssh_name(const Key *k)
                return "ssh-rsa-cert-v01@openssh.com";
        case KEY_DSA_CERT:
                return "ssh-dss-cert-v01@openssh.com";
+#ifdef OPENSSL_HAS_ECC
+       case KEY_ECDSA:
+               switch (nid) {
+               case NID_X9_62_prime256v1:
+                       return "ecdsa-sha2-nistp256";
+               case NID_secp384r1:
+                       return "ecdsa-sha2-nistp384";
+               case NID_secp521r1:
+                       return "ecdsa-sha2-nistp521";
+               default:
+                       break;
+               }
+               break;
+       case KEY_ECDSA_CERT:
+               switch (nid) {
+               case NID_X9_62_prime256v1:
+                       return "ecdsa-sha2-nistp256-cert-v01@openssh.com";
+               case NID_secp384r1:
+                       return "ecdsa-sha2-nistp384-cert-v01@openssh.com";
+               case NID_secp521r1:
+                       return "ecdsa-sha2-nistp521-cert-v01@openssh.com";
+               default:
+                       break;
+               }
+               break;
+#endif /* OPENSSL_HAS_ECC */
        }
        return "ssh-unknown";
 }
 
+const char *
+key_ssh_name(const Key *k)
+{
+       return key_ssh_name_from_type_nid(k->type, k->ecdsa_nid);
+}
+
+const char *
+key_ssh_name_plain(const Key *k)
+{
+       return key_ssh_name_from_type_nid(key_type_plain(k->type),
+           k->ecdsa_nid);
+}
+
 u_int
 key_size(const Key *k)
 {
@@ -868,6 +1001,11 @@ key_size(const Key *k)
        case KEY_DSA_CERT_V00:
        case KEY_DSA_CERT:
                return BN_num_bits(k->dsa->p);
+#ifdef OPENSSL_HAS_ECC
+       case KEY_ECDSA:
+       case KEY_ECDSA_CERT:
+               return key_curve_nid_to_bits(k->ecdsa_nid);
+#endif
        }
        return 0;
 }
@@ -875,28 +1013,116 @@ key_size(const Key *k)
 static RSA *
 rsa_generate_private_key(u_int bits)
 {
-       RSA *private;
+       RSA *private = RSA_new();
+       BIGNUM *f4 = BN_new();
 
-       private = RSA_generate_key(bits, RSA_F4, NULL, NULL);
        if (private == NULL)
-               fatal("rsa_generate_private_key: key generation failed.");
+               fatal("%s: RSA_new failed", __func__);
+       if (f4 == NULL)
+               fatal("%s: BN_new failed", __func__);
+       if (!BN_set_word(f4, RSA_F4))
+               fatal("%s: BN_new failed", __func__);
+       if (!RSA_generate_key_ex(private, bits, f4, NULL))
+               fatal("%s: key generation failed.", __func__);
+       BN_free(f4);
        return private;
 }
 
 static DSA*
 dsa_generate_private_key(u_int bits)
 {
-       DSA *private = DSA_generate_parameters(bits, NULL, 0, NULL, NULL, NULL, NULL);
+       DSA *private = DSA_new();
 
        if (private == NULL)
-               fatal("dsa_generate_private_key: DSA_generate_parameters failed");
+               fatal("%s: DSA_new failed", __func__);
+       if (!DSA_generate_parameters_ex(private, bits, NULL, 0, NULL,
+           NULL, NULL))
+               fatal("%s: DSA_generate_parameters failed", __func__);
        if (!DSA_generate_key(private))
-               fatal("dsa_generate_private_key: DSA_generate_key failed.");
-       if (private == NULL)
-               fatal("dsa_generate_private_key: NULL.");
+               fatal("%s: DSA_generate_key failed.", __func__);
        return private;
 }
 
+int
+key_ecdsa_bits_to_nid(int bits)
+{
+       switch (bits) {
+#ifdef OPENSSL_HAS_ECC
+       case 256:
+               return NID_X9_62_prime256v1;
+       case 384:
+               return NID_secp384r1;
+       case 521:
+               return NID_secp521r1;
+#endif
+       default:
+               return -1;
+       }
+}
+
+#ifdef OPENSSL_HAS_ECC
+int
+key_ecdsa_key_to_nid(EC_KEY *k)
+{
+       EC_GROUP *eg;
+       int nids[] = {
+               NID_X9_62_prime256v1,
+               NID_secp384r1,
+               NID_secp521r1,
+               -1
+       };
+       int nid;
+       u_int i;
+       BN_CTX *bnctx;
+       const EC_GROUP *g = EC_KEY_get0_group(k);
+
+       /*
+        * The group may be stored in a ASN.1 encoded private key in one of two
+        * ways: as a "named group", which is reconstituted by ASN.1 object ID
+        * or explicit group parameters encoded into the key blob. Only the
+        * "named group" case sets the group NID for us, but we can figure
+        * it out for the other case by comparing against all the groups that
+        * are supported.
+        */
+       if ((nid = EC_GROUP_get_curve_name(g)) > 0)
+               return nid;
+       if ((bnctx = BN_CTX_new()) == NULL)
+               fatal("%s: BN_CTX_new() failed", __func__);
+       for (i = 0; nids[i] != -1; i++) {
+               if ((eg = EC_GROUP_new_by_curve_name(nids[i])) == NULL)
+                       fatal("%s: EC_GROUP_new_by_curve_name failed",
+                           __func__);
+               if (EC_GROUP_cmp(g, eg, bnctx) == 0)
+                       break;
+               EC_GROUP_free(eg);
+       }
+       BN_CTX_free(bnctx);
+       debug3("%s: nid = %d", __func__, nids[i]);
+       if (nids[i] != -1) {
+               /* Use the group with the NID attached */
+               EC_GROUP_set_asn1_flag(eg, OPENSSL_EC_NAMED_CURVE);
+               if (EC_KEY_set_group(k, eg) != 1)
+                       fatal("%s: EC_KEY_set_group", __func__);
+       }
+       return nids[i];
+}
+
+static EC_KEY*
+ecdsa_generate_private_key(u_int bits, int *nid)
+{
+       EC_KEY *private;
+
+       if ((*nid = key_ecdsa_bits_to_nid(bits)) == -1)
+               fatal("%s: invalid key length", __func__);
+       if ((private = EC_KEY_new_by_curve_name(*nid)) == NULL)
+               fatal("%s: EC_KEY_new_by_curve_name failed", __func__);
+       if (EC_KEY_generate_key(private) != 1)
+               fatal("%s: EC_KEY_generate_key failed", __func__);
+       EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE);
+       return private;
+}
+#endif /* OPENSSL_HAS_ECC */
+
 Key *
 key_generate(int type, u_int bits)
 {
@@ -905,6 +1131,11 @@ key_generate(int type, u_int bits)
        case KEY_DSA:
                k->dsa = dsa_generate_private_key(bits);
                break;
+#ifdef OPENSSL_HAS_ECC
+       case KEY_ECDSA:
+               k->ecdsa = ecdsa_generate_private_key(bits, &k->ecdsa_nid);
+               break;
+#endif
        case KEY_RSA:
        case KEY_RSA1:
                k->rsa = rsa_generate_private_key(bits);
@@ -981,6 +1212,18 @@ key_from_private(const Key *k)
                    (BN_copy(n->dsa->pub_key, k->dsa->pub_key) == NULL))
                        fatal("key_from_private: BN_copy failed");
                break;
+#ifdef OPENSSL_HAS_ECC
+       case KEY_ECDSA:
+       case KEY_ECDSA_CERT:
+               n = key_new(k->type);
+               n->ecdsa_nid = k->ecdsa_nid;
+               if ((n->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid)) == NULL)
+                       fatal("%s: EC_KEY_new_by_curve_name failed", __func__);
+               if (EC_KEY_set_public_key(n->ecdsa,
+                   EC_KEY_get0_public_key(k->ecdsa)) != 1)
+                       fatal("%s: EC_KEY_set_public_key failed", __func__);
+               break;
+#endif
        case KEY_RSA:
        case KEY_RSA1:
        case KEY_RSA_CERT_V00:
@@ -1012,6 +1255,13 @@ key_type_from_name(char *name)
                return KEY_RSA;
        } else if (strcmp(name, "ssh-dss") == 0) {
                return KEY_DSA;
+#ifdef OPENSSL_HAS_ECC
+       } else if (strcmp(name, "ecdsa") == 0 ||
+           strcmp(name, "ecdsa-sha2-nistp256") == 0 ||
+           strcmp(name, "ecdsa-sha2-nistp384") == 0 ||
+           strcmp(name, "ecdsa-sha2-nistp521") == 0) {
+               return KEY_ECDSA;
+#endif
        } 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) {
@@ -1020,11 +1270,37 @@ key_type_from_name(char *name)
                return KEY_RSA_CERT;
        } else if (strcmp(name, "ssh-dss-cert-v01@openssh.com") == 0) {
                return KEY_DSA_CERT;
+#ifdef OPENSSL_HAS_ECC
+       } else if (strcmp(name, "ecdsa-sha2-nistp256-cert-v01@openssh.com") == 0 ||
+           strcmp(name, "ecdsa-sha2-nistp384-cert-v01@openssh.com") == 0 ||
+           strcmp(name, "ecdsa-sha2-nistp521-cert-v01@openssh.com") == 0) {
+               return KEY_ECDSA_CERT;
+#endif
        }
+
        debug2("key_type_from_name: unknown key type '%s'", name);
        return KEY_UNSPEC;
 }
 
+int
+key_ecdsa_nid_from_name(const char *name)
+{
+#ifdef OPENSSL_HAS_ECC
+       if (strcmp(name, "ecdsa-sha2-nistp256") == 0 ||
+           strcmp(name, "ecdsa-sha2-nistp256-cert-v01@openssh.com") == 0)
+               return NID_X9_62_prime256v1;
+       if (strcmp(name, "ecdsa-sha2-nistp384") == 0 ||
+           strcmp(name, "ecdsa-sha2-nistp384-cert-v01@openssh.com") == 0)
+               return NID_secp384r1;
+       if (strcmp(name, "ecdsa-sha2-nistp521") == 0 ||
+           strcmp(name, "ecdsa-sha2-nistp521-cert-v01@openssh.com") == 0)
+               return NID_secp521r1;
+#endif /* OPENSSL_HAS_ECC */
+
+       debug2("%s: unknown/non-ECDSA key type '%s'", __func__, name);
+       return -1;
+}
+
 int
 key_names_valid2(const char *names)
 {
@@ -1067,7 +1343,7 @@ cert_parse(Buffer *b, Key *key, const u_char *blob, u_int blen)
        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 ||
+           (key->cert->key_id = buffer_get_cstring_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 ||
@@ -1105,15 +1381,10 @@ cert_parse(Buffer *b, Key *key, const u_char *blob, u_int blen)
                        error("%s: Too many principals", __func__);
                        goto out;
                }
-               if ((principal = buffer_get_string_ret(&tmp, &plen)) == NULL) {
+               if ((principal = buffer_get_cstring_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;
@@ -1151,7 +1422,8 @@ cert_parse(Buffer *b, Key *key, const u_char *blob, u_int blen)
                goto out;
        }
        if (key->cert->signature_key->type != KEY_RSA &&
-           key->cert->signature_key->type != KEY_DSA) {
+           key->cert->signature_key->type != KEY_DSA &&
+           key->cert->signature_key->type != KEY_ECDSA) {
                error("%s: Invalid signature key type %s (%d)", __func__,
                    key_type(key->cert->signature_key),
                    key->cert->signature_key->type);
@@ -1192,20 +1464,28 @@ key_from_blob(const u_char *blob, u_int blen)
 {
        Buffer b;
        int rlen, type;
-       char *ktype = NULL;
+       char *ktype = NULL, *curve = NULL;
        Key *key = NULL;
+#ifdef OPENSSL_HAS_ECC
+       EC_POINT *q = NULL;
+       int nid = -1;
+#endif
 
 #ifdef DEBUG_PK
        dump_base64(stderr, blob, blen);
 #endif
        buffer_init(&b);
        buffer_append(&b, blob, blen);
-       if ((ktype = buffer_get_string_ret(&b, NULL)) == NULL) {
+       if ((ktype = buffer_get_cstring_ret(&b, NULL)) == NULL) {
                error("key_from_blob: can't read key type");
                goto out;
        }
 
        type = key_type_from_name(ktype);
+#ifdef OPENSSL_HAS_ECC
+       if (key_type_plain(type) == KEY_ECDSA)
+               nid = key_ecdsa_nid_from_name(ktype);
+#endif
 
        switch (type) {
        case KEY_RSA_CERT:
@@ -1243,6 +1523,43 @@ key_from_blob(const u_char *blob, u_int blen)
                DSA_print_fp(stderr, key->dsa, 8);
 #endif
                break;
+#ifdef OPENSSL_HAS_ECC
+       case KEY_ECDSA_CERT:
+               (void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */
+               /* FALLTHROUGH */
+       case KEY_ECDSA:
+               key = key_new(type);
+               key->ecdsa_nid = nid;
+               if ((curve = buffer_get_string_ret(&b, NULL)) == NULL) {
+                       error("key_from_blob: can't read ecdsa curve");
+                       goto badkey;
+               }
+               if (key->ecdsa_nid != key_curve_name_to_nid(curve)) {
+                       error("key_from_blob: ecdsa curve doesn't match type");
+                       goto badkey;
+               }
+               if (key->ecdsa != NULL)
+                       EC_KEY_free(key->ecdsa);
+               if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid))
+                   == NULL)
+                       fatal("key_from_blob: EC_KEY_new_by_curve_name failed");
+               if ((q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL)
+                       fatal("key_from_blob: EC_POINT_new failed");
+               if (buffer_get_ecpoint_ret(&b, EC_KEY_get0_group(key->ecdsa),
+                   q) == -1) {
+                       error("key_from_blob: can't read ecdsa key point");
+                       goto badkey;
+               }
+               if (key_ec_validate_public(EC_KEY_get0_group(key->ecdsa),
+                   q) != 0)
+                       goto badkey;
+               if (EC_KEY_set_public_key(key->ecdsa, q) != 1)
+                       fatal("key_from_blob: EC_KEY_set_public_key failed");
+#ifdef DEBUG_PK
+               key_dump_ec_point(EC_KEY_get0_group(key->ecdsa), q);
+#endif
+               break;
+#endif /* OPENSSL_HAS_ECC */
        case KEY_UNSPEC:
                key = key_new(type);
                break;
@@ -1260,6 +1577,12 @@ key_from_blob(const u_char *blob, u_int blen)
  out:
        if (ktype != NULL)
                xfree(ktype);
+       if (curve != NULL)
+               xfree(curve);
+#ifdef OPENSSL_HAS_ECC
+       if (q != NULL)
+               EC_POINT_free(q);
+#endif
        buffer_free(&b);
        return key;
 }
@@ -1279,6 +1602,7 @@ key_to_blob(const Key *key, u_char **blobp, u_int *lenp)
        case KEY_DSA_CERT_V00:
        case KEY_RSA_CERT_V00:
        case KEY_DSA_CERT:
+       case KEY_ECDSA_CERT:
        case KEY_RSA_CERT:
                /* Use the existing blob */
                buffer_append(&b, buffer_ptr(&key->cert->certblob),
@@ -1291,6 +1615,14 @@ key_to_blob(const Key *key, u_char **blobp, u_int *lenp)
                buffer_put_bignum2(&b, key->dsa->g);
                buffer_put_bignum2(&b, key->dsa->pub_key);
                break;
+#ifdef OPENSSL_HAS_ECC
+       case KEY_ECDSA:
+               buffer_put_cstring(&b, key_ssh_name(key));
+               buffer_put_cstring(&b, key_curve_nid_to_name(key->ecdsa_nid));
+               buffer_put_ecpoint(&b, EC_KEY_get0_group(key->ecdsa),
+                   EC_KEY_get0_public_key(key->ecdsa));
+               break;
+#endif
        case KEY_RSA:
                buffer_put_cstring(&b, key_ssh_name(key));
                buffer_put_bignum2(&b, key->rsa->e);
@@ -1324,6 +1656,11 @@ key_sign(
        case KEY_DSA_CERT:
        case KEY_DSA:
                return ssh_dss_sign(key, sigp, lenp, data, datalen);
+#ifdef OPENSSL_HAS_ECC
+       case KEY_ECDSA_CERT:
+       case KEY_ECDSA:
+               return ssh_ecdsa_sign(key, sigp, lenp, data, datalen);
+#endif
        case KEY_RSA_CERT_V00:
        case KEY_RSA_CERT:
        case KEY_RSA:
@@ -1352,6 +1689,11 @@ key_verify(
        case KEY_DSA_CERT:
        case KEY_DSA:
                return ssh_dss_verify(key, signature, signaturelen, data, datalen);
+#ifdef OPENSSL_HAS_ECC
+       case KEY_ECDSA_CERT:
+       case KEY_ECDSA:
+               return ssh_ecdsa_verify(key, signature, signaturelen, data, datalen);
+#endif
        case KEY_RSA_CERT_V00:
        case KEY_RSA_CERT:
        case KEY_RSA:
@@ -1371,7 +1713,9 @@ key_demote(const Key *k)
        pk = xcalloc(1, sizeof(*pk));
        pk->type = k->type;
        pk->flags = k->flags;
+       pk->ecdsa_nid = k->ecdsa_nid;
        pk->dsa = NULL;
+       pk->ecdsa = NULL;
        pk->rsa = NULL;
 
        switch (k->type) {
@@ -1404,6 +1748,18 @@ key_demote(const Key *k)
                if ((pk->dsa->pub_key = BN_dup(k->dsa->pub_key)) == NULL)
                        fatal("key_demote: BN_dup failed");
                break;
+#ifdef OPENSSL_HAS_ECC
+       case KEY_ECDSA_CERT:
+               key_cert_copy(k, pk);
+               /* FALLTHROUGH */
+       case KEY_ECDSA:
+               if ((pk->ecdsa = EC_KEY_new_by_curve_name(pk->ecdsa_nid)) == NULL)
+                       fatal("key_demote: EC_KEY_new_by_curve_name failed");
+               if (EC_KEY_set_public_key(pk->ecdsa,
+                   EC_KEY_get0_public_key(k->ecdsa)) != 1)
+                       fatal("key_demote: EC_KEY_set_public_key failed");
+               break;
+#endif
        default:
                fatal("key_free: bad key type %d", k->type);
                break;
@@ -1422,6 +1778,7 @@ key_is_cert(const Key *k)
        case KEY_DSA_CERT_V00:
        case KEY_RSA_CERT:
        case KEY_DSA_CERT:
+       case KEY_ECDSA_CERT:
                return 1;
        default:
                return 0;
@@ -1439,6 +1796,8 @@ key_type_plain(int type)
        case KEY_DSA_CERT_V00:
        case KEY_DSA_CERT:
                return KEY_DSA;
+       case KEY_ECDSA_CERT:
+               return KEY_ECDSA;
        default:
                return type;
        }
@@ -1457,6 +1816,10 @@ key_to_certified(Key *k, int legacy)
                k->cert = cert_new();
                k->type = legacy ? KEY_DSA_CERT_V00 : KEY_DSA_CERT;
                return 0;
+       case KEY_ECDSA:
+               k->cert = cert_new();
+               k->type = KEY_ECDSA_CERT;
+               return 0;
        default:
                error("%s: key has incorrect type %s", __func__, key_type(k));
                return -1;
@@ -1478,13 +1841,20 @@ key_drop_cert(Key *k)
                cert_free(k->cert);
                k->type = KEY_DSA;
                return 0;
+       case KEY_ECDSA_CERT:
+               cert_free(k->cert);
+               k->type = KEY_ECDSA;
+               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 */
+/*
+ * Sign a KEY_RSA_CERT, KEY_DSA_CERT or KEY_ECDSA_CERT, (re-)generating
+ * the signed certblob
+ */
 int
 key_certify(Key *k, Key *ca)
 {
@@ -1503,7 +1873,8 @@ key_certify(Key *k, Key *ca)
                return -1;
        }
 
-       if (ca->type != KEY_RSA && ca->type != KEY_DSA) {
+       if (ca->type != KEY_RSA && ca->type != KEY_DSA &&
+           ca->type != KEY_ECDSA) {
                error("%s: CA key has unsupported type %s", __func__,
                    key_type(ca));
                return -1;
@@ -1515,10 +1886,9 @@ key_certify(Key *k, Key *ca)
        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));
+       arc4random_buf(&nonce, sizeof(nonce));
+       if (!key_cert_is_legacy(k))
                buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce));
-       }
 
        switch (k->type) {
        case KEY_DSA_CERT_V00:
@@ -1528,6 +1898,15 @@ key_certify(Key *k, Key *ca)
                buffer_put_bignum2(&k->cert->certblob, k->dsa->g);
                buffer_put_bignum2(&k->cert->certblob, k->dsa->pub_key);
                break;
+#ifdef OPENSSL_HAS_ECC
+       case KEY_ECDSA_CERT:
+               buffer_put_cstring(&k->cert->certblob,
+                   key_curve_nid_to_name(k->ecdsa_nid));
+               buffer_put_ecpoint(&k->cert->certblob,
+                   EC_KEY_get0_group(k->ecdsa),
+                   EC_KEY_get0_public_key(k->ecdsa));
+               break;
+#endif
        case KEY_RSA_CERT_V00:
        case KEY_RSA_CERT:
                buffer_put_bignum2(&k->cert->certblob, k->rsa->e);
@@ -1541,7 +1920,7 @@ key_certify(Key *k, Key *ca)
        }
 
        /* -v01 certs have a serial number next */
-       if (k->type == KEY_DSA_CERT || k->type == KEY_RSA_CERT)
+       if (!key_cert_is_legacy(k))
                buffer_put_int64(&k->cert->certblob, k->cert->serial);
 
        buffer_put_int(&k->cert->certblob, k->cert->type);
@@ -1560,14 +1939,14 @@ key_certify(Key *k, Key *ca)
            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) {
+       if (!key_cert_is_legacy(k)) {
                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)
+       if (key_cert_is_legacy(k))
                buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce));
 
        buffer_put_string(&k->cert->certblob, NULL, 0); /* reserved */
@@ -1652,3 +2031,240 @@ key_cert_is_legacy(Key *k)
                return 0;
        }
 }
+
+/* XXX: these are really begging for a table-driven approach */
+int
+key_curve_name_to_nid(const char *name)
+{
+#ifdef OPENSSL_HAS_ECC
+       if (strcmp(name, "nistp256") == 0)
+               return NID_X9_62_prime256v1;
+       else if (strcmp(name, "nistp384") == 0)
+               return NID_secp384r1;
+       else if (strcmp(name, "nistp521") == 0)
+               return NID_secp521r1;
+#endif
+
+       debug("%s: unsupported EC curve name \"%.100s\"", __func__, name);
+       return -1;
+}
+
+u_int
+key_curve_nid_to_bits(int nid)
+{
+       switch (nid) {
+#ifdef OPENSSL_HAS_ECC
+       case NID_X9_62_prime256v1:
+               return 256;
+       case NID_secp384r1:
+               return 384;
+       case NID_secp521r1:
+               return 521;
+#endif
+       default:
+               error("%s: unsupported EC curve nid %d", __func__, nid);
+               return 0;
+       }
+}
+
+const char *
+key_curve_nid_to_name(int nid)
+{
+#ifdef OPENSSL_HAS_ECC
+       if (nid == NID_X9_62_prime256v1)
+               return "nistp256";
+       else if (nid == NID_secp384r1)
+               return "nistp384";
+       else if (nid == NID_secp521r1)
+               return "nistp521";
+#endif
+       error("%s: unsupported EC curve nid %d", __func__, nid);
+       return NULL;
+}
+
+#ifdef OPENSSL_HAS_ECC
+const EVP_MD *
+key_ec_nid_to_evpmd(int nid)
+{
+       int kbits = key_curve_nid_to_bits(nid);
+
+       if (kbits == 0)
+               fatal("%s: invalid nid %d", __func__, nid);
+       /* RFC5656 section 6.2.1 */
+       if (kbits <= 256)
+               return EVP_sha256();
+       else if (kbits <= 384)
+               return EVP_sha384();
+       else
+               return EVP_sha512();
+}
+
+int
+key_ec_validate_public(const EC_GROUP *group, const EC_POINT *public)
+{
+       BN_CTX *bnctx;
+       EC_POINT *nq = NULL;
+       BIGNUM *order, *x, *y, *tmp;
+       int ret = -1;
+
+       if ((bnctx = BN_CTX_new()) == NULL)
+               fatal("%s: BN_CTX_new failed", __func__);
+       BN_CTX_start(bnctx);
+
+       /*
+        * We shouldn't ever hit this case because bignum_get_ecpoint()
+        * refuses to load GF2m points.
+        */
+       if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) !=
+           NID_X9_62_prime_field) {
+               error("%s: group is not a prime field", __func__);
+               goto out;
+       }
+
+       /* Q != infinity */
+       if (EC_POINT_is_at_infinity(group, public)) {
+               error("%s: received degenerate public key (infinity)",
+                   __func__);
+               goto out;
+       }
+
+       if ((x = BN_CTX_get(bnctx)) == NULL ||
+           (y = BN_CTX_get(bnctx)) == NULL ||
+           (order = BN_CTX_get(bnctx)) == NULL ||
+           (tmp = BN_CTX_get(bnctx)) == NULL)
+               fatal("%s: BN_CTX_get failed", __func__);
+
+       /* log2(x) > log2(order)/2, log2(y) > log2(order)/2 */
+       if (EC_GROUP_get_order(group, order, bnctx) != 1)
+               fatal("%s: EC_GROUP_get_order failed", __func__);
+       if (EC_POINT_get_affine_coordinates_GFp(group, public,
+           x, y, bnctx) != 1)
+               fatal("%s: EC_POINT_get_affine_coordinates_GFp", __func__);
+       if (BN_num_bits(x) <= BN_num_bits(order) / 2) {
+               error("%s: public key x coordinate too small: "
+                   "bits(x) = %d, bits(order)/2 = %d", __func__,
+                   BN_num_bits(x), BN_num_bits(order) / 2);
+               goto out;
+       }
+       if (BN_num_bits(y) <= BN_num_bits(order) / 2) {
+               error("%s: public key y coordinate too small: "
+                   "bits(y) = %d, bits(order)/2 = %d", __func__,
+                   BN_num_bits(x), BN_num_bits(order) / 2);
+               goto out;
+       }
+
+       /* nQ == infinity (n == order of subgroup) */
+       if ((nq = EC_POINT_new(group)) == NULL)
+               fatal("%s: BN_CTX_tmp failed", __func__);
+       if (EC_POINT_mul(group, nq, NULL, public, order, bnctx) != 1)
+               fatal("%s: EC_GROUP_mul failed", __func__);
+       if (EC_POINT_is_at_infinity(group, nq) != 1) {
+               error("%s: received degenerate public key (nQ != infinity)",
+                   __func__);
+               goto out;
+       }
+
+       /* x < order - 1, y < order - 1 */
+       if (!BN_sub(tmp, order, BN_value_one()))
+               fatal("%s: BN_sub failed", __func__);
+       if (BN_cmp(x, tmp) >= 0) {
+               error("%s: public key x coordinate >= group order - 1",
+                   __func__);
+               goto out;
+       }
+       if (BN_cmp(y, tmp) >= 0) {
+               error("%s: public key y coordinate >= group order - 1",
+                   __func__);
+               goto out;
+       }
+       ret = 0;
+ out:
+       BN_CTX_free(bnctx);
+       EC_POINT_free(nq);
+       return ret;
+}
+
+int
+key_ec_validate_private(const EC_KEY *key)
+{
+       BN_CTX *bnctx;
+       BIGNUM *order, *tmp;
+       int ret = -1;
+
+       if ((bnctx = BN_CTX_new()) == NULL)
+               fatal("%s: BN_CTX_new failed", __func__);
+       BN_CTX_start(bnctx);
+
+       if ((order = BN_CTX_get(bnctx)) == NULL ||
+           (tmp = BN_CTX_get(bnctx)) == NULL)
+               fatal("%s: BN_CTX_get failed", __func__);
+
+       /* log2(private) > log2(order)/2 */
+       if (EC_GROUP_get_order(EC_KEY_get0_group(key), order, bnctx) != 1)
+               fatal("%s: EC_GROUP_get_order failed", __func__);
+       if (BN_num_bits(EC_KEY_get0_private_key(key)) <=
+           BN_num_bits(order) / 2) {
+               error("%s: private key too small: "
+                   "bits(y) = %d, bits(order)/2 = %d", __func__,
+                   BN_num_bits(EC_KEY_get0_private_key(key)),
+                   BN_num_bits(order) / 2);
+               goto out;
+       }
+
+       /* private < order - 1 */
+       if (!BN_sub(tmp, order, BN_value_one()))
+               fatal("%s: BN_sub failed", __func__);
+       if (BN_cmp(EC_KEY_get0_private_key(key), tmp) >= 0) {
+               error("%s: private key >= group order - 1", __func__);
+               goto out;
+       }
+       ret = 0;
+ out:
+       BN_CTX_free(bnctx);
+       return ret;
+}
+
+#if defined(DEBUG_KEXECDH) || defined(DEBUG_PK)
+void
+key_dump_ec_point(const EC_GROUP *group, const EC_POINT *point)
+{
+       BIGNUM *x, *y;
+       BN_CTX *bnctx;
+
+       if (point == NULL) {
+               fputs("point=(NULL)\n", stderr);
+               return;
+       }
+       if ((bnctx = BN_CTX_new()) == NULL)
+               fatal("%s: BN_CTX_new failed", __func__);
+       BN_CTX_start(bnctx);
+       if ((x = BN_CTX_get(bnctx)) == NULL || (y = BN_CTX_get(bnctx)) == NULL)
+               fatal("%s: BN_CTX_get failed", __func__);
+       if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) !=
+           NID_X9_62_prime_field)
+               fatal("%s: group is not a prime field", __func__);
+       if (EC_POINT_get_affine_coordinates_GFp(group, point, x, y, bnctx) != 1)
+               fatal("%s: EC_POINT_get_affine_coordinates_GFp", __func__);
+       fputs("x=", stderr);
+       BN_print_fp(stderr, x);
+       fputs("\ny=", stderr);
+       BN_print_fp(stderr, y);
+       fputs("\n", stderr);
+       BN_CTX_free(bnctx);
+}
+
+void
+key_dump_ec_key(const EC_KEY *key)
+{
+       const BIGNUM *exponent;
+
+       key_dump_ec_point(EC_KEY_get0_group(key), EC_KEY_get0_public_key(key));
+       fputs("exponent=", stderr);
+       if ((exponent = EC_KEY_get0_private_key(key)) == NULL)
+               fputs("(NULL)", stderr);
+       else
+               BN_print_fp(stderr, EC_KEY_get0_private_key(key));
+       fputs("\n", stderr);
+}
+#endif /* defined(DEBUG_KEXECDH) || defined(DEBUG_PK) */
+#endif /* OPENSSL_HAS_ECC */
index 11d30ea..ec5ac5e 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: key.h,v 1.30 2010/04/16 01:47:26 djm Exp $ */
+/* $OpenBSD: key.h,v 1.33 2010/10/28 11:22:09 djm Exp $ */
 
 /*
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
 #include "buffer.h"
 #include <openssl/rsa.h>
 #include <openssl/dsa.h>
+#ifdef OPENSSL_HAS_ECC
+#include <openssl/ec.h>
+#endif
 
 typedef struct Key Key;
 enum types {
        KEY_RSA1,
        KEY_RSA,
        KEY_DSA,
+       KEY_ECDSA,
        KEY_RSA_CERT,
        KEY_DSA_CERT,
+       KEY_ECDSA_CERT,
        KEY_RSA_CERT_V00,
        KEY_DSA_CERT_V00,
        KEY_UNSPEC
@@ -73,6 +78,12 @@ struct Key {
        int      flags;
        RSA     *rsa;
        DSA     *dsa;
+       int      ecdsa_nid;     /* NID of curve */
+#ifdef OPENSSL_HAS_ECC
+       EC_KEY  *ecdsa;
+#else
+       void    *ecdsa;
+#endif
        struct KeyCert *cert;
 };
 
@@ -104,9 +115,22 @@ int         key_cert_check_authority(const Key *, int, int, const char *,
            const char **);
 int     key_cert_is_legacy(Key *);
 
+int             key_ecdsa_nid_from_name(const char *);
+int             key_curve_name_to_nid(const char *);
+const char *    key_curve_nid_to_name(int);
+u_int           key_curve_nid_to_bits(int);
+int             key_ecdsa_bits_to_nid(int);
+#ifdef OPENSSL_HAS_ECC
+int             key_ecdsa_key_to_nid(EC_KEY *);
+const EVP_MD *  key_ec_nid_to_evpmd(int nid);
+int             key_ec_validate_public(const EC_GROUP *, const EC_POINT *);
+int             key_ec_validate_private(const EC_KEY *);
+#endif
+
 Key            *key_from_blob(const u_char *, u_int);
 int             key_to_blob(const Key *, u_char **, u_int *);
 const char     *key_ssh_name(const Key *);
+const char     *key_ssh_name_plain(const Key *);
 int             key_names_valid2(const char *);
 
 int     key_sign(const Key *, u_char **, u_int *, const u_char *, u_int);
@@ -114,7 +138,14 @@ int         key_verify(const Key *, const u_char *, u_int, const u_char *, u_int);
 
 int     ssh_dss_sign(const Key *, u_char **, u_int *, const u_char *, u_int);
 int     ssh_dss_verify(const Key *, const u_char *, u_int, const u_char *, u_int);
+int     ssh_ecdsa_sign(const Key *, u_char **, u_int *, const u_char *, u_int);
+int     ssh_ecdsa_verify(const Key *, const u_char *, u_int, const u_char *, u_int);
 int     ssh_rsa_sign(const Key *, u_char **, u_int *, const u_char *, u_int);
 int     ssh_rsa_verify(const Key *, const u_char *, u_int, const u_char *, u_int);
 
+#if defined(OPENSSL_HAS_ECC) && (defined(DEBUG_KEXECDH) || defined(DEBUG_PK))
+void   key_dump_ec_point(const EC_GROUP *, const EC_POINT *);
+void   key_dump_ec_key(const EC_KEY *);
+#endif
+
 #endif
index 6f655cb..32941c9 100644 (file)
@@ -273,7 +273,7 @@ login_logout(struct logininfo *li)
  *                try to retrieve lastlog information from wtmp/wtmpx.
  */
 unsigned int
-login_get_lastlog_time(const int uid)
+login_get_lastlog_time(const uid_t uid)
 {
        struct logininfo li;
 
@@ -297,7 +297,7 @@ login_get_lastlog_time(const int uid)
  *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
  */
 struct logininfo *
-login_get_lastlog(struct logininfo *li, const int uid)
+login_get_lastlog(struct logininfo *li, const uid_t uid)
 {
        struct passwd *pw;
 
@@ -311,7 +311,8 @@ login_get_lastlog(struct logininfo *li, const int uid)
         */
        pw = getpwuid(uid);
        if (pw == NULL)
-               fatal("%s: Cannot find account for uid %i", __func__, uid);
+               fatal("%s: Cannot find account for uid %ld", __func__,
+                   (long)uid);
 
        /* No MIN_SIZEOF here - we absolutely *must not* truncate the
         * username (XXX - so check for trunc!) */
@@ -335,7 +336,7 @@ login_get_lastlog(struct logininfo *li, const int uid)
  * allocation fails, the program halts.
  */
 struct
-logininfo *login_alloc_entry(int pid, const char *username,
+logininfo *login_alloc_entry(pid_t pid, const char *username,
     const char *hostname, const char *line)
 {
        struct logininfo *newli;
@@ -363,7 +364,7 @@ login_free_entry(struct logininfo *li)
  * Returns: 1
  */
 int
-login_init_entry(struct logininfo *li, int pid, const char *username,
+login_init_entry(struct logininfo *li, pid_t pid, const char *username,
     const char *hostname, const char *line)
 {
        struct passwd *pw;
@@ -468,9 +469,9 @@ login_write(struct logininfo *li)
 #endif
 #ifdef SSH_AUDIT_EVENTS
        if (li->type == LTYPE_LOGIN)
-               audit_session_open(li->line);
+               audit_session_open(li);
        else if (li->type == LTYPE_LOGOUT)
-               audit_session_close(li->line);
+               audit_session_close(li);
 #endif
        return (0);
 }
@@ -872,11 +873,13 @@ utmp_write_direct(struct logininfo *li, struct utmp *ut)
                pos = (off_t)tty * sizeof(struct utmp);
                if ((ret = lseek(fd, pos, SEEK_SET)) == -1) {
                        logit("%s: lseek: %s", __func__, strerror(errno));
+                       close(fd);
                        return (0);
                }
                if (ret != pos) {
                        logit("%s: Couldn't seek to tty %d slot in %s",
                            __func__, tty, UTMP_FILE);
+                       close(fd);
                        return (0);
                }
                /*
@@ -892,16 +895,20 @@ utmp_write_direct(struct logininfo *li, struct utmp *ut)
 
                if ((ret = lseek(fd, pos, SEEK_SET)) == -1) {
                        logit("%s: lseek: %s", __func__, strerror(errno));
+                       close(fd);
                        return (0);
                }
                if (ret != pos) {
                        logit("%s: Couldn't seek to tty %d slot in %s",
                            __func__, tty, UTMP_FILE);
+                       close(fd);
                        return (0);
                }
                if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
                        logit("%s: error writing %s: %s", __func__,
                            UTMP_FILE, strerror(errno));
+                       close(fd);
+                       return (0);
                }
 
                close(fd);
@@ -1205,7 +1212,7 @@ wtmp_get_entry(struct logininfo *li)
                        close (fd);
                        return (0);
                }
-               if ( wtmp_islogin(li, &ut) ) {
+               if (wtmp_islogin(li, &ut) ) {
                        found = 1;
                        /*
                         * We've already checked for a time in struct
@@ -1496,11 +1503,12 @@ lastlog_openseek(struct logininfo *li, int *fd, int filemode)
 
        if (S_ISREG(st.st_mode)) {
                /* find this uid's offset in the lastlog file */
-               offset = (off_t) ((long)li->uid * sizeof(struct lastlog));
+               offset = (off_t) ((u_long)li->uid * sizeof(struct lastlog));
 
                if (lseek(*fd, offset, SEEK_SET) != offset) {
                        logit("%s: %s->lseek(): %s", __func__,
                            lastlog_file, strerror(errno));
+                       close(*fd);
                        return (0);
                }
        }
@@ -1672,7 +1680,7 @@ record_failed_login(const char *username, const char *hostname,
                    strerror(errno));
                goto out;
        }
-       if((fst.st_mode & (S_IRWXG | S_IRWXO)) || (fst.st_uid != 0)){
+       if((fst.st_mode & (S_IXGRP | S_IRWXO)) || (fst.st_uid != 0)){
                logit("Excess permission or bad ownership on file %s",
                    _PATH_BTMP);
                goto out;
index 84b4865..28923e7 100644 (file)
@@ -63,8 +63,8 @@ struct logininfo {
        char       progname[LINFO_PROGSIZE];     /* name of program (for PAM) */
        int        progname_null;
        short int  type;                         /* type of login (LTYPE_*) */
-       int        pid;                          /* PID of login process */
-       int        uid;                          /* UID of this user */
+       pid_t      pid;                          /* PID of login process */
+       uid_t      uid;                          /* UID of this user */
        char       line[LINFO_LINESIZE];         /* tty/pty name */
        char       username[LINFO_NAMESIZE];     /* login username */
        char       hostname[LINFO_HOSTSIZE];     /* remote hostname */
@@ -86,12 +86,12 @@ struct logininfo {
 /** 'public' functions */
 
 /* construct a new login entry */
-struct logininfo *login_alloc_entry(int pid, const char *username,
+struct logininfo *login_alloc_entry(pid_t pid, const char *username,
                                    const char *hostname, const char *line);
 /* free a structure */
 void login_free_entry(struct logininfo *li);
 /* fill out a pre-allocated structure with useful information */
-int login_init_entry(struct logininfo *li, int pid, const char *username,
+int login_init_entry(struct logininfo *li, pid_t pid, const char *username,
                     const char *hostname, const char *line);
 /* place the current time in a logininfo struct */
 void login_set_current_time(struct logininfo *li);
@@ -117,9 +117,9 @@ void login_set_addr(struct logininfo *li, const struct sockaddr *sa,
  * lastlog retrieval functions
  */
 /* lastlog *entry* functions fill out a logininfo */
-struct logininfo *login_get_lastlog(struct logininfo *li, const int uid);
+struct logininfo *login_get_lastlog(struct logininfo *li, const uid_t uid);
 /* lastlog *time* functions return time_t equivalent (uint) */
-unsigned int login_get_lastlog_time(const int uid);
+unsigned int login_get_lastlog_time(const uid_t uid);
 
 /* produce various forms of the line filename */
 char *line_fullname(char *dst, const char *src, u_int dstsize);
index a82e793..919b04e 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: misc.c,v 1.80 2010/07/21 02:10:58 djm Exp $ */
+/* $OpenBSD: misc.c,v 1.84 2010/11/21 01:01:13 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  * Copyright (c) 2005,2006 Damien Miller.  All rights reserved.
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <unistd.h>
 
 #include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
 #include <netinet/tcp.h>
 
 #include <errno.h>
@@ -850,16 +853,138 @@ ms_to_timeval(struct timeval *tv, int ms)
        tv->tv_usec = (ms % 1000) * 1000;
 }
 
+void
+bandwidth_limit_init(struct bwlimit *bw, u_int64_t kbps, size_t buflen)
+{
+       bw->buflen = buflen;
+       bw->rate = kbps;
+       bw->thresh = bw->rate;
+       bw->lamt = 0;
+       timerclear(&bw->bwstart);
+       timerclear(&bw->bwend);
+}      
+
+/* Callback from read/write loop to insert bandwidth-limiting delays */
+void
+bandwidth_limit(struct bwlimit *bw, size_t read_len)
+{
+       u_int64_t waitlen;
+       struct timespec ts, rm;
+
+       if (!timerisset(&bw->bwstart)) {
+               gettimeofday(&bw->bwstart, NULL);
+               return;
+       }
+
+       bw->lamt += read_len;
+       if (bw->lamt < bw->thresh)
+               return;
+
+       gettimeofday(&bw->bwend, NULL);
+       timersub(&bw->bwend, &bw->bwstart, &bw->bwend);
+       if (!timerisset(&bw->bwend))
+               return;
+
+       bw->lamt *= 8;
+       waitlen = (double)1000000L * bw->lamt / bw->rate;
+
+       bw->bwstart.tv_sec = waitlen / 1000000L;
+       bw->bwstart.tv_usec = waitlen % 1000000L;
+
+       if (timercmp(&bw->bwstart, &bw->bwend, >)) {
+               timersub(&bw->bwstart, &bw->bwend, &bw->bwend);
+
+               /* Adjust the wait time */
+               if (bw->bwend.tv_sec) {
+                       bw->thresh /= 2;
+                       if (bw->thresh < bw->buflen / 4)
+                               bw->thresh = bw->buflen / 4;
+               } else if (bw->bwend.tv_usec < 10000) {
+                       bw->thresh *= 2;
+                       if (bw->thresh > bw->buflen * 8)
+                               bw->thresh = bw->buflen * 8;
+               }
+
+               TIMEVAL_TO_TIMESPEC(&bw->bwend, &ts);
+               while (nanosleep(&ts, &rm) == -1) {
+                       if (errno != EINTR)
+                               break;
+                       ts = rm;
+               }
+       }
+
+       bw->lamt = 0;
+       gettimeofday(&bw->bwstart, NULL);
+}
+
+/* Make a template filename for mk[sd]temp() */
+void
+mktemp_proto(char *s, size_t len)
+{
+       const char *tmpdir;
+       int r;
+
+       if ((tmpdir = getenv("TMPDIR")) != NULL) {
+               r = snprintf(s, len, "%s/ssh-XXXXXXXXXXXX", tmpdir);
+               if (r > 0 && (size_t)r < len)
+                       return;
+       }
+       r = snprintf(s, len, "/tmp/ssh-XXXXXXXXXXXX");
+       if (r < 0 || (size_t)r >= len)
+               fatal("%s: template string too short", __func__);
+}
+
+static const struct {
+       const char *name;
+       int value;
+} ipqos[] = {
+       { "af11", IPTOS_DSCP_AF11 },
+       { "af12", IPTOS_DSCP_AF12 },
+       { "af13", IPTOS_DSCP_AF13 },
+       { "af14", IPTOS_DSCP_AF21 },
+       { "af22", IPTOS_DSCP_AF22 },
+       { "af23", IPTOS_DSCP_AF23 },
+       { "af31", IPTOS_DSCP_AF31 },
+       { "af32", IPTOS_DSCP_AF32 },
+       { "af33", IPTOS_DSCP_AF33 },
+       { "af41", IPTOS_DSCP_AF41 },
+       { "af42", IPTOS_DSCP_AF42 },
+       { "af43", IPTOS_DSCP_AF43 },
+       { "cs0", IPTOS_DSCP_CS0 },
+       { "cs1", IPTOS_DSCP_CS1 },
+       { "cs2", IPTOS_DSCP_CS2 },
+       { "cs3", IPTOS_DSCP_CS3 },
+       { "cs4", IPTOS_DSCP_CS4 },
+       { "cs5", IPTOS_DSCP_CS5 },
+       { "cs6", IPTOS_DSCP_CS6 },
+       { "cs7", IPTOS_DSCP_CS7 },
+       { "ef", IPTOS_DSCP_EF },
+       { "lowdelay", IPTOS_LOWDELAY },
+       { "throughput", IPTOS_THROUGHPUT },
+       { "reliability", IPTOS_RELIABILITY },
+       { NULL, -1 }
+};
+
 int
-timingsafe_bcmp(const void *b1, const void *b2, size_t n)
+parse_ipqos(const char *cp)
 {
-       const unsigned char *p1 = b1, *p2 = b2;
-       int ret = 0;
+       u_int i;
+       char *ep;
+       long val;
 
-       for (; n > 0; n--)
-               ret |= *p1++ ^ *p2++;
-       return (ret != 0);
+       if (cp == NULL)
+               return -1;
+       for (i = 0; ipqos[i].name != NULL; i++) {
+               if (strcasecmp(cp, ipqos[i].name) == 0)
+                       return ipqos[i].value;
+       }
+       /* Try parsing as an integer */
+       val = strtol(cp, &ep, 0);
+       if (*cp == '\0' || *ep != '\0' || val < 0 || val > 255)
+               return -1;
+       return val;
 }
+
 void
 sock_set_v6only(int s)
 {
index bb799f6..65cf4a6 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: misc.h,v 1.43 2010/07/13 23:13:16 djm Exp $ */
+/* $OpenBSD: misc.h,v 1.47 2010/11/21 01:01:13 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -36,7 +36,6 @@ 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);
@@ -80,6 +79,17 @@ void         put_u32(void *, u_int32_t)
 void           put_u16(void *, u_int16_t)
     __attribute__((__bounded__( __minbytes__, 1, 2)));
 
+struct bwlimit {
+       size_t buflen;
+       u_int64_t rate, thresh, lamt;
+       struct timeval bwstart, bwend;
+};
+
+void bandwidth_limit_init(struct bwlimit *, u_int64_t, size_t);
+void bandwidth_limit(struct bwlimit *, size_t);
+
+int parse_ipqos(const char *);
+void mktemp_proto(char *, size_t);
 
 /* readpass.c */
 
index f737cb3..2964a8b 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: moduli.c,v 1.21 2008/06/26 09:19:40 djm Exp $ */
+/* $OpenBSD: moduli.c,v 1.22 2010/11/10 01:33:07 djm Exp $ */
 /*
  * Copyright 1994 Phil Karn <karn@qualcomm.com>
  * Copyright 1996-1998, 2003 William Allen Simpson <wsimpson@greendragon.com>
@@ -54,6 +54,8 @@
 #include "dh.h"
 #include "log.h"
 
+#include "openbsd-compat/openssl-compat.h"
+
 /*
  * File output defines
  */
@@ -600,7 +602,7 @@ prime_test(FILE *in, FILE *out, u_int32_t trials, u_int32_t generator_wanted)
                 * that p is also prime. A single pass will weed out the
                 * vast majority of composite q's.
                 */
-               if (BN_is_prime(q, 1, NULL, ctx, NULL) <= 0) {
+               if (BN_is_prime_ex(q, 1, ctx, NULL) <= 0) {
                        debug("%10u: q failed first possible prime test",
                            count_in);
                        continue;
@@ -613,14 +615,14 @@ prime_test(FILE *in, FILE *out, u_int32_t trials, u_int32_t generator_wanted)
                 * will show up on the first Rabin-Miller iteration so it
                 * doesn't hurt to specify a high iteration count.
                 */
-               if (!BN_is_prime(p, trials, NULL, ctx, NULL)) {
+               if (!BN_is_prime_ex(p, trials, ctx, NULL)) {
                        debug("%10u: p is not prime", count_in);
                        continue;
                }
                debug("%10u: p is almost certainly prime", count_in);
 
                /* recheck q more rigorously */
-               if (!BN_is_prime(q, trials - 1, NULL, ctx, NULL)) {
+               if (!BN_is_prime_ex(q, trials - 1, ctx, NULL)) {
                        debug("%10u: q is not prime", count_in);
                        continue;
                }
index 9eb4e35..29d987c 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor.c,v 1.108 2010/07/13 23:13:16 djm Exp $ */
+/* $OpenBSD: monitor.c,v 1.110 2010/09/09 10:45:45 djm Exp $ */
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
  * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -590,10 +590,10 @@ mm_answer_sign(int sock, Buffer *m)
        p = buffer_get_string(m, &datlen);
 
        /*
-        * Supported KEX types will only return SHA1 (20 byte) or
-        * SHA256 (32 byte) hashes
+        * Supported KEX types use SHA1 (20 bytes), SHA256 (32 bytes),
+        * SHA384 (48 bytes) and SHA512 (64 bytes).
         */
-       if (datlen != 20 && datlen != 32)
+       if (datlen != 20 && datlen != 32 && datlen != 48 && datlen != 64)
                fatal("%s: data length incorrect: %u", __func__, datlen);
 
        /* save session id, it will be passed on the first call */
@@ -1691,6 +1691,7 @@ mm_get_kex(Buffer *m)
        kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server;
        kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
        kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
+       kex->kex[KEX_ECDH_SHA2] = kexecdh_server;
        kex->server = 1;
        kex->hostkey_type = buffer_get_int(m);
        kex->kex_type = buffer_get_int(m);
index faeb02c..1a5dda5 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor_wrap.c,v 1.69 2010/03/07 11:57:13 dtucker Exp $ */
+/* $OpenBSD: monitor_wrap.c,v 1.70 2010/08/31 11:54:45 djm Exp $ */
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
  * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -73,6 +73,7 @@
 #include "misc.h"
 #include "schnorr.h"
 #include "jpake.h"
+#include "uuencode.h"
 
 #include "channels.h"
 #include "session.h"
index 5c3857e..e370462 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: mux.c,v 1.21 2010/06/25 23:15:36 djm Exp $ */
+/* $OpenBSD: mux.c,v 1.24 2011/01/13 21:54:53 djm Exp $ */
 /*
  * Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org>
  *
@@ -879,7 +879,7 @@ process_mux_stdio_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r)
 
        if (options.control_master == SSHCTL_MASTER_ASK ||
            options.control_master == SSHCTL_MASTER_AUTO_ASK) {
-               if (!ask_permission("Allow forward to to %s:%u? ",
+               if (!ask_permission("Allow forward to %s:%u? ",
                    chost, cport)) {
                        debug2("%s: stdio fwd refused by user", __func__);
                        /* prepare reply */
@@ -1026,6 +1026,9 @@ muxserver_listen(void)
        struct sockaddr_un addr;
        socklen_t sun_len;
        mode_t old_umask;
+       char *orig_control_path = options.control_path;
+       char rbuf[16+1];
+       u_int i, r;
 
        if (options.control_path == NULL ||
            options.control_master == SSHCTL_MASTER_NO)
@@ -1033,6 +1036,23 @@ muxserver_listen(void)
 
        debug("setting up multiplex master socket");
 
+       /*
+        * Use a temporary path before listen so we can pseudo-atomically
+        * establish the listening socket in its final location to avoid
+        * other processes racing in between bind() and listen() and hitting
+        * an unready socket.
+        */
+       for (i = 0; i < sizeof(rbuf) - 1; i++) {
+               r = arc4random_uniform(26+26+10);
+               rbuf[i] = (r < 26) ? 'a' + r :
+                   (r < 26*2) ? 'A' + r - 26 :
+                   '0' + r - 26 - 26;
+       }
+       rbuf[sizeof(rbuf) - 1] = '\0';
+       options.control_path = NULL;
+       xasprintf(&options.control_path, "%s.%s", orig_control_path, rbuf);
+       debug3("%s: temporary control path %s", __func__, options.control_path);
+
        memset(&addr, '\0', sizeof(addr));
        addr.sun_family = AF_UNIX;
        sun_len = offsetof(struct sockaddr_un, sun_path) +
@@ -1051,6 +1071,7 @@ muxserver_listen(void)
                if (errno == EINVAL || errno == EADDRINUSE) {
                        error("ControlSocket %s already exists, "
                            "disabling multiplexing", options.control_path);
+ disable_mux_master:
                        close(muxserver_sock);
                        muxserver_sock = -1;
                        xfree(options.control_path);
@@ -1065,12 +1086,29 @@ muxserver_listen(void)
        if (listen(muxserver_sock, 64) == -1)
                fatal("%s listen(): %s", __func__, strerror(errno));
 
+       /* Now atomically "move" the mux socket into position */
+       if (link(options.control_path, orig_control_path) != 0) {
+               if (errno != EEXIST) {
+                       fatal("%s: link mux listener %s => %s: %s", __func__, 
+                           options.control_path, orig_control_path,
+                           strerror(errno));
+               }
+               error("ControlSocket %s already exists, disabling multiplexing",
+                   orig_control_path);
+               xfree(orig_control_path);
+               unlink(options.control_path);
+               goto disable_mux_master;
+       }
+       unlink(options.control_path);
+       xfree(options.control_path);
+       options.control_path = orig_control_path;
+
        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);
+           0, options.control_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);
@@ -1492,7 +1530,7 @@ mux_client_request_forward(int fd, u_int ftype, Forward *fwd)
        case MUX_S_FAILURE:
                e = buffer_get_string(&m, NULL);
                buffer_free(&m);
-               error("%s: session request failed: %s", __func__, e);
+               error("%s: forwarding request failed: %s", __func__, e);
                return -1;
        default:
                fatal("%s: unexpected response from master 0x%08x",
@@ -1611,12 +1649,12 @@ mux_client_request_session(int fd)
        case MUX_S_PERMISSION_DENIED:
                e = buffer_get_string(&m, NULL);
                buffer_free(&m);
-               error("Master refused forwarding request: %s", e);
+               error("Master refused session 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);
+               error("%s: session request failed: %s", __func__, e);
                return -1;
        default:
                buffer_free(&m);
@@ -1743,7 +1781,7 @@ mux_client_request_stdio_fwd(int fd)
        case MUX_S_PERMISSION_DENIED:
                e = buffer_get_string(&m, NULL);
                buffer_free(&m);
-               fatal("Master refused forwarding request: %s", e);
+               fatal("Master refused stdio forwarding request: %s", e);
        case MUX_S_FAILURE:
                e = buffer_get_string(&m, NULL);
                buffer_free(&m);
@@ -1823,9 +1861,13 @@ muxclient(const char *path)
                        fatal("Control socket connect(%.100s): %s", path,
                            strerror(errno));
                }
-               if (errno == ENOENT)
+               if (errno == ECONNREFUSED &&
+                   options.control_master != SSHCTL_MASTER_NO) {
+                       debug("Stale control socket %.100s, unlinking", path);
+                       unlink(path);
+               } else if (errno == ENOENT) {
                        debug("Control socket \"%.100s\" does not exist", path);
-               else {
+               else {
                        error("Control socket connect(%.100s): %s", path,
                            strerror(errno));
                }
index 7bedfab..2c43607 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: myproposal.h,v 1.25 2010/04/16 01:47:26 djm Exp $ */
+/* $OpenBSD: myproposal.h,v 1.27 2010/09/01 22:42:13 djm Exp $ */
 
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
 
 #include <openssl/opensslv.h>
 
+#ifdef OPENSSL_HAS_ECC
+# define KEX_ECDH_METHODS \
+       "ecdh-sha2-nistp256," \
+       "ecdh-sha2-nistp384," \
+       "ecdh-sha2-nistp521,"
+# define HOSTKEY_ECDSA_CERT_METHODS \
+       "ecdsa-sha2-nistp256-cert-v01@openssh.com," \
+       "ecdsa-sha2-nistp384-cert-v01@openssh.com," \
+       "ecdsa-sha2-nistp521-cert-v01@openssh.com,"
+# define HOSTKEY_ECDSA_METHODS \
+       "ecdsa-sha2-nistp256," \
+       "ecdsa-sha2-nistp384," \
+       "ecdsa-sha2-nistp521,"
+#else
+# define KEX_ECDH_METHODS
+# define HOSTKEY_ECDSA_CERT_METHODS
+# define HOSTKEY_ECDSA_METHODS
+#endif
+
 /* Old OpenSSL doesn't support what we need for DHGEX-sha256 */
-#if OPENSSL_VERSION_NUMBER < 0x00907000L
-# define KEX_DEFAULT_KEX               \
-       "diffie-hellman-group-exchange-sha1," \
-       "diffie-hellman-group14-sha1," \
-       "diffie-hellman-group1-sha1"
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+# define KEX_SHA256_METHODS \
+       "diffie-hellman-group-exchange-sha256,"
 #else
-# define KEX_DEFAULT_KEX               \
-       "diffie-hellman-group-exchange-sha256," \
+# define KEX_SHA256_METHODS
+#endif
+
+# define KEX_DEFAULT_KEX \
+       KEX_ECDH_METHODS \
+       KEX_SHA256_METHODS \
        "diffie-hellman-group-exchange-sha1," \
        "diffie-hellman-group14-sha1," \
        "diffie-hellman-group1-sha1"
-#endif
 
 #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"
+       HOSTKEY_ECDSA_CERT_METHODS \
+       "ssh-rsa-cert-v01@openssh.com," \
+       "ssh-dss-cert-v01@openssh.com," \
+       "ssh-rsa-cert-v00@openssh.com," \
+       "ssh-dss-cert-v00@openssh.com," \
+       HOSTKEY_ECDSA_METHODS \
+       "ssh-rsa," \
+       "ssh-dss"
 
 #define        KEX_DEFAULT_ENCRYPT \
        "aes128-ctr,aes192-ctr,aes256-ctr," \
index 55f100a..3ef373f 100644 (file)
@@ -240,3 +240,10 @@ strdup(const char *str)
        return NULL;
 }
 #endif
+
+#ifndef HAVE_ISBLANK
+int isblank(int c)
+{
+       return (c == ' ' || c == '\t');
+}
+#endif
index b61ec42..e70c3f9 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: bsd-misc.h,v 1.18 2005/02/25 23:07:38 dtucker Exp $ */
+/* $Id: bsd-misc.h,v 1.19 2010/11/08 22:26:23 tim Exp $ */
 
 /*
  * Copyright (c) 1999-2004 Damien Miller <djm@mindrot.org>
@@ -95,4 +95,8 @@ mysig_t mysignal(int sig, mysig_t act);
 
 #define signal(a,b) mysignal(a,b)
 
+#ifndef HAVE_ISBLANK
+int    isblank(int);
+#endif
+
 #endif /* _BSD_MISC_H */
index a2b36f9..f8a7fa5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: glob.h,v 1.10 2005/12/13 00:35:22 millert Exp $       */
+/*     $OpenBSD: glob.h,v 1.11 2010/09/24 13:32:55 djm Exp $   */
 /*     $NetBSD: glob.h,v 1.5 1994/10/26 00:55:56 cgd Exp $     */
 
 /*
 /* OPENBSD ORIGINAL: include/glob.h */
 
 #if !defined(HAVE_GLOB_H) || !defined(GLOB_HAS_ALTDIRFUNC) || \
-    !defined(GLOB_HAS_GL_MATCHC) || \
+    !defined(GLOB_HAS_GL_MATCHC) || !defined(GLOB_HAS_GL_STATV) || \
     !defined(HAVE_DECL_GLOB_NOMATCH) || HAVE_DECL_GLOB_NOMATCH == 0 || \
     defined(BROKEN_GLOB)
 
 #ifndef _GLOB_H_
 #define        _GLOB_H_
 
+#include <sys/stat.h>
+
 struct stat;
 typedef struct {
        int gl_pathc;           /* Count of total paths so far. */
@@ -52,6 +54,7 @@ typedef struct {
        int gl_offs;            /* Reserved at beginning of gl_pathv. */
        int gl_flags;           /* Copy of flags parameter to glob. */
        char **gl_pathv;        /* List of paths matching pattern. */
+       struct stat **gl_statv; /* Stat entries corresponding to gl_pathv */
                                /* Copy of errfunc parameter to glob. */
        int (*gl_errfunc)(const char *, int);
 
@@ -75,12 +78,10 @@ typedef struct {
 #define        GLOB_NOSORT     0x0020  /* Don't sort. */
 #define        GLOB_NOESCAPE   0x1000  /* Disable backslash escaping. */
 
-/* Error values returned by glob(3) */
 #define        GLOB_NOSPACE    (-1)    /* Malloc call failed. */
 #define        GLOB_ABORTED    (-2)    /* Unignored error. */
 #define        GLOB_NOMATCH    (-3)    /* No match and GLOB_NOCHECK not set. */
 #define        GLOB_NOSYS      (-4)    /* Function not supported. */
-#define GLOB_ABEND     GLOB_ABORTED
 
 #define        GLOB_ALTDIRFUNC 0x0040  /* Use alternately specified directory funcs. */
 #define        GLOB_BRACE      0x0080  /* Expand braces ala csh. */
@@ -89,6 +90,8 @@ typedef struct {
 #define        GLOB_QUOTE      0x0400  /* Quote special chars with \. */
 #define        GLOB_TILDE      0x0800  /* Expand tilde names from the passwd file. */
 #define GLOB_LIMIT     0x2000  /* Limit pattern match output to ARG_MAX */
+#define        GLOB_KEEPSTAT   0x4000  /* Retain stat data for paths in gl_statv. */
+#define GLOB_ABEND     GLOB_ABORTED /* backward compatibility */
 
 int    glob(const char *, int, int (*)(const char *, int), glob_t *);
 void   globfree(glob_t *);
@@ -96,5 +99,5 @@ void  globfree(glob_t *);
 #endif /* !_GLOB_H_ */
 
 #endif /* !defined(HAVE_GLOB_H) || !defined(GLOB_HAS_ALTDIRFUNC)  ||
-         !defined(GLOB_HAS_GL_MATCHC */
+         !defined(GLOB_HAS_GL_MATCHC) || !defined(GLOH_HAS_GL_STATV) */
 
index e15d2bd..77c5ed2 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: openbsd-compat.h,v 1.50 2010/08/16 03:15:23 dtucker Exp $ */
+/* $Id: openbsd-compat.h,v 1.51 2010/10/07 10:25:29 djm Exp $ */
 
 /*
  * Copyright (c) 1999-2003 Damien Miller.  All rights reserved.
@@ -213,6 +213,10 @@ char *user_from_uid(uid_t, int);
 char *group_from_gid(gid_t, int);
 #endif
 
+#ifndef HAVE_TIMINGSAFE_BCMP
+int timingsafe_bcmp(const void *, const void *, size_t);
+#endif
+
 void *xmmap(size_t size);
 char *xcrypt(const char *password, const char *salt);
 char *shadow_pw(struct passwd *pw);
index b7caa65..6d4f3f2 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: openssl-compat.h,v 1.15 2010/05/12 07:50:02 djm Exp $ */
+/* $Id: openssl-compat.h,v 1.18 2011/01/21 22:37:06 dtucker Exp $ */
 
 /*
  * Copyright (c) 2005 Darren Tucker <dtucker@zip.com.au>
@@ -17,6 +17,7 @@
  */
 
 #include "includes.h"
+#include <openssl/opensslv.h>
 #include <openssl/evp.h>
 #include <openssl/rsa.h>
 #include <openssl/dsa.h>
 # define EVP_CIPHER_CTX_get_app_data(e)                ((e)->app_data)
 #endif
 
+#if OPENSSL_VERSION_NUMBER < 0x1000000fL
+# define LIBCRYPTO_EVP_INL_TYPE unsigned int
+#else
+# define LIBCRYPTO_EVP_INL_TYPE size_t
+#endif
+
 #if (OPENSSL_VERSION_NUMBER < 0x00907000L) || defined(OPENSSL_LOBOTOMISED_AES)
 # define USE_BUILTIN_RIJNDAEL
 #endif
@@ -71,6 +78,10 @@ extern const EVP_CIPHER *evp_acss(void);
 # define EVP_CIPHER_CTX_key_length(c) ((c)->key_len)
 #endif
 
+#ifndef HAVE_RSA_GET_DEFAULT_METHOD
+RSA_METHOD *RSA_get_default_method(void);
+#endif
+
 /*
  * We overload some of the OpenSSL crypto functions with ssh_* equivalents
  * which cater for older and/or less featureful OpenSSL version.
@@ -101,6 +112,19 @@ extern const EVP_CIPHER *evp_acss(void);
 #  define SSLeay_add_all_algorithms()  ssh_SSLeay_add_all_algorithms()
 # endif
 
+# ifndef HAVE_BN_IS_PRIME_EX
+int BN_is_prime_ex(const BIGNUM *, int, BN_CTX *, void *);
+# endif
+
+# ifndef HAVE_DSA_GENERATE_PARAMETERS_EX
+int DSA_generate_parameters_ex(DSA *, int, const unsigned char *, int, int *,
+    unsigned long *, void *);
+# endif
+
+# ifndef HAVE_RSA_GENERATE_KEY_EX
+int RSA_generate_key_ex(RSA *, int, BIGNUM *, void *);
+# endif
+
 int ssh_EVP_CipherInit(EVP_CIPHER_CTX *, const EVP_CIPHER *, unsigned char *,
     unsigned char *, int);
 int ssh_EVP_Cipher(EVP_CIPHER_CTX *, char *, char *, int);
index 209d9a7..c2f6184 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: port-linux.h,v 1.4 2009/12/08 02:39:48 dtucker Exp $ */
+/* $Id: port-linux.h,v 1.4.10.1 2011/02/04 00:42:21 djm Exp $ */
 
 /*
  * Copyright (c) 2006 Damien Miller <djm@openbsd.org>
@@ -24,6 +24,7 @@ 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 *);
+void ssh_selinux_setfscreatecon(const char *);
 #endif
 
 #ifdef LINUX_OOM_ADJUST
index 4c32487..cd442e7 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: port-solaris.h,v 1.1 2006/08/30 17:24:42 djm Exp $ */
+/* $Id: port-solaris.h,v 1.2 2010/11/05 01:03:05 dtucker Exp $ */
 
 /*
  * Copyright (c) 2006 Chad Mynhier.
 
 #include <sys/types.h>
 
+#include <pwd.h>
+
 void solaris_contract_pre_fork(void);
 void solaris_contract_post_fork_child(void);
 void solaris_contract_post_fork_parent(pid_t pid);
+void solaris_set_default_project(struct passwd *);
 
 #endif
similarity index 61%
copy from crypto/openssh/platform.h
copy to crypto/openssh/openbsd-compat/timingsafe_bcmp.c
index 30a1d22..7e28c0e 100644 (file)
@@ -1,7 +1,6 @@
-/* $Id: platform.h,v 1.4 2010/01/14 01:44:16 djm Exp $ */
-
+/*     $OpenBSD: timingsafe_bcmp.c,v 1.1 2010/09/24 13:33:00 matthew Exp $     */
 /*
- * Copyright (c) 2006 Darren Tucker.  All rights reserved.
+ * Copyright (c) 2010 Damien Miller.  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
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <sys/types.h>
+/* OPENBSD ORIGINAL: lib/libc/string/timingsafe_bcmp.c */
+
+#include "includes.h"
+#ifndef HAVE_TIMINGSAFE_BCMP
 
-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 *);
+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);
+}
 
+#endif /* TIMINGSAFE_BCMP */
index 48f7fe6..b4e01f7 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: packet.c,v 1.168 2010/07/13 23:13:16 djm Exp $ */
+/* $OpenBSD: packet.c,v 1.172 2010/11/13 23:27:50 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -199,13 +199,13 @@ static struct session_state *active_state, *backup_state;
 static struct session_state *
 alloc_session_state(void)
 {
-    struct session_state *s = xcalloc(1, sizeof(*s));
+       struct session_state *s = xcalloc(1, sizeof(*s));
 
-    s->connection_in = -1;
-    s->connection_out = -1;
-    s->max_packet_size = 32768;
-    s->packet_timeout_ms = -1;
-    return s;
+       s->connection_in = -1;
+       s->connection_out = -1;
+       s->max_packet_size = 32768;
+       s->packet_timeout_ms = -1;
+       return s;
 }
 
 /*
@@ -391,8 +391,8 @@ packet_get_ssh1_cipher(void)
 }
 
 void
-packet_get_state(int mode, u_int32_t *seqnr, u_int64_t *blocks, u_int32_t *packets,
-    u_int64_t *bytes)
+packet_get_state(int mode, u_int32_t *seqnr, u_int64_t *blocks,
+    u_int32_t *packets, u_int64_t *bytes)
 {
        struct packet_state *state;
 
@@ -547,8 +547,7 @@ packet_start_compression(int level)
  */
 
 void
-packet_set_encryption_key(const u_char *key, u_int keylen,
-    int number)
+packet_set_encryption_key(const u_char *key, u_int keylen, int number)
 {
        Cipher *cipher = cipher_by_number(number);
 
@@ -641,6 +640,14 @@ packet_put_bignum2(BIGNUM * value)
        buffer_put_bignum2(&active_state->outgoing_packet, value);
 }
 
+#ifdef OPENSSL_HAS_ECC
+void
+packet_put_ecpoint(const EC_GROUP *curve, const EC_POINT *point)
+{
+       buffer_put_ecpoint(&active_state->outgoing_packet, curve, point);
+}
+#endif
+
 /*
  * Finalizes and sends the packet.  If the encryption key has been set,
  * encrypts the packet before sending.
@@ -1511,6 +1518,14 @@ packet_get_bignum2(BIGNUM * value)
        buffer_get_bignum2(&active_state->incoming_packet, value);
 }
 
+#ifdef OPENSSL_HAS_ECC
+void
+packet_get_ecpoint(const EC_GROUP *curve, EC_POINT *point)
+{
+       buffer_get_ecpoint(&active_state->incoming_packet, curve, point);
+}
+#endif
+
 void *
 packet_get_raw(u_int *length_ptr)
 {
@@ -1546,6 +1561,13 @@ packet_get_string_ptr(u_int *length_ptr)
        return buffer_get_string_ptr(&active_state->incoming_packet, length_ptr);
 }
 
+/* Ensures the returned string has no embedded \0 characters in it. */
+char *
+packet_get_cstring(u_int *length_ptr)
+{
+       return buffer_get_cstring(&active_state->incoming_packet, length_ptr);
+}
+
 /*
  * Sends a diagnostic message from the server to the client.  This message
  * can be sent at any time (but not while constructing another message). The
@@ -1728,14 +1750,13 @@ packet_not_very_much_data_to_write(void)
 }
 
 static void
-packet_set_tos(int interactive)
+packet_set_tos(int tos)
 {
 #if defined(IP_TOS) && !defined(IP_TOS_IS_BROKEN)
-       int tos = interactive ? IPTOS_LOWDELAY : IPTOS_THROUGHPUT;
-
        if (!packet_connection_is_on_socket() ||
            !packet_connection_is_ipv4())
                return;
+       debug3("%s: set IP_TOS 0x%02x", __func__, tos);
        if (setsockopt(active_state->connection_in, IPPROTO_IP, IP_TOS, &tos,
            sizeof(tos)) < 0)
                error("setsockopt IP_TOS %d: %.100s:",
@@ -1746,7 +1767,7 @@ packet_set_tos(int interactive)
 /* Informs that the current session is interactive.  Sets IP flags for that. */
 
 void
-packet_set_interactive(int interactive)
+packet_set_interactive(int interactive, int qos_interactive, int qos_bulk)
 {
        if (active_state->set_interactive_called)
                return;
@@ -1759,7 +1780,7 @@ packet_set_interactive(int interactive)
        if (!packet_connection_is_on_socket())
                return;
        set_nodelay(active_state->connection_in);
-       packet_set_tos(interactive);
+       packet_set_tos(interactive ? qos_interactive : qos_bulk);
 }
 
 /* Returns true if the current connection is interactive. */
index 33523d7..d516aae 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: packet.h,v 1.52 2009/06/27 09:29:06 andreas Exp $ */
+/* $OpenBSD: packet.h,v 1.55 2010/11/13 23:27:50 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -19,6 +19,9 @@
 #include <termios.h>
 
 #include <openssl/bn.h>
+#ifdef OPENSSL_HAS_ECC
+#include <openssl/ec.h>
+#endif
 
 void     packet_set_connection(int, int);
 void     packet_set_timeout(int, int);
@@ -31,7 +34,7 @@ u_int  packet_get_encryption_key(u_char *);
 void     packet_set_protocol_flags(u_int);
 u_int   packet_get_protocol_flags(void);
 void     packet_start_compression(int);
-void     packet_set_interactive(int);
+void     packet_set_interactive(int, int, int);
 int      packet_is_interactive(void);
 void     packet_set_server(void);
 void     packet_set_authenticated(void);
@@ -42,6 +45,9 @@ void     packet_put_int(u_int value);
 void     packet_put_int64(u_int64_t value);
 void     packet_put_bignum(BIGNUM * value);
 void     packet_put_bignum2(BIGNUM * value);
+#ifdef OPENSSL_HAS_ECC
+void     packet_put_ecpoint(const EC_GROUP *, const EC_POINT *);
+#endif
 void     packet_put_string(const void *buf, u_int len);
 void     packet_put_cstring(const char *str);
 void     packet_put_raw(const void *buf, u_int len);
@@ -59,8 +65,12 @@ u_int         packet_get_int(void);
 u_int64_t packet_get_int64(void);
 void     packet_get_bignum(BIGNUM * value);
 void     packet_get_bignum2(BIGNUM * value);
+#ifdef OPENSSL_HAS_ECC
+void    packet_get_ecpoint(const EC_GROUP *, EC_POINT *);
+#endif
 void   *packet_get_raw(u_int *length_ptr);
 void   *packet_get_string(u_int *length_ptr);
+char   *packet_get_cstring(u_int *length_ptr);
 void   *packet_get_string_ptr(u_int *length_ptr);
 void     packet_disconnect(const char *fmt,...) __attribute__((format(printf, 1, 2)));
 void     packet_send_debug(const char *fmt,...) __attribute__((format(printf, 1, 2)));
index 9e50950..e2dd49a 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: pathnames.h,v 1.19 2010/02/11 20:37:47 djm Exp $ */
+/* $OpenBSD: pathnames.h,v 1.20 2010/08/31 11:54:45 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -38,6 +38,7 @@
 #define _PATH_HOST_CONFIG_FILE         SSHDIR "/ssh_config"
 #define _PATH_HOST_KEY_FILE            SSHDIR "/ssh_host_key"
 #define _PATH_HOST_DSA_KEY_FILE                SSHDIR "/ssh_host_dsa_key"
+#define _PATH_HOST_ECDSA_KEY_FILE      SSHDIR "/ssh_host_ecdsa_key"
 #define _PATH_HOST_RSA_KEY_FILE                SSHDIR "/ssh_host_rsa_key"
 #define _PATH_DH_MODULI                        SSHDIR "/moduli"
 /* Backwards compatibility */
@@ -74,6 +75,7 @@
  */
 #define _PATH_SSH_CLIENT_IDENTITY      ".ssh/identity"
 #define _PATH_SSH_CLIENT_ID_DSA                ".ssh/id_dsa"
+#define _PATH_SSH_CLIENT_ID_ECDSA      ".ssh/id_ecdsa"
 #define _PATH_SSH_CLIENT_ID_RSA                ".ssh/id_rsa"
 
 /*
index e3a428a..a455472 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: platform.c,v 1.3 2009/12/20 23:49:22 dtucker Exp $ */
+/* $Id: platform.c,v 1.18 2011/01/11 06:02:25 djm Exp $ */
 
 /*
  * Copyright (c) 2006 Darren Tucker.  All rights reserved.
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include "config.h"
+#include "includes.h"
+
+#include <sys/types.h>
+
+#include <stdarg.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "buffer.h"
+#include "servconf.h"
+#include "key.h"
+#include "hostfile.h"
+#include "auth.h"
+#include "auth-pam.h"
 #include "platform.h"
 
 #include "openbsd-compat/openbsd-compat.h"
 
+extern int use_privsep;
+extern ServerOptions options;
+
 void
 platform_pre_listen(void)
 {
@@ -57,6 +73,118 @@ platform_post_fork_child(void)
 #endif
 }
 
+/* return 1 if we are running with privilege to swap UIDs, 0 otherwise */
+int
+platform_privileged_uidswap(void)
+{
+#ifdef HAVE_CYGWIN
+       /* uid 0 is not special on Cygwin so always try */
+       return 1;
+#else
+       return (getuid() == 0 || geteuid() == 0);
+#endif
+}
+
+/*
+ * This gets called before switching UIDs, and is called even when sshd is
+ * not running as root.
+ */
+void
+platform_setusercontext(struct passwd *pw)
+{
+#ifdef WITH_SELINUX
+       /* Cache selinux status for later use */
+       (void)ssh_selinux_enabled();
+#endif
+
+#ifdef USE_SOLARIS_PROJECTS
+       /* if solaris projects were detected, set the default now */
+       if (getuid() == 0 || geteuid() == 0)
+               solaris_set_default_project(pw);
+#endif
+
+#if defined(HAVE_LOGIN_CAP) && defined (__bsdi__)
+       if (getuid() == 0 || geteuid() == 0)
+               setpgid(0, 0);
+# endif
+
+#if defined(HAVE_LOGIN_CAP) && defined(USE_PAM)
+       /*
+        * If we have both LOGIN_CAP and PAM, we want to establish creds
+        * before calling setusercontext (in session.c:do_setusercontext).
+        */
+       if (getuid() == 0 || geteuid() == 0) {
+               if (options.use_pam) {
+                       do_pam_setcred(use_privsep);
+               }
+       }
+# endif /* USE_PAM */
+
+#if !defined(HAVE_LOGIN_CAP) && defined(HAVE_GETLUID) && defined(HAVE_SETLUID)
+       if (getuid() == 0 || geteuid() == 0) {
+               /* Sets login uid for accounting */
+               if (getluid() == -1 && setluid(pw->pw_uid) == -1)
+                       error("setluid: %s", strerror(errno));
+       }
+#endif
+}
+
+/*
+ * This gets called after we've established the user's groups, and is only
+ * called if sshd is running as root.
+ */
+void
+platform_setusercontext_post_groups(struct passwd *pw)
+{
+#if !defined(HAVE_LOGIN_CAP) && defined(USE_PAM)
+       /*
+        * PAM credentials may take the form of supplementary groups.
+        * These will have been wiped by the above initgroups() call.
+        * Reestablish them here.
+        */
+       if (options.use_pam) {
+               do_pam_setcred(use_privsep);
+       }
+#endif /* USE_PAM */
+
+#if !defined(HAVE_LOGIN_CAP) && (defined(WITH_IRIX_PROJECT) || \
+    defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY))
+       irix_setusercontext(pw);
+#endif /* defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) */
+
+#ifdef _AIX
+       aix_usrinfo(pw);
+#endif /* _AIX */
+
+#if !defined(HAVE_LOGIN_CAP) && defined(USE_LIBIAF)
+       if (set_id(pw->pw_name) != 0) {
+               exit(1);
+       }
+# endif /* USE_LIBIAF */
+
+#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
+}
+
 char *
 platform_krb5_get_principal_name(const char *pw_name)
 {
index 30a1d22..944d2c3 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: platform.h,v 1.4 2010/01/14 01:44:16 djm Exp $ */
+/* $Id: platform.h,v 1.7 2010/11/05 03:47:01 dtucker Exp $ */
 
 /*
  * Copyright (c) 2006 Darren Tucker.  All rights reserved.
 
 #include <sys/types.h>
 
+#include <pwd.h>
+
 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);
+int  platform_privileged_uidswap(void);
+void platform_setusercontext(struct passwd *);
+void platform_setusercontext_post_groups(struct passwd *);
 char *platform_get_krb5_client(const char *);
 char *platform_krb5_get_principal_name(const char *);
 
index 0296590..eb4a8b9 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.c,v 1.187 2010/07/19 09:15:12 djm Exp $ */
+/* $OpenBSD: readconf.c,v 1.190 2010/11/13 23:27:50 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -19,6 +19,8 @@
 #include <sys/socket.h>
 
 #include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
 
 #include <ctype.h>
 #include <errno.h>
@@ -132,6 +134,7 @@ typedef enum {
        oHashKnownHosts,
        oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
        oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication,
+       oKexAlgorithms, oIPQoS,
        oDeprecated, oUnsupported
 } OpCodes;
 
@@ -240,6 +243,8 @@ static struct {
 #else
        { "zeroknowledgepasswordauthentication", oUnsupported },
 #endif
+       { "kexalgorithms", oKexAlgorithms },
+       { "ipqos", oIPQoS },
 
        { NULL, oBadOption }
 };
@@ -699,6 +704,18 @@ parse_int:
                        options->macs = xstrdup(arg);
                break;
 
+       case oKexAlgorithms:
+               arg = strdelim(&s);
+               if (!arg || *arg == '\0')
+                       fatal("%.200s line %d: Missing argument.",
+                           filename, linenum);
+               if (!kex_names_valid(arg))
+                       fatal("%.200s line %d: Bad SSH2 KexAlgorithms '%s'.",
+                           filename, linenum, arg ? arg : "<NONE>");
+               if (*activep && options->kex_algorithms == NULL)
+                       options->kex_algorithms = xstrdup(arg);
+               break;
+
        case oHostKeyAlgorithms:
                arg = strdelim(&s);
                if (!arg || *arg == '\0')
@@ -959,6 +976,23 @@ parse_int:
                intptr = &options->visual_host_key;
                goto parse_flag;
 
+       case oIPQoS:
+               arg = strdelim(&s);
+               if ((value = parse_ipqos(arg)) == -1)
+                       fatal("%s line %d: Bad IPQoS value: %s",
+                           filename, linenum, arg);
+               arg = strdelim(&s);
+               if (arg == NULL)
+                       value2 = value;
+               else if ((value2 = parse_ipqos(arg)) == -1)
+                       fatal("%s line %d: Bad IPQoS value: %s",
+                           filename, linenum, arg);
+               if (*activep) {
+                       options->ip_qos_interactive = value;
+                       options->ip_qos_bulk = value2;
+               }
+               break;
+
        case oUseRoaming:
                intptr = &options->use_roaming;
                goto parse_flag;
@@ -1078,6 +1112,7 @@ initialize_options(Options * options)
        options->cipher = -1;
        options->ciphers = NULL;
        options->macs = NULL;
+       options->kex_algorithms = NULL;
        options->hostkeyalgorithms = NULL;
        options->protocol = SSH_PROTO_UNKNOWN;
        options->num_identity_files = 0;
@@ -1120,6 +1155,8 @@ initialize_options(Options * options)
        options->use_roaming = -1;
        options->visual_host_key = -1;
        options->zero_knowledge_password_authentication = -1;
+       options->ip_qos_interactive = -1;
+       options->ip_qos_bulk = -1;
 }
 
 /*
@@ -1191,6 +1228,7 @@ fill_default_options(Options * options)
                options->cipher = SSH_CIPHER_NOT_SET;
        /* options->ciphers, default set in myproposals.h */
        /* options->macs, default set in myproposals.h */
+       /* options->kex_algorithms, default set in myproposals.h */
        /* options->hostkeyalgorithms, default set in myproposals.h */
        if (options->protocol == SSH_PROTO_UNKNOWN)
                options->protocol = SSH_PROTO_2;
@@ -1214,6 +1252,13 @@ fill_default_options(Options * options)
                            xmalloc(len);
                        snprintf(options->identity_files[options->num_identity_files++],
                            len, "~/%.100s", _PATH_SSH_CLIENT_ID_DSA);
+#ifdef OPENSSL_HAS_ECC
+                       len = 2 + strlen(_PATH_SSH_CLIENT_ID_ECDSA) + 1;
+                       options->identity_files[options->num_identity_files] =
+                           xmalloc(len);
+                       snprintf(options->identity_files[options->num_identity_files++],
+                           len, "~/%.100s", _PATH_SSH_CLIENT_ID_ECDSA);
+#endif
                }
        }
        if (options->escape_char == -1)
@@ -1266,6 +1311,10 @@ fill_default_options(Options * options)
                options->visual_host_key = 0;
        if (options->zero_knowledge_password_authentication == -1)
                options->zero_knowledge_password_authentication = 0;
+       if (options->ip_qos_interactive == -1)
+               options->ip_qos_interactive = IPTOS_LOWDELAY;
+       if (options->ip_qos_bulk == -1)
+               options->ip_qos_bulk = IPTOS_THROUGHPUT;
        /* options->local_command should not be set by default */
        /* options->proxy_command should not be set by default */
        /* options->user will be set in the main program if appropriate */
index 95d1046..ee160df 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.h,v 1.86 2010/07/19 09:15:12 djm Exp $ */
+/* $OpenBSD: readconf.h,v 1.88 2010/11/13 23:27:50 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -59,6 +59,8 @@ typedef struct {
        int     compression_level;      /* Compression level 1 (fast) to 9
                                         * (best). */
        int     tcp_keep_alive; /* Set SO_KEEPALIVE. */
+       int     ip_qos_interactive;     /* IP ToS/DSCP/class for interactive */
+       int     ip_qos_bulk;            /* IP ToS/DSCP/class for bulk traffic */
        LogLevel log_level;     /* Level for logging. */
 
        int     port;           /* Port to connect. */
@@ -73,6 +75,7 @@ typedef struct {
        char   *ciphers;        /* SSH2 ciphers in order of preference. */
        char   *macs;           /* SSH2 macs in order of preference. */
        char   *hostkeyalgorithms;      /* SSH2 server key types in order of preference. */
+       char   *kex_algorithms; /* SSH2 kex methods in order of preference. */
        int     protocol;       /* Protocol in order of preference. */
        char   *hostname;       /* Real host to connect. */
        char   *host_key_alias; /* hostname alias for .ssh/known_hosts */
index bd144c2..599c8ef 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: readpass.c,v 1.47 2006/08/03 03:34:42 deraadt Exp $ */
+/* $OpenBSD: readpass.c,v 1.48 2010/12/15 00:49:27 djm Exp $ */
 /*
  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
  *
@@ -33,6 +33,7 @@
 #ifdef HAVE_PATHS_H
 # include <paths.h>
 #endif
+#include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 static char *
 ssh_askpass(char *askpass, const char *msg)
 {
-       pid_t pid;
+       pid_t pid, ret;
        size_t len;
        char *pass;
-       int p[2], status, ret;
+       int p[2], status;
        char buf[1024];
+       void (*osigchld)(int);
 
        if (fflush(stdout) != 0)
                error("ssh_askpass: fflush: %s", strerror(errno));
@@ -63,8 +65,10 @@ ssh_askpass(char *askpass, const char *msg)
                error("ssh_askpass: pipe: %s", strerror(errno));
                return NULL;
        }
+       osigchld = signal(SIGCHLD, SIG_DFL);
        if ((pid = fork()) < 0) {
                error("ssh_askpass: fork: %s", strerror(errno));
+               signal(SIGCHLD, osigchld);
                return NULL;
        }
        if (pid == 0) {
@@ -77,23 +81,24 @@ ssh_askpass(char *askpass, const char *msg)
        }
        close(p[1]);
 
-       len = ret = 0;
+       len = 0;
        do {
-               ret = read(p[0], buf + len, sizeof(buf) - 1 - len);
-               if (ret == -1 && errno == EINTR)
+               ssize_t r = read(p[0], buf + len, sizeof(buf) - 1 - len);
+
+               if (r == -1 && errno == EINTR)
                        continue;
-               if (ret <= 0)
+               if (r <= 0)
                        break;
-               len += ret;
+               len += r;
        } while (sizeof(buf) - 1 - len > 0);
        buf[len] = '\0';
 
        close(p[0]);
-       while (waitpid(pid, &status, 0) < 0)
+       while ((ret = waitpid(pid, &status, 0)) < 0)
                if (errno != EINTR)
                        break;
-
-       if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+       signal(SIGCHLD, osigchld);
+       if (ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
                memset(buf, 0, sizeof(buf));
                return NULL;
        }
index c17ff32..4d54d68 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: schnorr.c,v 1.3 2009/03/05 07:18:19 djm Exp $ */
+/* $OpenBSD: schnorr.c,v 1.5 2010/12/03 23:49:26 djm Exp $ */
 /*
  * Copyright (c) 2008 Damien Miller.  All rights reserved.
  *
@@ -138,6 +138,10 @@ schnorr_sign(const BIGNUM *grp_p, const BIGNUM *grp_q, const BIGNUM *grp_g,
                error("%s: g_x < 1", __func__);
                return -1;
        }
+       if (BN_cmp(g_x, grp_p) >= 0) {
+               error("%s: g_x > g", __func__);
+               return -1;
+       }
 
        h = g_v = r = tmp = v = NULL;
        if ((bn_ctx = BN_CTX_new()) == NULL) {
@@ -254,14 +258,19 @@ schnorr_verify(const BIGNUM *grp_p, const BIGNUM *grp_q, const BIGNUM *grp_g,
     const BIGNUM *r, const BIGNUM *e)
 {
        int success = -1;
-       BIGNUM *h, *g_xh, *g_r, *expected;
+       BIGNUM *h = NULL, *g_xh = NULL, *g_r = NULL, *gx_q = NULL;
+       BIGNUM *expected = NULL;
        BN_CTX *bn_ctx;
 
        SCHNORR_DEBUG_BN((g_x, "%s: g_x = ", __func__));
 
        /* Avoid degenerate cases: g^0 yields a spoofable signature */
        if (BN_cmp(g_x, BN_value_one()) <= 0) {
-               error("%s: g_x < 1", __func__);
+               error("%s: g_x <= 1", __func__);
+               return -1;
+       }
+       if (BN_cmp(g_x, grp_p) >= 0) {
+               error("%s: g_x >= p", __func__);
                return -1;
        }
 
@@ -272,6 +281,7 @@ schnorr_verify(const BIGNUM *grp_p, const BIGNUM *grp_q, const BIGNUM *grp_g,
        }
        if ((g_xh = BN_new()) == NULL ||
            (g_r = BN_new()) == NULL ||
+           (gx_q = BN_new()) == NULL ||
            (expected = BN_new()) == NULL) {
                error("%s: BN_new", __func__);
                goto out;
@@ -280,6 +290,17 @@ schnorr_verify(const BIGNUM *grp_p, const BIGNUM *grp_q, const BIGNUM *grp_g,
        SCHNORR_DEBUG_BN((e, "%s: e = ", __func__));
        SCHNORR_DEBUG_BN((r, "%s: r = ", __func__));
 
+       /* gx_q = (g^x)^q must === 1 mod p */
+       if (BN_mod_exp(gx_q, g_x, grp_q, grp_p, bn_ctx) == -1) {
+               error("%s: BN_mod_exp (g_x^q mod p)", __func__);
+               goto out;
+       }
+       if (BN_cmp(gx_q, BN_value_one()) != 0) {
+               error("%s: Invalid signature (g^x)^q != 1 mod p", __func__);
+               goto out;
+       }
+
+       SCHNORR_DEBUG_BN((g_xh, "%s: g_xh = ", __func__));
        /* h = H(g || g^v || g^x || id) */
        if ((h = schnorr_hash(grp_p, grp_q, grp_g, evp_md, e, g_x,
            id, idlen)) == NULL) {
@@ -314,9 +335,14 @@ schnorr_verify(const BIGNUM *grp_p, const BIGNUM *grp_q, const BIGNUM *grp_g,
        BN_CTX_free(bn_ctx);
        if (h != NULL)
                BN_clear_free(h);
-       BN_clear_free(g_xh);
-       BN_clear_free(g_r);
-       BN_clear_free(expected);
+       if (gx_q != NULL)
+               BN_clear_free(gx_q);
+       if (g_xh != NULL)
+               BN_clear_free(g_xh);
+       if (g_r != NULL)
+               BN_clear_free(g_r);
+       if (expected != NULL)
+               BN_clear_free(expected);
        return success;
 }
 
index bc5e259..577dd52 100644 (file)
@@ -1,4 +1,3 @@
-.\"  -*- nroff -*-
 .\"
 .\" scp.1
 .\"
@@ -9,9 +8,9 @@
 .\"
 .\" Created: Sun May  7 00:14:37 1995 ylo
 .\"
-.\" $OpenBSD: scp.1,v 1.50 2010/02/08 10:50:20 markus Exp $
+.\" $OpenBSD: scp.1,v 1.56 2010/12/09 14:13:32 jmc Exp $
 .\"
-.Dd $Mdocdate: February 8 2010 $
+.Dd $Mdocdate: December 9 2010 $
 .Dt SCP 1
 .Os
 .Sh NAME
@@ -20,7 +19,7 @@
 .Sh SYNOPSIS
 .Nm scp
 .Bk -words
-.Op Fl 1246BCpqrv
+.Op Fl 12346BCpqrv
 .Op Fl c Ar cipher
 .Op Fl F Ar ssh_config
 .Op Fl i Ar identity_file
@@ -76,6 +75,11 @@ to use protocol 1.
 Forces
 .Nm
 to use protocol 2.
+.It Fl 3
+Copies between two remote hosts are transferred through the local host.
+Without this option the data is copied directly between the two remote
+hosts.
+Note that this option disables the progress meter.
 .It Fl 4
 Forces
 .Nm
@@ -147,7 +151,9 @@ For full details of the options listed below, and their possible values, see
 .It HostName
 .It IdentityFile
 .It IdentitiesOnly
+.It IPQoS
 .It KbdInteractiveDevices
+.It KexAlgorithms
 .It LogLevel
 .It MACs
 .It NoHostAuthenticationForLocalhost
@@ -209,7 +215,7 @@ to print debugging messages about their progress.
 This is helpful in
 debugging connection, authentication, and configuration problems.
 .El
-.Pp
+.Sh EXIT STATUS
 .Ex -std scp
 .Sh SEE ALSO
 .Xr rcp 1 ,
index e07de42..18b2597 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: scp.c,v 1.166 2010/07/01 13:06:59 millert Exp $ */
+/* $OpenBSD: scp.c,v 1.170 2010/12/09 14:13:33 jmc Exp $ */
 /*
  * scp - secure remote copy.  This is basically patched BSD rcp which
  * uses ssh to do the data transfer (instead of using rcmd).
@@ -119,14 +119,15 @@ extern char *__progname;
 #define COPY_BUFLEN    16384
 
 int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout);
-
-void bwlimit(int);
+int do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout);
 
 /* Struct for addargs */
 arglist args;
+arglist remote_remote_args;
 
 /* Bandwidth limit */
-off_t limit_rate = 0;
+long long limit_kbps = 0;
+struct bwlimit bwlimit;
 
 /* Name of current file being transferred. */
 char *curfile;
@@ -137,6 +138,12 @@ int verbose_mode = 0;
 /* This is set to zero if the progressmeter is not desired. */
 int showprogress = 1;
 
+/*
+ * This is set to non-zero if remote-remote copy should be piped
+ * through this process.
+ */
+int throughlocal = 0;
+
 /* This is the program to execute for the secured connection. ("ssh" or -S) */
 char *ssh_program = _PATH_SSH_PROGRAM;
 
@@ -287,6 +294,50 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout)
        return 0;
 }
 
+/*
+ * This functions executes a command simlar to do_cmd(), but expects the
+ * input and output descriptors to be setup by a previous call to do_cmd().
+ * This way the input and output of two commands can be connected.
+ */
+int
+do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout)
+{
+       pid_t pid;
+       int status;
+
+       if (verbose_mode)
+               fprintf(stderr,
+                   "Executing: 2nd program %s host %s, user %s, command %s\n",
+                   ssh_program, host,
+                   remuser ? remuser : "(unspecified)", cmd);
+
+       /* Fork a child to execute the command on the remote host using ssh. */
+       pid = fork();
+       if (pid == 0) {
+               dup2(fdin, 0);
+               dup2(fdout, 1);
+
+               replacearg(&args, 0, "%s", ssh_program);
+               if (remuser != NULL) {
+                       addargs(&args, "-l");
+                       addargs(&args, "%s", remuser);
+               }
+               addargs(&args, "--");
+               addargs(&args, "%s", host);
+               addargs(&args, "%s", cmd);
+
+               execvp(ssh_program, args.list);
+               perror(ssh_program);
+               exit(1);
+       } else if (pid == -1) {
+               fatal("fork: %s", strerror(errno));
+       }
+       while (waitpid(pid, &status, 0) == -1)
+               if (errno != EINTR)
+                       fatal("do_cmd2: waitpid: %s", strerror(errno));
+       return 0;
+}
+
 typedef struct {
        size_t cnt;
        char *buf;
@@ -312,15 +363,14 @@ void sink(int, char *[]);
 void source(int, char *[]);
 void tolocal(int, char *[]);
 void toremote(char *, int, char *[]);
-size_t scpio(ssize_t (*)(int, void *, size_t), int, void *, size_t, off_t *);
 void usage(void);
 
 int
 main(int argc, char **argv)
 {
        int ch, fflag, tflag, status, n;
-       double speed;
-       char *targ, *endp, **newargv;
+       char *targ, **newargv;
+       const char *errstr;
        extern char *optarg;
        extern int optind;
 
@@ -336,15 +386,16 @@ main(int argc, char **argv)
        __progname = ssh_get_progname(argv[0]);
 
        memset(&args, '\0', sizeof(args));
-       args.list = NULL;
+       memset(&remote_remote_args, '\0', sizeof(remote_remote_args));
+       args.list = remote_remote_args.list = NULL;
        addargs(&args, "%s", ssh_program);
        addargs(&args, "-x");
-       addargs(&args, "-oForwardAgent no");
-       addargs(&args, "-oPermitLocalCommand no");
-       addargs(&args, "-oClearAllForwardings yes");
+       addargs(&args, "-oForwardAgent=no");
+       addargs(&args, "-oPermitLocalCommand=no");
+       addargs(&args, "-oClearAllForwardings=yes");
 
        fflag = tflag = 0;
-       while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q1246S:o:F:")) != -1)
+       while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1)
                switch (ch) {
                /* User-visible flags. */
                case '1':
@@ -353,26 +404,37 @@ main(int argc, char **argv)
                case '6':
                case 'C':
                        addargs(&args, "-%c", ch);
+                       addargs(&remote_remote_args, "-%c", ch);
+                       break;
+               case '3':
+                       throughlocal = 1;
                        break;
                case 'o':
                case 'c':
                case 'i':
                case 'F':
+                       addargs(&remote_remote_args, "-%c", ch);
+                       addargs(&remote_remote_args, "%s", optarg);
                        addargs(&args, "-%c", ch);
                        addargs(&args, "%s", optarg);
                        break;
                case 'P':
+                       addargs(&remote_remote_args, "-p");
+                       addargs(&remote_remote_args, "%s", optarg);
                        addargs(&args, "-p");
                        addargs(&args, "%s", optarg);
                        break;
                case 'B':
-                       addargs(&args, "-oBatchmode yes");
+                       addargs(&remote_remote_args, "-oBatchmode=yes");
+                       addargs(&args, "-oBatchmode=yes");
                        break;
                case 'l':
-                       speed = strtod(optarg, &endp);
-                       if (speed <= 0 || *endp != '\0')
+                       limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
+                           &errstr);
+                       if (errstr != NULL)
                                usage();
-                       limit_rate = speed * 1024;
+                       limit_kbps *= 1024; /* kbps */
+                       bandwidth_limit_init(&bwlimit, limit_kbps, COPY_BUFLEN);
                        break;
                case 'p':
                        pflag = 1;
@@ -385,10 +447,12 @@ main(int argc, char **argv)
                        break;
                case 'v':
                        addargs(&args, "-v");
+                       addargs(&remote_remote_args, "-v");
                        verbose_mode = 1;
                        break;
                case 'q':
                        addargs(&args, "-q");
+                       addargs(&remote_remote_args, "-q");
                        showprogress = 0;
                        break;
 
@@ -474,41 +538,16 @@ main(int argc, char **argv)
        exit(errs != 0);
 }
 
-/*
- * atomicio-like wrapper that also applies bandwidth limits and updates
- * the progressmeter counter.
- */
-size_t
-scpio(ssize_t (*f)(int, void *, size_t), int fd, void *_p, size_t l, off_t *c)
+/* Callback from atomicio6 to update progress meter and limit bandwidth */
+static int
+scpio(void *_cnt, size_t s)
 {
-       u_char *p = (u_char *)_p;
-       size_t offset;
-       ssize_t r;
-       struct pollfd pfd;
-
-       pfd.fd = fd;
-       pfd.events = f == read ? POLLIN : POLLOUT;
-       for (offset = 0; offset < l;) {
-               r = f(fd, p + offset, l - offset);
-               if (r == 0) {
-                       errno = EPIPE;
-                       return offset;
-               }
-               if (r < 0) {
-                       if (errno == EINTR)
-                               continue;
-                       if (errno == EAGAIN || errno == EWOULDBLOCK) {
-                               (void)poll(&pfd, 1, -1); /* Ignore errors */
-                               continue;
-                       }
-                       return offset;
-               }
-               offset += (size_t)r;
-               *c += (off_t)r;
-               if (limit_rate)
-                       bwlimit(r);
-       }
-       return offset;
+       off_t *cnt = (off_t *)_cnt;
+
+       *cnt += s;
+       if (limit_kbps > 0)
+               bandwidth_limit(&bwlimit, s);
+       return 0;
 }
 
 void
@@ -517,6 +556,7 @@ toremote(char *targ, int argc, char **argv)
        char *bp, *host, *src, *suser, *thost, *tuser, *arg;
        arglist alist;
        int i;
+       u_int j;
 
        memset(&alist, '\0', sizeof(alist));
        alist.list = NULL;
@@ -544,15 +584,45 @@ toremote(char *targ, int argc, char **argv)
 
        for (i = 0; i < argc - 1; i++) {
                src = colon(argv[i]);
-               if (src) {      /* remote to remote */
+               if (src && throughlocal) {      /* extended remote to remote */
+                       *src++ = 0;
+                       if (*src == 0)
+                               src = ".";
+                       host = strrchr(argv[i], '@');
+                       if (host) {
+                               *host++ = 0;
+                               host = cleanhostname(host);
+                               suser = argv[i];
+                               if (*suser == '\0')
+                                       suser = pwd->pw_name;
+                               else if (!okname(suser))
+                                       continue;
+                       } else {
+                               host = cleanhostname(argv[i]);
+                               suser = NULL;
+                       }
+                       xasprintf(&bp, "%s -f -- %s", cmd, src);
+                       if (do_cmd(host, suser, bp, &remin, &remout) < 0)
+                               exit(1);
+                       (void) xfree(bp);
+                       host = cleanhostname(thost);
+                       xasprintf(&bp, "%s -t -- %s", cmd, targ);
+                       if (do_cmd2(host, tuser, bp, remin, remout) < 0)
+                               exit(1);
+                       (void) xfree(bp);
+                       (void) close(remin);
+                       (void) close(remout);
+                       remin = remout = -1;
+               } else if (src) {       /* standard remote to remote */
                        freeargs(&alist);
                        addargs(&alist, "%s", ssh_program);
-                       if (verbose_mode)
-                               addargs(&alist, "-v");
                        addargs(&alist, "-x");
-                       addargs(&alist, "-oClearAllForwardings yes");
+                       addargs(&alist, "-oClearAllForwardings=yes");
                        addargs(&alist, "-n");
-
+                       for (j = 0; j < remote_remote_args.num; j++) {
+                               addargs(&alist, "%s",
+                                   remote_remote_args.list[j]);
+                       }
                        *src++ = 0;
                        if (*src == 0)
                                src = ".";
@@ -750,7 +820,7 @@ next:                       if (fd != -1) {
                                (void)atomicio(vwrite, remout, bp->buf, amt);
                                continue;
                        }
-                       if (scpio(vwrite, remout, bp->buf, amt,
+                       if (atomicio6(vwrite, remout, bp->buf, amt, scpio,
                            &statbytes) != amt)
                                haderr = errno;
                }
@@ -824,60 +894,6 @@ rsource(char *name, struct stat *statp)
        (void) response();
 }
 
-void
-bwlimit(int amount)
-{
-       static struct timeval bwstart, bwend;
-       static int lamt, thresh = 16384;
-       u_int64_t waitlen;
-       struct timespec ts, rm;
-
-       if (!timerisset(&bwstart)) {
-               gettimeofday(&bwstart, NULL);
-               return;
-       }
-
-       lamt += amount;
-       if (lamt < thresh)
-               return;
-
-       gettimeofday(&bwend, NULL);
-       timersub(&bwend, &bwstart, &bwend);
-       if (!timerisset(&bwend))
-               return;
-
-       lamt *= 8;
-       waitlen = (double)1000000L * lamt / limit_rate;
-
-       bwstart.tv_sec = waitlen / 1000000L;
-       bwstart.tv_usec = waitlen % 1000000L;
-
-       if (timercmp(&bwstart, &bwend, >)) {
-               timersub(&bwstart, &bwend, &bwend);
-
-               /* Adjust the wait time */
-               if (bwend.tv_sec) {
-                       thresh /= 2;
-                       if (thresh < 2048)
-                               thresh = 2048;
-               } else if (bwend.tv_usec < 10000) {
-                       thresh *= 2;
-                       if (thresh > COPY_BUFLEN * 4)
-                               thresh = COPY_BUFLEN * 4;
-               }
-
-               TIMEVAL_TO_TIMESPEC(&bwend, &ts);
-               while (nanosleep(&ts, &rm) == -1) {
-                       if (errno != EINTR)
-                               break;
-                       ts = rm;
-               }
-       }
-
-       lamt = 0;
-       gettimeofday(&bwstart, NULL);
-}
-
 void
 sink(int argc, char **argv)
 {
@@ -1071,7 +1087,8 @@ bad:                      run_err("%s: %s", np, strerror(errno));
                                amt = size - i;
                        count += amt;
                        do {
-                               j = scpio(read, remin, cp, amt, &statbytes);
+                               j = atomicio6(read, remin, cp, amt,
+                                   scpio, &statbytes);
                                if (j == 0) {
                                        run_err("%s", j != EPIPE ?
                                            strerror(errno) :
@@ -1197,7 +1214,7 @@ void
 usage(void)
 {
        (void) fprintf(stderr,
-           "usage: scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n"