forked from jshiffer/matterbridge
75 lines
2.5 KiB
Go
75 lines
2.5 KiB
Go
package sshd
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"errors"
|
|
"net"
|
|
|
|
"github.com/shazow/ssh-chat/internal/sanitize"
|
|
"golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
// Auth is used to authenticate connections based on public keys.
|
|
type Auth interface {
|
|
// Whether to allow connections without a public key.
|
|
AllowAnonymous() bool
|
|
// Given address and public key and client agent string, returns nil if the connection should be allowed.
|
|
Check(net.Addr, ssh.PublicKey, string) error
|
|
}
|
|
|
|
// MakeAuth makes an ssh.ServerConfig which performs authentication against an Auth implementation.
|
|
// TODO: Switch to using ssh.AuthMethod instead?
|
|
func MakeAuth(auth Auth) *ssh.ServerConfig {
|
|
config := ssh.ServerConfig{
|
|
NoClientAuth: false,
|
|
// Auth-related things should be constant-time to avoid timing attacks.
|
|
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
|
|
err := auth.Check(conn.RemoteAddr(), key, sanitize.Data(string(conn.ClientVersion()), 64))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
perm := &ssh.Permissions{Extensions: map[string]string{
|
|
"pubkey": string(key.Marshal()),
|
|
}}
|
|
return perm, nil
|
|
},
|
|
KeyboardInteractiveCallback: func(conn ssh.ConnMetadata, challenge ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error) {
|
|
if !auth.AllowAnonymous() {
|
|
return nil, errors.New("public key authentication required")
|
|
}
|
|
err := auth.Check(conn.RemoteAddr(), nil, sanitize.Data(string(conn.ClientVersion()), 64))
|
|
return nil, err
|
|
},
|
|
}
|
|
|
|
return &config
|
|
}
|
|
|
|
// MakeNoAuth makes a simple ssh.ServerConfig which allows all connections.
|
|
// Primarily used for testing.
|
|
func MakeNoAuth() *ssh.ServerConfig {
|
|
config := ssh.ServerConfig{
|
|
NoClientAuth: false,
|
|
// Auth-related things should be constant-time to avoid timing attacks.
|
|
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
|
|
perm := &ssh.Permissions{Extensions: map[string]string{
|
|
"pubkey": string(key.Marshal()),
|
|
}}
|
|
return perm, nil
|
|
},
|
|
KeyboardInteractiveCallback: func(conn ssh.ConnMetadata, challenge ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error) {
|
|
return nil, nil
|
|
},
|
|
}
|
|
|
|
return &config
|
|
}
|
|
|
|
// Fingerprint performs a SHA256 BASE64 fingerprint of the PublicKey, similar to OpenSSH.
|
|
// See: https://anongit.mindrot.org/openssh.git/commit/?id=56d1c83cdd1ac
|
|
func Fingerprint(k ssh.PublicKey) string {
|
|
hash := sha256.Sum256(k.Marshal())
|
|
return "SHA256:" + base64.StdEncoding.EncodeToString(hash[:])
|
|
}
|