summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorplanc <hubenchang0515@outlook.com>2021-12-27 13:06:55 +0800
committerTomas Mraz <tmraz@fedoraproject.org>2022-01-14 17:23:33 +0100
commit3102dd822db3e613308c05fca554ced5e81c181d (patch)
treecd3d8e4147d622f8211e67485ab71404bf0a4485
parente1870443d59ccf07278f7b016b99783f430b06b1 (diff)
downloadlinux-pam-git-3102dd822db3e613308c05fca554ced5e81c181d.tar.gz
Add a conversation function example
* examples/Makefile.am: Add tty_conv to noinst_PROGRAMS * examples/tty_conv.c: A new example of conversation function.
-rw-r--r--examples/Makefile.am2
-rw-r--r--examples/tty_conv.c177
2 files changed, 178 insertions, 1 deletions
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 722ec686..c4c3c261 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -11,4 +11,4 @@ AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
LDADD = $(top_builddir)/libpam/libpam.la \
$(top_builddir)/libpam_misc/libpam_misc.la
-noinst_PROGRAMS = xsh vpass blank check_user
+noinst_PROGRAMS = xsh vpass blank check_user tty_conv
diff --git a/examples/tty_conv.c b/examples/tty_conv.c
new file mode 100644
index 00000000..23f0684c
--- /dev/null
+++ b/examples/tty_conv.c
@@ -0,0 +1,177 @@
+/* PlanC (hubenchang0515@outlook.com) -- an example application
+ * that implements a custom conversation */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <termio.h>
+#include <security/pam_appl.h>
+
+/***************************************
+ * @brief echo off/on
+ * @param[in] fd file descriptor
+ * @param[in] off 1 - echo off,0 - echo on
+ ***************************************/
+static void echoOff(int fd, int off)
+{
+ struct termio tty;
+ if (ioctl(fd, TCGETA, &tty) < 0)
+ {
+ fprintf(stderr, "TCGETA failed: %s\n", strerror(errno));
+ return;
+ }
+
+ if (off)
+ {
+ tty.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+ if (ioctl(fd, TCSETAF, &tty) < 0)
+ {
+ fprintf(stderr, "TCSETAF failed: %s\n", strerror(errno));
+ }
+ }
+ else
+ {
+ tty.c_lflag |= (ECHO | ECHOE | ECHOK | ECHONL);
+ if (ioctl(fd, TCSETAW, &tty) < 0)
+ {
+ fprintf(stderr, "TCSETAW failed: %s\n", strerror(errno));
+ }
+ }
+}
+
+/***************************************
+ * @brief echo off stdin
+ ***************************************/
+static void echoOffStdin(void)
+{
+ echoOff(fileno(stdin), 1);
+}
+
+/***************************************
+ * @brief echo on stdin
+ ***************************************/
+static void echoOnStdin(void)
+{
+ echoOff(fileno(stdin), 0);
+}
+
+/***************************************
+ * @brief read a line input
+ * @return the input string
+ ***************************************/
+static char *readline(void)
+{
+ char input[PAM_MAX_RESP_SIZE];
+ int i;
+
+ flockfile(stdin);
+ for (i = 0; i < PAM_MAX_RESP_SIZE; i++)
+ {
+ int ch = getchar_unlocked();
+ if (ch == '\n' || ch == '\r' ||ch == EOF)
+ break;
+ input[i] = ch;
+ }
+ funlockfile(stdin);
+ input[i] = '\0';
+
+ return (strdup(input));
+}
+
+/**************************************************
+ * @brief callback of PAM conversation
+ * @param[in] num_msg the count of message
+ * @param[in] msg PAM message
+ * @param[out] resp our response
+ * @param[in] appdata_ptr custom data passed by struct pam_conv.appdata_ptr
+ * @return state
+ **************************************************/
+static int conversation(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
+{
+ (void)(appdata_ptr);
+ int i;
+
+ /* check the count of message */
+ if (num_msg <= 0 || num_msg >= PAM_MAX_MSG_SIZE)
+ {
+ fprintf(stderr, "invalid num_msg(%d)\n", num_msg);
+ return PAM_CONV_ERR;
+ }
+
+ /* alloc memory for response */
+ if ((resp[0] = malloc(num_msg * sizeof(struct pam_response))) == NULL)
+ {
+ fprintf(stderr, "bad alloc\n");
+ return PAM_BUF_ERR;
+ }
+
+ /* response for message */
+ for (i = 0; i < num_msg; i++)
+ {
+ const struct pam_message *m = *msg + i;
+ struct pam_response *r = *resp + i;
+ r->resp_retcode = 0; /* currently un-used, zero expected */
+ switch (m->msg_style)
+ {
+ case PAM_PROMPT_ECHO_OFF: /* get the input with echo off, like the password */
+ printf("%s", m->msg);
+ echoOffStdin();
+ r->resp = readline();
+ echoOnStdin();
+ printf("\n");
+ break;
+
+ case PAM_PROMPT_ECHO_ON: /* get the input with echo on, like the username */
+ printf("%s", m->msg);
+ r->resp = readline();
+ break;
+
+ case PAM_TEXT_INFO: /* normal info */
+ printf("%s\n", m->msg);
+ break;
+
+ case PAM_ERROR_MSG: /* error info */
+ fprintf(stderr, "%s\n", m->msg);
+ break;
+
+ default:
+ fprintf(stderr, "unexpected msg_style: %d\n", m->msg_style);
+ break;
+ }
+ }
+ return PAM_SUCCESS;
+}
+
+int main(void)
+{
+ struct pam_conv pam_conv = {conversation, NULL};
+ pam_handle_t *pamh;
+
+ /* echo on while exist, like Ctrl+C on input password */
+ atexit(echoOnStdin);
+
+ if (PAM_SUCCESS != pam_start("login", NULL, &pam_conv, &pamh))
+ {
+ fprintf(stderr, "pam_start failed\n");
+ return EXIT_FAILURE;
+ }
+
+ if (PAM_SUCCESS != pam_authenticate(pamh, 0))
+ {
+ fprintf(stderr, "pam_authenticate failed\n");
+ pam_end(pamh, 0);
+ return EXIT_FAILURE;
+ }
+
+ if (PAM_SUCCESS != pam_acct_mgmt(pamh, 0))
+ {
+ fprintf(stderr, "pam_acct_mgmt failed\n");
+ pam_end(pamh, 0);
+ return EXIT_FAILURE;
+ }
+
+ pam_end(pamh, 0);
+ return EXIT_SUCCESS;
+}