106 lines
1.9 KiB
Go
106 lines
1.9 KiB
Go
//go:build linux
|
|
|
|
package auth
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"os"
|
|
"os/user"
|
|
"path/filepath"
|
|
|
|
"github.com/msteinert/pam"
|
|
"golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
type system struct{}
|
|
|
|
type systemPrincipal struct {
|
|
*user.User
|
|
attributes Attributes
|
|
}
|
|
|
|
func newSystemPrincipal(name string) (*systemPrincipal, error) {
|
|
u, err := user.Lookup(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
a := Attributes{
|
|
Custom: make(map[string]any),
|
|
}
|
|
if gids, err := u.GroupIds(); err == nil {
|
|
a.Custom["groups"] = gids
|
|
for _, gid := range gids {
|
|
if g, err := user.LookupGroupId(gid); err == nil {
|
|
a.Groups = append(a.Groups, g.Name)
|
|
}
|
|
}
|
|
}
|
|
|
|
return &systemPrincipal{
|
|
User: u,
|
|
attributes: a,
|
|
}, nil
|
|
}
|
|
|
|
func (u systemPrincipal) Type() string {
|
|
return "user"
|
|
}
|
|
|
|
func (u systemPrincipal) Identity() string {
|
|
return u.Name
|
|
}
|
|
|
|
func (u systemPrincipal) Attributes() Attributes {
|
|
return u.attributes
|
|
}
|
|
|
|
func SystemPassword() Password {
|
|
return system{}
|
|
}
|
|
|
|
func (system) VerifyPassword(username, password string) (Principal, error) {
|
|
t, err := pam.StartFunc("sshd", username, func(s pam.Style, msg string) (string, error) {
|
|
switch s {
|
|
case pam.PromptEchoOff:
|
|
return password, nil
|
|
case pam.PromptEchoOn:
|
|
return username, nil
|
|
default:
|
|
return "", errors.New("unrecognized message style")
|
|
}
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err = t.Authenticate(0); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return newSystemPrincipal(username)
|
|
}
|
|
|
|
func (system) VerifyPublicKey(username string, key ssh.PublicKey) (Principal, error) {
|
|
p, err := newSystemPrincipal(username)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rest, err := os.ReadFile(filepath.Join(p.HomeDir, ".ssh", "authorized_keys"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for len(rest) > 0 {
|
|
var out ssh.PublicKey
|
|
if out, _, _, rest, err = ssh.ParseAuthorizedKey(rest); err != nil {
|
|
return nil, err
|
|
}
|
|
if bytes.Equal(out.Marshal(), key.Marshal()) {
|
|
return p, nil
|
|
}
|
|
}
|
|
return nil, ErrAuthorized
|
|
}
|