4 * A generic conversation function for text based applications
6 * Written by Andrew Morgan <morgan@linux.kernel.org>
9 #include <security/_pam_aconf.h>
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 */
40 static void pam_misc_conv_delete_binary(void **delete_me)
42 if (delete_me && *delete_me) {
43 unsigned char *packet = *(unsigned char **)delete_me;
46 length = (packet[0]<<24)+(packet[1]<<16)+(packet[2]<<8)+packet[3];
47 memset(packet, 0, length);
49 *delete_me = packet = NULL;
53 /* These function pointers are for application specific binary
54 conversations. One or both of the arguments to the first function
55 must be non-NULL. The first function must return PAM_SUCCESS or
56 PAM_CONV_ERR. If input is non-NULL, a response is expected, this
57 response should be malloc()'d and will eventually be free()'d by
58 the calling module. The structure of this malloc()'d response is as
61 { int length, char data[length] }
63 For convenience, the pointer used by the two function pointer
64 prototypes is 'void *'.
66 The ...free() fn pointer is used to discard a binary message that
67 is not of the default form. It should be explicitly overwritten
68 when using some other convention for the structure of a binary
69 prompt (not recommended). */
71 int (*pam_binary_handler_fn)(const void *send, void **receive) = NULL;
72 void (*pam_binary_handler_free)(void **packet_p) = pam_misc_conv_delete_binary;
74 /* the following code is used to get text input */
76 volatile static int expired=0;
78 /* return to the previous signal handling */
79 static void reset_alarm(struct sigaction *o_ptr)
81 (void) alarm(0); /* stop alarm clock - if still ticking */
82 (void) sigaction(SIGALRM, o_ptr, NULL);
85 /* this is where we intercept the alarm signal */
86 static void time_is_up(int ignore)
91 /* set the new alarm to hit the time_is_up() function */
92 static int set_alarm(int delay, struct sigaction *o_ptr)
94 struct sigaction new_sig;
96 sigemptyset(&new_sig.sa_mask);
98 new_sig.sa_handler = time_is_up;
99 if ( sigaction(SIGALRM, &new_sig, o_ptr) ) {
100 return 1; /* setting signal failed */
102 if ( alarm(delay) ) {
103 (void) sigaction(SIGALRM, o_ptr, NULL);
104 return 1; /* failed to set alarm */
106 return 0; /* all seems to have worked */
109 /* return the number of seconds to next alarm. 0 = no delay, -1 = expired */
110 static int get_delay(void)
114 expired = 0; /* reset flag */
117 /* has the quit time past? */
118 if (pam_misc_conv_die_time && now >= pam_misc_conv_die_time) {
119 fprintf(stderr,"%s",pam_misc_conv_die_line);
121 pam_misc_conv_died = 1; /* note we do not reset the die_time */
122 return -1; /* time is up */
125 /* has the warning time past? */
126 if (pam_misc_conv_warn_time && now >= pam_misc_conv_warn_time) {
127 fprintf(stderr, "%s", pam_misc_conv_warn_line);
128 pam_misc_conv_warn_time = 0; /* reset warn_time */
130 /* indicate remaining delay - if any */
132 return (pam_misc_conv_die_time ? pam_misc_conv_die_time - now:0 );
135 /* indicate possible warning delay */
137 if (pam_misc_conv_warn_time)
138 return (pam_misc_conv_warn_time - now);
139 else if (pam_misc_conv_die_time)
140 return (pam_misc_conv_die_time - now);
145 /* read a line of input string, giving prompt when appropriate */
146 static char *read_string(int echo, const char *prompt)
148 struct termios term_before, term_tmp;
149 char line[INPUTSIZE];
150 struct sigaction old_sig;
151 int delay, nc, have_term=0;
153 D(("called with echo='%s', prompt='%s'.", echo ? "ON":"OFF" , prompt));
155 if (isatty(STDIN_FILENO)) { /* terminal state */
157 /* is a terminal so record settings and flush it */
158 if ( tcgetattr(STDIN_FILENO, &term_before) != 0 ) {
159 D(("<error: failed to get terminal settings>"));
162 memcpy(&term_tmp, &term_before, sizeof(term_tmp));
164 term_tmp.c_lflag &= ~(ECHO);
169 D(("<warning: cannot turn echo off>"));
172 /* set up the signal handling */
175 /* reading the line */
178 fprintf(stderr, "%s", prompt);
179 /* this may, or may not set echo off -- drop pending input */
181 (void) tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_tmp);
183 if ( delay > 0 && set_alarm(delay, &old_sig) ) {
184 D(("<failed to set alarm>"));
187 nc = read(STDIN_FILENO, line, INPUTSIZE-1);
189 (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &term_before);
190 if (!echo || expired) /* do we need a newline? */
191 fprintf(stderr,"\n");
194 reset_alarm(&old_sig);
198 } else if (nc > 0) { /* we got some user input */
201 if (nc > 0 && line[nc-1] == '\n') { /* <NUL> terminate */
206 input = x_strdup(line);
207 _pam_overwrite(line);
209 return input; /* return malloc()ed string */
210 } else if (nc == 0) { /* Ctrl-D */
211 D(("user did not want to type anything"));
212 fprintf(stderr, "\n");
218 /* getting here implies that the timer expired */
220 (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &term_before);
222 memset(line, 0, INPUTSIZE); /* clean up */
226 /* end of read_string functions */
228 int misc_conv(int num_msg, const struct pam_message **msgm,
229 struct pam_response **response, void *appdata_ptr)
232 struct pam_response *reply;
237 D(("allocating empty response structure array."));
239 reply = (struct pam_response *) calloc(num_msg,
240 sizeof(struct pam_response));
242 D(("no memory for responses"));
246 D(("entering conversation function."));
248 for (count=0; count < num_msg; ++count) {
251 switch (msgm[count]->msg_style) {
252 case PAM_PROMPT_ECHO_OFF:
253 string = read_string(CONV_ECHO_OFF,msgm[count]->msg);
254 if (string == NULL) {
255 goto failed_conversation;
258 case PAM_PROMPT_ECHO_ON:
259 string = read_string(CONV_ECHO_ON,msgm[count]->msg);
260 if (string == NULL) {
261 goto failed_conversation;
265 if (fprintf(stderr,"%s\n",msgm[count]->msg) < 0) {
266 goto failed_conversation;
270 if (fprintf(stdout,"%s\n",msgm[count]->msg) < 0) {
271 goto failed_conversation;
274 case PAM_BINARY_PROMPT:
277 const void *pack_in = msgm[count]->msg;
279 if (!pam_binary_handler_fn
280 || pam_binary_handler_fn(pack_in, &pack_out) != PAM_SUCCESS
281 || pack_out == NULL) {
282 goto failed_conversation;
284 string = (char *) pack_out;
290 fprintf(stderr, "erroneous conversation (%d)\n"
291 ,msgm[count]->msg_style);
292 goto failed_conversation;
295 if (string) { /* must add to reply array */
296 /* add string to list of responses */
298 reply[count].resp_retcode = 0;
299 reply[count].resp = string;
304 /* New (0.59+) behavior is to always have a reply - this is
305 compatable with the X/Open (March 1997) spec. */
314 for (count=0; count<num_msg; ++count) {
315 if (reply[count].resp == NULL) {
318 switch (msgm[count]->msg_style) {
319 case PAM_PROMPT_ECHO_ON:
320 case PAM_PROMPT_ECHO_OFF:
321 _pam_overwrite(reply[count].resp);
322 free(reply[count].resp);
324 case PAM_BINARY_PROMPT:
325 pam_binary_handler_free((void **) &reply[count].resp);
329 /* should not actually be able to get here... */
330 free(reply[count].resp);
332 reply[count].resp = NULL;
334 /* forget reply too */