00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <config.h>
00021
00022 #include "sbuild-auth-conv-tty.h"
00023 #include "sbuild-log.h"
00024
00025 #include <iostream>
00026
00027 #include <signal.h>
00028 #include <termios.h>
00029 #include <unistd.h>
00030
00031 #include <boost/format.hpp>
00032
00033 using std::cerr;
00034 using std::endl;
00035 using boost::format;
00036 using namespace sbuild;
00037
00038 namespace
00039 {
00040
00041 typedef std::pair<auth_conv_tty::error_code,const char *> emap;
00042
00047 emap init_errors[] =
00048 {
00049 emap(auth_conv_tty::TIMEOUT, N_("Timed out")),
00050
00051 emap(auth_conv_tty::TIMEOUT_PENDING, N_("Time is running out...")),
00052 emap(auth_conv_tty::TERMIOS, N_("Failed to get terminal settings")),
00053
00054 emap(auth_conv_tty::CONV_TYPE, N_("Unsupported conversation type '%1%'"))
00055 };
00056
00057 volatile sig_atomic_t timer_expired = false;
00058
00064 void
00065 reset_alarm (struct sigaction *orig_sa)
00066 {
00067
00068 alarm (0);
00069
00070 sigaction (SIGALRM, orig_sa, 0);
00071 }
00072
00078 void
00079 alarm_handler (int ignore)
00080 {
00081 timer_expired = true;
00082 }
00083
00092 bool
00093 set_alarm (int delay,
00094 struct sigaction *orig_sa)
00095 {
00096 struct sigaction new_sa;
00097 sigemptyset(&new_sa.sa_mask);
00098 new_sa.sa_flags = 0;
00099 new_sa.sa_handler = alarm_handler;
00100
00101 if (sigaction(SIGALRM, &new_sa, orig_sa) != 0)
00102 {
00103 return false;
00104 }
00105 if (alarm(delay) != 0)
00106 {
00107 sigaction(SIGALRM, orig_sa, 0);
00108 return false;
00109 }
00110
00111 return true;
00112 }
00113
00114 }
00115
00116 template<>
00117 error<auth_conv_tty::error_code>::map_type
00118 error<auth_conv_tty::error_code>::error_strings
00119 (init_errors,
00120 init_errors + (sizeof(init_errors) / sizeof(init_errors[0])));
00121
00122 auth_conv_tty::auth_conv_tty ():
00123 warning_timeout(0),
00124 fatal_timeout(0),
00125 start_time(0)
00126 {
00127 }
00128
00129 auth_conv_tty::~auth_conv_tty ()
00130 {
00131 }
00132
00133 time_t
00134 auth_conv_tty::get_warning_timeout ()
00135 {
00136 return this->warning_timeout;
00137 }
00138
00139 void
00140 auth_conv_tty::set_warning_timeout (time_t timeout)
00141 {
00142 this->warning_timeout = timeout;
00143 }
00144
00145 time_t
00146 auth_conv_tty::get_fatal_timeout ()
00147 {
00148 return this->fatal_timeout;
00149 }
00150
00151 void
00152 auth_conv_tty::set_fatal_timeout (time_t timeout)
00153 {
00154 this->fatal_timeout = timeout;
00155 }
00156
00157 int
00158 auth_conv_tty::get_delay ()
00159 {
00160 timer_expired = 0;
00161 time (&this->start_time);
00162
00163 if (this->fatal_timeout != 0 &&
00164 this->start_time >= this->fatal_timeout)
00165 throw error(TIMEOUT);
00166
00167 if (this->warning_timeout != 0 &&
00168 this->start_time >= this->warning_timeout)
00169 {
00170 error e(TIMEOUT_PENDING);
00171 log_exception_warning(e);
00172 return (this->fatal_timeout ?
00173 this->fatal_timeout - this->start_time : 0);
00174 }
00175
00176 if (this->warning_timeout != 0)
00177 return this->warning_timeout - this->start_time;
00178 else if (this->fatal_timeout != 0)
00179 return this->fatal_timeout - this->start_time;
00180 else
00181 return 0;
00182 }
00183
00184 std::string
00185 auth_conv_tty::read_string (std::string message,
00186 bool echo)
00187 {
00188
00189
00190 struct termios orig_termios, noecho_termios;
00191 struct sigaction saved_signals;
00192 sigset_t old_sigs, new_sigs;
00193 bool use_termios = false;
00194 std::string retval;
00195
00196 if (isatty(STDIN_FILENO))
00197 {
00198 use_termios = true;
00199
00200 if (tcgetattr(STDIN_FILENO, &orig_termios) != 0)
00201 throw error(TERMIOS);
00202
00203 memcpy(&noecho_termios, &orig_termios, sizeof(struct termios));
00204
00205 if (echo == false)
00206 noecho_termios.c_lflag &= ~(ECHO);
00207
00208 sigemptyset(&new_sigs);
00209 sigaddset(&new_sigs, SIGINT);
00210 sigaddset(&new_sigs, SIGTSTP);
00211 sigprocmask(SIG_BLOCK, &new_sigs, &old_sigs);
00212 }
00213
00214 char input[PAM_MAX_MSG_SIZE];
00215
00216 int delay = get_delay();
00217
00218 while (delay >= 0)
00219 {
00220 cerr << message;
00221
00222 if (use_termios == true)
00223 tcsetattr(STDIN_FILENO, TCSAFLUSH, &noecho_termios);
00224
00225 if (delay > 0 && set_alarm(delay, &saved_signals) == false)
00226 break;
00227 else
00228 {
00229 int nchars = read(STDIN_FILENO, input, PAM_MAX_MSG_SIZE - 1);
00230 if (use_termios)
00231 {
00232 tcsetattr(STDIN_FILENO, TCSADRAIN, &orig_termios);
00233 if (echo == false && timer_expired == true)
00234 cerr << endl;
00235 }
00236 if (delay > 0)
00237 reset_alarm(&saved_signals);
00238 if (timer_expired == true)
00239 {
00240 delay = get_delay();
00241 }
00242 else if (nchars > 0)
00243 {
00244 if (echo == false)
00245 cerr << endl;
00246
00247 if (input[nchars-1] == '\n')
00248 input[--nchars] = '\0';
00249 else
00250 input[nchars] = '\0';
00251
00252 retval = input;
00253 break;
00254 }
00255 else if (nchars == 0)
00256 {
00257 if (echo == false)
00258 cerr << endl;
00259
00260 retval = "";
00261 break;
00262 }
00263 }
00264 }
00265
00266 memset(input, 0, sizeof(input));
00267
00268 if (use_termios == true)
00269 {
00270 sigprocmask(SIG_SETMASK, &old_sigs, 0);
00271 tcsetattr(STDIN_FILENO, TCSADRAIN, &orig_termios);
00272 }
00273
00274 return retval;
00275 }
00276
00277 void
00278 auth_conv_tty::conversation (auth_conv::message_list& messages)
00279 {
00280 log_debug(DEBUG_NOTICE) << "PAM TTY conversation handler started" << endl;
00281
00282 for (std::vector<auth_message>::iterator cur = messages.begin();
00283 cur != messages.end();
00284 ++cur)
00285 {
00286 switch (cur->type)
00287 {
00288 case auth_message::MESSAGE_PROMPT_NOECHO:
00289 log_debug(DEBUG_NOTICE) << "PAM TTY input prompt (noecho)" << endl;
00290 cur->response = read_string(cur->message, false);
00291 break;
00292 case auth_message::MESSAGE_PROMPT_ECHO:
00293 log_debug(DEBUG_NOTICE) << "PAM TTY input prompt (echo)" << endl;
00294 cur->response = read_string(cur->message, true);
00295 break;
00296 case auth_message::MESSAGE_ERROR:
00297 log_debug(DEBUG_NOTICE) << "PAM TTY output error" << endl;
00298
00299 log_error() << cur->message << endl;
00300 break;
00301 case auth_message::MESSAGE_INFO:
00302 log_debug(DEBUG_NOTICE) << "PAM TTY output info" << endl;
00303
00304 log_info() << cur->message << endl;
00305 break;
00306 default:
00307 throw error(cur->type, CONV_TYPE);
00308 break;
00309 }
00310 }
00311
00312 log_debug(DEBUG_NOTICE) << "PAM TTY conversation handler ended" << endl;
00313 }