2 * $Id: misc_conv.c,v 1.5 1997/01/04 20:16:48 morgan Exp morgan $
3 * $FreeBSD: src/contrib/libpam/libpam_misc/misc_conv.c,v 1.1.1.1.6.3 2003/02/10 12:15:30 des Exp $
5 * A generic conversation function for text based applications
7 * Written by Andrew Morgan <morgan@linux.kernel.org>
9 * $Log: misc_conv.c,v $
10 * Revision 1.5 1997/01/04 20:16:48 morgan
11 * removed getpass. Replaced with POSIX code for same function which
12 * also observes timeouts specified by the parent application
14 * Revision 1.4 1996/12/01 03:26:51 morgan
15 * *** empty log message ***
17 * Revision 1.3 1996/11/10 20:10:01 morgan
20 * Revision 1.2 1996/07/07 23:59:56 morgan
21 * changed the name of the misc include file
23 * Revision 1.1 1996/05/02 05:17:06 morgan
36 #include <sys/types.h>
41 #include <security/pam_appl.h>
42 #include <security/pam_misc.h>
44 #define INPUTSIZE PAM_MAX_MSG_SIZE /* maximum length of input+1 */
45 #define CONV_ECHO_ON 1 /* types of echo state */
46 #define CONV_ECHO_OFF 0
49 * external timeout definitions - these can be overriden by the
53 time_t pam_misc_conv_warn_time = 0; /* time when we warn */
54 time_t pam_misc_conv_die_time = 0; /* time when we timeout */
56 const char *pam_misc_conv_warn_line = "..\a.Time is running out...\n";
57 const char *pam_misc_conv_die_line = "..\a.Sorry, your time is up!\n";
59 int pam_misc_conv_died=0; /* application can probe this for timeout */
61 static void pam_misc_conv_delete_binary(void **delete_me)
63 if (delete_me && *delete_me) {
64 unsigned char *packet = *(unsigned char **)delete_me;
67 length = 4+(packet[0]<<24)+(packet[1]<<16)+(packet[2]<<8)+packet[3];
68 memset(packet, 0, length);
70 *delete_me = packet = NULL;
74 /* These function pointers are for application specific binary
75 conversations. One or both of the arguments to the first function
76 must be non-NULL. The first function must return PAM_SUCCESS or
77 PAM_CONV_ERR. If input is non-NULL, a response is expected, this
78 response should be malloc()'d and will eventually be free()'d by
79 the calling module. The structure of this malloc()'d response is as
82 { int length, char data[length] }
84 For convenience, the pointer used by the two function pointer
85 prototypes is 'void *'.
87 The ...free() fn pointer is used to discard a binary message that
88 is not of the default form. It should be explicitly overwritten
89 when using some other convention for the structure of a binary
90 prompt (not recommended). */
92 int (*pam_binary_handler_fn)(const void *send, void **receive) = NULL;
93 void (*pam_binary_handler_free)(void **packet_p) = pam_misc_conv_delete_binary;
95 /* the following code is used to get text input */
97 volatile static int expired=0;
99 /* return to the previous signal handling */
100 static void reset_alarm(struct sigaction *o_ptr)
102 (void) alarm(0); /* stop alarm clock - if still ticking */
103 (void) sigaction(SIGALRM, o_ptr, NULL);
106 /* this is where we intercept the alarm signal */
107 static void time_is_up(int ignore)
112 /* set the new alarm to hit the time_is_up() function */
113 static int set_alarm(int delay, struct sigaction *o_ptr)
115 struct sigaction new_sig;
117 sigemptyset(&new_sig.sa_mask);
118 new_sig.sa_flags = 0;
119 new_sig.sa_handler = time_is_up;
120 if ( sigaction(SIGALRM, &new_sig, o_ptr) ) {
121 return 1; /* setting signal failed */
123 if ( alarm(delay) ) {
124 (void) sigaction(SIGALRM, o_ptr, NULL);
125 return 1; /* failed to set alarm */
127 return 0; /* all seems to have worked */
130 /* return the number of seconds to next alarm. 0 = no delay, -1 = expired */
131 static int get_delay(void)
135 expired = 0; /* reset flag */
138 /* has the quit time past? */
139 if (pam_misc_conv_die_time && now >= pam_misc_conv_die_time) {
140 fprintf(stderr,"%s",pam_misc_conv_die_line);
142 pam_misc_conv_died = 1; /* note we do not reset the die_time */
143 return -1; /* time is up */
146 /* has the warning time past? */
147 if (pam_misc_conv_warn_time && now >= pam_misc_conv_warn_time) {
148 fprintf(stderr, "%s", pam_misc_conv_warn_line);
149 pam_misc_conv_warn_time = 0; /* reset warn_time */
151 /* indicate remaining delay - if any */
153 return (pam_misc_conv_die_time ? pam_misc_conv_die_time - now:0 );
156 /* indicate possible warning delay */
158 if (pam_misc_conv_warn_time)
159 return (pam_misc_conv_warn_time - now);
160 else if (pam_misc_conv_die_time)
161 return (pam_misc_conv_die_time - now);
166 /* read a line of input string, giving prompt when appropriate */
167 static char *read_string(int echo, const char *prompt)
169 struct termios term_before, term_tmp;
170 char line[INPUTSIZE];
171 struct sigaction old_sig;
172 int delay, nc, have_term=0;
174 D(("called with echo='%s', prompt='%s'.", echo ? "ON":"OFF" , prompt));
176 if (isatty(STDIN_FILENO)) { /* terminal state */
178 /* is a terminal so record settings and flush it */
179 if ( tcgetattr(STDIN_FILENO, &term_before) != 0 ) {
180 D(("<error: failed to get terminal settings>"));
183 memcpy(&term_tmp, &term_before, sizeof(term_tmp));
185 term_tmp.c_lflag |= ECHO;
187 term_tmp.c_lflag &= ~(ECHO);
192 D(("<warning: cannot turn echo off>"));
195 /* set up the signal handling */
198 /* reading the line */
201 fprintf(stderr, "%s", prompt);
202 /* this may, or may not set echo off -- drop pending input */
204 (void) tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_tmp);
206 if ( delay > 0 && set_alarm(delay, &old_sig) ) {
207 D(("<failed to set alarm>"));
210 nc = read(STDIN_FILENO, line, INPUTSIZE-1);
212 (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &term_before);
213 if (!echo || expired) /* do we need a newline? */
214 fprintf(stderr,"\n");
217 reset_alarm(&old_sig);
221 } else if (nc > 0) { /* we got some user input */
224 if (nc > 0 && line[nc-1] == '\n') { /* <NUL> terminate */
229 input = x_strdup(line);
230 _pam_overwrite(line);
232 return input; /* return malloc()ed string */
233 } else if (nc == 0) { /* Ctrl-D */
234 D(("user did not want to type anything"));
235 fprintf(stderr, "\n");
241 /* getting here implies that the timer expired */
243 (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &term_before);
245 memset(line, 0, INPUTSIZE); /* clean up */
249 /* end of read_string functions */
251 int misc_conv(int num_msg, const struct pam_message **msgm,
252 struct pam_response **response, void *appdata_ptr)
255 struct pam_response *reply;
260 D(("allocating empty response structure array."));
262 reply = (struct pam_response *) calloc(num_msg,
263 sizeof(struct pam_response));
265 D(("no memory for responses"));
269 D(("entering conversation function."));
271 for (count=0; count < num_msg; ++count) {
274 switch (msgm[count]->msg_style) {
275 case PAM_PROMPT_ECHO_OFF:
276 string = read_string(CONV_ECHO_OFF,msgm[count]->msg);
277 if (string == NULL) {
278 goto failed_conversation;
281 case PAM_PROMPT_ECHO_ON:
282 string = read_string(CONV_ECHO_ON,msgm[count]->msg);
283 if (string == NULL) {
284 goto failed_conversation;
288 if (fprintf(stderr,"%s\n",msgm[count]->msg) < 0) {
289 goto failed_conversation;
293 if (fprintf(stdout,"%s\n",msgm[count]->msg) < 0) {
294 goto failed_conversation;
297 case PAM_BINARY_PROMPT:
300 const void *pack_in = msgm[count]->msg;
302 if (!pam_binary_handler_fn
303 || pam_binary_handler_fn(pack_in, &pack_out) != PAM_SUCCESS
304 || pack_out == NULL) {
305 goto failed_conversation;
307 string = (char *) pack_out;
314 const void *pack_in = msgm[count]->msg;
315 if (!pam_binary_handler_fn
316 || pam_binary_handler_fn(pack_in, NULL) != PAM_SUCCESS) {
317 goto failed_conversation;
322 fprintf(stderr, "erroneous conversation (%d)\n"
323 ,msgm[count]->msg_style);
324 goto failed_conversation;
327 if (string) { /* must add to reply array */
328 /* add string to list of responses */
330 reply[count].resp_retcode = 0;
331 reply[count].resp = string;
336 /* New (0.59+) behavior is to always have a reply - this is
337 compatable with the X/Open (March 1997) spec. */
346 for (count=0; count<num_msg; ++count) {
347 if (reply[count].resp == NULL) {
350 switch (msgm[count]->msg_style) {
351 case PAM_PROMPT_ECHO_ON:
352 case PAM_PROMPT_ECHO_OFF:
353 _pam_overwrite(reply[count].resp);
354 free(reply[count].resp);
356 case PAM_BINARY_PROMPT:
357 pam_binary_handler_free((void **) &reply[count].resp);
362 /* should not actually be able to get here... */
363 free(reply[count].resp);
365 reply[count].resp = NULL;
367 /* forget reply too */