From 03012f54b1bcd31e0b817fc0222a9a47709c4018 Mon Sep 17 00:00:00 2001 From: Laurent Bercot Date: Sun, 4 Feb 2018 23:22:53 +0000 Subject: Initial commit --- src/include/pamela/common.h | 101 +++++++++ src/include/pamela/pam.h | 161 ++++++++++++++ src/include/pamela/pamela.h | 80 +++++++ src/pamela/deps-exe/pamelad | 2 + src/pamela/deps-lib/pamela | 27 +++ src/pamela/pam_acct_mgmt.c | 11 + src/pamela/pam_authenticate.c | 11 + src/pamela/pam_chauthtok.c | 11 + src/pamela/pam_close_session.c | 11 + src/pamela/pam_end.c | 17 ++ src/pamela/pam_fail_delay.c | 11 + src/pamela/pam_get_item.c | 52 +++++ src/pamela/pam_getenv.c | 32 +++ src/pamela/pam_getenvlist.c | 42 ++++ src/pamela/pam_open_session.c | 11 + src/pamela/pam_putenv.c | 14 ++ src/pamela/pam_set_item.c | 50 +++++ src/pamela/pam_setcred.c | 11 + src/pamela/pam_start.c | 34 +++ src/pamela/pam_strerror.c | 17 ++ src/pamela/pamela-internal.h | 13 ++ src/pamela/pamela_end.c | 16 ++ src/pamela/pamela_get_item.c | 11 + src/pamela/pamela_getenvlist.c | 9 + src/pamela/pamela_op.c | 123 +++++++++++ src/pamela/pamela_pam_response_free.c | 11 + src/pamela/pamela_query_string.c | 20 ++ src/pamela/pamela_set_item.c | 16 ++ src/pamela/pamela_set_item_internal.c | 14 ++ src/pamela/pamela_set_itemv.c | 14 ++ src/pamela/pamela_startf.c | 40 ++++ src/pamela/pamela_strerror.c | 11 + src/pamela/pamela_zero.c | 5 + src/pamela/pamelad.c | 385 ++++++++++++++++++++++++++++++++++ 34 files changed, 1394 insertions(+) create mode 100644 src/include/pamela/common.h create mode 100644 src/include/pamela/pam.h create mode 100644 src/include/pamela/pamela.h create mode 100644 src/pamela/deps-exe/pamelad create mode 100644 src/pamela/deps-lib/pamela create mode 100644 src/pamela/pam_acct_mgmt.c create mode 100644 src/pamela/pam_authenticate.c create mode 100644 src/pamela/pam_chauthtok.c create mode 100644 src/pamela/pam_close_session.c create mode 100644 src/pamela/pam_end.c create mode 100644 src/pamela/pam_fail_delay.c create mode 100644 src/pamela/pam_get_item.c create mode 100644 src/pamela/pam_getenv.c create mode 100644 src/pamela/pam_getenvlist.c create mode 100644 src/pamela/pam_open_session.c create mode 100644 src/pamela/pam_putenv.c create mode 100644 src/pamela/pam_set_item.c create mode 100644 src/pamela/pam_setcred.c create mode 100644 src/pamela/pam_start.c create mode 100644 src/pamela/pam_strerror.c create mode 100644 src/pamela/pamela-internal.h create mode 100644 src/pamela/pamela_end.c create mode 100644 src/pamela/pamela_get_item.c create mode 100644 src/pamela/pamela_getenvlist.c create mode 100644 src/pamela/pamela_op.c create mode 100644 src/pamela/pamela_pam_response_free.c create mode 100644 src/pamela/pamela_query_string.c create mode 100644 src/pamela/pamela_set_item.c create mode 100644 src/pamela/pamela_set_item_internal.c create mode 100644 src/pamela/pamela_set_itemv.c create mode 100644 src/pamela/pamela_startf.c create mode 100644 src/pamela/pamela_strerror.c create mode 100644 src/pamela/pamela_zero.c create mode 100644 src/pamela/pamelad.c (limited to 'src') diff --git a/src/include/pamela/common.h b/src/include/pamela/common.h new file mode 100644 index 0000000..5ae689e --- /dev/null +++ b/src/include/pamela/common.h @@ -0,0 +1,101 @@ +/* ISC license. */ + +#ifndef PAMELA_COMMON_H +#define PAMELA_COMMON_H + +#define PAMELA_BUFSIZE 4096 + +enum pamela_retcode_e +{ + PAMELA_PAM_SUCCESS = 0, + PAMELA_PAM_OPEN_ERR, + PAMELA_PAM_SYMBOL_ERR, + PAMELA_PAM_SERVICE_ERR, + PAMELA_PAM_SYSTEM_ERR, + PAMELA_PAM_BUF_ERR, + PAMELA_PAM_PERM_DENIED, + PAMELA_PAM_AUTH_ERR, + PAMELA_PAM_CRED_INSUFFICIENT, + PAMELA_PAM_AUTHINFO_UNAVAIL, + PAMELA_PAM_USER_UNKNOWN, + PAMELA_PAM_MAXTRIES, + PAMELA_PAM_NEW_AUTHTOK_REQD, + PAMELA_PAM_ACCT_EXPIRED, + PAMELA_PAM_SESSION_ERR, + PAMELA_PAM_CRED_UNAVAIL, + PAMELA_PAM_CRED_EXPIRED, + PAMELA_PAM_CRED_ERR, + PAMELA_PAM_NO_MODULE_DATA, + PAMELA_PAM_CONV_ERR, + PAMELA_PAM_AUTHTOK_ERR, + PAMELA_PAM_AUTHTOK_RECOVERY_ERR, + PAMELA_PAM_AUTHTOK_LOCK_BUSY, + PAMELA_PAM_AUTHTOK_DISABLE_AGING, + PAMELA_PAM_TRY_AGAIN, + PAMELA_PAM_IGNORE, + PAMELA_PAM_ABORT, + PAMELA_PAM_AUTHTOK_EXPIRED, + PAMELA_PAM_MODULE_UNKNOWN, + PAMELA_PAM_BAD_ITEM, + PAMELA_PAM_CONV_AGAIN, + PAMELA_PAM_INCOMPLETE +} ; + + +enum pamela_item_e +{ + PAMELA_ENV = 0, + PAMELA_PAM_SERVICE, + PAMELA_PAM_USER, + PAMELA_PAM_TTY, + PAMELA_PAM_RHOST, + PAMELA_PAM_CONV, + PAMELA_PAM_AUTHTOK, + PAMELA_PAM_OLDAUTHTOK, + PAMELA_PAM_RUSER, + PAMELA_PAM_USER_PROMPT, + PAMELA_PAM_FAIL_DELAY, + PAMELA_PAM_XDISPLAY, + PAMELA_PAM_XAUTHDATA, + PAMELA_PAM_AUTHTOK_TYPE +} ; + + +enum pamela_convmsgtype_e +{ + PAMELA_PAM_PROMPT_ECHO_OFF = 1, + PAMELA_PAM_PROMPT_ECHO_ON, + PAMELA_PAM_ERROR_MSG, + PAMELA_PAM_TEXT_INFO, + PAMELA_PAM_RADIO_TYPE, + PAMELA_PAM_BINARY_PROMPT = 7 +} ; + +#define PAMELA_PAM_CONV_MAX_MESSAGES 1024 + + +enum pamela_op_e +{ + PAMELA_OP_ACCT_MGMT = 1, + PAMELA_OP_AUTHENTICATE, + PAMELA_OP_CHAUTHTOK, + PAMELA_OP_CLOSE_SESSION, + PAMELA_OP_FAIL_DELAY, + PAMELA_OP_OPEN_SESSION, + PAMELA_OP_SETCRED, + PAMELA_OP_SETFAILDELAY, + PAMELA_OP_END +} ; + + + /* flags */ + +#define PAMELA_PAM_SILENT 0x8000U +#define PAMELA_PAM_DISALLOW_NULL_AUTHTOK 0x0001U +#define PAMELA_PAM_ESTABLISH_CRED 0x0002U +#define PAMELA_PAM_DELETE_CRED 0x0004U +#define PAMELA_PAM_REINITIALIZE_CRED 0x0008U +#define PAMELA_PAM_REFRESH_CRED 0x0010U +#define PAMELA_PAM_CHANGE_EXPIRED_AUTHTOK 0x0020U + +#endif diff --git a/src/include/pamela/pam.h b/src/include/pamela/pam.h new file mode 100644 index 0000000..aab7d7d --- /dev/null +++ b/src/include/pamela/pam.h @@ -0,0 +1,161 @@ +/* ISC license. */ + +#ifndef PAMELA_PAM_H +#define PAMELA_PAM_H + + /* + This is pamela's client-side PAM library. + + */ + +#include +#include +#include + +#define __LINUX_PAM__ 1 +#define __LINUX_PAM_MINOR__ 0 + + + /* Return codes */ + +#define _PAM_RETURN_VALUES 32 + +#define PAM_SUCCESS PAMELA_PAM_SUCCESS +#define PAM_OPEN_ERR PAMELA_PAM_OPEN_ERR +#define PAM_SYMBOL_ERR PAMELA_PAM_SYMBOL_ERR +#define PAM_SERVICE_ERR PAMELA_PAM_SERVICE_ERR +#define PAM_SYSTEM_ERR PAMELA_PAM_SYSTEM_ERR +#define PAM_BUF_ERR PAMELA_PAM_BUF_ERR +#define PAM_PERM_DENIED PAMELA_PAM_PERM_DENIED +#define PAM_AUTH_ERR PAMELA_PAM_AUTH_ERR +#define PAM_CRED_INSUFFICIENT PAMELA_PAM_CRED_INSUFFICIENT +#define PAM_AUTHINFO_UNAVAIL PAMELA_PAM_AUTHINFO_UNAVAIL +#define PAM_USER_UNKNOWN PAMELA_PAM_USER_UNKNOWN +#define PAM_MAXTRIES PAMELA_PAM_MAXTRIES +#define PAM_NEW_AUTHTOK_REQD PAMELA_PAM_NEW_AUTHTOK_REQD +#define PAM_ACCT_EXPIRED PAMELA_PAM_ACCT_EXPIRED +#define PAM_SESSION_ERR PAMELA_PAM_SESSION_ERR +#define PAM_CRED_UNAVAIL PAMELA_PAM_CRED_UNAVAIL +#define PAM_CRED_EXPIRED PAMELA_PAM_CRED_EXPIRED +#define PAM_CRED_ERR PAMELA_PAM_CRED_ERR +#define PAM_NO_MODULE_DATA PAMELA_PAM_NO_MODULE_DATA +#define PAM_CONV_ERR PAMELA_PAM_CONV_ERR +#define PAM_AUTHTOK_ERR PAMELA_PAM_AUTHTOK_ERR +#define PAM_AUTHTOK_RECOVERY_ERR PAMELA_PAM_AUTHTOK_RECOVERY_ERR +#define PAM_AUTHTOK_LOCK_BUSY PAMELA_PAM_AUTHTOK_LOCK_BUSY +#define PAM_AUTHTOK_DISABLE_AGING PAMELA_PAM_AUTHTOK_DISABLE_AGING +#define PAM_TRY_AGAIN PAMELA_PAM_TRY_AGAIN +#define PAM_IGNORE PAMELA_PAM_IGNORE +#define PAM_ABORT PAMELA_PAM_ABORT +#define PAM_AUTHTOK_EXPIRED PAMELA_PAM_AUTHTOK_EXPIRED +#define PAM_MODULE_UNKNOWN PAMELA_PAM_MODULE_UNKNOWN +#define PAM_BAD_ITEM PAMELA_PAM_BAD_ITEM +#define PAM_CONV_AGAIN PAMELA_PAM_CONV_AGAIN +#define PAM_INCOMPLETE PAMELA_PAM_INCOMPLETE + + + /* Items */ + +#define PAM_ITEM_MAX 14 /* 13 items + environment */ + +#define PAM_SERVICE PAMELA_PAM_SERVICE +#define PAM_USER PAMELA_PAM_USER +#define PAM_TTY PAMELA_PAM_TTY +#define PAM_RHOST PAMELA_PAM_RHOST +#define PAM_CONV PAMELA_PAM_CONV +#define PAM_AUTHTOK PAMELA_PAM_AUTHTOK +#define PAM_OLDAUTHTOK PAMELA_PAM_OLDAUTHTOK +#define PAM_RUSER PAMELA_PAM_RUSER +#define PAM_USER_PROMPT PAMELA_PAM_USER_PROMPT +#define PAM_FAIL_DELAY PAMELA_PAM_FAIL_DELAY +#define PAM_XDISPLAY PAMELA_PAM_XDISPLAY +#define PAM_XAUTHDATA PAMELA_PAM_XAUTHDATA +#define PAM_AUTHTOK_TYPE PAMELA_PAM_AUTHTOK_TYPE + + + /* Flags */ + +#define PAM_SILENT PAMELA_PAM_SILENT +#define PAM_DISALLOW_NULL_AUTHTOK PAMELA_PAM_DISALLOW_NULL_AUTHTOK +#define PAM_ESTABLISH_CRED PAMELA_PAM_ESTABLISH_CRED +#define PAM_DELETE_CRED PAMELA_PAM_DELETE_CRED +#define PAM_REINITIALIZE_CRED PAMELA_PAM_REINITIALIZE_CRED +#define PAM_REFRESH_CRED PAMELA_PAM_REFRESH_CRED +#define PAM_CHANGE_EXPIRED_AUTHTOK PAMELA_PAM_CHANGE_EXPIRED_AUTHTOK + + + /* Conversation types */ + +#define PAM_PROMPT_ECHO_OFF PAMELA_PAM_PROMPT_ECHO_OFF +#define PAM_PROMPT_ECHO_ON PAMELA_PAM_PROMPT_ECHO_ON +#define PAM_ERROR_MSG PAMELA_PAM_ERROR_MSG +#define PAM_TEXT_INFO PAMELA_PAM_TEXT_INFO +#define PAM_RADIO_TYPE PAMELA_PAM_RADIO_TYPE +#define PAM_BINARY_PROMPT PAMELA_PAM_BINARY_PROMPT + + + /* Misc Linux-PAM stuff */ + +#define PAM_MAX_NUM_MSG 32 +#define PAM_MAX_MSG_SIZE 512 +#define PAM_MAX_RESP_SIZE 512 + +#define PAM_DATA_SILENT PAMELA_PAM_DATA_SILENT + + + /* Data structures and functions */ + +struct pam_message +{ + int msg_style ; + char const *msg ; +} ; + +struct pam_response +{ + char *resp ; + int resp_retcode ; +} ; + +struct pam_conv +{ + int (*conv) (int, struct pam_message const **, struct pam_response **, void *) ; + void *appdata_ptr ; +} ; + +struct pam_xauth_data +{ + int namelen ; + char *name ; + int datalen ; + char *data ; +} ; + +typedef struct pam_handle_s pam_handle_t ; +struct pam_handle_s +{ + pamela_t handle ; + struct pam_xauth_data xauthdata ; + stralloc err[_PAM_RETURN_VALUES] ; + stralloc item[PAM_ITEM_MAX] ; + uint64_t flagerrcached : _PAM_RETURN_VALUES ; + uint64_t flagenvcached : 1 ; +} ; + +extern int pam_start (char const *, char const *, struct pam_conv const *, pam_handle_t **) ; +extern int pam_end (pam_handle_t *, int) ; +extern int pam_set_item (pam_handle_t *, int, void const *) ; +extern int pam_get_item (pam_handle_t *, int, void const **) ; +extern char const *pam_strerror (pam_handle_t *, int) ; +extern int pam_fail_delay (pam_handle_t *, unsigned int) ; +extern int pam_authenticate (pam_handle_t *, int) ; +extern int pam_setcred (pam_handle_t *, int) ; +extern int pam_acct_mgmt (pam_handle_t *, int) ; +extern int pam_chauthtok (pam_handle_t *, int) ; +extern int pam_open_session (pam_handle_t *, int) ; +extern int pam_close_session (pam_handle_t *, int) ; +extern int pam_putenv (pam_handle_t *, char const *) ; +extern char const *pam_getenv (pam_handle_t *, char const *) ; +extern char **pam_getenvlist (pam_handle_t *) ; + +#endif diff --git a/src/include/pamela/pamela.h b/src/include/pamela/pamela.h new file mode 100644 index 0000000..ca45124 --- /dev/null +++ b/src/include/pamela/pamela.h @@ -0,0 +1,80 @@ +/* ISC license. */ + +#ifndef PAMELA_H +#define PAMELA_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include + + + /* pam_fail_delay */ + +typedef void pamela_pam_delay_func_t (int, unsigned int, void *) ; +typedef pamela_pam_delay_func_t *pamela_pam_delay_func_t_ref ; + + + /* Conversations */ + +typedef struct pamela_pam_message_s pamela_pam_message_t, *pamela_pam_message_t_ref ; +struct pamela_pam_message_s +{ + int msg_style ; + char const *msg ; +} ; + +typedef struct pamela_pam_response_s pamela_pam_response_t, *pamela_pam_response_t_ref ; +struct pamela_pam_response_s +{ + char *resp ; + int *resp_retcode ; +} ; + +extern void pamela_pam_response_free (pamela_pam_response_t *, uint32_t) ; + +typedef int pamela_pam_conv_func_t (int, pamela_pam_message_t const **, pamela_pam_response_t **, void *) ; +typedef pamela_pam_conv_func_t *pamela_pam_conv_func_t_ref ; + + + /* Client handle */ + +typedef struct pamela_s pamela_t, *pamela_t_ref ; +struct pamela_s +{ + textmessage_receiver_t in ; + textmessage_sender_t out ; + pid_t pid ; + pamela_pam_delay_func_t_ref delayfn ; + pamela_pam_conv_func_t_ref convfn ; + void *aux ; + char inbuf[PAMELA_BUFSIZE] ; +} ; +#define PAMELA_ZERO { TEXTMESSAGE_RECEIVER_ZERO, TEXTMESSAGE_SENDER_ZERO, 0, 0, 0, 0, "" } + +extern pamela_t const pamela_zero ; + + + /* User-facing functions */ + +extern int pamela_startf (pamela_t *, char const *, char const *, pamela_pam_conv_func_t_ref, void *) ; +extern void pamela_end (pamela_t *) ; +extern int pamela_strerror (pamela_t *, unsigned char, stralloc *) ; +extern int pamela_getenvlist (pamela_t *, stralloc *) ; +extern int pamela_get_item (pamela_t *, unsigned char, stralloc *) ; +extern int pamela_set_item (pamela_t *, unsigned char, char const *) ; +extern int pamela_set_itemv (pamela_t *, unsigned char, struct iovec const *, unsigned int) ; +extern int pamela_op (pamela_t *, unsigned char, int) ; + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/pamela/deps-exe/pamelad b/src/pamela/deps-exe/pamelad new file mode 100644 index 0000000..3294b7e --- /dev/null +++ b/src/pamela/deps-exe/pamelad @@ -0,0 +1,2 @@ +-lskarnet +${PAM_LIB} diff --git a/src/pamela/deps-lib/pamela b/src/pamela/deps-lib/pamela new file mode 100644 index 0000000..0b749c3 --- /dev/null +++ b/src/pamela/deps-lib/pamela @@ -0,0 +1,27 @@ +pam_acct_mgmt.o +pam_authenticate.o +pam_chauthtok.o +pam_close_session.o +pam_end.o +pam_fail_delay.o +pam_get_item.o +pam_getenv.o +pam_getenvlist.o +pam_open_session.o +pam_putenv.o +pam_set_item.o +pam_setcred.o +pam_start.o +pam_strerror.o +pamela_end.o +pamela_get_item.o +pamela_getenvlist.o +pamela_op.o +pamela_pam_response_free.o +pamela_query_string.o +pamela_set_item.o +pamela_set_item_internal.o +pamela_set_itemv.o +pamela_startf.o +pamela_strerror.o +pamela_zero.o diff --git a/src/pamela/pam_acct_mgmt.c b/src/pamela/pam_acct_mgmt.c new file mode 100644 index 0000000..bd65b2a --- /dev/null +++ b/src/pamela/pam_acct_mgmt.c @@ -0,0 +1,11 @@ +/* ISC license. */ + +#include +#include +#include + +int pam_acct_mgmt (pam_handle_t *pamh, int flags) +{ + if (!pamh) return PAM_SYSTEM_ERR ; + return pamela_op(&pamh->handle, PAMELA_OP_ACCT_MGMT, flags) ; +} diff --git a/src/pamela/pam_authenticate.c b/src/pamela/pam_authenticate.c new file mode 100644 index 0000000..5e31b0b --- /dev/null +++ b/src/pamela/pam_authenticate.c @@ -0,0 +1,11 @@ +/* ISC license. */ + +#include +#include +#include + +int pam_authenticate (pam_handle_t *pamh, int flags) +{ + if (!pamh) return PAM_SYSTEM_ERR ; + return pamela_op(&pamh->handle, PAMELA_OP_AUTHENTICATE, flags) ; +} diff --git a/src/pamela/pam_chauthtok.c b/src/pamela/pam_chauthtok.c new file mode 100644 index 0000000..53f639f --- /dev/null +++ b/src/pamela/pam_chauthtok.c @@ -0,0 +1,11 @@ +/* ISC license. */ + +#include +#include +#include + +int pam_chauthtok (pam_handle_t *pamh, int flags) +{ + if (!pamh) return PAM_SYSTEM_ERR ; + return pamela_op(&pamh->handle, PAMELA_OP_CHAUTHTOK, flags) ; +} diff --git a/src/pamela/pam_close_session.c b/src/pamela/pam_close_session.c new file mode 100644 index 0000000..989bddf --- /dev/null +++ b/src/pamela/pam_close_session.c @@ -0,0 +1,11 @@ +/* ISC license. */ + +#include +#include +#include + +int pam_close_session (pam_handle_t *pamh, int flags) +{ + if (!pamh) return PAM_SYSTEM_ERR ; + return pamela_op(&pamh->handle, PAMELA_OP_CLOSE_SESSION, flags) ; +} diff --git a/src/pamela/pam_end.c b/src/pamela/pam_end.c new file mode 100644 index 0000000..8da0dbd --- /dev/null +++ b/src/pamela/pam_end.c @@ -0,0 +1,17 @@ +/* ISC license. */ + +#include +#include +#include +#include + +int pam_end (pam_handle_t *pamh, int pam_status) +{ + if (!pamh) return PAM_SYSTEM_ERR ; + pamela_op(&pamh->handle, PAMELA_OP_END, pam_status) ; + pamela_end(&pamh->handle) ; + for (unsigned int i = 0 ; i < _PAM_RETURN_VALUES ; i++) stralloc_free(&pamh->err[i]) ; + for (unsigned int i = 0 ; i < PAM_ITEM_MAX ; i++) stralloc_free(&pamh->item[i]) ; + free(pamh) ; + return PAM_SUCCESS ; +} diff --git a/src/pamela/pam_fail_delay.c b/src/pamela/pam_fail_delay.c new file mode 100644 index 0000000..ca5ffb4 --- /dev/null +++ b/src/pamela/pam_fail_delay.c @@ -0,0 +1,11 @@ +/* ISC license. */ + +#include +#include +#include + +int pam_fail_delay (pam_handle_t *pamh, unsigned int usec) +{ + if (!pamh) return PAM_SYSTEM_ERR ; + return pamela_op(&pamh->handle, PAMELA_OP_FAIL_DELAY, (int)usec) ; +} diff --git a/src/pamela/pam_get_item.c b/src/pamela/pam_get_item.c new file mode 100644 index 0000000..e9499fd --- /dev/null +++ b/src/pamela/pam_get_item.c @@ -0,0 +1,52 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include + +static int xauthdata_unpack (pam_handle_t *pamh) +{ + uint32_t namelen, datalen ; + uint32_unpack_big(pamh->item[PAM_XAUTHDATA].s, &namelen) ; + uint32_unpack_big(pamh->item[PAM_XAUTHDATA].s + 4, &datalen) ; + if (namelen + datalen + 10 != pamh->item[PAM_XAUTHDATA].len) return 0 ; + pamh->xauthdata.namelen = (int)namelen ; + pamh->xauthdata.datalen = (int)namelen ; + pamh->xauthdata.name = pamh->item[PAM_XAUTHDATA].s + 8 ; + pamh->xauthdata.data = pamh->item[PAM_XAUTHDATA].s + 9 + namelen ; + return 1 ; +} + +int pam_get_item (pam_handle_t *pamh, int item_type, void const **item) +{ + if (!pamh) return PAM_SYSTEM_ERR ; + if (item_type < 1 || item_type >= PAM_ITEM_MAX) return PAM_BAD_ITEM ; + switch (item_type) + { + case PAM_FAIL_DELAY : + *item = (void const *)pamh->handle.delayfn ; + return PAM_SUCCESS ; + case PAM_CONV : + *item = (void const *)pamh->handle.convfn ; + return PAM_SUCCESS ; + default : break ; + } + pamh->item[item_type].len = 0 ; + { + int e = pamela_get_item(&pamh->handle, (unsigned char)item_type, &pamh->item[item_type]) ; + if (e) return e ; + } + switch (item_type) + { + case PAM_XAUTHDATA : + if (!xauthdata_unpack(pamh)) return PAM_ABORT ; + *item = (void const *)&pamh->xauthdata ; + break ; + default : + *item = (void const *)pamh->item[item_type].s ; + break ; + } + return PAM_SUCCESS ; +} diff --git a/src/pamela/pam_getenv.c b/src/pamela/pam_getenv.c new file mode 100644 index 0000000..f743b48 --- /dev/null +++ b/src/pamela/pam_getenv.c @@ -0,0 +1,32 @@ +/* ISC license. */ + +#include +#include +#include +#include + +static char const *getvar (char const *s, size_t len, char const *var) +{ + size_t varlen = strlen(var) ; + size_t i = 0 ; + while (i < len) + { + if (!strncmp(var, s + i, varlen) && s[i + varlen] == '=') break ; + i += strlen(s + i) + 1 ; + } + return i < len ? s + i + varlen + 1 : 0 ; +} + +char const *pam_getenv (pam_handle_t *pamh, char const *name) +{ + stralloc *sa ; + if (!pamh) return 0 ; + sa = &pamh->item[PAMELA_ENV] ; + if (!pamh->flagenvcached) + { + sa->len = 0 ; + if (!pamela_getenvlist(&pamh->handle, sa)) return 0 ; + pamh->flagenvcached = 1 ; + } + return getvar(sa->s, sa->len, name) ; +} diff --git a/src/pamela/pam_getenvlist.c b/src/pamela/pam_getenvlist.c new file mode 100644 index 0000000..18b0da3 --- /dev/null +++ b/src/pamela/pam_getenvlist.c @@ -0,0 +1,42 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include + +char **pam_getenvlist (pam_handle_t *pamh) +{ + stralloc *sa ; + char **arr ; + char *p ; + size_t n ; + size_t i = 0 ; + if (!pamh) return 0 ; + sa = &pamh->item[PAMELA_ENV] ; + if (!pamh->flagenvcached) + { + sa->len = 0 ; + if (!pamela_getenvlist(&pamh->handle, sa)) return 0 ; + pamh->flagenvcached = 1 ; + } + n = byte_count(sa->s, sa->len, 0) ; + arr = malloc((n+1) * sizeof(char *)) ; + if (!arr) return 0 ; + p = sa->s ; + for (; i < n ; i++) + { + arr[i] = strdup(p) ; + if (!arr[i]) goto err ; + p += strlen(p) + 1 ; + } + arr[n] = 0 ; + return arr ; + + err: + while (i--) free(arr[i]) ; + free(arr) ; + return 0 ; +} diff --git a/src/pamela/pam_open_session.c b/src/pamela/pam_open_session.c new file mode 100644 index 0000000..25881b7 --- /dev/null +++ b/src/pamela/pam_open_session.c @@ -0,0 +1,11 @@ +/* ISC license. */ + +#include +#include +#include + +int pam_open_session (pam_handle_t *pamh, int flags) +{ + if (!pamh) return PAM_SYSTEM_ERR ; + return pamela_op(&pamh->handle, PAMELA_OP_OPEN_SESSION, flags) ; +} diff --git a/src/pamela/pam_putenv.c b/src/pamela/pam_putenv.c new file mode 100644 index 0000000..066d447 --- /dev/null +++ b/src/pamela/pam_putenv.c @@ -0,0 +1,14 @@ +/* ISC license. */ + +#include +#include + +int pam_putenv (pam_handle_t *pamh, char const *name_value) +{ + int e ; + if (!pamh) return PAM_SYSTEM_ERR ; + e = pamela_set_item(&pamh->handle, PAMELA_ENV, name_value) ; + if (e) return e ; + pamh->flagenvcached = 0 ; + return PAM_SUCCESS ; +} diff --git a/src/pamela/pam_set_item.c b/src/pamela/pam_set_item.c new file mode 100644 index 0000000..03e08c0 --- /dev/null +++ b/src/pamela/pam_set_item.c @@ -0,0 +1,50 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include + +static int xauthdata_pack_and_set (pamela_t *a, struct pam_xauth_data const *d) +{ + if (d->namelen < 0 || d->datalen < 0) return PAM_SYSTEM_ERR ; + { + char buf[8] ; + struct iovec v[3] = + { + { .iov_base = buf, .iov_len = 8 }, + { .iov_base = d->name, .iov_len = d->namelen + 1 }, + { .iov_base = d->data, .iov_len = d->datalen + 1 } + } ; + uint32_pack_big(buf, (uint32_t)d->namelen) ; + uint32_pack_big(buf + 4, (uint32_t)d->datalen) ; + return pamela_set_itemv(a, PAM_XAUTHDATA, v, 3) ; + } +} + +int pam_set_item (pam_handle_t *pamh, int item_type, void const *item) +{ + if (!pamh) return PAM_SYSTEM_ERR ; + if (item_type < 1 || item_type >= PAM_ITEM_MAX) return PAM_BAD_ITEM ; + switch (item_type) + { + case PAM_FAIL_DELAY : + { + int e ; + pamh->handle.delayfn = (pamela_pam_delay_func_t_ref)item ; + e = pamela_op(&pamh->handle, PAMELA_OP_SETFAILDELAY, 0) ; + if (e != PAM_SUCCESS) return e ; + break ; + } + case PAM_CONV : + pamh->handle.convfn = (pamela_pam_conv_func_t_ref)item ; + break ; + case PAM_XAUTHDATA : + return xauthdata_pack_and_set(&pamh->handle, (struct pam_xauth_data const *)item) ; + default : + return pamela_set_item(&pamh->handle, (uint32_t)item_type, (char const *)item) ; + } + return PAM_SUCCESS ; +} diff --git a/src/pamela/pam_setcred.c b/src/pamela/pam_setcred.c new file mode 100644 index 0000000..eddd2f9 --- /dev/null +++ b/src/pamela/pam_setcred.c @@ -0,0 +1,11 @@ +/* ISC license. */ + +#include +#include +#include + +int pam_setcred (pam_handle_t *pamh, int flags) +{ + if (!pamh) return PAM_SYSTEM_ERR ; + return pamela_op(&pamh->handle, PAMELA_OP_SETCRED, flags) ; +} diff --git a/src/pamela/pam_start.c b/src/pamela/pam_start.c new file mode 100644 index 0000000..3847080 --- /dev/null +++ b/src/pamela/pam_start.c @@ -0,0 +1,34 @@ +/* ISC license. */ + +#include +#include +#include +#include + +static int pamela_dummy_conv (int num_msg, pamela_pam_message_t const **msg, pamela_pam_response_t **resp, void *aux) +{ + (void)num_msg ; + (void)msg ; + (void)resp ; + (void)aux ; + return PAMELA_PAM_CONV_ERR ; +} + +int pam_start (char const *service_name, char const *user, struct pam_conv const *pam_conversation, pam_handle_t **pamh) +{ + int e ; + pam_handle_t *a = malloc(sizeof(pam_handle_t)) ; + if (!a) return PAM_BUF_ERR ; + a->handle = pamela_zero ; + a->flagerrcached = a->flagenvcached = 0 ; + for (unsigned int i = 0 ; i < _PAM_RETURN_VALUES ; i++) a->err[i] = stralloc_zero ; + for (unsigned int i = 0 ; i < PAM_ITEM_MAX ; i++) a->item[i] = stralloc_zero ; + e = pamela_startf(&a->handle, service_name, user, pam_conversation && pam_conversation->conv ? (pamela_pam_conv_func_t_ref)pam_conversation->conv : &pamela_dummy_conv, pam_conversation ? pam_conversation->appdata_ptr : 0) ; + if (e) + { + free(a) ; + return e ; + } + *pamh = a ; + return PAM_SUCCESS ; +} diff --git a/src/pamela/pam_strerror.c b/src/pamela/pam_strerror.c new file mode 100644 index 0000000..fee63df --- /dev/null +++ b/src/pamela/pam_strerror.c @@ -0,0 +1,17 @@ +/* ISC license. */ + +#include +#include + +char const *pam_strerror (pam_handle_t *pamh, int errnum) +{ + if (errnum < 0 || errnum >= _PAM_RETURN_VALUES) return 0 ; + if (!(pamh->flagerrcached & (1ULL << errnum))) + { + pamh->err[errnum].len = 0 ; + if (!pamela_strerror(&pamh->handle, (unsigned char)errnum, &pamh->err[errnum])) return 0 ; + if (!stralloc_0(&pamh->err[errnum])) return 0 ; + pamh->flagerrcached |= (1ULL << errnum) ; + } + return (char const *)pamh->err[errnum].s ; +} diff --git a/src/pamela/pamela-internal.h b/src/pamela/pamela-internal.h new file mode 100644 index 0000000..8de3a20 --- /dev/null +++ b/src/pamela/pamela-internal.h @@ -0,0 +1,13 @@ +/* ISC license. */ + +#ifndef PAMELA_INTERNAL_H +#define PAMELA_INTERNAL_H + +#include +#include +#include + +extern int pamela_query_string (pamela_t *, char const *, size_t, stralloc *) ; +extern int pamela_set_item_internal (pamela_t *, struct iovec const *, unsigned int) ; + +#endif diff --git a/src/pamela/pamela_end.c b/src/pamela/pamela_end.c new file mode 100644 index 0000000..09bb186 --- /dev/null +++ b/src/pamela/pamela_end.c @@ -0,0 +1,16 @@ +/* ISC license. */ + +#include +#include +#include + +void pamela_end (pamela_t *a) +{ + int wstat ; + fd_close(textmessage_sender_fd(&a->out)) ; + textmessage_sender_free(&a->out) ; + fd_close(textmessage_receiver_fd(&a->in)) ; + textmessage_receiver_free(&a->in) ; + waitpid_nointr(a->pid, &wstat, 0) ; + *a = pamela_zero ; +} diff --git a/src/pamela/pamela_get_item.c b/src/pamela/pamela_get_item.c new file mode 100644 index 0000000..14fa56e --- /dev/null +++ b/src/pamela/pamela_get_item.c @@ -0,0 +1,11 @@ +/* ISC license. */ + +#include +#include "pamela-internal.h" + +int pamela_get_item (pamela_t *a, unsigned char what, stralloc *sa) +{ + char s[2] = "G" ; + s[1] = what ; + return pamela_query_string(a, s, 2, sa) ; +} diff --git a/src/pamela/pamela_getenvlist.c b/src/pamela/pamela_getenvlist.c new file mode 100644 index 0000000..ecc5013 --- /dev/null +++ b/src/pamela/pamela_getenvlist.c @@ -0,0 +1,9 @@ +/* ISC license. */ + +#include +#include "pamela-internal.h" + +int pamela_getenvlist (pamela_t *a, stralloc *sa) +{ + return pamela_query_string(a, "V", 1, sa) ; +} diff --git a/src/pamela/pamela_op.c b/src/pamela/pamela_op.c new file mode 100644 index 0000000..2d8ff76 --- /dev/null +++ b/src/pamela/pamela_op.c @@ -0,0 +1,123 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline int pamela_fail_delay (pamela_t *a, int r, unsigned int usec) +{ + (*a->delayfn)(r, usec, a->aux) ; + if (!textmessage_timed_send(&a->out, "D", 1, 0, 0)) return PAMELA_PAM_ABORT ; + return PAMELA_PAM_SUCCESS ; +} + +static inline int pamela_converse_and_answer (pamela_t *a, char const *s, size_t len) +{ + pamela_pam_response_t *res ; + uint32_t n ; + int e ; + char ans[2] = "C" ; + if (len < 4) return PAMELA_PAM_ABORT ; + uint32_unpack_big(s, &n) ; + s += 4 ; len -= 4 ; + if (n > PAMELA_PAM_CONV_MAX_MESSAGES) return PAMELA_PAM_ABORT ; + if (len < n * 5) return PAMELA_PAM_ABORT ; + { + pamela_pam_message_t messages[n] ; + pamela_pam_message_t const *arr[n] ; + for (uint32_t i = 0 ; i < n ; i++) + { + uint32_t u ; + uint32_unpack_big(s + 4 * i, &u) ; + messages[i].msg_style = u ; + arr[i] = &messages[i] ; + } + s += n * 4 ; len -= n * 4 ; + for (uint32_t i = 0 ; i < n ; i++) + { + size_t pos = strnlen(s, len) ; + if (pos == len) return PAMELA_PAM_ABORT ; + messages[i].msg = s ; + s += pos + 1 ; len -= pos + 1 ; + } + if (len) return PAMELA_PAM_ABORT ; + e = (*a->convfn)((int)n, arr, &res, a->aux) ; + } + if (e != PAMELA_PAM_SUCCESS) + { + ans[1] = (unsigned char)e ; + if (!textmessage_timed_send(&a->out, ans, 2, 0, 0)) return PAMELA_PAM_ABORT ; + } + else + { + struct iovec v[n+1] ; + v[0].iov_base = ans ; + v[0].iov_len = 2 ; + for (uint32_t i = 0 ; i < n ; i++) + { + v[i+1].iov_base = res[i].resp ; + v[i+1].iov_len = strlen(res[i].resp) + 1 ; + } + if (!textmessage_timed_sendv(&a->out, v, n+1, 0, 0)) + { + pamela_pam_response_free(res, n) ; + return PAMELA_PAM_ABORT ; + } + pamela_pam_response_free(res, n) ; + } + return PAMELA_PAM_SUCCESS ; +} + +int pamela_op (pamela_t *a, unsigned char type, int num) +{ + { + char pack[sizeof(int)] ; + struct iovec v[3] = + { + { .iov_base = "O", .iov_len = 1 }, + { .iov_base = &type, .iov_len = 1 }, + { .iov_base = pack, .iov_len = sizeof(int) } + } ; + int_pack_big(pack, num) ; + if (!textmessage_timed_sendv(&a->out, v, 3, 0, 0)) return PAMELA_PAM_SYSTEM_ERR ; + } + for (;;) + { + struct iovec v ; + char const *s ; + if (textmessage_timed_receive(&a->in, &v, 0, 0) <= 0) return PAMELA_PAM_ABORT ; + if (v.iov_len < 2) return PAMELA_PAM_ABORT ; + s = v.iov_base ; + switch (s[0]) + { + case 'o' : /* operation returns */ + if (v.iov_len != 2) return PAMELA_PAM_ABORT ; + return s[1] ; + case 'c' : /* conversation request */ + { + int e = pamela_converse_and_answer(a, s + 1, v.iov_len - 1) ; + if (e != PAMELA_PAM_SUCCESS) return e ; + break ; + } + case 'd' : /* fail_delay request */ + { + uint32_t u ; + int r ; + unsigned int usec ; + if (v.iov_len != 9) return PAMELA_PAM_ABORT ; + uint32_unpack_big(s + 1, &u) ; r = u ; + uint32_unpack_big(s + 5, &u) ; usec = u ; + r = pamela_fail_delay(a, r, usec) ; + if (r != PAMELA_PAM_SUCCESS) return r ; + break ; + } + default : return PAMELA_PAM_ABORT ; + } + } +} diff --git a/src/pamela/pamela_pam_response_free.c b/src/pamela/pamela_pam_response_free.c new file mode 100644 index 0000000..496928b --- /dev/null +++ b/src/pamela/pamela_pam_response_free.c @@ -0,0 +1,11 @@ +/* ISC license. */ + +#include +#include +#include + +void pamela_pam_response_free (pamela_pam_response_t *res, uint32_t n) +{ + for (uint32_t i = 0 ; i < n ; i++) free(res[i].resp) ; + free(res) ; +} diff --git a/src/pamela/pamela_query_string.c b/src/pamela/pamela_query_string.c new file mode 100644 index 0000000..27c131f --- /dev/null +++ b/src/pamela/pamela_query_string.c @@ -0,0 +1,20 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include + +int pamela_query_string (pamela_t *a, char const *s, size_t len, stralloc *sa) +{ + struct iovec v ; + if (!textmessage_timed_send(&a->out, s, len, 0, 0)) return 0 ; + if (textmessage_timed_receive(&a->in, &v, 0, 0) <= 0) return 0 ; + if (!v.iov_len) return (errno = EPROTO, 0) ; + s = v.iov_base ; + if (s[0]) return (errno = s[0], 0) ; + if (!stralloc_catb(sa, s + 1, v.iov_len - 1)) return 0 ; + return 1 ; +} diff --git a/src/pamela/pamela_set_item.c b/src/pamela/pamela_set_item.c new file mode 100644 index 0000000..faa3ee1 --- /dev/null +++ b/src/pamela/pamela_set_item.c @@ -0,0 +1,16 @@ +/* ISC license. */ + +#include +#include +#include "pamela-internal.h" + +int pamela_set_item (pamela_t *a, unsigned char type, char const *s) +{ + struct iovec const v[3] = + { + { .iov_base = "S", .iov_len = 1 }, + { .iov_base = &type, .iov_len = 1 }, + { .iov_base = (char *)s, .iov_len = strlen(s) + 1 } + } ; + return pamela_set_item_internal(a, v, 3) ; +} diff --git a/src/pamela/pamela_set_item_internal.c b/src/pamela/pamela_set_item_internal.c new file mode 100644 index 0000000..ca92903 --- /dev/null +++ b/src/pamela/pamela_set_item_internal.c @@ -0,0 +1,14 @@ +/* ISC license. */ + +#include +#include +#include + +int pamela_set_item_internal (pamela_t *a, struct iovec const *v, unsigned int n) +{ + struct iovec r ; + if (!textmessage_timed_sendv(&a->out, v, n, 0, 0)) return PAMELA_PAM_SYSTEM_ERR ; + if (textmessage_timed_receive(&a->in, &r, 0, 0) <= 0) return PAMELA_PAM_ABORT ; + if (r.iov_len != 1) return PAMELA_PAM_ABORT ; + return ((unsigned char const *)r.iov_base)[0] ; +} diff --git a/src/pamela/pamela_set_itemv.c b/src/pamela/pamela_set_itemv.c new file mode 100644 index 0000000..7f64d5a --- /dev/null +++ b/src/pamela/pamela_set_itemv.c @@ -0,0 +1,14 @@ +/* ISC license. */ + +#include +#include +#include "pamela-internal.h" + +int pamela_set_itemv (pamela_t *a, unsigned char type, struct iovec const *v, unsigned int n) +{ + struct iovec vv[n+2] ; + vv[0].iov_base = "S" ; vv[0].iov_len = 1 ; + vv[1].iov_base = &type ; vv[1].iov_len = 1 ; + for (unsigned int i = 0 ; i < n ; i++) vv[i+2] = v[i] ; + return pamela_set_item_internal(a, vv, n+2) ; +} diff --git a/src/pamela/pamela_startf.c b/src/pamela/pamela_startf.c new file mode 100644 index 0000000..4d7ff46 --- /dev/null +++ b/src/pamela/pamela_startf.c @@ -0,0 +1,40 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include + +int pamela_startf (pamela_t *a, char const *service_name, char const *user, pamela_pam_conv_func_t_ref convfn, void *aux) +{ + char const *argv[4] = { PAMELA_LIBEXECPREFIX "pamelad", service_name, user, 0 } ; + int fd[2] ; + pid_t pid = child_spawn2(argv[0], argv, (char const *const *)environ, fd) ; + int e = PAMELA_PAM_ABORT ; + struct iovec v ; + if (!pid) return 0 ; + textmessage_receiver_init(&a->in, fd[0], a->inbuf, PAMELA_BUFSIZE, TEXTMESSAGE_MAXLEN) ; + if (textmessage_timed_receive(&a->in, &v, 0, 0) <= 0) goto err ; + if (v.iov_len != 1) goto ferr ; + e = ((unsigned char const *)v.iov_base)[0] ; if (e) goto ferr ; + a->pid = pid ; + a->delayfn = 0 ; + a->convfn = convfn ; + a->aux = aux ; + textmessage_sender_init(&a->out, fd[1]) ; + return e ; + + ferr: + textmessage_receiver_free(&a->in) ; + err: + fd_close(fd[1]) ; + fd_close(fd[0]) ; + { + int wstat ; + waitpid_nointr(pid, &wstat, 0) ; + } + return e ; +} diff --git a/src/pamela/pamela_strerror.c b/src/pamela/pamela_strerror.c new file mode 100644 index 0000000..2e72a01 --- /dev/null +++ b/src/pamela/pamela_strerror.c @@ -0,0 +1,11 @@ +/* ISC license. */ + +#include +#include "pamela-internal.h" + +int pamela_strerror (pamela_t *a, unsigned char e, stralloc *sa) +{ + char s[2] = "E" ; + s[1] = e ; + return pamela_query_string(a, s, 2, sa) ; +} diff --git a/src/pamela/pamela_zero.c b/src/pamela/pamela_zero.c new file mode 100644 index 0000000..70b3682 --- /dev/null +++ b/src/pamela/pamela_zero.c @@ -0,0 +1,5 @@ +/* ISC license. */ + +#include + +pamela_t const pamela_zero = PAMELA_ZERO ; diff --git a/src/pamela/pamelad.c b/src/pamela/pamelad.c new file mode 100644 index 0000000..0a55f66 --- /dev/null +++ b/src/pamela/pamelad.c @@ -0,0 +1,385 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "pamelad service_name user" + +static int cont = 1 ; +static pam_handle_t *pamh ; + + + /* Utility */ + +static void get (struct iovec *v) +{ + ssize_t r = textmessage_timed_receive(textmessage_receiver_0, v, 0, 0) ; + if (r < 0) _exit(1) ; + if (!r) _exit(0) ; +} + +static void put (char const *s, size_t len) +{ + if (!textmessage_timed_send(textmessage_sender_1, s, len, 0, 0)) + _exit(1) ; +} + +static void putv (struct iovec const *v, unsigned int n) +{ + if (!textmessage_timed_sendv(textmessage_sender_1, v, n, 0, 0)) + _exit(1) ; +} + +static inline void env_free (char **envp) +{ + char **p = envp ; + while (*p) free(*p++) ; + free(envp) ; +} + + + /* If the app customizes fail_delay */ + +static void custom_delay (int retval, unsigned int usec, void *aux) +{ + struct iovec v ; + char pack[9] = "d" ; + uint32_pack_big(pack+1, (uint32_t)retval) ; + uint32_pack_big(pack+5, (uint32_t)usec) ; + put(pack, 9) ; + get(&v) ; + if (v.iov_len != 1 || ((char const *)v.iov_base)[0] != 'D') _exit(1) ; + (void)aux ; +} + + + /* Conversation */ + +static void freeres (struct pam_response *res, unsigned int i) +{ + while (i--) free(res[i].resp) ; + free(res) ; +} + +static int converse (int n, struct pam_message const **msg, struct pam_response **resp, void *aux) +{ + if (n < 0 || n > PAMELA_PAM_CONV_MAX_MESSAGES) return PAM_SYSTEM_ERR ; + { + char pack[n * 4] ; + struct iovec v[n+2] ; + v[0].iov_base = "c" ; + v[0].iov_len = 1 ; + v[1].iov_base = pack ; + v[1].iov_len = n << 2 ; + for (uint32_t i = 0 ; i < n ; i++) uint32_pack_big(pack + 4 * i, (uint32_t)msg[i]->msg_style) ; + for (uint32_t i = 0 ; i < n ; i++) + { + v[i+2].iov_base = (char *)msg[i]->msg ; + v[i+2].iov_len = strlen(msg[i]->msg) + 1 ; + } + putv(v, n+2) ; + } + { + struct pam_response *res ; + struct iovec v ; + char const *s ; + size_t len ; + get(&v) ; + if (v.iov_len < 2) return PAM_ABORT ; + s = v.iov_base ; + len = v.iov_len - 2 ; + if (s[0] != 'C') return PAM_ABORT ; + if (s[1]) return s[1] ; + res = malloc(n * sizeof(struct pam_response)) ; + if (!res) return PAM_BUF_ERR ; + for (uint32_t i = 0 ; i < n ; i++) + { + size_t pos = strnlen(s, len) ; + if (pos == len) return PAM_ABORT ; + res[i].resp_retcode = 0 ; + res[i].resp = strdup(s) ; + if (!res[i].resp) + { + freeres(res, i) ; + return PAM_BUF_ERR ; + } + s += pos + 1 ; len -= pos + 1 ; + } + if (len) + { + freeres(res, n) ; + return PAM_ABORT ; + } + *resp = res ; + } + (void)aux ; + return PAM_SUCCESS ; +} + + + /* Protocol and actions */ + +static void do_strerror (int num) +{ + char const *x = pam_strerror(pamh, num) ; + if (!x) + { + char c = errno ; + put(&c, 1) ; + } + else + { + struct iovec v[2] = + { + { .iov_base = "", .iov_len = 1 }, + { .iov_base = (char *)x, .iov_len = strlen(x) + 1 } + } ; + putv(v, 2) ; + } +} + +static void do_getenvlist (void) +{ + char **envp = pam_getenvlist(pamh) ; + if (!envp) + { + char c = errno ; + put(&c, 1) ; + } + else + { + size_t n = env_len((char const *const *)envp) ; + struct iovec v[n+1] ; + v[0].iov_base = "" ; + v[0].iov_len = 1 ; + for (size_t i = 0 ; i < n ; i++) + { + v[i+1].iov_base = envp[i] ; + v[i+1].iov_len = strlen(envp[i]) + 1 ; + } + putv(v, n+1) ; + env_free(envp) ; + } +} + +static void do_getitem (int num) +{ + void const *item ; + int e = pam_get_item(pamh, num, &item) ; + if (e != PAM_SUCCESS) + { + char c = e ; + put(&c, 1) ; + return ; + } + switch (num) + { + case PAMELA_PAM_FAIL_DELAY : + case PAMELA_PAM_CONV : + { + char c = PAMELA_PAM_BAD_ITEM ; + put(&c, 1) ; + break ; + } + case PAMELA_PAM_XAUTHDATA : + { + struct pam_xauth_data const *p = item ; + char pack[8] ; + struct iovec v[4] = + { + { .iov_base = "", .iov_len = 1 }, + { .iov_base = pack, .iov_len = 8 }, + { .iov_base = p->name, .iov_len = p->namelen + 1 }, + { .iov_base = p->data, .iov_len = p->datalen + 1 } + } ; + uint32_pack_big(pack, (uint32_t)p->namelen) ; + uint32_pack_big(pack + 4, (uint32_t)p->datalen) ; + putv(v, 4) ; + break ; + } + default : + { + struct iovec v[2] = + { + { .iov_base = "", .iov_len = 1 }, + { .iov_base = (char *)item, .iov_len = strlen((char const *)item) + 1 } + } ; + putv(v, 2) ; + break ; + } + } +} + +static void do_setitem (int num, char const *s, size_t len) +{ + char c ; + switch (num) + { + case PAMELA_PAM_FAIL_DELAY : + case PAMELA_PAM_CONV : + baditem: + c = PAMELA_PAM_BAD_ITEM ; + break ; + case PAMELA_ENV : + if (s[len-1]) goto baditem ; + c = pam_putenv(pamh, s) ; + break ; + case PAMELA_PAM_XAUTHDATA : + { + struct pam_xauth_data xd ; + uint32_t u ; + if (len < 10) goto baditem ; + uint32_unpack_big(s, &u) ; + xd.namelen = u ; + uint32_unpack_big(s + 4, &u) ; + xd.datalen = u ; + if (len != 10 + xd.namelen + xd.datalen) goto baditem ; + xd.name = (char *)s + 8 ; + xd.data = (char *)s + 9 + xd.namelen ; + c = pam_set_item(pamh, PAM_XAUTHDATA, &xd) ; + break ; + } + default : + if (s[len-1]) goto baditem ; + c = pam_set_item(pamh, num, s) ; + break ; + } + put(&c, 1) ; +} + +static void do_op (char type, int num) +{ + char s[2] = "o" ; + switch (type) + { + case PAMELA_OP_ACCT_MGMT : + s[1] = pam_acct_mgmt(pamh, num) ; + break ; + case PAMELA_OP_AUTHENTICATE : + s[1] = pam_authenticate(pamh, num) ; + break ; + case PAMELA_OP_CHAUTHTOK : + s[1] = pam_chauthtok(pamh, num) ; + break ; + case PAMELA_OP_CLOSE_SESSION : + s[1] = pam_close_session(pamh, num) ; + break ; + case PAMELA_OP_FAIL_DELAY : + s[1] = pam_fail_delay(pamh, (unsigned int)num) ; + break ; + case PAMELA_OP_OPEN_SESSION : + s[1] = pam_open_session(pamh, num) ; + break ; + case PAMELA_OP_SETCRED : + s[1] = pam_setcred(pamh, num) ; + break ; + case PAMELA_OP_SETFAILDELAY : + s[1] = pam_set_item(pamh, PAM_FAIL_DELAY, &custom_delay) ; + break ; + case PAMELA_OP_END : + s[1] = pam_end(pamh, num) ; + break ; + default : + s[1] = PAM_ABORT ; + break ; + } + put(s, 2) ; + if (type == PAMELA_OP_END) _exit(0) ; +} + + + /* Main */ + +int main (int argc, char const *const *argv) +{ + PROG = "pamelad" ; + if (argc < 3) strerr_dieusage(100, USAGE) ; + if (ndelay_on(0) < 0) strerr_diefu2sys(111, "ndelay_on ", "0") ; + if (ndelay_on(1) < 0) strerr_diefu2sys(111, "ndelay_on ", "1") ; + if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "ignore SIGPIPE") ; + + if (!getgid()) + { + char const *x = getenv("PAMELA_GID") ; + if (x) + { + gid_t g ; + if (!gid0_scan(x, &g)) + strerr_warnw1x("invalid PAMELA_GID, keeping gid 0") ; + else setgid(g) ; + } + } + if (!getuid()) + { + char const *x = getenv("PAMELA_UID") ; + if (x) + { + uid_t u ; + if (!uid0_scan(x, &u)) + strerr_warnw1x("invalid PAMELA_UID, keeping uid 0") ; + else setuid(u) ; + } + } + + { + struct pam_conv conv = { .conv = &converse, .appdata_ptr = 0 } ; + char c = pam_start(argv[1], argv[2], &conv, &pamh) ; + put(&c, 1) ; + if (c != PAM_SUCCESS) return 2 ; + } + + while (cont) + { + struct iovec v ; + char const *s ; + size_t len ; + get(&v) ; + len = v.iov_len ; + if (!len) return 1 ; + s = v.iov_base ; + switch (s[0]) + { + case 'E' : + if (len != 2) return 1 ; + do_strerror(s[1]) ; + break ; + case 'V' : + if (len != 1) return 1 ; + do_getenvlist() ; + break ; + case 'G' : + if (len != 2) return 1 ; + do_getitem(s[1]) ; + break ; + case 'S' : + if (len < 3) return 1 ; + do_setitem(s[1], s + 2, len - 2) ; + break ; + case 'O' : + { + unsigned int arg ; + if (len != 2 + sizeof(int)) return 1 ; + uint_unpack(s + 2, &arg) ; + do_op(s[1], (int)arg) ; + break ; + } + default : return 1 ; + } + } + return 0 ; +} -- cgit v1.2.3