Skip to content

Commit

Permalink
Pass public key via auth callback permissions
Browse files Browse the repository at this point in the history
  • Loading branch information
belak committed Dec 12, 2024
1 parent 8a2025d commit c9ca86c
Showing 1 changed file with 57 additions and 4 deletions.
61 changes: 57 additions & 4 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ssh

import (
"context"
"encoding/base64"
"errors"
"fmt"
"net"
Expand Down Expand Up @@ -29,6 +30,16 @@ var DefaultChannelHandlers = map[string]ChannelHandler{
"session": DefaultSessionHandler,
}

var permissionsPublicKeyExt = "gliderlabs/ssh.PublicKey"

func ensureNoPKInPermissions(ctx Context) error {
if _, ok := ctx.Permissions().Permissions.Extensions[permissionsPublicKeyExt]; ok {
return errors.New("misconfigured server: public key incorrectly set")
}

return nil
}

// Server defines parameters for running an SSH server. The zero value for
// Server is a valid configuration. When both PasswordHandler and
// PublicKeyHandler are nil, no client authentication is performed.
Expand Down Expand Up @@ -149,7 +160,12 @@ func (srv *Server) config(ctx Context) *gossh.ServerConfig {
config.PasswordCallback = func(conn gossh.ConnMetadata, password []byte) (*gossh.Permissions, error) {
resetPermissions(ctx)
applyConnMetadata(ctx, conn)
if ok := srv.PasswordHandler(ctx, string(password)); !ok {
err := ensureNoPKInPermissions(ctx)
if err != nil {
return ctx.Permissions().Permissions, err
}
ok := srv.PasswordHandler(ctx, string(password))
if !ok {
return ctx.Permissions().Permissions, fmt.Errorf("permission denied")
}
return ctx.Permissions().Permissions, nil
Expand All @@ -159,18 +175,31 @@ func (srv *Server) config(ctx Context) *gossh.ServerConfig {
config.PublicKeyCallback = func(conn gossh.ConnMetadata, key gossh.PublicKey) (*gossh.Permissions, error) {
resetPermissions(ctx)
applyConnMetadata(ctx, conn)
if ok := srv.PublicKeyHandler(ctx, key); !ok {
err := ensureNoPKInPermissions(ctx)
if err != nil {
return ctx.Permissions().Permissions, err
}
ok := srv.PublicKeyHandler(ctx, key)
if !ok {
return ctx.Permissions().Permissions, fmt.Errorf("permission denied")
}
ctx.SetValue(ContextKeyPublicKey, key)

pkStr := base64.StdEncoding.EncodeToString(key.Marshal())
ctx.Permissions().Permissions.Extensions[permissionsPublicKeyExt] = pkStr

return ctx.Permissions().Permissions, nil
}
}
if srv.KeyboardInteractiveHandler != nil {
config.KeyboardInteractiveCallback = func(conn gossh.ConnMetadata, challenger gossh.KeyboardInteractiveChallenge) (*gossh.Permissions, error) {
resetPermissions(ctx)
applyConnMetadata(ctx, conn)
if ok := srv.KeyboardInteractiveHandler(ctx, challenger); !ok {
ok := srv.KeyboardInteractiveHandler(ctx, challenger)
err := ensureNoPKInPermissions(ctx)
if err != nil {
return ctx.Permissions().Permissions, err
}
if !ok {
return ctx.Permissions().Permissions, fmt.Errorf("permission denied")
}
return ctx.Permissions().Permissions, nil
Expand Down Expand Up @@ -302,6 +331,30 @@ func (srv *Server) HandleConn(newConn net.Conn) {
return
}

if sshConn.Permissions != nil {
// Now that the connection was authed, if the permissionsPublicKeyExt was
// attached, we need to re-parse it as a public key.
if keyData, ok := sshConn.Permissions.Extensions[permissionsPublicKeyExt]; ok {
decodedData, err := base64.StdEncoding.DecodeString(keyData)
if err != nil {
if srv.ConnectionFailedCallback != nil {
srv.ConnectionFailedCallback(conn, err)
}
return
}

key, err := gossh.ParsePublicKey(decodedData)
if err != nil {
if srv.ConnectionFailedCallback != nil {
srv.ConnectionFailedCallback(conn, err)
}
return
}

ctx.SetValue(ContextKeyPublicKey, key)
}
}

// Additionally, now that the connection was authed, we can take the
// permissions off of the gossh.Conn and re-attach them to the Permissions
// object stored in the Context.
Expand Down

0 comments on commit c9ca86c

Please sign in to comment.