@@ -2,6 +2,7 @@ package ssh
2
2
3
3
import (
4
4
"context"
5
+ "encoding/base64"
5
6
"errors"
6
7
"fmt"
7
8
"net"
@@ -29,6 +30,16 @@ var DefaultChannelHandlers = map[string]ChannelHandler{
29
30
"session" : DefaultSessionHandler ,
30
31
}
31
32
33
+ var permissionsPublicKeyExt = "gliderlabs/ssh.PublicKey"
34
+
35
+ func ensureNoPKInPermissions (ctx Context ) error {
36
+ if _ , ok := ctx .Permissions ().Permissions .Extensions [permissionsPublicKeyExt ]; ok {
37
+ return errors .New ("misconfigured server: public key incorrectly set" )
38
+ }
39
+
40
+ return nil
41
+ }
42
+
32
43
// Server defines parameters for running an SSH server. The zero value for
33
44
// Server is a valid configuration. When both PasswordHandler and
34
45
// PublicKeyHandler are nil, no client authentication is performed.
@@ -149,7 +160,12 @@ func (srv *Server) config(ctx Context) *gossh.ServerConfig {
149
160
config .PasswordCallback = func (conn gossh.ConnMetadata , password []byte ) (* gossh.Permissions , error ) {
150
161
resetPermissions (ctx )
151
162
applyConnMetadata (ctx , conn )
152
- if ok := srv .PasswordHandler (ctx , string (password )); ! ok {
163
+ err := ensureNoPKInPermissions (ctx )
164
+ if err != nil {
165
+ return ctx .Permissions ().Permissions , err
166
+ }
167
+ ok := srv .PasswordHandler (ctx , string (password ))
168
+ if ! ok {
153
169
return ctx .Permissions ().Permissions , fmt .Errorf ("permission denied" )
154
170
}
155
171
return ctx .Permissions ().Permissions , nil
@@ -159,18 +175,31 @@ func (srv *Server) config(ctx Context) *gossh.ServerConfig {
159
175
config .PublicKeyCallback = func (conn gossh.ConnMetadata , key gossh.PublicKey ) (* gossh.Permissions , error ) {
160
176
resetPermissions (ctx )
161
177
applyConnMetadata (ctx , conn )
162
- if ok := srv .PublicKeyHandler (ctx , key ); ! ok {
178
+ err := ensureNoPKInPermissions (ctx )
179
+ if err != nil {
180
+ return ctx .Permissions ().Permissions , err
181
+ }
182
+ ok := srv .PublicKeyHandler (ctx , key )
183
+ if ! ok {
163
184
return ctx .Permissions ().Permissions , fmt .Errorf ("permission denied" )
164
185
}
165
- ctx .SetValue (ContextKeyPublicKey , key )
186
+
187
+ pkStr := base64 .StdEncoding .EncodeToString (key .Marshal ())
188
+ ctx .Permissions ().Permissions .Extensions [permissionsPublicKeyExt ] = pkStr
189
+
166
190
return ctx .Permissions ().Permissions , nil
167
191
}
168
192
}
169
193
if srv .KeyboardInteractiveHandler != nil {
170
194
config .KeyboardInteractiveCallback = func (conn gossh.ConnMetadata , challenger gossh.KeyboardInteractiveChallenge ) (* gossh.Permissions , error ) {
171
195
resetPermissions (ctx )
172
196
applyConnMetadata (ctx , conn )
173
- if ok := srv .KeyboardInteractiveHandler (ctx , challenger ); ! ok {
197
+ ok := srv .KeyboardInteractiveHandler (ctx , challenger )
198
+ err := ensureNoPKInPermissions (ctx )
199
+ if err != nil {
200
+ return ctx .Permissions ().Permissions , err
201
+ }
202
+ if ! ok {
174
203
return ctx .Permissions ().Permissions , fmt .Errorf ("permission denied" )
175
204
}
176
205
return ctx .Permissions ().Permissions , nil
@@ -302,6 +331,30 @@ func (srv *Server) HandleConn(newConn net.Conn) {
302
331
return
303
332
}
304
333
334
+ if sshConn .Permissions != nil {
335
+ // Now that the connection was authed, if the permissionsPublicKeyExt was
336
+ // attached, we need to re-parse it as a public key.
337
+ if keyData , ok := sshConn .Permissions .Extensions [permissionsPublicKeyExt ]; ok {
338
+ decodedData , err := base64 .StdEncoding .DecodeString (keyData )
339
+ if err != nil {
340
+ if srv .ConnectionFailedCallback != nil {
341
+ srv .ConnectionFailedCallback (conn , err )
342
+ }
343
+ return
344
+ }
345
+
346
+ key , err := gossh .ParsePublicKey (decodedData )
347
+ if err != nil {
348
+ if srv .ConnectionFailedCallback != nil {
349
+ srv .ConnectionFailedCallback (conn , err )
350
+ }
351
+ return
352
+ }
353
+
354
+ ctx .SetValue (ContextKeyPublicKey , key )
355
+ }
356
+ }
357
+
305
358
// Additionally, now that the connection was authed, we can take the
306
359
// permissions off of the gossh.Conn and re-attach them to the Permissions
307
360
// object stored in the Context.
0 commit comments