summaryrefslogtreecommitdiff
path: root/transaction_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'transaction_test.go')
-rw-r--r--transaction_test.go264
1 files changed, 262 insertions, 2 deletions
diff --git a/transaction_test.go b/transaction_test.go
index 94aa9c1..d358b0e 100644
--- a/transaction_test.go
+++ b/transaction_test.go
@@ -2,10 +2,42 @@ package pam
import (
"errors"
+ "fmt"
+ "os"
"os/user"
+ "path/filepath"
+ "runtime"
+ "sync/atomic"
"testing"
+ "time"
+ "unsafe"
)
+func maybeEndTransaction(t *testing.T, tx *Transaction) {
+ t.Helper()
+
+ if tx == nil {
+ return
+ }
+ err := tx.End()
+ if err != nil {
+ t.Fatalf("end #error: %v", err)
+ }
+}
+
+func ensureTransactionEnds(t *testing.T, tx *Transaction) {
+ t.Helper()
+
+ runtime.SetFinalizer(tx, func(tx *Transaction) {
+ // #nosec:G103 - the pointer conversion is checked.
+ handle := atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&tx.handle)))
+ if handle == nil {
+ return
+ }
+ t.Fatalf("transaction has not been finalized")
+ })
+}
+
func TestPAM_001(t *testing.T) {
u, _ := user.Current()
if u.Uid != "0" {
@@ -15,6 +47,8 @@ func TestPAM_001(t *testing.T) {
tx, err := StartFunc("", "test", func(s Style, msg string) (string, error) {
return p, nil
})
+ ensureTransactionEnds(t, tx)
+ defer maybeEndTransaction(t, tx)
if err != nil {
t.Fatalf("start #error: %v", err)
}
@@ -46,6 +80,8 @@ func TestPAM_002(t *testing.T) {
}
return "", errors.New("unexpected")
})
+ ensureTransactionEnds(t, tx)
+ defer maybeEndTransaction(t, tx)
if err != nil {
t.Fatalf("start #error: %v", err)
}
@@ -80,6 +116,8 @@ func TestPAM_003(t *testing.T) {
Password: "secret",
}
tx, err := Start("", "", c)
+ ensureTransactionEnds(t, tx)
+ defer maybeEndTransaction(t, tx)
if err != nil {
t.Fatalf("start #error: %v", err)
}
@@ -98,6 +136,8 @@ func TestPAM_004(t *testing.T) {
Password: "secret",
}
tx, err := Start("", "test", c)
+ ensureTransactionEnds(t, tx)
+ defer maybeEndTransaction(t, tx)
if err != nil {
t.Fatalf("start #error: %v", err)
}
@@ -115,9 +155,18 @@ func TestPAM_005(t *testing.T) {
tx, err := StartFunc("passwd", "test", func(s Style, msg string) (string, error) {
return "secret", nil
})
+ ensureTransactionEnds(t, tx)
+ defer maybeEndTransaction(t, tx)
if err != nil {
t.Fatalf("start #error: %v", err)
}
+ service, err := tx.GetItem(Service)
+ if err != nil {
+ t.Fatalf("GetItem #error: %v", err)
+ }
+ if service != "passwd" {
+ t.Fatalf("Unexpected service: %v", service)
+ }
err = tx.ChangeAuthTok(Silent)
if err != nil {
t.Fatalf("chauthtok #error: %v", err)
@@ -132,6 +181,8 @@ func TestPAM_006(t *testing.T) {
tx, err := StartFunc("passwd", u.Username, func(s Style, msg string) (string, error) {
return "secret", nil
})
+ ensureTransactionEnds(t, tx)
+ defer maybeEndTransaction(t, tx)
if err != nil {
t.Fatalf("start #error: %v", err)
}
@@ -153,6 +204,8 @@ func TestPAM_007(t *testing.T) {
tx, err := StartFunc("", "test", func(s Style, msg string) (string, error) {
return "", errors.New("Sorry, it didn't work")
})
+ ensureTransactionEnds(t, tx)
+ defer maybeEndTransaction(t, tx)
if err != nil {
t.Fatalf("start #error: %v", err)
}
@@ -164,6 +217,9 @@ func TestPAM_007(t *testing.T) {
if len(s) == 0 {
t.Fatalf("error #expected an error message")
}
+ if !errors.Is(err, ErrAuth) {
+ t.Fatalf("error #unexpected error %v", err)
+ }
}
func TestPAM_ConfDir(t *testing.T) {
@@ -173,6 +229,11 @@ func TestPAM_ConfDir(t *testing.T) {
Password: "wrongsecret",
}
tx, err := StartConfDir("permit-service", u.Username, c, "test-services")
+ defer func() {
+ if tx != nil {
+ _ = tx.End()
+ }
+ }()
if !CheckPamHasStartConfdir() {
if err == nil {
t.Fatalf("start should have errored out as pam_start_confdir is not available: %v", err)
@@ -180,6 +241,13 @@ func TestPAM_ConfDir(t *testing.T) {
// nothing else we do, we don't support it.
return
}
+ service, err := tx.GetItem(Service)
+ if err != nil {
+ t.Fatalf("GetItem #error: %v", err)
+ }
+ if service != "permit-service" {
+ t.Fatalf("Unexpected service: %v", service)
+ }
if err != nil {
t.Fatalf("start #error: %v", err)
}
@@ -190,18 +258,31 @@ func TestPAM_ConfDir(t *testing.T) {
}
func TestPAM_ConfDir_FailNoServiceOrUnsupported(t *testing.T) {
+ if !CheckPamHasStartConfdir() {
+ t.Skip("this requires PAM with Conf dir support")
+ }
u, _ := user.Current()
c := Credentials{
Password: "secret",
}
- _, err := StartConfDir("does-not-exists", u.Username, c, ".")
+ tx, err := StartConfDir("does-not-exists", u.Username, c, ".")
if err == nil {
t.Fatalf("authenticate #expected an error")
}
+ if tx != nil {
+ t.Fatalf("authenticate #unexpected transaction")
+ }
s := err.Error()
if len(s) == 0 {
t.Fatalf("error #expected an error message")
}
+ var pamErr Error
+ if !errors.As(err, &pamErr) {
+ t.Fatalf("error #unexpected type: %#v", err)
+ }
+ if pamErr != ErrAbort {
+ t.Fatalf("error #unexpected status: %v", pamErr)
+ }
}
func TestPAM_ConfDir_InfoMessage(t *testing.T) {
@@ -216,9 +297,18 @@ func TestPAM_ConfDir_InfoMessage(t *testing.T) {
}
return "", errors.New("unexpected")
}), "test-services")
+ ensureTransactionEnds(t, tx)
+ defer maybeEndTransaction(t, tx)
if err != nil {
t.Fatalf("start #error: %v", err)
}
+ service, err := tx.GetItem(Service)
+ if err != nil {
+ t.Fatalf("GetItem #error: %v", err)
+ }
+ if service != "echo-service" {
+ t.Fatalf("Unexpected service: %v", service)
+ }
err = tx.Authenticate(0)
if err != nil {
t.Fatalf("authenticate #error: %v", err)
@@ -229,11 +319,23 @@ func TestPAM_ConfDir_InfoMessage(t *testing.T) {
}
func TestPAM_ConfDir_Deny(t *testing.T) {
+ if !CheckPamHasStartConfdir() {
+ t.Skip("this requires PAM with Conf dir support")
+ }
u, _ := user.Current()
tx, err := StartConfDir("deny-service", u.Username, Credentials{}, "test-services")
+ ensureTransactionEnds(t, tx)
+ defer maybeEndTransaction(t, tx)
if err != nil {
t.Fatalf("start #error: %v", err)
}
+ service, err := tx.GetItem(Service)
+ if err != nil {
+ t.Fatalf("GetItem #error: %v", err)
+ }
+ if service != "deny-service" {
+ t.Fatalf("Unexpected service: %v", service)
+ }
err = tx.Authenticate(0)
if err == nil {
t.Fatalf("authenticate #expected an error")
@@ -242,6 +344,9 @@ func TestPAM_ConfDir_Deny(t *testing.T) {
if len(s) == 0 {
t.Fatalf("error #expected an error message")
}
+ if !errors.Is(err, ErrAuth) {
+ t.Fatalf("error #unexpected error %v", err)
+ }
}
func TestPAM_ConfDir_PromptForUserName(t *testing.T) {
@@ -251,6 +356,8 @@ func TestPAM_ConfDir_PromptForUserName(t *testing.T) {
Password: "wrongsecret",
}
tx, err := StartConfDir("succeed-if-user-test", "", c, "test-services")
+ ensureTransactionEnds(t, tx)
+ defer maybeEndTransaction(t, tx)
if !CheckPamHasStartConfdir() {
if err == nil {
t.Fatalf("start should have errored out as pam_start_confdir is not available: %v", err)
@@ -273,6 +380,8 @@ func TestPAM_ConfDir_WrongUserName(t *testing.T) {
Password: "wrongsecret",
}
tx, err := StartConfDir("succeed-if-user-test", "", c, "test-services")
+ ensureTransactionEnds(t, tx)
+ defer maybeEndTransaction(t, tx)
if !CheckPamHasStartConfdir() {
if err == nil {
t.Fatalf("start should have errored out as pam_start_confdir is not available: %v", err)
@@ -288,12 +397,20 @@ func TestPAM_ConfDir_WrongUserName(t *testing.T) {
if len(s) == 0 {
t.Fatalf("error #expected an error message")
}
+ if !errors.Is(err, ErrAuth) {
+ t.Fatalf("error #unexpected error %v", err)
+ }
}
func TestItem(t *testing.T) {
- tx, _ := StartFunc("passwd", "test", func(s Style, msg string) (string, error) {
+ tx, err := StartFunc("passwd", "test", func(s Style, msg string) (string, error) {
return "", nil
})
+ ensureTransactionEnds(t, tx)
+ defer maybeEndTransaction(t, tx)
+ if err != nil {
+ t.Fatalf("start #error: %v", err)
+ }
s, err := tx.GetItem(Service)
if err != nil {
@@ -328,6 +445,8 @@ func TestEnv(t *testing.T) {
tx, err := StartFunc("", "", func(s Style, msg string) (string, error) {
return "", nil
})
+ ensureTransactionEnds(t, tx)
+ defer maybeEndTransaction(t, tx)
if err != nil {
t.Fatalf("start #error: %v", err)
}
@@ -390,6 +509,139 @@ func TestEnv(t *testing.T) {
}
}
+func Test_Error(t *testing.T) {
+ t.Parallel()
+ if !CheckPamHasStartConfdir() {
+ t.Skip("this requires PAM with Conf dir support")
+ }
+
+ statuses := map[string]error{
+ "success": nil,
+ "open_err": ErrOpen,
+ "symbol_err": ErrSymbol,
+ "service_err": ErrService,
+ "system_err": ErrSystem,
+ "buf_err": ErrBuf,
+ "perm_denied": ErrPermDenied,
+ "auth_err": ErrAuth,
+ "cred_insufficient": ErrCredInsufficient,
+ "authinfo_unavail": ErrAuthinfoUnavail,
+ "user_unknown": ErrUserUnknown,
+ "maxtries": ErrMaxtries,
+ "new_authtok_reqd": ErrNewAuthtokReqd,
+ "acct_expired": ErrAcctExpired,
+ "session_err": ErrSession,
+ "cred_unavail": ErrCredUnavail,
+ "cred_expired": ErrCredExpired,
+ "cred_err": ErrCred,
+ "no_module_data": ErrNoModuleData,
+ "conv_err": ErrConv,
+ "authtok_err": ErrAuthtok,
+ "authtok_recover_err": ErrAuthtokRecovery,
+ "authtok_lock_busy": ErrAuthtokLockBusy,
+ "authtok_disable_aging": ErrAuthtokDisableAging,
+ "try_again": ErrTryAgain,
+ "ignore": nil, /* Ignore can't be returned */
+ "abort": ErrAbort,
+ "authtok_expired": ErrAuthtokExpired,
+ "module_unknown": ErrModuleUnknown,
+ "bad_item": ErrBadItem,
+ "conv_again": ErrConvAgain,
+ "incomplete": ErrIncomplete,
+ }
+
+ type Action int
+ const (
+ account Action = iota + 1
+ auth
+ password
+ session
+ )
+ actions := map[string]Action{
+ "account": account,
+ "auth": auth,
+ "password": password,
+ "session": session,
+ }
+
+ c := Credentials{}
+
+ servicePath := t.TempDir()
+
+ for ret, expected := range statuses {
+ ret := ret
+ expected := expected
+ for actionName, action := range actions {
+ actionName := actionName
+ action := action
+ t.Run(fmt.Sprintf("%s %s", ret, actionName), func(t *testing.T) {
+ t.Parallel()
+ serviceName := ret + "-" + actionName
+ serviceFile := filepath.Join(servicePath, serviceName)
+ contents := fmt.Sprintf("%[1]s requisite pam_debug.so "+
+ "auth=%[2]s cred=%[2]s acct=%[2]s prechauthtok=%[2]s "+
+ "chauthtok=%[2]s open_session=%[2]s close_session=%[2]s\n"+
+ "%[1]s requisite pam_permit.so\n", actionName, ret)
+
+ if err := os.WriteFile(serviceFile,
+ []byte(contents), 0600); err != nil {
+ t.Fatalf("can't create service file %v: %v", serviceFile, err)
+ }
+
+ tx, err := StartConfDir(serviceName, "user", c, servicePath)
+ ensureTransactionEnds(t, tx)
+ defer maybeEndTransaction(t, tx)
+ if err != nil {
+ t.Fatalf("start #error: %v", err)
+ }
+
+ switch action {
+ case account:
+ err = tx.AcctMgmt(0)
+ case auth:
+ err = tx.Authenticate(0)
+ case password:
+ err = tx.ChangeAuthTok(0)
+ case session:
+ err = tx.OpenSession(0)
+ }
+
+ if !errors.Is(err, expected) {
+ t.Fatalf("error #unexpected status %#v vs %#v", err,
+ expected)
+ }
+
+ if err != nil {
+ var status Error
+ if !errors.As(err, &status) || err.Error() != status.Error() {
+ t.Fatalf("error #unexpected status %#v vs %#v", err.Error(),
+ status.Error())
+ }
+ }
+ })
+ }
+ }
+}
+
+func Test_Finalizer(t *testing.T) {
+ if !CheckPamHasStartConfdir() {
+ t.Skip("this requires PAM with Conf dir support")
+ }
+
+ func() {
+ tx, err := StartConfDir("permit-service", "", nil, "test-services")
+ ensureTransactionEnds(t, tx)
+ defer maybeEndTransaction(t, tx)
+ if err != nil {
+ t.Fatalf("start #error: %v", err)
+ }
+ }()
+
+ runtime.GC()
+ // sleep to switch to finalizer goroutine
+ time.Sleep(5 * time.Millisecond)
+}
+
func TestFailure_001(t *testing.T) {
tx := Transaction{}
_, err := tx.GetEnvList()
@@ -461,3 +713,11 @@ func TestFailure_009(t *testing.T) {
t.Fatalf("getenvlist #expected an error")
}
}
+
+func TestFailure_010(t *testing.T) {
+ tx := Transaction{}
+ err := tx.End()
+ if err != nil {
+ t.Fatalf("end #unexpected error %v", err)
+ }
+}