Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PAM module stub generator, modules implmentations and integration tests #13

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4b982c8
transaction: Add a transaction base type to define more transaction k…
3v1n0 Sep 25, 2023
8cf5c51
transaction: Add ModuleTransaction type and ModuleHandler interface
3v1n0 Sep 25, 2023
22b9e81
transaction: Properly handle nil bytes in binary transactions
3v1n0 Oct 10, 2023
959de37
pam-moduler: Add first implementation of a Go PAM Module generator
3v1n0 Sep 25, 2023
75278e8
transaction: Define C functions as unexported static inlines
3v1n0 Nov 20, 2023
4b61910
transaction, moduler: Do not export PAM conv handler function to modules
3v1n0 Nov 20, 2023
c0be314
transaction: Move PAM app side function only to app-transaction
3v1n0 Nov 20, 2023
1b09841
moduler: Move module transaction invoke handling to transaction itself
3v1n0 Sep 25, 2023
e9dffa7
pam-moduler: Add test that generates a new debug module and verify it…
3v1n0 Sep 25, 2023
5fbbb88
tests: Add a module implementation with dynamic control from the app
3v1n0 Sep 29, 2023
5f3c15c
module-transaction: Add GetUser() method that prompts an user if non-set
3v1n0 Sep 29, 2023
13989bb
module-transaction: Add support for setting/getting module data
3v1n0 Oct 3, 2023
bc34d3b
module-transaction: Add support for initiating PAM Conversations
3v1n0 Oct 4, 2023
ce46e15
module-transaction: Add support for binary conversations
3v1n0 Oct 5, 2023
a047550
module-transaction: Do not allow parallel conversations by default
3v1n0 Oct 13, 2023
3253288
github/test: Run tests with address sanitizer
3v1n0 Oct 19, 2023
62b69f7
transaction: Add BinaryConversationFunc adapter
3v1n0 Oct 25, 2023
5689405
transaction: Add support for using raw binary pointers conversation h…
3v1n0 Nov 7, 2023
5c4d796
README: Update how to run tests
3v1n0 Oct 19, 2023
61621ce
ci: Show coverage for all packages
3v1n0 Dec 1, 2023
e182844
transaction: Fix typo in conversation doc
3v1n0 Feb 18, 2024
f199038
transaction: Add some missing flags for modules
3v1n0 Mar 21, 2024
979a110
fixup! pam-moduler: Add first implementation of a Go PAM Module gener…
3v1n0 Apr 11, 2024
e0753f6
fixup! pam-moduler: Add first implementation of a Go PAM Module gener…
3v1n0 Apr 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ignore:
# Ignore pam-moduler generated files
- "**/pam_module.go"
19 changes: 18 additions & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,24 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- name: Test
run: sudo go test -v -cover -coverprofile=coverage.out ./...
run: sudo go test -v -cover -coverprofile=coverage.out -coverpkg=./... ./...
- name: Test with Address Sanitizer
env:
GO_PAM_TEST_WITH_ASAN: true
CGO_CFLAGS: "-O0 -g3 -fno-omit-frame-pointer"
run: |
# Do not run sudo-requiring go tests because as PAM has some leaks in 22.04
go test -v -asan -cover -coverprofile=coverage-asan-tx.out -coverpkg=./... -gcflags=all="-N -l"

# Run the rest of tests normally
sudo go test -v -cover -coverprofile=coverage-asan-module.out -coverpkg=./... -asan -gcflags=all="-N -l" -run Module
sudo go test -C cmd -coverprofile=coverage-asan.out -v -coverpkg=./... -asan -gcflags=all="-N -l" ./...
- 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: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
env:
Expand Down
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
coverage.out
coverage*.out
example-module/*.so
example-module/*.h
cmd/pam-moduler/tests/*/*.so
cmd/pam-moduler/tests/*/*.h
121 changes: 121 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,124 @@

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 (acct_mgmt)
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
Expand All @@ -25,5 +143,8 @@ 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 ./...`

[1]: http://godoc.org/github.com/msteinert/pam/v2
[2]: http://www.linux-pam.org/Linux-PAM-html/Linux-PAM_ADG.html
Loading
Loading