1 /* PlanC (hubenchang0515@outlook.com) -- an example application
2 * that implements a custom conversation */
3
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <errno.h>
8 #include <unistd.h>
9 #include <termio.h>
10 #include <security/pam_appl.h>
11
12 /***************************************
13 * @brief echo off/on
14 * @param[in] fd file descriptor
15 * @param[in] off 1 - echo off,0 - echo on
16 ***************************************/
17 static void echoOff(int fd, int off)
18 {
19 struct termio tty;
20 if (ioctl(fd, TCGETA, &tty) < 0)
21 {
22 fprintf(stderr, "TCGETA failed: %s\n", strerror(errno));
23 return;
24 }
25
26 if (off)
27 {
28 tty.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
29 if (ioctl(fd, TCSETAF, &tty) < 0)
30 {
31 fprintf(stderr, "TCSETAF failed: %s\n", strerror(errno));
32 }
33 }
34 else
35 {
36 tty.c_lflag |= (ECHO | ECHOE | ECHOK | ECHONL);
37 if (ioctl(fd, TCSETAW, &tty) < 0)
38 {
39 fprintf(stderr, "TCSETAW failed: %s\n", strerror(errno));
40 }
41 }
42 }
43
44 /***************************************
45 * @brief echo off stdin
46 ***************************************/
47 static void echoOffStdin(void)
48 {
49 echoOff(fileno(stdin), 1);
50 }
51
52 /***************************************
53 * @brief echo on stdin
54 ***************************************/
55 static void echoOnStdin(void)
56 {
57 echoOff(fileno(stdin), 0);
58 }
59
60 /***************************************
61 * @brief read a line input
62 * @return the input string
63 ***************************************/
64 static char *readline(void)
65 {
66 char input[PAM_MAX_RESP_SIZE];
67 int i;
68
69 flockfile(stdin);
70 for (i = 0; i < PAM_MAX_RESP_SIZE; i++)
71 {
72 int ch = getchar_unlocked();
73 if (ch == '\n' || ch == '\r' ||ch == EOF)
74 break;
75 input[i] = ch;
76 }
77 funlockfile(stdin);
78 input[i] = '\0';
79
80 return (strdup(input));
81 }
82
83 /**************************************************
84 * @brief callback of PAM conversation
85 * @param[in] num_msg the count of message
86 * @param[in] msg PAM message
87 * @param[out] resp our response
88 * @param[in] appdata_ptr custom data passed by struct pam_conv.appdata_ptr
89 * @return state
90 **************************************************/
91 static int conversation(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
92 {
93 (void)(appdata_ptr);
94 int i;
95
96 /* check the count of message */
97 if (num_msg <= 0 || num_msg >= PAM_MAX_MSG_SIZE)
98 {
99 fprintf(stderr, "invalid num_msg(%d)\n", num_msg);
100 return PAM_CONV_ERR;
101 }
102
103 /* alloc memory for response */
104 if ((resp[0] = malloc(num_msg * sizeof(struct pam_response))) == NULL)
105 {
106 fprintf(stderr, "bad alloc\n");
107 return PAM_BUF_ERR;
108 }
109
110 /* response for message */
111 for (i = 0; i < num_msg; i++)
112 {
113 const struct pam_message *m = *msg + i;
114 struct pam_response *r = *resp + i;
115 r->resp_retcode = 0; /* currently un-used, zero expected */
116 switch (m->msg_style)
117 {
118 case PAM_PROMPT_ECHO_OFF: /* get the input with echo off, like the password */
119 printf("%s", m->msg);
120 echoOffStdin();
121 r->resp = readline();
122 echoOnStdin();
123 printf("\n");
124 break;
125
126 case PAM_PROMPT_ECHO_ON: /* get the input with echo on, like the username */
127 printf("%s", m->msg);
128 r->resp = readline();
129 break;
130
131 case PAM_TEXT_INFO: /* normal info */
132 printf("%s\n", m->msg);
133 break;
134
135 case PAM_ERROR_MSG: /* error info */
136 fprintf(stderr, "%s\n", m->msg);
137 break;
138
139 default:
140 fprintf(stderr, "unexpected msg_style: %d\n", m->msg_style);
141 break;
142 }
143 }
144 return PAM_SUCCESS;
145 }
146
147 int main(void)
148 {
149 struct pam_conv pam_conv = {conversation, NULL};
150 pam_handle_t *pamh;
151
152 /* echo on while exist, like Ctrl+C on input password */
153 atexit(echoOnStdin);
154
155 if (PAM_SUCCESS != pam_start("login", NULL, &pam_conv, &pamh))
156 {
157 fprintf(stderr, "pam_start failed\n");
158 return EXIT_FAILURE;
159 }
160
161 if (PAM_SUCCESS != pam_authenticate(pamh, 0))
162 {
163 fprintf(stderr, "pam_authenticate failed\n");
164 pam_end(pamh, 0);
165 return EXIT_FAILURE;
166 }
167
168 if (PAM_SUCCESS != pam_acct_mgmt(pamh, 0))
169 {
170 fprintf(stderr, "pam_acct_mgmt failed\n");
171 pam_end(pamh, 0);
172 return EXIT_FAILURE;
173 }
174
175 pam_end(pamh, 0);
176 return EXIT_SUCCESS;
177 }