Skip to content

Commit

Permalink
module-transaction: Do not allow parallel conversations by default
Browse files Browse the repository at this point in the history
Pam conversations per se may also run in parallel, but this implies that
the application supports this.

Since this normally not the case, do not create modules that may invoke
the pam conversations in parallel by default, adding a mutex to protect
such calls.
  • Loading branch information
3v1n0 committed Oct 18, 2023
1 parent a9602e2 commit f7e71e1
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 9 deletions.
12 changes: 10 additions & 2 deletions cmd/pam-moduler/moduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ var (
moduleBuildFlags = flag.String("build-flags", "", "comma-separated list of go build flags to use when generating the module")
moduleBuildTags = flag.String("build-tags", "", "comma-separated list of build tags to use when generating the module")
noMain = flag.Bool("no-main", false, "whether to add an empty main to generated file")
parallelConv = flag.Bool("parallel-conv", false, "whether to support performing PAM conversations in parallel")
)

// Usage is a replacement usage function for the flags package.
Expand Down Expand Up @@ -136,6 +137,7 @@ func main() {
generateTags: generateTags,
noMain: *noMain,
typeName: *typeName,
parallelConv: *parallelConv,
}

// Print the header and package clause.
Expand Down Expand Up @@ -168,6 +170,7 @@ type Generator struct {
generateTags []string
buildFlags []string
noMain bool
parallelConv bool
}

func (g *Generator) Printf(format string, args ...interface{}) {
Expand All @@ -185,6 +188,11 @@ func (g *Generator) generate() {
buildTagsArg = fmt.Sprintf("-tags %s", strings.Join(g.generateTags, ","))
}

var transactionCreator = "NewModuleTransactionInvoker"
if g.parallelConv {
transactionCreator = "NewModuleTransactionInvokerParallelConv"
}

vFuncs := map[string]string{
"authenticate": "Authenticate",
"setcred": "SetCred",
Expand Down Expand Up @@ -247,7 +255,7 @@ func handlePamCall(pamh *C.pam_handle_t, flags C.int, argc C.int,
return pam.Ignore
}
mt := pam.NewModuleTransactionInvoker(pam.NativeHandle(pamh))
mt := pam.%s(pam.NativeHandle(pamh))
ret, err := mt.InvokeHandler(moduleFunc, pam.Flags(flags),
sliceFromArgv(argc, argv))
Expand All @@ -257,7 +265,7 @@ func handlePamCall(pamh *C.pam_handle_t, flags C.int, argc C.int,
return ret
}
`)
`, transactionCreator)

for cName, goName := range vFuncs {
g.Printf(`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:generate go run github.com/msteinert/pam/cmd/pam-moduler -type integrationTesterModule
//go:generate go run github.com/msteinert/pam/cmd/pam-moduler -type integrationTesterModule -parallel-conv
//go:generate go generate --skip="pam_module.go"

package main
Expand Down
29 changes: 26 additions & 3 deletions module-transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"
"runtime"
"runtime/cgo"
"sync"
"sync/atomic"
"unsafe"
)
Expand Down Expand Up @@ -51,6 +52,7 @@ type ModuleHandlerFunc func(ModuleTransaction, Flags, []string) error
// ModuleTransaction is the module-side handle for a PAM transaction
type moduleTransaction struct {
transactionBase
convMutex *sync.Mutex
}

// ModuleHandler is an interface for objects that can be used to create
Expand All @@ -71,10 +73,27 @@ type ModuleTransactionInvoker interface {
InvokeHandler(handler ModuleHandlerFunc, flags Flags, args []string) (Status, error)
}

// NewModuleTransactionInvoker allows initializing a transaction invoker from
// the module side.
// NewModuleTransaction allows initializing a transaction from the module side.
// Conversations using this transaction can be multi-thread, but this requires
// the application loading the module to support this, otherwise we may just
// break their assumptions.
func NewModuleTransactionParallelConv(handle NativeHandle) ModuleTransaction {
return &moduleTransaction{transactionBase{handle: handle}, nil}
}

// NewModuleTransactionInvoker allows initializing a transaction invoker from the
// module side.
func NewModuleTransactionInvoker(handle NativeHandle) ModuleTransactionInvoker {
return &moduleTransaction{transactionBase{handle: handle}}
return &moduleTransaction{transactionBase{handle: handle}, &sync.Mutex{}}
}

// NewModuleTransactionInvokerParallelConv allows initializing a transaction invoker
// from the module side.
// Conversations using this transaction can be multi-thread, but this requires
// the application loading the module to support this, otherwise we may just
// break their assumptions.
func NewModuleTransactionInvokerParallelConv(handle NativeHandle) ModuleTransactionInvoker {
return &moduleTransaction{transactionBase{handle: handle}, nil}
}

func (m *moduleTransaction) InvokeHandler(handler ModuleHandlerFunc,
Expand Down Expand Up @@ -467,6 +486,10 @@ func (m *moduleTransaction) startConvMultiImpl(iface moduleTransactionIface,
}
}

if m.convMutex != nil {
m.convMutex.Lock()
defer m.convMutex.Unlock()
}
var cResponses *C.struct_pam_response
if err := m.handlePamStatus(
iface.startConv(conv, C.int(len(requests)), cMessages, &cResponses)); err != nil {
Expand Down
13 changes: 10 additions & 3 deletions module-transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,11 +296,9 @@ func Test_ModuleTransaction_InvokeHandler(t *testing.T) {
}
}

func Test_MockModuleTransaction(t *testing.T) {
func testMockModuleTransaction(t *testing.T, mt *moduleTransaction) {
t.Parallel()

mt := NewModuleTransactionInvoker(nil).(*moduleTransaction)

tests := map[string]struct {
testFunc func(mock *mockModuleTransaction) (any, error)
mockExpectations mockModuleTransactionExpectations
Expand Down Expand Up @@ -857,3 +855,12 @@ func Test_MockModuleTransaction(t *testing.T) {
})
}
}

func Test_MockModuleTransaction(t *testing.T) {
testMockModuleTransaction(t, NewModuleTransactionInvoker(nil).(*moduleTransaction))
}

func Test_MockModuleTransactionParallelConv(t *testing.T) {
testMockModuleTransaction(t,
NewModuleTransactionInvokerParallelConv(nil).(*moduleTransaction))
}

0 comments on commit f7e71e1

Please sign in to comment.