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