forgejo/modules/auth/pam/pam.go
Baptiste Daroussin 9762f9ea20 fix: portable error reporting for PAM (#11296)
Linux PAM reports "Authentication Failure"
OpenPAM reports "authentication error"

This resulted in forgejo reporting error 500 on FreeBSD when pam
authentication failed.

Add a sentinel error to make this portable: ErrInvalidCredentials

Signed-off-by: Baptiste Daroussin <bapt@FreeBSD.org>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11296
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Baptiste Daroussin <bapt@FreeBSD.org>
Co-committed-by: Baptiste Daroussin <bapt@FreeBSD.org>
2026-02-16 05:57:01 +01:00

55 lines
1.4 KiB
Go

// Copyright 2014 The Gogs Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build pam
package pam
import (
"errors"
"fmt"
"github.com/msteinert/pam/v2"
)
// ErrInvalidCredentials is returned when PAM reports an authentication
// or account error (wrong password, unknown user, expired account, etc.).
var ErrInvalidCredentials = errors.New("invalid PAM credentials")
// Supported is true when built with PAM
var Supported = true
// Auth pam auth service
func Auth(serviceName, userName, passwd string) (string, error) {
t, err := pam.StartFunc(serviceName, userName, func(s pam.Style, msg string) (string, error) {
switch s {
case pam.PromptEchoOff:
return passwd, nil
case pam.PromptEchoOn, pam.ErrorMsg, pam.TextInfo:
return "", nil
}
return "", errors.New("Unrecognized PAM message style")
})
if err != nil {
return "", err
}
defer t.End()
if err = t.Authenticate(0); err != nil {
if errors.Is(err, pam.ErrAuth) || errors.Is(err, pam.ErrUserUnknown) {
return "", fmt.Errorf("%w: %v", ErrInvalidCredentials, err)
}
return "", err
}
if err = t.AcctMgmt(0); err != nil {
if errors.Is(err, pam.ErrAcctExpired) || errors.Is(err, pam.ErrPermDenied) {
return "", fmt.Errorf("%w: %v", ErrInvalidCredentials, err)
}
return "", err
}
// PAM login names might suffer transformations in the PAM stack.
// We should take whatever the PAM stack returns for it.
return t.GetItem(pam.User)
}