Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 1 | // Copyright 2011 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | package ssh |
| 6 | |
| 7 | import ( |
Han-Wen Nienhuys | d7d50b0 | 2013-08-28 10:50:25 -0400 | [diff] [blame] | 8 | "crypto" |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 9 | "fmt" |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 10 | "sync" |
Jonathan Pittman | 6a743c5 | 2013-09-09 13:06:04 -0400 | [diff] [blame] | 11 | |
| 12 | _ "crypto/sha1" |
| 13 | _ "crypto/sha256" |
| 14 | _ "crypto/sha512" |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 15 | ) |
| 16 | |
| 17 | // These are string constants in the SSH protocol. |
| 18 | const ( |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 19 | compressionNone = "none" |
| 20 | serviceUserAuth = "ssh-userauth" |
| 21 | serviceSSH = "ssh-connection" |
| 22 | ) |
| 23 | |
Han-Wen Nienhuys | d7d50b0 | 2013-08-28 10:50:25 -0400 | [diff] [blame] | 24 | var supportedKexAlgos = []string{ |
| 25 | kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521, |
| 26 | kexAlgoDH14SHA1, kexAlgoDH1SHA1, |
| 27 | } |
| 28 | |
Han-Wen Nienhuys | 41400fe | 2013-10-07 18:30:34 -0400 | [diff] [blame^] | 29 | var supportedHostKeyAlgos = []string{ |
| 30 | KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, |
| 31 | KeyAlgoRSA, KeyAlgoDSA, |
| 32 | } |
| 33 | |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 34 | var supportedCompressions = []string{compressionNone} |
| 35 | |
Jonathan Pittman | 6a743c5 | 2013-09-09 13:06:04 -0400 | [diff] [blame] | 36 | // hashFuncs keeps the mapping of supported algorithms to their respective |
| 37 | // hashes needed for signature verification. |
| 38 | var hashFuncs = map[string]crypto.Hash{ |
| 39 | KeyAlgoRSA: crypto.SHA1, |
| 40 | KeyAlgoDSA: crypto.SHA1, |
| 41 | KeyAlgoECDSA256: crypto.SHA256, |
| 42 | KeyAlgoECDSA384: crypto.SHA384, |
| 43 | KeyAlgoECDSA521: crypto.SHA512, |
| 44 | CertAlgoRSAv01: crypto.SHA1, |
| 45 | CertAlgoDSAv01: crypto.SHA1, |
| 46 | CertAlgoECDSA256v01: crypto.SHA256, |
| 47 | CertAlgoECDSA384v01: crypto.SHA384, |
| 48 | CertAlgoECDSA521v01: crypto.SHA512, |
| 49 | } |
| 50 | |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 51 | // UnexpectedMessageError results when the SSH message that we received didn't |
| 52 | // match what we wanted. |
| 53 | type UnexpectedMessageError struct { |
| 54 | expected, got uint8 |
| 55 | } |
| 56 | |
| 57 | func (u UnexpectedMessageError) Error() string { |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 58 | return fmt.Sprintf("ssh: unexpected message type %d (expected %d)", u.got, u.expected) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 59 | } |
| 60 | |
| 61 | // ParseError results from a malformed SSH message. |
| 62 | type ParseError struct { |
| 63 | msgType uint8 |
| 64 | } |
| 65 | |
| 66 | func (p ParseError) Error() string { |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 67 | return fmt.Sprintf("ssh: parse error in message type %d", p.msgType) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 68 | } |
| 69 | |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 70 | func findCommonAlgorithm(clientAlgos []string, serverAlgos []string) (commonAlgo string, ok bool) { |
| 71 | for _, clientAlgo := range clientAlgos { |
| 72 | for _, serverAlgo := range serverAlgos { |
| 73 | if clientAlgo == serverAlgo { |
| 74 | return clientAlgo, true |
| 75 | } |
| 76 | } |
| 77 | } |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 78 | return |
| 79 | } |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 80 | |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 81 | func findCommonCipher(clientCiphers []string, serverCiphers []string) (commonCipher string, ok bool) { |
| 82 | for _, clientCipher := range clientCiphers { |
| 83 | for _, serverCipher := range serverCiphers { |
| 84 | // reject the cipher if we have no cipherModes definition |
| 85 | if clientCipher == serverCipher && cipherModes[clientCipher] != nil { |
| 86 | return clientCipher, true |
| 87 | } |
| 88 | } |
| 89 | } |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 90 | return |
| 91 | } |
| 92 | |
| 93 | func findAgreedAlgorithms(transport *transport, clientKexInit, serverKexInit *kexInitMsg) (kexAlgo, hostKeyAlgo string, ok bool) { |
| 94 | kexAlgo, ok = findCommonAlgorithm(clientKexInit.KexAlgos, serverKexInit.KexAlgos) |
| 95 | if !ok { |
| 96 | return |
| 97 | } |
| 98 | |
| 99 | hostKeyAlgo, ok = findCommonAlgorithm(clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos) |
| 100 | if !ok { |
| 101 | return |
| 102 | } |
| 103 | |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 104 | transport.writer.cipherAlgo, ok = findCommonCipher(clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 105 | if !ok { |
| 106 | return |
| 107 | } |
| 108 | |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 109 | transport.reader.cipherAlgo, ok = findCommonCipher(clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 110 | if !ok { |
| 111 | return |
| 112 | } |
| 113 | |
| 114 | transport.writer.macAlgo, ok = findCommonAlgorithm(clientKexInit.MACsClientServer, serverKexInit.MACsClientServer) |
| 115 | if !ok { |
| 116 | return |
| 117 | } |
| 118 | |
| 119 | transport.reader.macAlgo, ok = findCommonAlgorithm(clientKexInit.MACsServerClient, serverKexInit.MACsServerClient) |
| 120 | if !ok { |
| 121 | return |
| 122 | } |
| 123 | |
| 124 | transport.writer.compressionAlgo, ok = findCommonAlgorithm(clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer) |
| 125 | if !ok { |
| 126 | return |
| 127 | } |
| 128 | |
| 129 | transport.reader.compressionAlgo, ok = findCommonAlgorithm(clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient) |
| 130 | if !ok { |
| 131 | return |
| 132 | } |
| 133 | |
| 134 | ok = true |
| 135 | return |
| 136 | } |
| 137 | |
| 138 | // Cryptographic configuration common to both ServerConfig and ClientConfig. |
| 139 | type CryptoConfig struct { |
Han-Wen Nienhuys | d7d50b0 | 2013-08-28 10:50:25 -0400 | [diff] [blame] | 140 | // The allowed key exchanges algorithms. If unspecified then a |
| 141 | // default set of algorithms is used. |
| 142 | KeyExchanges []string |
| 143 | |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 144 | // The allowed cipher algorithms. If unspecified then DefaultCipherOrder is |
| 145 | // used. |
| 146 | Ciphers []string |
Dave Cheney | 6de97b5 | 2012-02-27 19:40:52 -0500 | [diff] [blame] | 147 | |
| 148 | // The allowed MAC algorithms. If unspecified then DefaultMACOrder is used. |
| 149 | MACs []string |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 150 | } |
| 151 | |
| 152 | func (c *CryptoConfig) ciphers() []string { |
| 153 | if c.Ciphers == nil { |
| 154 | return DefaultCipherOrder |
| 155 | } |
| 156 | return c.Ciphers |
| 157 | } |
| 158 | |
Han-Wen Nienhuys | d7d50b0 | 2013-08-28 10:50:25 -0400 | [diff] [blame] | 159 | func (c *CryptoConfig) kexes() []string { |
| 160 | if c.KeyExchanges == nil { |
| 161 | return defaultKeyExchangeOrder |
| 162 | } |
| 163 | return c.KeyExchanges |
| 164 | } |
| 165 | |
Dave Cheney | 6de97b5 | 2012-02-27 19:40:52 -0500 | [diff] [blame] | 166 | func (c *CryptoConfig) macs() []string { |
| 167 | if c.MACs == nil { |
| 168 | return DefaultMACOrder |
| 169 | } |
| 170 | return c.MACs |
| 171 | } |
| 172 | |
Han-Wen Nienhuys | e62b2ae | 2013-09-13 14:25:14 -0400 | [diff] [blame] | 173 | // serialize a signed slice according to RFC 4254 6.6. The name should |
| 174 | // be a key type name, rather than a cert type name. |
| 175 | func serializeSignature(name string, sig []byte) []byte { |
| 176 | length := stringLength(len(name)) |
Adam Langley | 63f855d | 2012-04-20 15:17:42 -0400 | [diff] [blame] | 177 | length += stringLength(len(sig)) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 178 | |
| 179 | ret := make([]byte, length) |
Han-Wen Nienhuys | e62b2ae | 2013-09-13 14:25:14 -0400 | [diff] [blame] | 180 | r := marshalString(ret, []byte(name)) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 181 | r = marshalString(r, sig) |
| 182 | |
| 183 | return ret |
| 184 | } |
| 185 | |
Han-Wen Nienhuys | e62b2ae | 2013-09-13 14:25:14 -0400 | [diff] [blame] | 186 | // MarshalPublicKey serializes a supported key or certificate for use |
| 187 | // by the SSH wire protocol. It can be used for comparison with the |
| 188 | // pubkey argument of ServerConfig's PublicKeyCallback as well as for |
| 189 | // generating an authorized_keys or host_keys file. |
| 190 | func MarshalPublicKey(key PublicKey) []byte { |
| 191 | // See also RFC 4253 6.6. |
| 192 | algoname := key.PrivateKeyAlgo() |
| 193 | blob := key.Marshal() |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 194 | |
Adam Langley | 63f855d | 2012-04-20 15:17:42 -0400 | [diff] [blame] | 195 | length := stringLength(len(algoname)) |
Han-Wen Nienhuys | e62b2ae | 2013-09-13 14:25:14 -0400 | [diff] [blame] | 196 | length += len(blob) |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 197 | ret := make([]byte, length) |
| 198 | r := marshalString(ret, []byte(algoname)) |
Han-Wen Nienhuys | e62b2ae | 2013-09-13 14:25:14 -0400 | [diff] [blame] | 199 | copy(r, blob) |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 200 | return ret |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 201 | } |
| 202 | |
Jonathan Pittman | 6a743c5 | 2013-09-09 13:06:04 -0400 | [diff] [blame] | 203 | // pubAlgoToPrivAlgo returns the private key algorithm format name that |
| 204 | // corresponds to a given public key algorithm format name. For most |
| 205 | // public keys, the private key algorithm name is the same. For some |
| 206 | // situations, such as openssh certificates, the private key algorithm and |
| 207 | // public key algorithm names differ. This accounts for those situations. |
| 208 | func pubAlgoToPrivAlgo(pubAlgo string) string { |
| 209 | switch pubAlgo { |
| 210 | case CertAlgoRSAv01: |
| 211 | return KeyAlgoRSA |
| 212 | case CertAlgoDSAv01: |
| 213 | return KeyAlgoDSA |
| 214 | case CertAlgoECDSA256v01: |
| 215 | return KeyAlgoECDSA256 |
| 216 | case CertAlgoECDSA384v01: |
| 217 | return KeyAlgoECDSA384 |
| 218 | case CertAlgoECDSA521v01: |
| 219 | return KeyAlgoECDSA521 |
| 220 | } |
| 221 | return pubAlgo |
| 222 | } |
| 223 | |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 224 | // buildDataSignedForAuth returns the data that is signed in order to prove |
| 225 | // posession of a private key. See RFC 4252, section 7. |
| 226 | func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte { |
| 227 | user := []byte(req.User) |
| 228 | service := []byte(req.Service) |
| 229 | method := []byte(req.Method) |
| 230 | |
Adam Langley | 63f855d | 2012-04-20 15:17:42 -0400 | [diff] [blame] | 231 | length := stringLength(len(sessionId)) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 232 | length += 1 |
Adam Langley | 63f855d | 2012-04-20 15:17:42 -0400 | [diff] [blame] | 233 | length += stringLength(len(user)) |
| 234 | length += stringLength(len(service)) |
| 235 | length += stringLength(len(method)) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 236 | length += 1 |
Adam Langley | 63f855d | 2012-04-20 15:17:42 -0400 | [diff] [blame] | 237 | length += stringLength(len(algo)) |
| 238 | length += stringLength(len(pubKey)) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 239 | |
| 240 | ret := make([]byte, length) |
| 241 | r := marshalString(ret, sessionId) |
| 242 | r[0] = msgUserAuthRequest |
| 243 | r = r[1:] |
| 244 | r = marshalString(r, user) |
| 245 | r = marshalString(r, service) |
| 246 | r = marshalString(r, method) |
| 247 | r[0] = 1 |
| 248 | r = r[1:] |
| 249 | r = marshalString(r, algo) |
| 250 | r = marshalString(r, pubKey) |
| 251 | return ret |
| 252 | } |
| 253 | |
Adam Langley | 4002be2 | 2012-12-10 18:12:36 -0500 | [diff] [blame] | 254 | // safeString sanitises s according to RFC 4251, section 9.2. |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 255 | // All control characters except tab, carriage return and newline are |
| 256 | // replaced by 0x20. |
| 257 | func safeString(s string) string { |
| 258 | out := []byte(s) |
| 259 | for i, c := range out { |
| 260 | if c < 0x20 && c != 0xd && c != 0xa && c != 0x9 { |
| 261 | out[i] = 0x20 |
| 262 | } |
| 263 | } |
| 264 | return string(out) |
| 265 | } |
Dave Cheney | 79d53bd | 2012-03-04 14:34:24 -0800 | [diff] [blame] | 266 | |
| 267 | func appendU16(buf []byte, n uint16) []byte { |
| 268 | return append(buf, byte(n>>8), byte(n)) |
| 269 | } |
| 270 | |
| 271 | func appendU32(buf []byte, n uint32) []byte { |
| 272 | return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) |
| 273 | } |
| 274 | |
| 275 | func appendInt(buf []byte, n int) []byte { |
| 276 | return appendU32(buf, uint32(n)) |
| 277 | } |
Dave Cheney | 8a2e7c9 | 2012-05-11 05:56:44 +1000 | [diff] [blame] | 278 | |
Han-Wen Nienhuys | c7df565 | 2013-06-06 10:44:12 -0400 | [diff] [blame] | 279 | func appendString(buf []byte, s string) []byte { |
| 280 | buf = appendU32(buf, uint32(len(s))) |
| 281 | buf = append(buf, s...) |
| 282 | return buf |
| 283 | } |
| 284 | |
| 285 | func appendBool(buf []byte, b bool) []byte { |
| 286 | if b { |
| 287 | buf = append(buf, 1) |
| 288 | } else { |
| 289 | buf = append(buf, 0) |
| 290 | } |
| 291 | return buf |
| 292 | } |
| 293 | |
Adam Langley | 4002be2 | 2012-12-10 18:12:36 -0500 | [diff] [blame] | 294 | // newCond is a helper to hide the fact that there is no usable zero |
Dave Cheney | 8a2e7c9 | 2012-05-11 05:56:44 +1000 | [diff] [blame] | 295 | // value for sync.Cond. |
| 296 | func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) } |
| 297 | |
Adam Langley | 4002be2 | 2012-12-10 18:12:36 -0500 | [diff] [blame] | 298 | // window represents the buffer available to clients |
Dave Cheney | 8a2e7c9 | 2012-05-11 05:56:44 +1000 | [diff] [blame] | 299 | // wishing to write to a channel. |
| 300 | type window struct { |
| 301 | *sync.Cond |
| 302 | win uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1 |
| 303 | } |
| 304 | |
| 305 | // add adds win to the amount of window available |
| 306 | // for consumers. |
| 307 | func (w *window) add(win uint32) bool { |
Dave Cheney | 55aa081 | 2012-05-22 12:04:51 +1000 | [diff] [blame] | 308 | // a zero sized window adjust is a noop. |
Dave Cheney | 8a2e7c9 | 2012-05-11 05:56:44 +1000 | [diff] [blame] | 309 | if win == 0 { |
Dave Cheney | 55aa081 | 2012-05-22 12:04:51 +1000 | [diff] [blame] | 310 | return true |
Dave Cheney | 8a2e7c9 | 2012-05-11 05:56:44 +1000 | [diff] [blame] | 311 | } |
| 312 | w.L.Lock() |
| 313 | if w.win+win < win { |
| 314 | w.L.Unlock() |
| 315 | return false |
| 316 | } |
| 317 | w.win += win |
| 318 | // It is unusual that multiple goroutines would be attempting to reserve |
Adam Langley | 4002be2 | 2012-12-10 18:12:36 -0500 | [diff] [blame] | 319 | // window space, but not guaranteed. Use broadcast to notify all waiters |
Dave Cheney | 8a2e7c9 | 2012-05-11 05:56:44 +1000 | [diff] [blame] | 320 | // that additional window is available. |
| 321 | w.Broadcast() |
| 322 | w.L.Unlock() |
| 323 | return true |
| 324 | } |
| 325 | |
| 326 | // reserve reserves win from the available window capacity. |
| 327 | // If no capacity remains, reserve will block. reserve may |
| 328 | // return less than requested. |
| 329 | func (w *window) reserve(win uint32) uint32 { |
| 330 | w.L.Lock() |
| 331 | for w.win == 0 { |
| 332 | w.Wait() |
| 333 | } |
| 334 | if w.win < win { |
| 335 | win = w.win |
| 336 | } |
| 337 | w.win -= win |
| 338 | w.L.Unlock() |
| 339 | return win |
| 340 | } |