func (k KnownHosts) HostKeyCallback(hostname string, remote net.Addr, key ssh.PublicKey) error { var addr *net.TCPAddr if v, ok := remote.(*net.TCPAddr); ok { addr = v } else { return UnsupportedAddrType } keyBytes := key.Marshal() var matched []*Host for _, l := range k { if l.CertAuthority { continue } if key.Type() != l.PublicKey.Type() { continue } lKeyBytes := l.PublicKey.Marshal() for _, h := range l.Hosts { if h.Match(hostname, addr) { if !bytes.Equal(keyBytes, lKeyBytes) { return HostKeyMismatchError } if l.Revoked { return HostRevokedError } matched = append(matched, h) } } } if len(matched) == 0 { return HostNotFoundError } return nil }
// compareKeys compares to key files and returns true of they match. func compareKeys(a, b ssh.PublicKey) bool { if a.Type() != b.Type() { return false } // The best way to compare just the key seems to be to marshal both and // then compare the output byte sequence. return subtle.ConstantTimeCompare(a.Marshal(), b.Marshal()) == 1 }
func (k *storedHostKey) Check(addr string, remote net.Addr, key ssh.PublicKey) error { k.checkCount++ algo := key.Type() if k.keys == nil || bytes.Compare(key.Marshal(), k.keys[algo]) != 0 { return fmt.Errorf("host key mismatch. Got %q, want %q", key, k.keys[algo]) } return nil }
/* logPubKey logs a public key attempt */ func logPubKey( conn ssh.ConnMetadata, key ssh.PublicKey, ) (*ssh.Permissions, error) { log.Printf( "%v Key(%v):%02X", ci(conn), key.Type(), md5.Sum(key.Marshal()), ) return nil, fmt.Errorf("invalid key") }
func testAgentInterface(t *testing.T, agent Agent, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) { signer, err := ssh.NewSignerFromKey(key) if err != nil { t.Fatalf("NewSignerFromKey(%T): %v", key, err) } // The agent should start up empty. if keys, err := agent.List(); err != nil { t.Fatalf("RequestIdentities: %v", err) } else if len(keys) > 0 { t.Fatalf("got %d keys, want 0: %v", len(keys), keys) } // Attempt to insert the key, with certificate if specified. var pubKey ssh.PublicKey if cert != nil { err = agent.Add(AddedKey{ PrivateKey: key, Certificate: cert, Comment: "comment", LifetimeSecs: lifetimeSecs, }) pubKey = cert } else { err = agent.Add(AddedKey{PrivateKey: key, Comment: "comment", LifetimeSecs: lifetimeSecs}) pubKey = signer.PublicKey() } if err != nil { t.Fatalf("insert(%T): %v", key, err) } // Did the key get inserted successfully? if keys, err := agent.List(); err != nil { t.Fatalf("List: %v", err) } else if len(keys) != 1 { t.Fatalf("got %v, want 1 key", keys) } else if keys[0].Comment != "comment" { t.Fatalf("key comment: got %v, want %v", keys[0].Comment, "comment") } else if !bytes.Equal(keys[0].Blob, pubKey.Marshal()) { t.Fatalf("key mismatch") } // Can the agent make a valid signature? data := []byte("hello") sig, err := agent.Sign(pubKey, data) if err != nil { t.Fatalf("Sign(%s): %v", pubKey.Type(), err) } if err := pubKey.Verify(data, sig); err != nil { t.Fatalf("Verify(%s): %v", pubKey.Type(), err) } }
func sshToCrypto(pk ssh.PublicKey) crypto.PublicKey { // Don't judge me, judge the ssh.PublicKey interface. And me. A bit. switch pk.Type() { case ssh.KeyAlgoRSA: return (*rsa.PublicKey)(unsafe.Pointer(reflect.ValueOf(pk).Elem().UnsafeAddr())) case ssh.KeyAlgoDSA: return (*dsa.PublicKey)(unsafe.Pointer(reflect.ValueOf(pk).Elem().UnsafeAddr())) case ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521: return (*ecdsa.PublicKey)(unsafe.Pointer(reflect.ValueOf(pk).Elem().UnsafeAddr())) default: return nil } }
// authKey records any incoming request trying to auth with an ssh key func authKey(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { h := sha256.New() h.Write(key.Marshal()) sum := h.Sum(nil) log.Printf("sshkey: %s %s %s %s\n", conn.RemoteAddr().String(), conn.User(), key.Type(), base64.StdEncoding.EncodeToString(sum)) return nil, errAuthenticationFailed }
// 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 }
func (inst *instance) hostKeyCallback(hostname string, remote net.Addr, key ssh.PublicKey) error { oldPublicKey, err := hex.DecodeString(inst.conn.Options.SSHPublicKey) if err != nil { return errors.New("XML is corrupt: " + err.Error()) } newPublicKey := key.Marshal() //TODO correctly marshal/unmarshal into xml newPublicMD5 := md5.Sum(newPublicKey) newPublicString := hex.EncodeToString(newPublicMD5[:]) if len(oldPublicKey) == 0 { color.Yellowln("Registering new SSH Public Key", key.Type(), newPublicString) inst.conn.Options.SSHPublicKey = hex.EncodeToString(newPublicKey) inst.changed = true return nil } oldPublicMD5 := md5.Sum(oldPublicKey) oldPublicString := hex.EncodeToString(oldPublicMD5[:]) same := subtle.ConstantTimeCompare(newPublicKey, oldPublicKey) if same == 1 { return nil } color.Redln("-----POSSIBLE ATTACK-----\nSSH key changed! expected (md5):", oldPublicString, "got:", newPublicString, "type:", key.Type()) inst.terminal.Stderr().Write([]byte("Accept change [Ny]? ")) buf := make([]byte, 128) n, err := inst.terminal.Stdin().Read(buf) if err != nil { color.Yellowln("Error reading answer:", err) return err } inst.terminal.Stderr().Write([]byte{'\n'}) text := strings.ToLower(string(buf[:n])) if text == "y" || text == "yes" { inst.conn.Options.SSHPublicKey = hex.EncodeToString(newPublicKey) inst.changed = true color.Yellowln("Saving new public key to connections.xml on exit.") return nil } return errors.New("Public key not accepted") }
func keyAuth(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { log.Println(conn.RemoteAddr(), "authenticate with", key.Type(), "for user", conn.User()) log.Println(base64.StdEncoding.EncodeToString(key.Marshal())) if isValidToken(conn.User()) { authRequestMap.Lock() authRequestMap.matches[conn.User()] = key.Type() + " " + base64.StdEncoding.EncodeToString(key.Marshal()) authRequestMap.timestamps[conn.User()] = time.Now() authRequestMap.Unlock() return nil, nil } //Causes "Permission denied (publickey)." for openssh. How can this bubble up to the user? return nil, errors.New("Invalid token/username.") }
func (s *Server) auth(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { k := key.Marshal() t := key.Type() perm := &ssh.Permissions{ Extensions: map[string]string{ "pubKey": string(k), "pubKeyType": t, }, } _, err := s.Auther(conn, key) if err == nil { return perm, nil } return nil, err }
func keyAuthCallback(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { guid := uuid.NewV4() ip, remotePort := parseIpPortFrom(conn) login := SshLogin{RemoteAddr: ip, RemotePort: remotePort, Username: conn.User(), Guid: guid.String(), Version: string(conn.ClientVersion()), PublicKey: key.Marshal(), KeyType: string(key.Type()), LoginType: "key", } go login.Save() //log.Println("Fail to authenticate", conn, ":", err) //return nil, errors.New("invalid authentication") return &ssh.Permissions{Extensions: map[string]string{"guid": guid.String()}}, nil }
// authKey records any incoming request trying to auth with an ssh key func authKey(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { r := &AuthEvent{ Time: fmt.Sprintf("%d", time.Now().Unix()), AuthType: "sshKey", SrcIP: strings.Split(conn.RemoteAddr().String(), ":")[0], DestIP: extIP, User: conn.User(), TypeData: fmt.Sprintf("ssh-key-type: %s client-version: %s", key.Type(), strconv.QuoteToASCII(string(conn.ClientVersion()))), } h := sha256.New() h.Write(key.Marshal()) r.Credentials = base64.StdEncoding.EncodeToString(h.Sum(nil)) addToBatch(r) return nil, errAuthenticationFailed }
func (config BeaconConfig) checkHostKey(hostname string, remote net.Addr, key ssh.PublicKey) error { hostPublicKeyBytes, err := ioutil.ReadFile(string(config.PublicKey)) if err != nil { return fmt.Errorf("failed to read host public key: %s", err) } hostPublicKey, _, _, _, err := ssh.ParseAuthorizedKey(hostPublicKeyBytes) if err != nil { return fmt.Errorf("failed to parse host public key: %s", err) } // note: hostname/addr are not verified; they may be behind a load balancer // so the definition gets a bit fuzzy if hostPublicKey.Type() != key.Type() || !bytes.Equal(hostPublicKey.Marshal(), key.Marshal()) { return errors.New("remote host public key mismatch") } return nil }
func keyAuth(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { log.Println(conn.RemoteAddr(), "authenticate with", key.Type()) return nil, nil }
func (k *storedHostKey) Add(key ssh.PublicKey) { if k.keys == nil { k.keys = map[string][]byte{} } k.keys[key.Type()] = key.Marshal() }
func keyAuth(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { log.Printf("(keyAuth) >> New client conn from '%s' authenticating with '%s'\n", conn.RemoteAddr(), key.Type()) // Check if the user is allowed to connect at all (meaning: the must be a subdirectory in the 'data' dir // matching the provided SSH username). authorizedPubKey, err := getPubKeyForUser(conn.User()) if err != nil { return nil, fmt.Errorf("(keyAuth) >> No pub key for user '%s' found / user not allowed to connect.", conn.User()) } fpProvidedPubKey, err := pubKeyFingerprint(key) if err != nil { log.Printf("(keyAuth) >> Error: Unable to create fingerprint for provided PubKey: %s\n", err.Error()) } log.Printf("(keyAuth) >> Fingerprint of provided PubKey : %s\n", fpProvidedPubKey) fpAuthorizedPubKey, err := pubKeyFingerprint(authorizedPubKey) if err != nil { log.Printf("(keyAuth) >> Error: Unable to create fingerprint for authorized PubKey: %s\n", err.Error()) } log.Printf("(keyAuth) >> Fingerprint of authorized PubKey: %s\n", fpAuthorizedPubKey) // Check if username and Public Key combination is allowed to establish a connection. if theseTwoPublicKeysAreEqual(key, authorizedPubKey) { log.Printf("(keyAuth) >> Correct username '%s' and public key provided.", conn.User()) // Signaling success / authentication passed. return nil, nil } log.Printf("(keyAuth) >> Wrong username '%s' and/or public key provided.", conn.User()) return nil, fmt.Errorf("Wrong username and/or public key.") }
func (c *SSHCluster) hostKeyCallback(hostname string, remote net.Addr, key ssh.PublicKey) error { if c.knownHosts == nil { if err := c.parseKnownHosts(); err != nil { return err } } err := c.knownHosts.HostKeyCallback(hostname, remote, key) if err == knownhosts.HostNotFoundError { fingerprint := c.fingerprintSSHKey(key) hostnameFormatted := hostname remoteAddr := remote.String() if remoteAddr != hostname { hostnameFormatted = fmt.Sprintf("%s (%s)", hostname, remoteAddr) } if c.base.YesNoPrompt(fmt.Sprintf("The authenticity of host '%s' can't be established.\n%s key fingerprint is %s.\nAre you sure you want to continue connecting?", hostnameFormatted, key.Type(), fingerprint)) { c.base.SendLog(fmt.Sprintf("Trusting '%s' with key fingerprint %s", hostnameFormatted, fingerprint)) file, err := os.OpenFile(c.knownHostsPath(), os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.FileMode(0644)) if err != nil { c.base.SendLog(fmt.Sprintf("WARNING: Can't write to %s: %s", c.knownHostsPath(), err)) } else { defer file.Close() } c.knownHosts.AppendHost(hostname, key, file) return nil } } return err }