Changeset 687 in openpam for trunk/lib/libpam
- Timestamp:
- Jul 11, 2013, 4:37:25 PM (8 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/lib/libpam/openpam_ttyconv.c
r667 r687 41 41 42 42 #include <sys/types.h> 43 #include <sys/poll.h> 44 #include <sys/time.h> 43 45 44 46 #include <errno.h> 45 #include < setjmp.h>47 #include <fcntl.h> 46 48 #include <signal.h> 47 49 #include <stdio.h> … … 57 59 int openpam_ttyconv_timeout = 0; 58 60 61 volatile sig_atomic_t caught_signal; 62 63 /* 64 * Handle incoming signals during tty conversation 65 */ 59 66 static void 60 timeout(int sig)67 catch_signal(int signo) 61 68 { 62 69 63 (void)sig; 70 switch (signo) { 71 case SIGINT: 72 case SIGQUIT: 73 case SIGTERM: 74 caught_signal = signo; 75 break; 76 } 64 77 } 65 78 66 static char * 67 prompt(const char *msg) 79 /* 80 * Accept a response from the user on a tty 81 */ 82 static int 83 prompt_tty(int ifd, int ofd, const char *message, char *response, int echo) 68 84 { 69 char buf[PAM_MAX_RESP_SIZE]; 70 struct sigaction action, saved_action; 71 sigset_t saved_sigset, the_sigset; 72 unsigned int saved_alarm; 73 int eof, error, fd; 74 size_t len; 75 char *retval; 85 struct sigaction action; 86 struct sigaction saction_sigint, saction_sigquit, saction_sigterm; 87 struct termios tcattr; 88 struct timeval now, target, remaining; 89 int remaining_ms; 90 tcflag_t slflag; 91 struct pollfd pfd; 92 int serrno; 93 int pos, ret; 76 94 char ch; 77 95 78 sigemptyset(&the_sigset); 79 sigaddset(&the_sigset, SIGINT); 80 sigaddset(&the_sigset, SIGTSTP); 81 sigprocmask(SIG_SETMASK, &the_sigset, &saved_sigset); 82 action.sa_handler = &timeout; 96 /* write prompt */ 97 if (write(ofd, message, strlen(message)) < 0) { 98 openpam_log(PAM_LOG_ERROR, "write(): %m"); 99 return (-1); 100 } 101 102 /* turn echo off if requested */ 103 slflag = 0; /* prevent bogus uninitialized variable warning */ 104 if (!echo) { 105 if (tcgetattr(ifd, &tcattr) != 0) { 106 openpam_log(PAM_LOG_ERROR, "tcgetattr(): %m"); 107 return (-1); 108 } 109 slflag = tcattr.c_lflag; 110 tcattr.c_lflag &= ~ECHO; 111 if (tcsetattr(ifd, TCSAFLUSH, &tcattr) != 0) { 112 openpam_log(PAM_LOG_ERROR, "tcsetattr(): %m"); 113 return (-1); 114 } 115 } 116 117 /* install signal handlers */ 118 caught_signal = 0; 119 action.sa_handler = &catch_signal; 83 120 action.sa_flags = 0; 84 sigemptyset(&action.sa_mask); 85 sigaction(SIGALRM, &action, &saved_action); 86 fputs(msg, stdout); 121 sigfillset(&action.sa_mask); 122 sigaction(SIGINT, &action, &saction_sigint); 123 sigaction(SIGQUIT, &action, &saction_sigquit); 124 sigaction(SIGTERM, &action, &saction_sigterm); 125 126 /* compute timeout */ 127 if (openpam_ttyconv_timeout > 0) { 128 (void)gettimeofday(&now, NULL); 129 remaining.tv_sec = openpam_ttyconv_timeout; 130 remaining.tv_usec = 0; 131 timeradd(&now, &remaining, &target); 132 } else { 133 /* prevent bogus uninitialized variable warning */ 134 now.tv_sec = now.tv_usec = 0; 135 remaining.tv_sec = remaining.tv_usec = 0; 136 target.tv_sec = target.tv_usec = 0; 137 } 138 139 /* input loop */ 140 pos = 0; 141 ret = -1; 142 serrno = 0; 143 while (!caught_signal) { 144 pfd.fd = ifd; 145 pfd.events = POLLIN; 146 pfd.revents = 0; 147 if (openpam_ttyconv_timeout > 0) { 148 gettimeofday(&now, NULL); 149 if (timercmp(&now, &target, >)) 150 break; 151 timersub(&target, &now, &remaining); 152 remaining_ms = remaining.tv_sec * 1000 + 153 remaining.tv_usec / 1000; 154 } else { 155 remaining_ms = INFTIM; 156 } 157 if ((ret = poll(&pfd, 1, remaining_ms)) < 0) { 158 serrno = errno; 159 if (errno == EINTR) 160 continue; 161 openpam_log(PAM_LOG_ERROR, "poll(): %m"); 162 break; 163 } else if (ret == 0) { 164 /* timeout */ 165 write(ofd, " timed out", 10); 166 openpam_log(PAM_LOG_NOTICE, "timed out"); 167 break; 168 } 169 if ((ret = read(ifd, &ch, 1)) < 0) { 170 serrno = errno; 171 openpam_log(PAM_LOG_ERROR, "read(): %m"); 172 break; 173 } else if (ret == 0 || ch == '\n') { 174 response[pos] = '\0'; 175 ret = pos; 176 break; 177 } 178 if (pos + 1 < PAM_MAX_RESP_SIZE) 179 response[pos++] = ch; 180 /* overflow is discarded */ 181 } 182 183 /* restore tty state */ 184 if (!echo) { 185 tcattr.c_lflag = slflag; 186 if (tcsetattr(ifd, 0, &tcattr) != 0) { 187 /* treat as non-fatal, since we have our answer */ 188 openpam_log(PAM_LOG_NOTICE, "tcsetattr(): %m"); 189 } 190 } 191 192 /* restore signal handlers and re-post caught signal*/ 193 sigaction(SIGINT, &saction_sigint, NULL); 194 sigaction(SIGQUIT, &saction_sigquit, NULL); 195 sigaction(SIGTERM, &saction_sigterm, NULL); 196 if (caught_signal != 0) { 197 openpam_log(PAM_LOG_ERROR, "caught signal %d", 198 (int)caught_signal); 199 raise((int)caught_signal); 200 /* if raise() had no effect... */ 201 serrno = EINTR; 202 ret = -1; 203 } 204 205 /* done */ 206 write(ofd, "\n", 1); 207 errno = serrno; 208 return (ret); 209 } 210 211 /* 212 * Accept a response from the user on a non-tty stdin. 213 */ 214 static int 215 prompt_notty(const char *message, char *response) 216 { 217 struct timeval now, target, remaining; 218 int remaining_ms; 219 struct pollfd pfd; 220 int ch, pos, ret; 221 222 /* show prompt */ 223 fputs(message, stdout); 87 224 fflush(stdout); 225 226 /* compute timeout */ 227 if (openpam_ttyconv_timeout > 0) { 228 (void)gettimeofday(&now, NULL); 229 remaining.tv_sec = openpam_ttyconv_timeout; 230 remaining.tv_usec = 0; 231 timeradd(&now, &remaining, &target); 232 } else { 233 /* prevent bogus uninitialized variable warning */ 234 now.tv_sec = now.tv_usec = 0; 235 remaining.tv_sec = remaining.tv_usec = 0; 236 target.tv_sec = target.tv_usec = 0; 237 } 238 239 /* input loop */ 240 pos = 0; 241 for (;;) { 242 pfd.fd = STDIN_FILENO; 243 pfd.events = POLLIN; 244 pfd.revents = 0; 245 if (openpam_ttyconv_timeout > 0) { 246 gettimeofday(&now, NULL); 247 if (timercmp(&now, &target, >)) 248 break; 249 timersub(&target, &now, &remaining); 250 remaining_ms = remaining.tv_sec * 1000 + 251 remaining.tv_usec / 1000; 252 } else { 253 remaining_ms = INFTIM; 254 } 255 if ((ret = poll(&pfd, 1, remaining_ms)) < 0) { 256 /* interrupt is ok, everything else -> bail */ 257 if (errno == EINTR) 258 continue; 259 perror("\nopenpam_ttyconv"); 260 return (-1); 261 } else if (ret == 0) { 262 /* timeout */ 263 break; 264 } else { 265 /* input */ 266 if ((ch = getchar()) == EOF && ferror(stdin)) { 267 perror("\nopenpam_ttyconv"); 268 return (-1); 269 } 270 if (ch == EOF || ch == '\n') { 271 response[pos] = '\0'; 272 return (pos); 273 } 274 if (pos + 1 < PAM_MAX_RESP_SIZE) 275 response[pos++] = ch; 276 /* overflow is discarded */ 277 } 278 } 279 fputs("\nopenpam_ttyconv: timeout\n", stderr); 280 return (-1); 281 } 282 283 /* 284 * Determine whether stdin is a tty; if not, try to open the tty; in 285 * either case, call the appropriate method. 286 */ 287 static int 288 prompt(const char *message, char *response, int echo) 289 { 290 int ifd, ofd, ret; 291 292 if (isatty(STDIN_FILENO)) { 293 fflush(stdout); 88 294 #ifdef HAVE_FPURGE 89 fpurge(stdin);295 fpurge(stdin); 90 296 #endif 91 fd = fileno(stdin); 92 buf[0] = '\0'; 93 eof = error = 0; 94 saved_alarm = 0; 95 if (openpam_ttyconv_timeout >= 0) 96 saved_alarm = alarm(openpam_ttyconv_timeout); 97 ch = '\0'; 98 for (len = 0; ch != '\n' && !eof && !error; ++len) { 99 switch (read(fd, &ch, 1)) { 100 case 1: 101 if (len < PAM_MAX_RESP_SIZE - 1) { 102 buf[len + 1] = '\0'; 103 buf[len] = ch; 104 } 105 break; 106 case 0: 107 eof = 1; 108 break; 109 default: 110 error = errno; 111 break; 112 } 113 } 114 if (openpam_ttyconv_timeout >= 0) 115 alarm(0); 116 sigaction(SIGALRM, &saved_action, NULL); 117 sigprocmask(SIG_SETMASK, &saved_sigset, NULL); 118 if (saved_alarm > 0) 119 alarm(saved_alarm); 120 if (error == EINTR) 121 fputs(" timeout!", stderr); 122 if (error || eof) { 123 fputs("\n", stderr); 124 memset(buf, 0, sizeof(buf)); 125 return (NULL); 126 } 127 /* trim trailing whitespace */ 128 for (len = strlen(buf); len > 0; --len) 129 if (buf[len - 1] != '\r' && buf[len - 1] != '\n') 130 break; 131 buf[len] = '\0'; 132 retval = strdup(buf); 133 memset(buf, 0, sizeof(buf)); 134 return (retval); 135 } 136 137 static char * 138 prompt_echo_off(const char *msg) 139 { 140 struct termios tattr; 141 tcflag_t lflag; 142 char *ret; 143 int fd; 144 145 fd = fileno(stdin); 146 if (tcgetattr(fd, &tattr) != 0) { 147 openpam_log(PAM_LOG_ERROR, "tcgetattr(): %m"); 148 return (NULL); 149 } 150 lflag = tattr.c_lflag; 151 tattr.c_lflag &= ~ECHO; 152 if (tcsetattr(fd, TCSAFLUSH, &tattr) != 0) { 153 openpam_log(PAM_LOG_ERROR, "tcsetattr(): %m"); 154 return (NULL); 155 } 156 ret = prompt(msg); 157 tattr.c_lflag = lflag; 158 (void)tcsetattr(fd, TCSANOW, &tattr); 159 if (ret != NULL) 160 fputs("\n", stdout); 297 ifd = STDIN_FILENO; 298 ofd = STDOUT_FILENO; 299 } else { 300 if ((ifd = open("/dev/tty", O_RDWR)) < 0) 301 /* no way to prevent echo */ 302 return (prompt_notty(message, response)); 303 ofd = ifd; 304 } 305 ret = prompt_tty(ifd, ofd, message, response, echo); 306 if (ifd != STDIN_FILENO) 307 close(ifd); 161 308 return (ret); 162 309 } … … 174 321 void *data) 175 322 { 323 char respbuf[PAM_MAX_RESP_SIZE]; 176 324 struct pam_response *aresp; 177 325 int i; … … 188 336 switch (msg[i]->msg_style) { 189 337 case PAM_PROMPT_ECHO_OFF: 190 aresp[i].resp = prompt_echo_off(msg[i]->msg);191 if (aresp[i].resp== NULL)338 if (prompt(msg[i]->msg, respbuf, 0) < 0 || 339 (aresp[i].resp = strdup(respbuf)) == NULL) 192 340 goto fail; 193 341 break; 194 342 case PAM_PROMPT_ECHO_ON: 195 aresp[i].resp = prompt(msg[i]->msg);196 if (aresp[i].resp== NULL)343 if (prompt(msg[i]->msg, respbuf, 1) < 0 || 344 (aresp[i].resp = strdup(respbuf)) == NULL) 197 345 goto fail; 198 346 break; … … 214 362 } 215 363 *resp = aresp; 364 memset(respbuf, 0, sizeof respbuf); 216 365 RETURNC(PAM_SUCCESS); 217 366 fail: … … 225 374 FREE(aresp); 226 375 *resp = NULL; 376 memset(respbuf, 0, sizeof respbuf); 227 377 RETURNC(PAM_CONV_ERR); 228 378 }
Note: See TracChangeset
for help on using the changeset viewer.