Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ssh/server/auth/publickey.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
gliderssh "github.com/gliderlabs/ssh"
"github.com/shellhub-io/shellhub/ssh/session"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh"
)

// PublicKeyHandler handles ShellHub client's connection using the public key authentication method.
Expand All @@ -14,6 +15,7 @@ func PublicKeyHandler(ctx gliderssh.Context, publicKey gliderssh.PublicKey) bool
log.Fields{
"uid": ctx.SessionID(),
"sshid": ctx.User(),
"key": ssh.MarshalAuthorizedKey(publicKey),
})

logger.Trace("trying to use public key authentication")
Expand Down
8 changes: 8 additions & 0 deletions ssh/web/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ func (c *Conn) ReadMessage(message *Message) (int, error) {
}

message.Data = dim
case messageKindSignature:
var signed string

if err = json.Unmarshal(data, &signed); err != nil {
return 0, errors.Join(ErrConnReadMessageJSONInvalid)
}

message.Data = signed
default:
return 0, errors.Join(ErrConnReadMessageKindInvalid)
}
Expand Down
3 changes: 3 additions & 0 deletions ssh/web/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ const (
// messageKindResize is the identifier to a resize request message. This kind of message contains the number of
// columns and rows what the terminal should have.
messageKindResize
// messageKindSignature is the identifier to a signature message. This kind of message contains the data to be
// signed by the user's private key.
messageKindSignature
)

type Message struct {
Expand Down
52 changes: 37 additions & 15 deletions ssh/web/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package web
import (
"bytes"
"context"
"crypto/rsa"
"encoding/base64"
"errors"
"fmt"
Expand All @@ -14,7 +13,6 @@ import (
"github.com/shellhub-io/shellhub/pkg/api/internalclient"
"github.com/shellhub-io/shellhub/pkg/cache"
"github.com/shellhub-io/shellhub/pkg/uuid"
"github.com/shellhub-io/shellhub/ssh/pkg/magickey"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh"
)
Expand All @@ -34,7 +32,7 @@ func (b *BannerError) Error() string {
}

// getAuth gets the authentication methods from credentials.
func getAuth(creds *Credentials, magicKey *rsa.PrivateKey) ([]ssh.AuthMethod, error) {
func getAuth(conn *Conn, creds *Credentials) ([]ssh.AuthMethod, error) {
if creds.isPassword() {
return []ssh.AuthMethod{ssh.Password(creds.Password)}, nil
}
Expand Down Expand Up @@ -71,24 +69,48 @@ func getAuth(creds *Credentials, magicKey *rsa.PrivateKey) ([]ssh.AuthMethod, er
return nil, ErrDataPublicKey
}

digest, err := base64.StdEncoding.DecodeString(creds.Signature)
if err != nil {
return nil, ErrSignaturePublicKey
signer := &Signer{
conn: conn,
publicKey: &pubKey,
}

if err := pubKey.Verify([]byte(creds.Username), &ssh.Signature{ //nolint: exhaustruct
Format: pubKey.Type(),
Blob: digest,
}); err != nil {
return nil, ErrVerifyPublicKey
return []ssh.AuthMethod{ssh.PublicKeys(signer)}, nil
}

type Signer struct {
conn *Conn
publicKey *ssh.PublicKey
}

func (s *Signer) PublicKey() ssh.PublicKey {
return *s.publicKey
}

func (s *Signer) Sign(rand io.Reader, data []byte) (*ssh.Signature, error) {
dataB64 := base64.StdEncoding.EncodeToString(data)
if _, err := s.conn.WriteMessage(&Message{Kind: messageKindSignature, Data: dataB64}); err != nil {
return nil, err
}

var msg Message
if _, err := s.conn.ReadMessage(&msg); err != nil {
return nil, fmt.Errorf("invalid signature response")
}

signed, ok := msg.Data.(string)
if !ok {
return nil, fmt.Errorf("data isn't a signed string")
}

signer, err := ssh.NewSignerFromKey(magicKey)
blob, err := base64.StdEncoding.DecodeString(signed)
if err != nil {
return nil, ErrSignerPublicKey
return nil, err
}

return []ssh.AuthMethod{ssh.PublicKeys(signer)}, nil
return &ssh.Signature{
Format: s.PublicKey().Type(),
Blob: blob,
}, nil
}

func newSession(ctx context.Context, cache cache.Cache, conn *Conn, creds *Credentials, dim Dimensions, info Info) error {
Expand All @@ -107,7 +129,7 @@ func newSession(ctx context.Context, cache cache.Cache, conn *Conn, creds *Crede
uuid := uuid.Generate()

user := fmt.Sprintf("%s@%s", creds.Username, uuid)
auth, err := getAuth(creds, magickey.GetRerefence())
auth, err := getAuth(conn, creds)
if err != nil {
logger.WithError(err).Debug("failed to get the credentials")

Expand Down
3 changes: 1 addition & 2 deletions ssh/web/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ type Credentials struct {
Password string `json:"password"`
// Fingerprint is the identifier of the public key used in the device's OS.
Fingerprint string `json:"fingerprint"`
Signature string `json:"signature"`
}

func (c *Credentials) encryptPassword(key *rsa.PrivateKey) error {
Expand Down Expand Up @@ -56,7 +55,7 @@ func (c *Credentials) decryptPassword(key *rsa.PrivateKey) error {
}

func (c *Credentials) isPublicKey() bool { // nolint: unused
return c.Fingerprint != "" && c.Signature != ""
return c.Fingerprint != ""
}

// isPassword checks if connection is using password method.
Expand Down
Loading