pam-moduler: Add first implementation of a Go PAM Module generator
A PAM module can be generated using pam-moduler and implemented fully in go without having to manually deal with the C setup. Module can be compiled using go generate, so go:generate directives can be used to make this process automatic, with a single go generate call as shown in the example.
This commit is contained in:
parent
f0d140c281
commit
a8c4a14150
|
@ -0,0 +1,3 @@
|
|||
ignore:
|
||||
# Ignore pam-moduler generated files
|
||||
- "**/pam_module.go"
|
|
@ -18,6 +18,12 @@ jobs:
|
|||
run: sudo useradd -d /tmp/test -p '$1$Qd8H95T5$RYSZQeoFbEB.gS19zS99A0' -s /bin/false test
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- name: Generate example module
|
||||
run: |
|
||||
rm -f example-module/pam_go.so
|
||||
go generate -C example-module -v
|
||||
test -e example-module/pam_go.so
|
||||
git diff --exit-code example-module
|
||||
- name: Test
|
||||
run: sudo go test -v -cover -coverprofile=coverage.out ./...
|
||||
- name: Upload coverage reports to Codecov
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
coverage.out
|
||||
example-module/*.so
|
||||
example-module/*.h
|
||||
|
|
117
README.md
117
README.md
|
@ -6,6 +6,123 @@
|
|||
|
||||
This is a Go wrapper for the PAM application API.
|
||||
|
||||
## Module support
|
||||
|
||||
Go PAM can also used to create PAM modules in a simple way, using the go.
|
||||
|
||||
The code can be generated using [pam-moduler](cmd/pam-moduler/moduler.go) and
|
||||
an example how to use it using `go generate` create them is available as an
|
||||
[example module](example-module/module.go).
|
||||
|
||||
### Modules and PAM applications
|
||||
|
||||
The modules generated with go can be used by any PAM application, however there
|
||||
are some caveats, in fact a Go shared library could misbehave when loaded
|
||||
improperly. In particular if a Go shared library is loaded and then the program
|
||||
`fork`s, the library will have an undefined behavior.
|
||||
|
||||
This is the case of SSHd that loads a pam library before forking, making any
|
||||
go PAM library to make it hang.
|
||||
|
||||
To solve this case, we can use a little workaround: to ensure that the go
|
||||
library is loaded only after the program has forked, we can just `dload` it once
|
||||
a PAM library is called, in this way go code will be loaded only after that the
|
||||
PAM application has `fork`'ed.
|
||||
|
||||
To do this, we can use a very simple wrapper written in C:
|
||||
|
||||
```c
|
||||
#include <dlfcn.h>
|
||||
#include <limits.h>
|
||||
#include <security/pam_modules.h>
|
||||
#include <security/pam_ext.h>
|
||||
|
||||
typedef int (*PamHandler)(pam_handle_t *,
|
||||
int flags,
|
||||
int argc,
|
||||
const char **argv);
|
||||
|
||||
static void
|
||||
on_go_module_removed (pam_handle_t *pamh,
|
||||
void *go_module,
|
||||
int error_status)
|
||||
{
|
||||
dlclose (go_module);
|
||||
}
|
||||
|
||||
static void *
|
||||
load_module (pam_handle_t *pamh,
|
||||
const char *module_path)
|
||||
{
|
||||
void *go_module;
|
||||
|
||||
if (pam_get_data (pamh, "go-module", (const void **) &go_module) == PAM_SUCCESS)
|
||||
return go_module;
|
||||
|
||||
go_module = dlopen (module_path, RTLD_LAZY);
|
||||
if (!go_module)
|
||||
return NULL;
|
||||
|
||||
pam_set_data (pamh, "go-module", go_module, on_go_module_removed);
|
||||
|
||||
return go_module;
|
||||
}
|
||||
|
||||
static inline int
|
||||
call_pam_function (pam_handle_t *pamh,
|
||||
const char *function,
|
||||
int flags,
|
||||
int argc,
|
||||
const char **argv)
|
||||
{
|
||||
char module_path[PATH_MAX] = {0};
|
||||
const char *sub_module;
|
||||
PamHandler func;
|
||||
void *go_module;
|
||||
|
||||
if (argc < 1)
|
||||
{
|
||||
pam_error (pamh, "%s: no module provided", function);
|
||||
return PAM_MODULE_UNKNOWN;
|
||||
}
|
||||
|
||||
sub_module = argv[0];
|
||||
argc -= 1;
|
||||
argv = (argc == 0) ? NULL : &argv[1];
|
||||
|
||||
strncpy (module_path, sub_module, PATH_MAX - 1);
|
||||
|
||||
go_module = load_module (pamh, module_path);
|
||||
if (!go_module)
|
||||
{
|
||||
pam_error (pamh, "Impossible to load module %s", module_path);
|
||||
return PAM_OPEN_ERR;
|
||||
}
|
||||
|
||||
*(void **) (&func) = dlsym (go_module, function);
|
||||
if (!func)
|
||||
{
|
||||
pam_error (pamh, "Symbol %s not found in %s", function, module_path);
|
||||
return PAM_OPEN_ERR;
|
||||
}
|
||||
|
||||
return func (pamh, flags, argc, argv);
|
||||
}
|
||||
|
||||
#define DEFINE_PAM_WRAPPER(name) \
|
||||
PAM_EXTERN int \
|
||||
(pam_sm_ ## name) (pam_handle_t * pamh, int flags, int argc, const char **argv) \
|
||||
{ \
|
||||
return call_pam_function (pamh, "pam_sm_" #name, flags, argc, argv); \
|
||||
}
|
||||
|
||||
DEFINE_PAM_WRAPPER (authenticate)
|
||||
DEFINE_PAM_WRAPPER (chauthtok)
|
||||
DEFINE_PAM_WRAPPER (close_session)
|
||||
DEFINE_PAM_WRAPPER (open_session)
|
||||
DEFINE_PAM_WRAPPER (setcred)
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
To run the full suite, the tests must be run as the root user. To setup your
|
||||
|
|
|
@ -0,0 +1,305 @@
|
|||
// pam-moduler is a tool to automate the creation of PAM Modules in go
|
||||
//
|
||||
// The file is created in the same package and directory as the package that
|
||||
// creates the module
|
||||
//
|
||||
// The module implementation should define a pamModuleHandler object that
|
||||
// implements the pam.ModuleHandler type and that will be used for each callback
|
||||
//
|
||||
// Otherwise it's possible to provide a typename from command line that will
|
||||
// be used for this purpose
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// //go:generate go run github.com/msteinert/pam/pam-moduler
|
||||
// //go:generate go generate --skip="pam_module"
|
||||
// package main
|
||||
//
|
||||
// import "github.com/msteinert/pam/v2"
|
||||
//
|
||||
// type ExampleHandler struct{}
|
||||
// var pamModuleHandler pam.ModuleHandler = &ExampleHandler{}
|
||||
//
|
||||
// func (h *ExampleHandler) AcctMgmt(pam.ModuleTransaction, pam.Flags, []string) error {
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// func (h *ExampleHandler) Authenticate(pam.ModuleTransaction, pam.Flags, []string) error {
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// func (h *ExampleHandler) ChangeAuthTok(pam.ModuleTransaction, pam.Flags, []string) error {
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// func (h *ExampleHandler) OpenSession(pam.ModuleTransaction, pam.Flags, []string) error {
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// func (h *ExampleHandler) CloseSession(pam.ModuleTransaction, pam.Flags, []string) error {
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// func (h *ExampleHandler) SetCred(pam.ModuleTransaction, pam.Flags, []string) error {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// Package main provides the module shared library.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const toolName = "pam-moduler"
|
||||
|
||||
var (
|
||||
output = flag.String("output", "", "output file name; default srcdir/pam_module.go")
|
||||
libName = flag.String("libname", "", "output library name; default pam_go.so")
|
||||
typeName = flag.String("type", "", "type name to be used as pam.ModuleHandler")
|
||||
buildTags = flag.String("tags", "", "build tags expression to append to use in the go:build directive")
|
||||
skipGenerator = flag.Bool("no-generator", false, "whether to add go:generator directives to the generated source")
|
||||
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")
|
||||
)
|
||||
|
||||
// Usage is a replacement usage function for the flags package.
|
||||
func Usage() {
|
||||
fmt.Fprintf(os.Stderr, "Usage of %s:\n", toolName)
|
||||
fmt.Fprintf(os.Stderr, "\t%s [flags] [-output O] [-libname pam_go] [-type N]\n", toolName)
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix(toolName + ": ")
|
||||
flag.Usage = Usage
|
||||
flag.Parse()
|
||||
|
||||
if *skipGenerator {
|
||||
if *libName != "" {
|
||||
fmt.Fprintf(os.Stderr,
|
||||
"Generator directives disabled, libname will have no effect\n")
|
||||
}
|
||||
if *moduleBuildTags != "" {
|
||||
fmt.Fprintf(os.Stderr,
|
||||
"Generator directives disabled, build-tags will have no effect\n")
|
||||
}
|
||||
if *moduleBuildFlags != "" {
|
||||
fmt.Fprintf(os.Stderr,
|
||||
"Generator directives disabled, build-flags will have no effect\n")
|
||||
}
|
||||
}
|
||||
|
||||
lib := *libName
|
||||
if lib == "" {
|
||||
lib = "pam_go"
|
||||
} else {
|
||||
lib, _ = strings.CutSuffix(lib, ".so")
|
||||
lib, _ = strings.CutPrefix(lib, "lib")
|
||||
}
|
||||
|
||||
outputName, _ := strings.CutSuffix(*output, ".go")
|
||||
if outputName == "" {
|
||||
baseName := "pam_module"
|
||||
outputName = filepath.Join(".", strings.ToLower(baseName))
|
||||
}
|
||||
outputName = outputName + ".go"
|
||||
|
||||
var tags string
|
||||
if *buildTags != "" {
|
||||
tags = *buildTags
|
||||
}
|
||||
|
||||
var generateTags []string
|
||||
if len(*moduleBuildTags) > 0 {
|
||||
generateTags = strings.Split(*moduleBuildTags, ",")
|
||||
}
|
||||
|
||||
var buildFlags []string
|
||||
if *moduleBuildFlags != "" {
|
||||
buildFlags = strings.Split(*moduleBuildFlags, ",")
|
||||
}
|
||||
|
||||
g := Generator{
|
||||
outputName: outputName,
|
||||
libName: lib,
|
||||
tags: tags,
|
||||
buildFlags: buildFlags,
|
||||
generateTags: generateTags,
|
||||
noMain: *noMain,
|
||||
typeName: *typeName,
|
||||
}
|
||||
|
||||
// Print the header and package clause.
|
||||
g.printf("// Code generated by \"%s %s\"; DO NOT EDIT.\n",
|
||||
toolName, strings.Join(os.Args[1:], " "))
|
||||
g.printf("\n")
|
||||
|
||||
// Generate the code
|
||||
g.generate()
|
||||
|
||||
// Format the output.
|
||||
src := g.format()
|
||||
|
||||
// Write to file.
|
||||
err := os.WriteFile(outputName, src, 0600)
|
||||
if err != nil {
|
||||
log.Fatalf("writing output: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Generator holds the state of the analysis. Primarily used to buffer
|
||||
// the output for format.Source.
|
||||
type Generator struct {
|
||||
buf bytes.Buffer // Accumulated output.
|
||||
|
||||
libName string
|
||||
outputName string
|
||||
typeName string
|
||||
tags string
|
||||
generateTags []string
|
||||
buildFlags []string
|
||||
noMain bool
|
||||
}
|
||||
|
||||
func (g *Generator) printf(format string, args ...interface{}) {
|
||||
fmt.Fprintf(&g.buf, format, args...)
|
||||
}
|
||||
|
||||
// generate produces the String method for the named type.
|
||||
func (g *Generator) generate() {
|
||||
if g.tags != "" {
|
||||
g.printf("//go:build %s\n", g.tags)
|
||||
}
|
||||
|
||||
var buildTagsArg string
|
||||
if len(g.generateTags) > 0 {
|
||||
buildTagsArg = fmt.Sprintf("-tags %s", strings.Join(g.generateTags, ","))
|
||||
}
|
||||
|
||||
// We use a slice since we want to keep order, for reproducible builds.
|
||||
vFuncs := []struct {
|
||||
cName string
|
||||
goName string
|
||||
}{
|
||||
{"authenticate", "Authenticate"},
|
||||
{"setcred", "SetCred"},
|
||||
{"acct_mgmt", "AcctMgmt"},
|
||||
{"open_session", "OpenSession"},
|
||||
{"close_session", "CloseSession"},
|
||||
{"chauthtok", "ChangeAuthTok"},
|
||||
}
|
||||
|
||||
g.printf(`//go:generate go build "-ldflags=-extldflags -Wl,-soname,%[2]s.so" `+
|
||||
`-buildmode=c-shared -o %[2]s.so %[3]s %[4]s
|
||||
`,
|
||||
g.outputName, g.libName, buildTagsArg, strings.Join(g.buildFlags, " "))
|
||||
|
||||
g.printf(`
|
||||
// Package main is the package for the PAM module library.
|
||||
package main
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -lpam -fPIC
|
||||
#include <security/pam_modules.h>
|
||||
|
||||
typedef const char _const_char_t;
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"unsafe"
|
||||
"github.com/msteinert/pam/v2"
|
||||
)
|
||||
`)
|
||||
|
||||
if g.typeName != "" {
|
||||
g.printf(`
|
||||
var pamModuleHandler pam.ModuleHandler = &%[1]s{}
|
||||
`, g.typeName)
|
||||
} else {
|
||||
g.printf(`
|
||||
// Do a typecheck at compile time
|
||||
var _ pam.ModuleHandler = pamModuleHandler;
|
||||
`)
|
||||
}
|
||||
|
||||
g.printf(`
|
||||
// sliceFromArgv returns a slice of strings given to the PAM module.
|
||||
func sliceFromArgv(argc C.int, argv **C._const_char_t) []string {
|
||||
r := make([]string, 0, argc)
|
||||
for _, s := range unsafe.Slice(argv, argc) {
|
||||
r = append(r, C.GoString(s))
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// handlePamCall is the function that translates C pam requests to Go.
|
||||
func handlePamCall(pamh *C.pam_handle_t, flags C.int, argc C.int,
|
||||
argv **C._const_char_t, moduleFunc pam.ModuleHandlerFunc) C.int {
|
||||
if pamModuleHandler == nil {
|
||||
return C.int(pam.ErrNoModuleData)
|
||||
}
|
||||
|
||||
if moduleFunc == nil {
|
||||
return C.int(pam.ErrIgnore)
|
||||
}
|
||||
|
||||
err := moduleFunc(pam.NewModuleTransaction(pam.NativeHandle(pamh)),
|
||||
pam.Flags(flags), sliceFromArgv(argc, argv))
|
||||
|
||||
if err == nil {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pam.Flags(flags) & pam.Silent) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "module returned error: %%v\n", err)
|
||||
}
|
||||
|
||||
var pamErr pam.Error
|
||||
if errors.As(err, &pamErr) {
|
||||
return C.int(pamErr)
|
||||
}
|
||||
|
||||
return C.int(pam.ErrSystem)
|
||||
}
|
||||
`)
|
||||
|
||||
for _, f := range vFuncs {
|
||||
g.printf(`
|
||||
//export pam_sm_%[1]s
|
||||
func pam_sm_%[1]s(pamh *C.pam_handle_t, flags C.int, argc C.int, argv **C._const_char_t) C.int {
|
||||
return handlePamCall(pamh, flags, argc, argv, pamModuleHandler.%[2]s)
|
||||
}
|
||||
`, f.cName, f.goName)
|
||||
}
|
||||
|
||||
if !g.noMain {
|
||||
g.printf("\nfunc main() {}\n")
|
||||
}
|
||||
}
|
||||
|
||||
// format returns the gofmt-ed contents of the Generator's buffer.
|
||||
func (g *Generator) format() []byte {
|
||||
src, err := format.Source(g.buf.Bytes())
|
||||
if err != nil {
|
||||
// Should never happen, but can arise when developing this code.
|
||||
// The user can compile the output to see the error.
|
||||
log.Printf("warning: internal error: invalid Go generated: %s", err)
|
||||
log.Printf("warning: compile the package to analyze the error")
|
||||
return g.buf.Bytes()
|
||||
}
|
||||
return src
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
// These go:generate directive allow to generate the module by just using
|
||||
// `go generate` once in the module directory.
|
||||
// This is not strictly needed
|
||||
|
||||
//go:generate go run github.com/msteinert/pam/v2/cmd/pam-moduler
|
||||
//go:generate go generate --skip="pam_module.go"
|
||||
|
||||
// Package main provides the module shared library.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/msteinert/pam/v2"
|
||||
)
|
||||
|
||||
type exampleHandler struct{}
|
||||
|
||||
var pamModuleHandler pam.ModuleHandler = &exampleHandler{}
|
||||
var _ = pamModuleHandler
|
||||
|
||||
// AcctMgmt is the module handle function for account management.
|
||||
func (h *exampleHandler) AcctMgmt(mt pam.ModuleTransaction, flags pam.Flags, args []string) error {
|
||||
return fmt.Errorf("AcctMgmt not implemented: %w", pam.ErrIgnore)
|
||||
}
|
||||
|
||||
// Authenticate is the module handle function for authentication.
|
||||
func (h *exampleHandler) Authenticate(mt pam.ModuleTransaction, flags pam.Flags, args []string) error {
|
||||
return pam.ErrAuthinfoUnavail
|
||||
}
|
||||
|
||||
// ChangeAuthTok is the module handle function for changing authentication token.
|
||||
func (h *exampleHandler) ChangeAuthTok(mt pam.ModuleTransaction, flags pam.Flags, args []string) error {
|
||||
return fmt.Errorf("ChangeAuthTok not implemented: %w", pam.ErrIgnore)
|
||||
}
|
||||
|
||||
// OpenSession is the module handle function for open session.
|
||||
func (h *exampleHandler) OpenSession(mt pam.ModuleTransaction, flags pam.Flags, args []string) error {
|
||||
return fmt.Errorf("OpenSession not implemented: %w", pam.ErrIgnore)
|
||||
}
|
||||
|
||||
// CloseSession is the module handle function for close session.
|
||||
func (h *exampleHandler) CloseSession(mt pam.ModuleTransaction, flags pam.Flags, args []string) error {
|
||||
return fmt.Errorf("CloseSession not implemented: %w", pam.ErrIgnore)
|
||||
}
|
||||
|
||||
// SetCred is the module handle function for set credentials.
|
||||
func (h *exampleHandler) SetCred(mt pam.ModuleTransaction, flags pam.Flags, args []string) error {
|
||||
return fmt.Errorf("SetCred not implemented: %w", pam.ErrIgnore)
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
// Code generated by "pam-moduler "; DO NOT EDIT.
|
||||
|
||||
//go:generate go build "-ldflags=-extldflags -Wl,-soname,pam_go.so" -buildmode=c-shared -o pam_go.so
|
||||
|
||||
// Package main is the package for the PAM module library.
|
||||
package main
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -lpam -fPIC
|
||||
#include <security/pam_modules.h>
|
||||
|
||||
typedef const char _const_char_t;
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/msteinert/pam/v2"
|
||||
"os"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Do a typecheck at compile time
|
||||
var _ pam.ModuleHandler = pamModuleHandler
|
||||
|
||||
// sliceFromArgv returns a slice of strings given to the PAM module.
|
||||
func sliceFromArgv(argc C.int, argv **C._const_char_t) []string {
|
||||
r := make([]string, 0, argc)
|
||||
for _, s := range unsafe.Slice(argv, argc) {
|
||||
r = append(r, C.GoString(s))
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// handlePamCall is the function that translates C pam requests to Go.
|
||||
func handlePamCall(pamh *C.pam_handle_t, flags C.int, argc C.int,
|
||||
argv **C._const_char_t, moduleFunc pam.ModuleHandlerFunc) C.int {
|
||||
if pamModuleHandler == nil {
|
||||
return C.int(pam.ErrNoModuleData)
|
||||
}
|
||||
|
||||
if moduleFunc == nil {
|
||||
return C.int(pam.ErrIgnore)
|
||||
}
|
||||
|
||||
err := moduleFunc(pam.NewModuleTransaction(pam.NativeHandle(pamh)),
|
||||
pam.Flags(flags), sliceFromArgv(argc, argv))
|
||||
|
||||
if err == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if (pam.Flags(flags) & pam.Silent) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "module returned error: %v\n", err)
|
||||
}
|
||||
|
||||
var pamErr pam.Error
|
||||
if errors.As(err, &pamErr) {
|
||||
return C.int(pamErr)
|
||||
}
|
||||
|
||||
return C.int(pam.ErrSystem)
|
||||
}
|
||||
|
||||
//export pam_sm_authenticate
|
||||
func pam_sm_authenticate(pamh *C.pam_handle_t, flags C.int, argc C.int, argv **C._const_char_t) C.int {
|
||||
return handlePamCall(pamh, flags, argc, argv, pamModuleHandler.Authenticate)
|
||||
}
|
||||
|
||||
//export pam_sm_setcred
|
||||
func pam_sm_setcred(pamh *C.pam_handle_t, flags C.int, argc C.int, argv **C._const_char_t) C.int {
|
||||
return handlePamCall(pamh, flags, argc, argv, pamModuleHandler.SetCred)
|
||||
}
|
||||
|
||||
//export pam_sm_acct_mgmt
|
||||
func pam_sm_acct_mgmt(pamh *C.pam_handle_t, flags C.int, argc C.int, argv **C._const_char_t) C.int {
|
||||
return handlePamCall(pamh, flags, argc, argv, pamModuleHandler.AcctMgmt)
|
||||
}
|
||||
|
||||
//export pam_sm_open_session
|
||||
func pam_sm_open_session(pamh *C.pam_handle_t, flags C.int, argc C.int, argv **C._const_char_t) C.int {
|
||||
return handlePamCall(pamh, flags, argc, argv, pamModuleHandler.OpenSession)
|
||||
}
|
||||
|
||||
//export pam_sm_close_session
|
||||
func pam_sm_close_session(pamh *C.pam_handle_t, flags C.int, argc C.int, argv **C._const_char_t) C.int {
|
||||
return handlePamCall(pamh, flags, argc, argv, pamModuleHandler.CloseSession)
|
||||
}
|
||||
|
||||
//export pam_sm_chauthtok
|
||||
func pam_sm_chauthtok(pamh *C.pam_handle_t, flags C.int, argc C.int, argv **C._const_char_t) C.int {
|
||||
return handlePamCall(pamh, flags, argc, argv, pamModuleHandler.ChangeAuthTok)
|
||||
}
|
||||
|
||||
func main() {}
|
|
@ -15,7 +15,9 @@ type ModuleTransaction interface {
|
|||
type ModuleHandlerFunc func(ModuleTransaction, Flags, []string) error
|
||||
|
||||
// ModuleTransaction is the module-side handle for a PAM transaction.
|
||||
type moduleTransaction = transactionBase
|
||||
type moduleTransaction struct {
|
||||
transactionBase
|
||||
}
|
||||
|
||||
// ModuleHandler is an interface for objects that can be used to create
|
||||
// PAM modules from go.
|
||||
|
@ -27,3 +29,9 @@ type ModuleHandler interface {
|
|||
OpenSession(ModuleTransaction, Flags, []string) error
|
||||
SetCred(ModuleTransaction, Flags, []string) error
|
||||
}
|
||||
|
||||
// NewModuleTransaction allows initializing a transaction invoker from
|
||||
// the module side.
|
||||
func NewModuleTransaction(handle NativeHandle) ModuleTransaction {
|
||||
return &moduleTransaction{transactionBase{handle: handle}}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue