This repository has been archived by the owner on Jul 2, 2023. It is now read-only.
/
hosts.go
134 lines (106 loc) · 2.92 KB
/
hosts.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package internal_ssh
import (
"errors"
"fmt"
"net"
"os"
"path/filepath"
"strings"
"github.com/exler/quickssh/internal"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/knownhosts"
)
func DefaultKnownHostsPath() (string, error) {
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
return filepath.Join(home, ".ssh", "known_hosts"), err
}
func DefaultKnownHosts() (ssh.HostKeyCallback, error) {
path, err := DefaultKnownHostsPath()
if err != nil {
return nil, err
}
return knownhosts.New(path)
}
func CheckKnownHost(host string, remote net.Addr, key ssh.PublicKey, knownHostsFile string) (found bool, err error) {
var keyErr *knownhosts.KeyError
if knownHostsFile == "" {
path, err := DefaultKnownHostsPath()
if err != nil {
return false, err
}
knownHostsFile = path
}
callback, err := knownhosts.New(knownHostsFile)
if err != nil {
return false, err
}
err = callback(host, remote, key)
// Known host exists
if err == nil {
return true, nil
}
// Make sure that the error returned from the callback is host not in file error.
// If keyErr.Want is greater than 0 length, that means host is in file with different key.
if errors.As(err, &keyErr) && len(keyErr.Want) > 0 {
return true, keyErr
}
// Some other error occurred and safest way to handle is to pass it back to user.
if err != nil {
return false, err
}
// Key is not trusted because it is not in the file.
return false, nil
}
func AddKnownHost(host string, remote net.Addr, key ssh.PublicKey, knownHostsFile string) (err error) {
if knownHostsFile == "" {
path, err := DefaultKnownHostsPath()
if err != nil {
return err
}
knownHostsFile = path
}
f, err := os.OpenFile(knownHostsFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
if err != nil {
return err
}
defer f.Close()
remoteNormalized := knownhosts.Normalize(remote.String())
hostNormalized := knownhosts.Normalize(host)
addresses := []string{remoteNormalized}
if hostNormalized != remoteNormalized {
addresses = append(addresses, hostNormalized)
}
_, err = f.WriteString(knownhosts.Line(addresses, key) + "\n")
return err
}
func VerifyHost(host string, remote net.Addr, key ssh.PublicKey) error {
// Check if host is in the known_hosts file
hostFound, err := CheckKnownHost(host, remote, key, "")
// Host is in known_hosts but the keys are mismatched
if hostFound {
if err != nil {
return err
} else {
return nil
}
}
// Ask the user whether he trusts the host
if !askIsHostTrusted(host, key) {
return errors.New("host key untrusted")
}
// Add the new host to known hosts file
return AddKnownHost(host, remote, key, "")
}
func askIsHostTrusted(host string, key ssh.PublicKey) bool {
fmt.Printf("Unknown Host: %s \nFingerprint: %s \n", host, ssh.FingerprintSHA256(key))
fmt.Print("Would you like to add it? [Y/n]: ")
answer := internal.GetUserInput()
if strings.ToLower(answer) == "n" {
return false
} else {
return true
}
}