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