source: trunk/debathena/third/schroot/sbuild/sbuild-auth-pam-conv-tty.cc @ 24167

Revision 24167, 7.5 KB checked in by broder, 15 years ago (diff)
Import schroot upstream into subversion.
Line 
1/* Copyright © 2005-2007  Roger Leigh <rleigh@debian.org>
2 *
3 * schroot is free software: you can redistribute it and/or modify it
4 * under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * schroot is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program.  If not, see
15 * <http://www.gnu.org/licenses/>.
16 *
17 *********************************************************************/
18
19#include <config.h>
20
21#include "sbuild-auth-pam-conv-tty.h"
22#include "sbuild-ctty.h"
23#include "sbuild-log.h"
24
25#include <iostream>
26
27#include <signal.h>
28#include <termios.h>
29#include <unistd.h>
30
31#include <boost/format.hpp>
32
33using std::endl;
34using boost::format;
35using namespace sbuild;
36
37namespace
38{
39
40  typedef std::pair<auth_pam_conv_tty::error_code,const char *> emap;
41
42  /**
43   * This is a list of the supported error codes.  It's used to
44   * construct the real error codes map.
45   */
46  emap init_errors[] =
47    {
48      emap(auth_pam_conv_tty::CTTY,            N_("No controlling terminal")),
49      emap(auth_pam_conv_tty::TIMEOUT,         N_("Timed out")),
50      // TRANSLATORS: Please use an ellipsis e.g. U+2026
51      emap(auth_pam_conv_tty::TIMEOUT_PENDING, N_("Time is running out...")),
52      emap(auth_pam_conv_tty::TERMIOS,         N_("Failed to get terminal settings")),
53      // TRANSLATORS: %1% = integer
54      emap(auth_pam_conv_tty::CONV_TYPE,       N_("Unsupported conversation type '%1%'"))
55    };
56
57  volatile sig_atomic_t timer_expired = false;
58
59  /**
60   * Disable the alarm and signal handler.
61   *
62   * @param orig_sa the signal handler to restore.
63   */
64  void
65  reset_alarm (struct sigaction *orig_sa)
66  {
67    // Stop alarm
68    alarm (0);
69    // Restore original handler
70    sigaction (SIGALRM, orig_sa, 0);
71  }
72
73  /**
74   * Handle the SIGALRM signal.
75   *
76   * @param ignore the signal number (unused).
77   */
78  void
79  alarm_handler (int ignore)
80  {
81    timer_expired = true;
82  }
83
84  /**
85   * Set the SIGALARM handler, and set the timeout to delay seconds.
86   * The old signal handler is stored in orig_sa.
87   *
88   * @param delay the delay (in seconds) before SIGALRM is raised.
89   * @param orig_sa the location to store the original signal handler.
90   * @returns true on success, false on failure.
91   */
92  bool
93  set_alarm (int delay,
94             struct sigaction *orig_sa)
95  {
96    struct sigaction new_sa;
97    sigemptyset(&new_sa.sa_mask);
98    new_sa.sa_flags = 0;
99    new_sa.sa_handler = alarm_handler;
100
101    if (sigaction(SIGALRM, &new_sa, orig_sa) != 0)
102      {
103        return false;
104      }
105    if (alarm(delay) != 0)
106      {
107        sigaction(SIGALRM, orig_sa, 0);
108        return false;
109      }
110
111    return true;
112  }
113
114}
115
116template<>
117error<auth_pam_conv_tty::error_code>::map_type
118error<auth_pam_conv_tty::error_code>::error_strings
119(init_errors,
120 init_errors + (sizeof(init_errors) / sizeof(init_errors[0])));
121
122auth_pam_conv_tty::auth_pam_conv_tty (auth_ptr auth):
123  auth(weak_auth_ptr(auth)),
124  warning_timeout(0),
125  fatal_timeout(0),
126  start_time(0)
127{
128}
129
130auth_pam_conv_tty::~auth_pam_conv_tty ()
131{
132}
133
134auth_pam_conv::ptr
135auth_pam_conv_tty::create (auth_ptr auth)
136{
137  return ptr(new auth_pam_conv_tty(auth));
138}
139
140auth_pam_conv::auth_ptr
141auth_pam_conv_tty::get_auth ()
142{
143  return auth_ptr(this->auth);
144}
145
146void
147auth_pam_conv_tty::set_auth (auth_ptr auth)
148{
149  this->auth = weak_auth_ptr(auth);
150}
151
152time_t
153auth_pam_conv_tty::get_warning_timeout ()
154{
155  return this->warning_timeout;
156}
157
158void
159auth_pam_conv_tty::set_warning_timeout (time_t timeout)
160{
161  this->warning_timeout = timeout;
162}
163
164time_t
165auth_pam_conv_tty::get_fatal_timeout ()
166{
167  return this->fatal_timeout;
168}
169
170void
171auth_pam_conv_tty::set_fatal_timeout (time_t timeout)
172{
173  this->fatal_timeout = timeout;
174}
175
176int
177auth_pam_conv_tty::get_delay ()
178{
179  timer_expired = 0;
180  time (&this->start_time);
181
182  if (this->fatal_timeout != 0 &&
183      this->start_time >= this->fatal_timeout)
184    throw error(TIMEOUT);
185
186  if (this->warning_timeout != 0 &&
187      this->start_time >= this->warning_timeout)
188    {
189      error e(TIMEOUT_PENDING);
190      log_ctty_exception_warning(e);
191      return (this->fatal_timeout ?
192              this->fatal_timeout - this->start_time : 0);
193    }
194
195  if (this->warning_timeout != 0)
196    return this->warning_timeout - this->start_time;
197  else if (this->fatal_timeout != 0)
198    return this->fatal_timeout - this->start_time;
199  else
200    return 0;
201}
202
203std::string
204auth_pam_conv_tty::read_string (std::string message,
205                                bool        echo)
206{
207  if (CTTY_FILENO < 0)
208    throw error(CTTY);
209
210  struct termios orig_termios, noecho_termios;
211  struct sigaction saved_signals;
212  sigset_t old_sigs, new_sigs;
213  bool use_termios = false;
214  std::string retval;
215
216  if (isatty(CTTY_FILENO))
217    {
218      use_termios = true;
219
220      if (tcgetattr(CTTY_FILENO, &orig_termios) != 0)
221        throw error(TERMIOS);
222
223      memcpy(&noecho_termios, &orig_termios, sizeof(struct termios));
224
225      if (echo == false)
226        noecho_termios.c_lflag &= ~(ECHO);
227
228      sigemptyset(&new_sigs);
229      sigaddset(&new_sigs, SIGINT);
230      sigaddset(&new_sigs, SIGTSTP);
231      sigprocmask(SIG_BLOCK, &new_sigs, &old_sigs);
232    }
233
234  char input[PAM_MAX_MSG_SIZE];
235
236  int delay = get_delay();
237
238  while (delay >= 0)
239    {
240      cctty << message << std::flush;
241
242      if (use_termios == true)
243        tcsetattr(CTTY_FILENO, TCSAFLUSH, &noecho_termios);
244
245      if (delay > 0 && set_alarm(delay, &saved_signals) == false)
246        break;
247      else
248        {
249          int nchars = read(CTTY_FILENO, input, PAM_MAX_MSG_SIZE - 1);
250          if (use_termios)
251            {
252              tcsetattr(CTTY_FILENO, TCSADRAIN, &orig_termios);
253              if (echo == false && timer_expired == true)
254                cctty << endl;
255            }
256          if (delay > 0)
257            reset_alarm(&saved_signals);
258          if (timer_expired == true)
259            {
260              delay = get_delay();
261            }
262          else if (nchars > 0)
263            {
264              if (echo == false)
265                cctty << endl;
266
267              if (input[nchars-1] == '\n')
268                input[--nchars] = '\0';
269              else
270                input[nchars] = '\0';
271
272              retval = input;
273              break;
274            }
275          else if (nchars == 0)
276            {
277              if (echo == false)
278                cctty << endl;
279
280              retval = "";
281              break;
282            }
283        }
284    }
285
286  memset(input, 0, sizeof(input));
287
288  if (use_termios == true)
289    {
290      sigprocmask(SIG_SETMASK, &old_sigs, 0);
291      tcsetattr(CTTY_FILENO, TCSADRAIN, &orig_termios);
292    }
293
294  return retval;
295}
296
297void
298auth_pam_conv_tty::conversation (auth_pam_conv::message_list& messages)
299{
300  log_debug(DEBUG_NOTICE) << "PAM TTY conversation handler started" << endl;
301
302  for (std::vector<auth_pam_message>::iterator cur = messages.begin();
303       cur != messages.end();
304       ++cur)
305    {
306      switch (cur->type)
307        {
308        case auth_pam_message::MESSAGE_PROMPT_NOECHO:
309          log_debug(DEBUG_NOTICE) << "PAM TTY input prompt (noecho)" << endl;
310          cur->response = read_string(cur->message, false);
311          break;
312        case auth_pam_message::MESSAGE_PROMPT_ECHO:
313          log_debug(DEBUG_NOTICE) << "PAM TTY input prompt (echo)" << endl;
314          cur->response = read_string(cur->message, true);
315          break;
316        case auth_pam_message::MESSAGE_ERROR:
317          log_debug(DEBUG_NOTICE) << "PAM TTY output error" << endl;
318          log_ctty_error() << cur->message << endl;
319          break;
320        case auth_pam_message::MESSAGE_INFO:
321          log_debug(DEBUG_NOTICE) << "PAM TTY output info" << endl;
322          log_ctty_info() << cur->message << endl;
323          break;
324        default:
325          throw error(cur->type, CONV_TYPE);
326          break;
327        }
328    }
329
330  log_debug(DEBUG_NOTICE) << "PAM TTY conversation handler ended" << endl;
331}
Note: See TracBrowser for help on using the repository browser.