This requires the allocating function to provide a binary pointer that
will be free'd by the conversation handlers finalizers.
This is for a more advanced usage scenario where the binary conversion
may be handled manually.
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.
Modules have the ability to start PAM conversations, so while the
transaction code can handle them we did not have a way to init them.
Yet.
So add some APIs allowing this, making it easier from the go side to
handle the conversations.
In this commit we only support text-based conversations, but code is
designed with the idea of supporting binary cases too.
Added the integration tests using the module that is now able to both
start conversation and handle them using Go only.
Module data is data associated with a module handle that is available
for the whole module loading time so it can be used also during
different operations.
We use cgo handles to preserve the life of the go objects so any value
can be associated with a pam transaction.
In order to properly test the interaction of a module transaction from
the application point of view, we need to perform operation in the
module and ensure that the expected values are returned and handled
In order to do this, without using the PAM apis that we want to test,
use a simple trick:
- Create an application that works as server using an unix socket
- Create a module that connects to it
- Pass the socket to the module via the module service file arguments
- Add some basic protocol that allows the application to send a request
and to the module to reply to that.
- Use reflection and serialization to automatically call module methods
and return the values to the application where we do the check
We mimic what pam_debug.so does by default, by implementing a similar
module fully in go, generated using pam-moduler.
This requires various utilities to generate the module and run the tests
that are in a separate internal modules so that it can be shared between
multiple implementations
This function is only needed when using go PAM for creating applications
so it's not something we expect to have exported to library modules.
To prevent this use an `asPamModule` tag to prevent compilation of
application-only features.
This will make it easier to avoid exporting unexpected symbols to the
generated PAM libraries.
Also it makes less messy handling C code inside go files.
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 allows to easily define go-handlers for module operations.
We need to expose few more types externally so that it's possible to
create the module transaction handler and return specific transaction
errors
A pam handler can be used both by a module and by an Application, go-pam
is meant to be used in the application side right now, but it can be
easily changed to also create modules.
This is the prerequisite work to support this.
We redefined various PAM constant values for items, flags and style, but
only few of them were marked as being Item's or Flag's. This caused go to
just consider them as generic integers instead of the actual subtype.
A PAM transaction needs to be ended in order to release the associated
resources, however this can't be sadly automated as the go finalizers
run in goroutines and this could cause problems to modules that we load.
In fact a module code may be called back during pam_end (to cleanup data
for example) and the module code could not be thread safe.
So let's make this more manual, but safer.
The transaction status is still preserved in the transaction so end will
be automatically called with the last-known status.
Closes: #14
While transaction does implement error, it's not a valid error
implementer because it may have bogous values since it's not thread-safe
and so we may read the result of Error() when it's into an invalid state
As per this never return it as an error, while always return the Status
unless when not available, where we still return pam.Error.
Transactions save the status of each operation in a status field, however
such field could be written concurrently by various operations, so we
need to be sure that:
- We always return the status for the current operation
- We store the status in a atomic way so that other actions won't
create write races
In general, in a multi-thread operation one should not rely on
Transaction.Error() to get info about the last operation.
All the pam functions return an integer with the status of the operation
so instead of duplicating the same code everywhere, that is quite error
prone, use an helper function.
It would have been nice to make this more dynamic, but cgo doesn't allow
us to do much magic here.
This is enough though.
If the transaction fails during start, there's no way to get the error
detail in a programmatic way, so let's wrap the pam.Error to allow more
per-type checks.