Skip to content

Commit 9058744

Browse files
committed
Fix multimple ssh keys support for one username
1 parent b560ef6 commit 9058744

File tree

3 files changed

+25
-34
lines changed

3 files changed

+25
-34
lines changed

main.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,14 @@ type Config struct {
6262
var cfg Config
6363

6464
type SSH_Info struct {
65-
keyType string
66-
keyData []byte
67-
session []byte
65+
keyType string
66+
keyData []byte
67+
username string
6868
}
6969

7070
var (
7171
telegramWidgetEnabled = false
72-
authorized_keys = map[string]SSH_Info{}
72+
authorized_keys = []SSH_Info{}
7373
domainToTokenSHA256 = map[string][]byte{}
7474
domainToLoginPage = map[string][]byte{} // gzip
7575
domainNoAuth = map[string]bool{}

ssh-server.go

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,6 @@ import (
1616
"golang.org/x/crypto/ssh"
1717
)
1818

19-
/*
20-
This global variable is only used in two methods: publicKeyCallback, handleChannels.
21-
Such a workaround is needed because there is no way to find out what public key
22-
the user has when processing channels (the token is passed there). Although SSH
23-
provides the User value, it cannot be changed and is passed from the user side.
24-
And we need the ability to assign any username for the key
25-
*/
26-
var sshAddrToUsername sync.Map
27-
2819
// Tokens that the browser generates and the user passes to us
2920
var (
3021
ssh_tokens = map[string]SSH_Token_Info{}
@@ -92,7 +83,8 @@ func startSshServer() {
9283
// Checks if the public key is in `authorized_keys` list
9384
func publicKeyCallback(sshConn ssh.ConnMetadata, remoteKey ssh.PublicKey) (*ssh.Permissions, error) {
9485
log.Printf("Trying to auth: %s (%s::%s) - %s ", sshConn.User(), sshConn.ClientVersion(), remoteKey.Type(), sshConn.RemoteAddr())
95-
for username, localKey := range authorized_keys {
86+
// TODO Do not show knowledge of public keys, somehow require the client to confirm the private key
87+
for _, localKey := range authorized_keys {
9688
// Make sure the key types match
9789
if remoteKey.Type() != localKey.keyType {
9890
continue
@@ -115,11 +107,13 @@ func publicKeyCallback(sshConn ssh.ConnMetadata, remoteKey ssh.PublicKey) (*ssh.
115107
continue
116108
}
117109
// Now we know user
118-
sshAddrToUsername.Store(sshConn.RemoteAddr().String(), username)
119-
log.Printf("Public key match: %s", username)
120-
return nil, nil
110+
log.Printf("Public key match: %s", localKey.username)
111+
// TODO can client send Extensions?
112+
perm := ssh.Permissions{Extensions: make(map[string]string)}
113+
perm.Extensions["username"] = localKey.username
114+
return &perm, nil
121115
}
122-
return nil, errors.New("not authorized key")
116+
return nil, errors.New(yellow("not authorized key"))
123117
}
124118

125119
// This is called for already authenticated(via publicKeyCallback) users
@@ -138,20 +132,8 @@ func handleChannels(sshConn ssh.ServerConn, channels <-chan ssh.NewChannel) {
138132
log.Printf("could not accept channel (%s)", err)
139133
continue
140134
}
141-
142135
// Get the previously(in publicKeyCallback) saved username
143-
addr := sshConn.RemoteAddr().String()
144-
tmp, ok := sshAddrToUsername.Load(addr)
145-
// It shouldn't be possible. But we'll be safe
146-
if !ok {
147-
channel.Close()
148-
}
149-
// Deletion is not necessary for security, because even if user logs in
150-
// under a different name from same address, value in `map` will change.
151-
// And the call to this method always occurs after checking public key.
152-
// Just saves some memory
153-
sshAddrToUsername.Delete(addr)
154-
username := tmp.(string)
136+
username := sshConn.Permissions.Extensions["username"]
155137
fmt.Fprintf(channel, "Authenticated username: %s \n", username)
156138

157139
// Typically SSH sessions have out-of-band requests such as "shell", "pty-req" and "env"
@@ -182,7 +164,7 @@ func handleChannels(sshConn ssh.ServerConn, channels <-chan ssh.NewChannel) {
182164
// Show the user some animation and check the connection at the same time
183165
_, err := fmt.Fprint(channel, ".")
184166
if err != nil {
185-
log.Printf("The SSH connection to user `%s` has been terminated", username)
167+
log.Printf(yellow("The SSH connection to user `%s` has been terminated"), username)
186168
break
187169
}
188170
// Lock and read from global var
@@ -237,7 +219,10 @@ func loadAuthorizedKeys(filename string) {
237219
if err != nil {
238220
log.Fatal(err)
239221
}
240-
authorized_keys[name] = SSH_Info{key.Type(), key.Marshal(), nil}
222+
authorized_keys = append(authorized_keys, SSH_Info{
223+
keyType: key.Type(),
224+
keyData: key.Marshal(),
225+
username: name})
241226
}
242227
} else {
243228
log.Fatalf("SSH: Can't open %s", filename)

tokens.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,13 @@ func loadTokens() error {
183183
in_tg = true
184184
}
185185
}
186-
_, in_ssh := authorized_keys[username]
186+
in_ssh := false
187+
for _, sshInfo := range authorized_keys {
188+
if sshInfo.username == username {
189+
in_ssh = true
190+
break
191+
}
192+
}
187193
if !in_tg && !in_ssh {
188194
log.Printf(red("Token for %s revoked as user is no longer registered"), username)
189195
continue

0 commit comments

Comments
 (0)