4 * A generic conversation function for text based applications
6 * Written by Andrew Morgan <morgan@linux.kernel.org>
15 #include <sys/types.h>
20 #include <security/pam_appl.h>
21 #include <security/pam_misc.h>
23 #define INPUTSIZE PAM_MAX_MSG_SIZE /* maximum length of input+1 */
24 #define CONV_ECHO_ON 1 /* types of echo state */
25 #define CONV_ECHO_OFF 0
28 * external timeout definitions - these can be overriden by the
32 time_t pam_misc_conv_warn_time = 0; /* time when we warn */
33 time_t pam_misc_conv_die_time = 0; /* time when we timeout */
35 const char *pam_misc_conv_warn_line = "..\a.Time is running out...\n";
36 const char *pam_misc_conv_die_line = "..\a.Sorry, your time is up!\n";
38 int pam_misc_conv_died=0; /* application can probe this for timeout */
41 * These functions are for binary prompt manipulation.
42 * The manner in which a binary prompt is processed is application
43 * specific, so these function pointers are provided and can be
44 * initialized by the application prior to the conversation function
48 static void pam_misc_conv_delete_binary(void *appdata,
51 PAM_BP_RENEW(delete_me, 0, 0);
54 int (*pam_binary_handler_fn)(void *appdata, pamc_bp_t *prompt_p) = NULL;
55 void (*pam_binary_handler_free)(void *appdata, pamc_bp_t *prompt_p)
56 = pam_misc_conv_delete_binary;
58 /* the following code is used to get text input */
60 static volatile int expired=0;
62 /* return to the previous signal handling */
63 static void reset_alarm(struct sigaction *o_ptr)
65 (void) alarm(0); /* stop alarm clock - if still ticking */
66 (void) sigaction(SIGALRM, o_ptr, NULL);
69 /* this is where we intercept the alarm signal */
70 static void time_is_up(int ignore)
75 /* set the new alarm to hit the time_is_up() function */
76 static int set_alarm(int delay, struct sigaction *o_ptr)
78 struct sigaction new_sig;
80 sigemptyset(&new_sig.sa_mask);
82 new_sig.sa_handler = time_is_up;
83 if ( sigaction(SIGALRM, &new_sig, o_ptr) ) {
84 return 1; /* setting signal failed */
87 (void) sigaction(SIGALRM, o_ptr, NULL);
88 return 1; /* failed to set alarm */
90 return 0; /* all seems to have worked */
93 /* return the number of seconds to next alarm. 0 = no delay, -1 = expired */
94 static int get_delay(void)
98 expired = 0; /* reset flag */
101 /* has the quit time past? */
102 if (pam_misc_conv_die_time && now >= pam_misc_conv_die_time) {
103 fprintf(stderr,"%s",pam_misc_conv_die_line);
105 pam_misc_conv_died = 1; /* note we do not reset the die_time */
106 return -1; /* time is up */
109 /* has the warning time past? */
110 if (pam_misc_conv_warn_time && now >= pam_misc_conv_warn_time) {
111 fprintf(stderr, "%s", pam_misc_conv_warn_line);
112 pam_misc_conv_warn_time = 0; /* reset warn_time */
114 /* indicate remaining delay - if any */
116 return (pam_misc_conv_die_time ? pam_misc_conv_die_time - now:0 );
119 /* indicate possible warning delay */
121 if (pam_misc_conv_warn_time)
122 return (pam_misc_conv_warn_time - now);
123 else if (pam_misc_conv_die_time)
124 return (pam_misc_conv_die_time - now);
129 /* read a line of input string, giving prompt when appropriate */
130 static int read_string(int echo, const char *prompt, char **retstr)
132 struct termios term_before, term_tmp;
133 char line[INPUTSIZE];
134 struct sigaction old_sig;
135 int delay, nc = -1, have_term = 0;
138 D(("called with echo='%s', prompt='%s'.", echo ? "ON":"OFF" , prompt));
140 if (isatty(STDIN_FILENO)) { /* terminal state */
142 /* is a terminal so record settings and flush it */
143 if ( tcgetattr(STDIN_FILENO, &term_before) != 0 ) {
144 D(("<error: failed to get terminal settings>"));
148 memcpy(&term_tmp, &term_before, sizeof(term_tmp));
150 term_tmp.c_lflag &= ~(ECHO);
155 * We make a simple attempt to block TTY signals from terminating
156 * the conversation without giving PAM a chance to clean up.
160 sigaddset(&nset, SIGINT);
161 sigaddset(&nset, SIGTSTP);
162 (void) sigprocmask(SIG_BLOCK, &nset, &oset);
165 D(("<warning: cannot turn echo off>"));
168 /* set up the signal handling */
171 /* reading the line */
173 /* this may, or may not set echo off -- drop pending input */
175 (void) tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_tmp);
177 fprintf(stderr, "%s", prompt);
179 if ( delay > 0 && set_alarm(delay, &old_sig) ) {
180 D(("<failed to set alarm>"));
183 nc = read(STDIN_FILENO, line, INPUTSIZE-1);
185 (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &term_before);
186 if (!echo || expired) /* do we need a newline? */
187 fprintf(stderr,"\n");
190 reset_alarm(&old_sig);
194 } else if (nc > 0) { /* we got some user input */
195 D(("we got some user input"));
197 if (nc > 0 && line[nc-1] == '\n') { /* <NUL> terminate */
201 fprintf(stderr, "\n");
205 *retstr = x_strdup(line);
206 _pam_overwrite(line);
208 goto cleanexit; /* return malloc()ed string */
210 } else if (nc == 0) { /* Ctrl-D */
211 D(("user did not want to type anything"));
215 fprintf(stderr, "\n");
217 goto cleanexit; /* return malloc()ed "" */
218 } else if (nc == -1) {
219 /* Don't loop forever if read() returns -1. */
220 D(("error reading input from the user: %s", strerror(errno)));
222 fprintf(stderr, "\n");
225 goto cleanexit; /* return NULL */
230 /* getting here implies that the timer expired */
232 D(("the timer appears to have expired"));
235 _pam_overwrite(line);
240 (void) sigprocmask(SIG_SETMASK, &oset, NULL);
241 (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &term_before);
247 /* end of read_string functions */
250 * This conversation function is supposed to be a generic PAM one.
251 * Unfortunately, it is _not_ completely compatible with the Solaris PAM
254 * Namely, for msgm's that contain multiple prompts, this function
255 * interprets "const struct pam_message **msgm" as equivalent to
256 * "const struct pam_message *msgm[]". The Solaris module
257 * implementation interprets the **msgm object as a pointer to a
258 * pointer to an array of "struct pam_message" objects (that is, a
259 * confusing amount of pointer indirection).
262 int misc_conv(int num_msg, const struct pam_message **msgm,
263 struct pam_response **response, void *appdata_ptr)
266 struct pam_response *reply;
271 D(("allocating empty response structure array."));
273 reply = (struct pam_response *) calloc(num_msg,
274 sizeof(struct pam_response));
276 D(("no memory for responses"));
280 D(("entering conversation function."));
282 for (count=0; count < num_msg; ++count) {
286 switch (msgm[count]->msg_style) {
287 case PAM_PROMPT_ECHO_OFF:
288 nc = read_string(CONV_ECHO_OFF,msgm[count]->msg, &string);
290 goto failed_conversation;
293 case PAM_PROMPT_ECHO_ON:
294 nc = read_string(CONV_ECHO_ON,msgm[count]->msg, &string);
296 goto failed_conversation;
300 if (fprintf(stderr,"%s\n",msgm[count]->msg) < 0) {
301 goto failed_conversation;
305 if (fprintf(stdout,"%s\n",msgm[count]->msg) < 0) {
306 goto failed_conversation;
309 case PAM_BINARY_PROMPT:
311 pamc_bp_t binary_prompt = NULL;
313 if (!msgm[count]->msg || !pam_binary_handler_fn) {
314 goto failed_conversation;
317 PAM_BP_RENEW(&binary_prompt,
318 PAM_BP_RCONTROL(msgm[count]->msg),
319 PAM_BP_LENGTH(msgm[count]->msg));
320 PAM_BP_FILL(binary_prompt, 0, PAM_BP_LENGTH(msgm[count]->msg),
321 PAM_BP_RDATA(msgm[count]->msg));
323 if (pam_binary_handler_fn(appdata_ptr,
324 &binary_prompt) != PAM_SUCCESS
325 || (binary_prompt == NULL)) {
326 goto failed_conversation;
328 string = (char *) binary_prompt;
329 binary_prompt = NULL;
334 fprintf(stderr, "erroneous conversation (%d)\n"
335 ,msgm[count]->msg_style);
336 goto failed_conversation;
339 if (string) { /* must add to reply array */
340 /* add string to list of responses */
342 reply[count].resp_retcode = 0;
343 reply[count].resp = string;
355 D(("the conversation failed"));
358 for (count=0; count<num_msg; ++count) {
359 if (reply[count].resp == NULL) {
362 switch (msgm[count]->msg_style) {
363 case PAM_PROMPT_ECHO_ON:
364 case PAM_PROMPT_ECHO_OFF:
365 _pam_overwrite(reply[count].resp);
366 free(reply[count].resp);
368 case PAM_BINARY_PROMPT:
369 pam_binary_handler_free(appdata_ptr,
370 (pamc_bp_t *) &reply[count].resp);
374 /* should not actually be able to get here... */
375 free(reply[count].resp);
377 reply[count].resp = NULL;
379 /* forget reply too */