Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / contrib / libpam / libpam_misc / misc_conv.c
CommitLineData
984263bc
MD
1/*
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 $
1de703da 4 * $DragonFly: src/contrib/libpam/libpam_misc/Attic/misc_conv.c,v 1.2 2003/06/17 04:24:03 dillon Exp $
984263bc
MD
5 *
6 * A generic conversation function for text based applications
7 *
8 * Written by Andrew Morgan <morgan@linux.kernel.org>
9 *
10 * $Log: misc_conv.c,v $
11 * Revision 1.5 1997/01/04 20:16:48 morgan
12 * removed getpass. Replaced with POSIX code for same function which
13 * also observes timeouts specified by the parent application
14 *
15 * Revision 1.4 1996/12/01 03:26:51 morgan
16 * *** empty log message ***
17 *
18 * Revision 1.3 1996/11/10 20:10:01 morgan
19 * sgi definition
20 *
21 * Revision 1.2 1996/07/07 23:59:56 morgan
22 * changed the name of the misc include file
23 *
24 * Revision 1.1 1996/05/02 05:17:06 morgan
25 * Initial revision
26 */
27
28#ifdef linux
29#define _GNU_SOURCE
30#include <features.h>
31#endif
32
33#include <signal.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <sys/types.h>
38#include <termios.h>
39#include <time.h>
40#include <unistd.h>
41
42#include <security/pam_appl.h>
43#include <security/pam_misc.h>
44
45#define INPUTSIZE PAM_MAX_MSG_SIZE /* maximum length of input+1 */
46#define CONV_ECHO_ON 1 /* types of echo state */
47#define CONV_ECHO_OFF 0
48
49/*
50 * external timeout definitions - these can be overriden by the
51 * application.
52 */
53
54time_t pam_misc_conv_warn_time = 0; /* time when we warn */
55time_t pam_misc_conv_die_time = 0; /* time when we timeout */
56
57const char *pam_misc_conv_warn_line = "..\a.Time is running out...\n";
58const char *pam_misc_conv_die_line = "..\a.Sorry, your time is up!\n";
59
60int pam_misc_conv_died=0; /* application can probe this for timeout */
61
62static void pam_misc_conv_delete_binary(void **delete_me)
63{
64 if (delete_me && *delete_me) {
65 unsigned char *packet = *(unsigned char **)delete_me;
66 int length;
67
68 length = 4+(packet[0]<<24)+(packet[1]<<16)+(packet[2]<<8)+packet[3];
69 memset(packet, 0, length);
70 free(packet);
71 *delete_me = packet = NULL;
72 }
73}
74
75/* These function pointers are for application specific binary
76 conversations. One or both of the arguments to the first function
77 must be non-NULL. The first function must return PAM_SUCCESS or
78 PAM_CONV_ERR. If input is non-NULL, a response is expected, this
79 response should be malloc()'d and will eventually be free()'d by
80 the calling module. The structure of this malloc()'d response is as
81 follows:
82
83 { int length, char data[length] }
84
85 For convenience, the pointer used by the two function pointer
86 prototypes is 'void *'.
87
88 The ...free() fn pointer is used to discard a binary message that
89 is not of the default form. It should be explicitly overwritten
90 when using some other convention for the structure of a binary
91 prompt (not recommended). */
92
93int (*pam_binary_handler_fn)(const void *send, void **receive) = NULL;
94void (*pam_binary_handler_free)(void **packet_p) = pam_misc_conv_delete_binary;
95
96/* the following code is used to get text input */
97
98volatile static int expired=0;
99
100/* return to the previous signal handling */
101static void reset_alarm(struct sigaction *o_ptr)
102{
103 (void) alarm(0); /* stop alarm clock - if still ticking */
104 (void) sigaction(SIGALRM, o_ptr, NULL);
105}
106
107/* this is where we intercept the alarm signal */
108static void time_is_up(int ignore)
109{
110 expired = 1;
111}
112
113/* set the new alarm to hit the time_is_up() function */
114static int set_alarm(int delay, struct sigaction *o_ptr)
115{
116 struct sigaction new_sig;
117
118 sigemptyset(&new_sig.sa_mask);
119 new_sig.sa_flags = 0;
120 new_sig.sa_handler = time_is_up;
121 if ( sigaction(SIGALRM, &new_sig, o_ptr) ) {
122 return 1; /* setting signal failed */
123 }
124 if ( alarm(delay) ) {
125 (void) sigaction(SIGALRM, o_ptr, NULL);
126 return 1; /* failed to set alarm */
127 }
128 return 0; /* all seems to have worked */
129}
130
131/* return the number of seconds to next alarm. 0 = no delay, -1 = expired */
132static int get_delay(void)
133{
134 time_t now;
135
136 expired = 0; /* reset flag */
137 (void) time(&now);
138
139 /* has the quit time past? */
140 if (pam_misc_conv_die_time && now >= pam_misc_conv_die_time) {
141 fprintf(stderr,"%s",pam_misc_conv_die_line);
142
143 pam_misc_conv_died = 1; /* note we do not reset the die_time */
144 return -1; /* time is up */
145 }
146
147 /* has the warning time past? */
148 if (pam_misc_conv_warn_time && now >= pam_misc_conv_warn_time) {
149 fprintf(stderr, "%s", pam_misc_conv_warn_line);
150 pam_misc_conv_warn_time = 0; /* reset warn_time */
151
152 /* indicate remaining delay - if any */
153
154 return (pam_misc_conv_die_time ? pam_misc_conv_die_time - now:0 );
155 }
156
157 /* indicate possible warning delay */
158
159 if (pam_misc_conv_warn_time)
160 return (pam_misc_conv_warn_time - now);
161 else if (pam_misc_conv_die_time)
162 return (pam_misc_conv_die_time - now);
163 else
164 return 0;
165}
166
167/* read a line of input string, giving prompt when appropriate */
168static char *read_string(int echo, const char *prompt)
169{
170 struct termios term_before, term_tmp;
171 char line[INPUTSIZE];
172 struct sigaction old_sig;
173 int delay, nc, have_term=0;
174
175 D(("called with echo='%s', prompt='%s'.", echo ? "ON":"OFF" , prompt));
176
177 if (isatty(STDIN_FILENO)) { /* terminal state */
178
179 /* is a terminal so record settings and flush it */
180 if ( tcgetattr(STDIN_FILENO, &term_before) != 0 ) {
181 D(("<error: failed to get terminal settings>"));
182 return NULL;
183 }
184 memcpy(&term_tmp, &term_before, sizeof(term_tmp));
185 if (echo) {
186 term_tmp.c_lflag |= ECHO;
187 } else {
188 term_tmp.c_lflag &= ~(ECHO);
189 }
190 have_term = 1;
191
192 } else if (!echo) {
193 D(("<warning: cannot turn echo off>"));
194 }
195
196 /* set up the signal handling */
197 delay = get_delay();
198
199 /* reading the line */
200 while (delay >= 0) {
201
202 fprintf(stderr, "%s", prompt);
203 /* this may, or may not set echo off -- drop pending input */
204 if (have_term)
205 (void) tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_tmp);
206
207 if ( delay > 0 && set_alarm(delay, &old_sig) ) {
208 D(("<failed to set alarm>"));
209 break;
210 } else {
211 nc = read(STDIN_FILENO, line, INPUTSIZE-1);
212 if (have_term) {
213 (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &term_before);
214 if (!echo || expired) /* do we need a newline? */
215 fprintf(stderr,"\n");
216 }
217 if ( delay > 0 ) {
218 reset_alarm(&old_sig);
219 }
220 if (expired) {
221 delay = get_delay();
222 } else if (nc > 0) { /* we got some user input */
223 char *input;
224
225 if (nc > 0 && line[nc-1] == '\n') { /* <NUL> terminate */
226 line[--nc] = '\0';
227 } else {
228 line[nc] = '\0';
229 }
230 input = x_strdup(line);
231 _pam_overwrite(line);
232
233 return input; /* return malloc()ed string */
234 } else if (nc == 0) { /* Ctrl-D */
235 D(("user did not want to type anything"));
236 fprintf(stderr, "\n");
237 break;
238 }
239 }
240 }
241
242 /* getting here implies that the timer expired */
243 if (have_term)
244 (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &term_before);
245
246 memset(line, 0, INPUTSIZE); /* clean up */
247 return NULL;
248}
249
250/* end of read_string functions */
251
252int misc_conv(int num_msg, const struct pam_message **msgm,
253 struct pam_response **response, void *appdata_ptr)
254{
255 int count=0;
256 struct pam_response *reply;
257
258 if (num_msg <= 0)
259 return PAM_CONV_ERR;
260
261 D(("allocating empty response structure array."));
262
263 reply = (struct pam_response *) calloc(num_msg,
264 sizeof(struct pam_response));
265 if (reply == NULL) {
266 D(("no memory for responses"));
267 return PAM_CONV_ERR;
268 }
269
270 D(("entering conversation function."));
271
272 for (count=0; count < num_msg; ++count) {
273 char *string=NULL;
274
275 switch (msgm[count]->msg_style) {
276 case PAM_PROMPT_ECHO_OFF:
277 string = read_string(CONV_ECHO_OFF,msgm[count]->msg);
278 if (string == NULL) {
279 goto failed_conversation;
280 }
281 break;
282 case PAM_PROMPT_ECHO_ON:
283 string = read_string(CONV_ECHO_ON,msgm[count]->msg);
284 if (string == NULL) {
285 goto failed_conversation;
286 }
287 break;
288 case PAM_ERROR_MSG:
289 if (fprintf(stderr,"%s\n",msgm[count]->msg) < 0) {
290 goto failed_conversation;
291 }
292 break;
293 case PAM_TEXT_INFO:
294 if (fprintf(stdout,"%s\n",msgm[count]->msg) < 0) {
295 goto failed_conversation;
296 }
297 break;
298 case PAM_BINARY_PROMPT:
299 {
300 void *pack_out=NULL;
301 const void *pack_in = msgm[count]->msg;
302
303 if (!pam_binary_handler_fn
304 || pam_binary_handler_fn(pack_in, &pack_out) != PAM_SUCCESS
305 || pack_out == NULL) {
306 goto failed_conversation;
307 }
308 string = (char *) pack_out;
309 pack_out = NULL;
310
311 break;
312 }
313 case PAM_BINARY_MSG:
314 {
315 const void *pack_in = msgm[count]->msg;
316 if (!pam_binary_handler_fn
317 || pam_binary_handler_fn(pack_in, NULL) != PAM_SUCCESS) {
318 goto failed_conversation;
319 }
320 break;
321 }
322 default:
323 fprintf(stderr, "erroneous conversation (%d)\n"
324 ,msgm[count]->msg_style);
325 goto failed_conversation;
326 }
327
328 if (string) { /* must add to reply array */
329 /* add string to list of responses */
330
331 reply[count].resp_retcode = 0;
332 reply[count].resp = string;
333 string = NULL;
334 }
335 }
336
337 /* New (0.59+) behavior is to always have a reply - this is
338 compatable with the X/Open (March 1997) spec. */
339 *response = reply;
340 reply = NULL;
341
342 return PAM_SUCCESS;
343
344failed_conversation:
345
346 if (reply) {
347 for (count=0; count<num_msg; ++count) {
348 if (reply[count].resp == NULL) {
349 continue;
350 }
351 switch (msgm[count]->msg_style) {
352 case PAM_PROMPT_ECHO_ON:
353 case PAM_PROMPT_ECHO_OFF:
354 _pam_overwrite(reply[count].resp);
355 free(reply[count].resp);
356 break;
357 case PAM_BINARY_PROMPT:
358 pam_binary_handler_free((void **) &reply[count].resp);
359 break;
360 case PAM_ERROR_MSG:
361 case PAM_TEXT_INFO:
362 case PAM_BINARY_MSG:
363 /* should not actually be able to get here... */
364 free(reply[count].resp);
365 }
366 reply[count].resp = NULL;
367 }
368 /* forget reply too */
369 free(reply);
370 reply = NULL;
371 }
372
373 return PAM_CONV_ERR;
374}
375