Skip to content

Instantly share code, notes, and snippets.

@sebschrader
Last active January 9, 2020 13:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sebschrader/be6f599effd50f5e229eab52f0cde9ee to your computer and use it in GitHub Desktop.
Save sebschrader/be6f599effd50f5e229eab52f0cde9ee to your computer and use it in GitHub Desktop.
PAM module that dumps the internal PAM state. Useful for debugging your own PAM modules without recompiling PAM.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <security/pam_appl.h>
#include <security/pam_modules.h>
struct handler {
int handler_type;
int (*func)(pam_handle_t *pamh, int flags, int argc, char **argv);
int actions[_PAM_RETURN_VALUES];
/* set by authenticate, open_session, chauthtok(1st)
consumed by setcred, close_session, chauthtok(2nd) */
int cached_retval; int *cached_retval_p;
int argc;
char **argv;
struct handler *next;
char *mod_name;
int stack_level;
int grantor;
};
#define PAM_HT_MODULE 0
#define PAM_HT_MUST_FAIL 1
#define PAM_HT_SUBSTACK 2
#define PAM_HT_SILENT_MODULE 3
struct loaded_module {
char *name;
int type; /* PAM_STATIC_MOD or PAM_DYNAMIC_MOD */
void *dl_handle;
};
#define PAM_MT_DYNAMIC_MOD 0
#define PAM_MT_STATIC_MOD 1
#define PAM_MT_FAULTY_MOD 2
struct handlers {
struct handler *authenticate;
struct handler *setcred;
struct handler *acct_mgmt;
struct handler *open_session;
struct handler *close_session;
struct handler *chauthtok;
};
struct service {
struct loaded_module *module; /* Array of modules */
int modules_allocated;
int modules_used;
int handlers_loaded;
struct handlers conf; /* the configured handlers */
struct handlers other; /* the default handlers */
};
struct pam_data {
char *name;
void *data;
void (*cleanup)(pam_handle_t *pamh, void *data, int error_status);
struct pam_data *next;
};
struct pam_environ {
int entries; /* the number of pointers available */
int requested; /* the number of pointers used: *
* 1 <= requested <= entries */
char **list; /* the environment storage (a list *
* of pointers to malloc() memory) */
};
#include <sys/time.h>
typedef enum { PAM_FALSE, PAM_TRUE } _pam_boolean;
struct _pam_fail_delay {
_pam_boolean set;
unsigned int delay;
time_t begin;
const void *delay_fn_ptr;
};
/* initial state in substack */
struct _pam_substack_state {
int impression;
int status;
};
struct _pam_former_state {
/* this is known and set by _pam_dispatch() */
int choice; /* which flavor of module function did we call? */
/* state info for the _pam_dispatch_aux() function */
int depth; /* how deep in the stack were we? */
int impression; /* the impression at that time */
int status; /* the status before returning incomplete */
struct _pam_substack_state *substates; /* array of initial substack states */
/* state info used by pam_get_user() function */
int fail_user;
int want_user;
char *prompt; /* saved prompt information */
/* state info for the pam_chauthtok() function */
_pam_boolean update;
};
struct pam_handle {
char *authtok;
unsigned caller_is;
struct pam_conv *pam_conversation;
char *oldauthtok;
char *prompt; /* for use by pam_get_user() */
char *service_name;
char *user;
char *rhost;
char *ruser;
char *tty;
char *xdisplay;
char *authtok_type; /* PAM_AUTHTOK_TYPE */
struct pam_data *data;
struct pam_environ *env; /* structure to maintain environment list */
struct _pam_fail_delay fail_delay; /* helper function for easy delays */
struct pam_xauth_data xauth; /* auth info for X display */
struct service handlers;
struct _pam_former_state former; /* library state - support for
event driven applications */
const char *mod_name; /* Name of the module currently executed */
int mod_argc; /* Number of module arguments */
char **mod_argv; /* module arguments */
int choice; /* Which function we call from the module */
#ifdef HAVE_LIBAUDIT
int audit_state; /* keep track of reported audit messages */
#endif
};
__attribute__((constructor)) static void init(void);
static FILE *log;
static void
init(void)
{
log = fopen("/tmp/pam.log", "w");
}
__attribute__((destructor)) static void fini(void);
static void
fini(void)
{
fclose(log);
}
static void
dump_handler(const char *prefix, struct handler *handler)
{
fprintf(log, "%s->handler_type: %d\n", prefix, handler->handler_type);
fprintf(log, "%s->func: %p\n", prefix, handler->func);
for (size_t i = 0; i < _PAM_RETURN_VALUES; i++) {
fprintf(log, "%s->actions[%zu]: %d\n", prefix, i, handler->actions[i]);
}
fprintf(log, "%s->cached_retval: %d\n", prefix, handler->cached_retval);
fprintf(log, "%s->cached_retval_p: %p\n", prefix, handler->cached_retval_p);
fprintf(log, "*%s->cached_retval_p: %i\n", prefix, *handler->cached_retval_p);
fprintf(log, "%s->argc: %d\n", prefix, handler->argc);
for (int i = 0; i < handler->argc; i++) {
fprintf(log, "%s->argv[%d]: %s\n", prefix, i, handler->argv[i]);
}
fprintf(log, "%s->mod_name: %s\n", prefix, handler->mod_name);
fprintf(log, "%s->stack_level: %d\n", prefix, handler->stack_level);
fprintf(log, "%s->grantor: %d\n", prefix, handler->grantor);
}
static void
dump_handlers(const char *prefix, struct handler *handler)
{
char indexed[128] = {0};
size_t prefixlen = strnlen(prefix, sizeof(indexed) - 1);
memcpy(indexed, prefix, prefixlen + 1);
char *index = indexed + prefixlen;
fprintf(log, "%s: %p\n", prefix, handler);
for (size_t i = 0; handler != NULL; handler = handler->next, i++)
{
snprintf(index, sizeof(indexed) - prefixlen - 1, "[%zu]", i);
dump_handler(indexed, handler);
}
}
static void
dump_state(const char *prefix, pam_handle_t *pamh)
{
char indexed[128] = {0};
size_t prefixlen = strnlen(prefix, sizeof(indexed) - 1);
memcpy(indexed, prefix, prefixlen + 1);
char *index = indexed + prefixlen;
fprintf(log, "%s->authtok: %s\n", indexed, pamh->authtok);
fprintf(log, "%s->caller_is: %u\n", indexed, pamh->caller_is);
fprintf(log, "%s->pam_conversation: %p\n", indexed, pamh->pam_conversation);
fprintf(log, "%s->pam_conversation->conv: %p\n", indexed, pamh->pam_conversation->conv);
fprintf(log, "%s->pam_conversation->appdata_ptr: %p\n", indexed, pamh->pam_conversation->appdata_ptr);
fprintf(log, "%s->oldauthtok: %s\n", indexed, pamh->oldauthtok);
fprintf(log, "%s->prompt: %s\n", indexed, pamh->prompt);
fprintf(log, "%s->service_name: %s\n", indexed, pamh->service_name);
fprintf(log, "%s->user: %s\n", indexed, pamh->user);
fprintf(log, "%s->rhost: %s\n", indexed, pamh->rhost);
fprintf(log, "%s->ruser: %s\n", indexed, pamh->ruser);
fprintf(log, "%s->tty: %s\n", indexed, pamh->tty);
fprintf(log, "%s->xdisplay: %s\n", indexed, pamh->xdisplay);
fprintf(log, "%s->authtok_type: %s\n", indexed, pamh->authtok_type);
{
size_t i = 0;
for (struct pam_data *data = pamh->data; data != NULL; data = data->next, i++) {
fprintf(log, "%s->data[%zu]: %p\n", indexed, i, data);
fprintf(log, "%s->data[%zu]->name: %s\n", indexed, i, data->name);
fprintf(log, "%s->data[%zu]->data: %p\n", indexed, i, data->data);
fprintf(log, "%s->data[%zu]->cleanup: %p\n", indexed, i, data->cleanup);
}
}
fprintf(log, "%s->env: %p\n", indexed, pamh->env);
fprintf(log, "%s->env->entries: %d\n", indexed, pamh->env->entries);
fprintf(log, "%s->env->requested: %d\n", indexed, pamh->env->requested);
for (int i = 0; i < pamh->env->requested; i++)
{
fprintf(log, "%s->env->list[%d]: %s\n", indexed, i, pamh->env->list[i]);
}
fprintf(log, "%s->fail_delay.set: %i\n", indexed, pamh->fail_delay.set);
fprintf(log, "%s->fail_delay.delay: %u\n", indexed, pamh->fail_delay.delay);
fprintf(log, "%s->fail_delay.begin: %ld\n", indexed, pamh->fail_delay.begin);
fprintf(log, "%s->fail_delay.delay_fn_ptr: %p\n", indexed, pamh->fail_delay.delay_fn_ptr);
fprintf(log, "%s->xauth.namelen: %i\n", indexed, pamh->xauth.namelen);
fprintf(log, "%s->xauth.name: %s\n", indexed, pamh->xauth.name);
fprintf(log, "%s->xauth.datalen: %i\n", indexed, pamh->xauth.datalen);
fprintf(log, "%s->xauth.data: %s\n", indexed, pamh->xauth.data);
fprintf(log, "%s->xauth.data: %s\n", indexed, pamh->xauth.data);
for (int i = 0; i < pamh->handlers.modules_used; i++)
{
fprintf(log, "%s->handlers.module[%d].name: %s\n", indexed, i, pamh->handlers.module[i].name);
fprintf(log, "%s->handlers.module[%d].type: %i\n", indexed, i, pamh->handlers.module[i].type);
fprintf(log, "%s->handlers.module[%d].dl_handle: %p\n", indexed, i, pamh->handlers.module[i].dl_handle);
}
fprintf(log, "%s->handlers.modules_allocated: %i\n", indexed, pamh->handlers.modules_allocated);
fprintf(log, "%s->handlers.modules_used: %d\n", indexed, pamh->handlers.modules_used);
fprintf(log, "%s->handlers.handlers_loaded: %i\n", indexed, pamh->handlers.handlers_loaded);
strncpy(index, "->handlers.conf.authenticate", sizeof(indexed) - prefixlen);
dump_handlers(indexed, pamh->handlers.conf.authenticate);
strncpy(index, "->handlers.conf.setcred", sizeof(indexed) - prefixlen);
dump_handlers(indexed, pamh->handlers.conf.authenticate);
strncpy(index, "->handlers.conf.acct_mgmt", sizeof(indexed) - prefixlen);
dump_handlers(indexed, pamh->handlers.conf.acct_mgmt);
strncpy(index, "->handlers.conf.open_session", sizeof(indexed) - prefixlen);
dump_handlers(indexed, pamh->handlers.conf.open_session);
strncpy(index, "->handlers.conf.close_session", sizeof(indexed) - prefixlen);
dump_handlers(indexed, pamh->handlers.conf.close_session);
strncpy(index, "->handlers.conf.chauthtok", sizeof(indexed) - prefixlen);
dump_handlers(indexed, pamh->handlers.conf.chauthtok);
fprintf(log, "%s->former.choice: %i\n", indexed, pamh->former.choice);
fprintf(log, "%s->former.depth: %i\n", indexed, pamh->former.depth);
fprintf(log, "%s->former.impression: %i\n", indexed, pamh->former.impression);
fprintf(log, "%s->former.status: %i\n", indexed, pamh->former.status);
for (int i = 0; i < pamh->former.depth; i++)
{
fprintf(log, "%s->former.substates[%d].impression: %i\n", indexed, i, pamh->former.substates[i].impression);
fprintf(log, "%s->former.substates[%d].status: %i\n", indexed, i, pamh->former.substates[i].status);
}
fprintf(log, "%s->former.fail_user: %i\n", indexed, pamh->former.fail_user);
fprintf(log, "%s->former.want_user: %i\n", indexed, pamh->former.want_user);
fprintf(log, "%s->former.prompt: %s\n", indexed, pamh->former.prompt);
fprintf(log, "%s->former.update: %i\n", indexed, pamh->former.update);
fprintf(log, "%s->mod_name: %s\n", indexed, pamh->mod_name);
fprintf(log, "%s->mod_argc: %d\n", indexed, pamh->mod_argc);
for (int i = 0; i < pamh->mod_argc; i++) {
fprintf(log, "%s->mod_argv[%d]: %s\n", indexed, i, pamh->mod_argv[i]);
}
fprintf(log, "%s->choice: %i\n", indexed, pamh->choice);
#ifdef HAVE_LIBAUDIT
fprintf(log, "%s->audit_state: %i\n", indexed, pamh->audit_state);
#endif
}
static const char *
get_arg(int argc, const char *argv[], const char *name)
{
size_t len = strlen(name);
for (int i = 0; i < argc; i++)
{
if (strncmp(name, argv[i], len) == 0) {
return argv[i] + len;
}
}
return NULL;
}
PAM_EXTERN int
pam_sm_authenticate(pam_handle_t *pamh, int flags,int argc, const char **argv )
{
char prefix[128] = {0};
snprintf(prefix, sizeof(prefix) - 1, "%s %s pamh", __func__, get_arg(argc, argv, "stage="));
dump_state(prefix, pamh);
return PAM_IGNORE;
}
PAM_EXTERN int
pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
char prefix[128] = {0};
snprintf(prefix, sizeof(prefix) - 1, "%s %s pamh", __func__, get_arg(argc, argv, "stage="));
dump_state(prefix, pamh);
return PAM_IGNORE;
}
PAM_EXTERN int
pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
char prefix[128] = {0};
snprintf(prefix, sizeof(prefix) - 1, "%s %s pamh", __func__, get_arg(argc, argv, "stage="));
dump_state(prefix, pamh);
return PAM_IGNORE;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment