summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2018-02-04 23:22:53 +0000
committerLaurent Bercot <ska-skaware@skarnet.org>2018-02-04 23:22:53 +0000
commit03012f54b1bcd31e0b817fc0222a9a47709c4018 (patch)
treed53c568d1e14bae1e1e0efadab8b0bf3e66982aa /src
downloadpamela-03012f54b1bcd31e0b817fc0222a9a47709c4018.tar.xz
Initial commit
Diffstat (limited to 'src')
-rw-r--r--src/include/pamela/common.h101
-rw-r--r--src/include/pamela/pam.h161
-rw-r--r--src/include/pamela/pamela.h80
-rw-r--r--src/pamela/deps-exe/pamelad2
-rw-r--r--src/pamela/deps-lib/pamela27
-rw-r--r--src/pamela/pam_acct_mgmt.c11
-rw-r--r--src/pamela/pam_authenticate.c11
-rw-r--r--src/pamela/pam_chauthtok.c11
-rw-r--r--src/pamela/pam_close_session.c11
-rw-r--r--src/pamela/pam_end.c17
-rw-r--r--src/pamela/pam_fail_delay.c11
-rw-r--r--src/pamela/pam_get_item.c52
-rw-r--r--src/pamela/pam_getenv.c32
-rw-r--r--src/pamela/pam_getenvlist.c42
-rw-r--r--src/pamela/pam_open_session.c11
-rw-r--r--src/pamela/pam_putenv.c14
-rw-r--r--src/pamela/pam_set_item.c50
-rw-r--r--src/pamela/pam_setcred.c11
-rw-r--r--src/pamela/pam_start.c34
-rw-r--r--src/pamela/pam_strerror.c17
-rw-r--r--src/pamela/pamela-internal.h13
-rw-r--r--src/pamela/pamela_end.c16
-rw-r--r--src/pamela/pamela_get_item.c11
-rw-r--r--src/pamela/pamela_getenvlist.c9
-rw-r--r--src/pamela/pamela_op.c123
-rw-r--r--src/pamela/pamela_pam_response_free.c11
-rw-r--r--src/pamela/pamela_query_string.c20
-rw-r--r--src/pamela/pamela_set_item.c16
-rw-r--r--src/pamela/pamela_set_item_internal.c14
-rw-r--r--src/pamela/pamela_set_itemv.c14
-rw-r--r--src/pamela/pamela_startf.c40
-rw-r--r--src/pamela/pamela_strerror.c11
-rw-r--r--src/pamela/pamela_zero.c5
-rw-r--r--src/pamela/pamelad.c385
34 files changed, 1394 insertions, 0 deletions
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 <skalibs/uint64.h>
+#include <skalibs/stralloc.h>
+#include <pamela/pamela.h>
+
+#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 <sys/types.h>
+#include <stdint.h>
+#include <sys/uio.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/textmessage.h>
+#include <pamela/common.h>
+
+
+ /* 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 <stdint.h>
+#include <pamela/pamela.h>
+#include <pamela/pam.h>
+
+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 <stdint.h>
+#include <pamela/pamela.h>
+#include <pamela/pam.h>
+
+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 <stdint.h>
+#include <pamela/pamela.h>
+#include <pamela/pam.h>
+
+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 <stdint.h>
+#include <pamela/pamela.h>
+#include <pamela/pam.h>
+
+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 <stdlib.h>
+#include <skalibs/stralloc.h>
+#include <pamela/pamela.h>
+#include <pamela/pam.h>
+
+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 <stdint.h>
+#include <pamela/pamela.h>
+#include <pamela/pam.h>
+
+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 <sys/types.h>
+#include <stdint.h>
+#include <skalibs/uint32.h>
+#include <pamela/pamela.h>
+#include <pamela/pam.h>
+
+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 <string.h>
+#include <skalibs/stralloc.h>
+#include <pamela/pamela.h>
+#include <pamela/pam.h>
+
+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 <string.h>
+#include <stdlib.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/stralloc.h>
+#include <pamela/pamela.h>
+#include <pamela/pam.h>
+
+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 <stdint.h>
+#include <pamela/pamela.h>
+#include <pamela/pam.h>
+
+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 <pamela/pamela.h>
+#include <pamela/pam.h>
+
+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 <stdint.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <skalibs/uint32.h>
+#include <pamela/pamela.h>
+#include <pamela/pam.h>
+
+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 <stdint.h>
+#include <pamela/pamela.h>
+#include <pamela/pam.h>
+
+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 <stdlib.h>
+#include <skalibs/stralloc.h>
+#include <pamela/pamela.h>
+#include <pamela/pam.h>
+
+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 <pamela/pamela.h>
+#include <pamela/pam.h>
+
+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 <sys/uio.h>
+#include <skalibs/stralloc.h>
+#include <pamela/pamela.h>
+
+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 <skalibs/djbunix.h>
+#include <skalibs/textmessage.h>
+#include <pamela/pamela.h>
+
+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 <pamela/pamela.h>
+#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 <pamela/pamela.h>
+#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 <string.h>
+#include <stdint.h>
+#include <sys/uio.h>
+#include <skalibs/posixplz.h>
+#include <skalibs/uint32.h>
+#include <skalibs/types.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/textmessage.h>
+#include <pamela/pamela.h>
+
+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 <stdint.h>
+#include <stdlib.h>
+#include <pamela/pamela.h>
+
+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 <errno.h>
+#include <sys/uio.h>
+#include <skalibs/error.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/textmessage.h>
+#include <pamela/pamela.h>
+
+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 <string.h>
+#include <pamela/pamela.h>
+#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 <sys/uio.h>
+#include <skalibs/textmessage.h>
+#include <pamela/pamela.h>
+
+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 <sys/uio.h>
+#include <pamela/pamela.h>
+#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 <sys/types.h>
+#include <sys/uio.h>
+#include <skalibs/environ.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/textmessage.h>
+#include <pamela/config.h>
+#include <pamela/pamela.h>
+
+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 <pamela/pamela.h>
+#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/pamela.h>
+
+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 <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <skalibs/posixplz.h>
+#include <skalibs/uint32.h>
+#include <skalibs/types.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/env.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/sig.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/textmessage.h>
+#include <pamela/common.h>
+#include <security/pam_appl.h>
+
+#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 ;
+}