Go to file
Lars Scheibling 5d43b022cb Change to v1 2024-01-12 09:32:51 +00:00
.github-disabled/workflows Updated path 2024-01-12 09:20:18 +00:00
cmd/pam-moduler Change to v1 2024-01-12 09:32:51 +00:00
example-module Change to v1 2024-01-12 09:32:51 +00:00
test-services transaction_test: Add root-less tests to check pam conversation 2023-09-19 18:46:25 +02:00
.clang-format Format transaction.c with clang-format 2023-09-21 08:23:39 -05:00
.codecov.yml pam-moduler: Add first implementation of a Go PAM Module generator 2023-12-14 22:07:48 +01:00
.gitignore github/test: Run tests with address sanitizer 2023-12-14 22:07:50 +01:00
.golangci.yaml ci: Use golang-ci linter 2023-11-30 01:16:38 +01:00
LICENSE Add 2-clause BSD license 2015-04-01 21:53:50 -05:00
README.md Updated path 2024-01-12 09:20:18 +00:00
app-transaction.go transaction: Add support for using raw binary pointers conversation handler 2023-12-14 22:07:50 +01:00
errors.go transaction: Add PAM Error types Go definitions 2023-11-30 01:16:39 +01:00
example_test.go Updated path 2024-01-12 09:20:18 +00:00
go.mod Change to v1 2024-01-12 09:32:51 +00:00
go.sum Update dependencies 2023-04-04 15:44:38 -05:00
module-transaction-mock.go module-transaction: Add support for binary conversations 2023-12-14 22:07:50 +01:00
module-transaction.go module-transaction: Do not allow parallel conversations by default 2023-12-14 22:07:50 +01:00
module-transaction_test.go transaction: Add support for using raw binary pointers conversation handler 2023-12-14 22:07:50 +01:00
transaction.go transaction: Move PAM app side function only to app-transaction 2023-12-14 22:07:50 +01:00
transaction.h module-transaction: Add support for binary conversations 2023-12-14 22:07:50 +01:00
transaction_test.go github/test: Run tests with address sanitizer 2023-12-14 22:07:50 +01:00
utils.go transaction: Add support for using raw binary pointers conversation handler 2023-12-14 22:07:50 +01:00

README.md

GoDoc codecov Go Report Card

Go PAM

This is a Go wrapper for the PAM application API.

Package path changed for ease of use, and to avoid conflicts with the original

Originally created by Mike Steinert Updated and modified by Marco Trevisan

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 and an example how to use it using go generate create them is available as an example module.

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 forks, 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:

#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 system for testing, create a user named "test" with the password "secret". For example:

$ sudo useradd test \
    -d /tmp/test \
    -p '$1$Qd8H95T5$RYSZQeoFbEB.gS19zS99A0' \
    -s /bin/false

Then execute the tests:

$ sudo GOPATH=$GOPATH $(which go) test -v

Other tests can instead run as user without any setup with normal go test ./...