// Check is called during the handshake to check the server's public key for // unexpected changes. The key argument is in SSH wire format. It can be parsed // using ssh.ParsePublicKey. The address before DNS resolution is passed in the // addr argument, so the key can also be checked against the hostname. // It returns any error encountered while checking the public key. A nil return // value indicates that the key was either successfully verified (against an // existing known_hosts entry), or accepted by the user as a new key. func (kc *HostKeyChecker) Check(addr string, remote net.Addr, key gossh.PublicKey) error { remoteAddr, err := kc.addrToHostPort(remote.String()) if err != nil { return err } algoStr := algoString(key.Type()) keyFingerprintStr := md5String(md5.Sum(key.Marshal())) hostKeys, err := kc.m.GetHostKeys() _, ok := err.(*os.PathError) if err != nil && !ok { log.Errorf("Failed to read known_hosts file %v: %v", kc.m.String(), err) } mismatched := false for pattern, keys := range hostKeys { if !matchHost(remoteAddr, pattern) { continue } for _, hostKey := range keys { // Any matching key is considered a success, irrespective of previous failures if hostKey.Type() == key.Type() && bytes.Compare(hostKey.Marshal(), key.Marshal()) == 0 { return nil } // TODO(jonboulle): could be super friendly like the OpenSSH client // and note exactly which key failed (file + line number) mismatched = true } } if mismatched { fmt.Fprintf(os.Stderr, warningRemoteHostChanged, algoStr, keyFingerprintStr, kc.m.String()) return ErrUnmatchKey } // If we get this far, we haven't matched on any of the hostname patterns, // so it's considered a new key. Prompt the user to trust it. if !kc.trustHost(remoteAddr, algoStr, keyFingerprintStr) { fmt.Fprintln(os.Stderr, "Host key verification failed.") return ErrUntrustHost } if err := kc.m.PutHostKey(remoteAddr, key); err != nil { fmt.Fprintf(os.Stderr, "Failed to add the host to the list of known hosts (%v).\n", kc.m) return nil } fmt.Fprintf(os.Stderr, "Warning: Permanently added '%v' (%v) to the list of known hosts.\n", remoteAddr, algoStr) return nil }
// Check is called during the handshake to check server's // public key for unexpected changes. The hostKey argument is // in SSH wire format. It can be parsed using // ssh.ParsePublicKey. The address before DNS resolution is // passed in the addr argument, so the key can also be checked // against the hostname. func (kc *HostKeyChecker) Check(addr string, remote net.Addr, key gossh.PublicKey) error { remoteAddr := remote.String() algoStr := algoString(key.Type()) keyFingerprintStr := md5String(md5.Sum(key.Marshal())) // get existing host keys hostKeys, err := kc.m.GetHostKeys() _, ok := err.(*os.PathError) if err != nil && !ok { kc.errLog.Println("Warning: read host file with", err) } // check existing host keys hostKey, ok := hostKeys[remoteAddr] if !ok { if kc.trustHost == nil { return ErrUnsetTrustFunc } if !kc.trustHost(remoteAddr, algoStr, keyFingerprintStr) { kc.errLog.Println("Host key verification failed.") return ErrUntrustHost } if err := kc.m.PutHostKey(remoteAddr, key); err != nil { kc.errLog.Printf("Failed to add the host to the list of known hosts (%v).\n", kc.m) return nil } kc.errLog.Printf("Warning: Permanently added '%v' (%v) to the list of known hosts.\n", remoteAddr, algoStr) return nil } if hostKey.Type() != key.Type() || bytes.Compare(hostKey.Marshal(), key.Marshal()) != 0 { kc.errLog.Printf(`%s Someone could be eavesdropping on you right now (man-in-the-middle attack)! It is also possible that a host key has just been changed. The fingerprint for the %v key sent by the remote host is %v. Please contact your system administrator. Add correct host key in %v to get rid of this message. Host key verification failed.%c`, warningRemoteHostChanged, algoStr, keyFingerprintStr, kc.m.String(), '\n') return ErrUnmatchKey } return nil }