/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to plugins.d/passprompt.c

  • Committer: Björn Påhlsson
  • Date: 2008-07-20 02:52:20 UTC
  • Revision ID: belorn@braxen-20080720025220-r5u0388uy9iu23h6
Added following support:
Pluginbased client handler
rewritten Mandos client
       Avahi instead of udp server discovery
       openpgp encrypted key support
Passprompt stand alone application for direct console input
Added logging for Mandos server

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  -*- coding: utf-8; mode: c; mode: orgtbl -*- */
2
 
/*
3
 
 * Password-prompt - Read a password from the terminal and print it
4
 
 * 
5
 
 * Copyright © 2008,2009 Teddy Hogeborn
6
 
 * Copyright © 2008,2009 Björn Påhlsson
7
 
 * 
8
 
 * This program is free software: you can redistribute it and/or
9
 
 * modify it under the terms of the GNU General Public License as
10
 
 * published by the Free Software Foundation, either version 3 of the
11
 
 * License, or (at your option) any later version.
12
 
 * 
13
 
 * This program is distributed in the hope that it will be useful, but
14
 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
15
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 
 * General Public License for more details.
17
 
 * 
18
 
 * You should have received a copy of the GNU General Public License
19
 
 * along with this program.  If not, see
20
 
 * <http://www.gnu.org/licenses/>.
21
 
 * 
22
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
23
 
 */
24
 
 
25
1
#define _GNU_SOURCE             /* getline() */
26
 
 
 
2
#define _FORTIFY_SOURCE 2
27
3
#include <termios.h>            /* struct termios, tcsetattr(),
28
4
                                   TCSAFLUSH, tcgetattr(), ECHO */
29
5
#include <unistd.h>             /* struct termios, tcsetattr(),
32
8
#include <signal.h>             /* sig_atomic_t, raise(), struct
33
9
                                   sigaction, sigemptyset(),
34
10
                                   sigaction(), sigaddset(), SIGINT,
35
 
                                   SIGQUIT, SIGHUP, SIGTERM,
36
 
                                   raise() */
37
 
#include <stddef.h>             /* NULL, size_t, ssize_t */
 
11
                                   SIGQUIT, SIGHUP, SIGTERM */
 
12
#include <stddef.h>             /* NULL, size_t */
38
13
#include <sys/types.h>          /* ssize_t */
39
 
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
40
 
                                   getopt_long, getenv() */
 
14
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE */
41
15
#include <stdio.h>              /* fprintf(), stderr, getline(),
42
16
                                   stdin, feof(), perror(), fputc(),
43
 
                                   stdout, getopt_long */
 
17
                                   stdout */
44
18
#include <errno.h>              /* errno, EINVAL */
45
19
#include <iso646.h>             /* or, not */
46
20
#include <stdbool.h>            /* bool, false, true */
47
 
#include <string.h>             /* strlen, rindex */
48
 
#include <argp.h>               /* struct argp_option, struct
49
 
                                   argp_state, struct argp,
50
 
                                   argp_parse(), error_t,
51
 
                                   ARGP_KEY_ARG, ARGP_KEY_END,
52
 
                                   ARGP_ERR_UNKNOWN */
53
 
 
54
 
volatile sig_atomic_t quit_now = 0;
55
 
int signal_received;
56
 
bool debug = false;
57
 
const char *argp_program_version = "password-prompt " VERSION;
58
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
59
 
 
60
 
static void termination_handler(int signum){
61
 
  if(quit_now){
62
 
    return;
63
 
  }
64
 
  quit_now = 1;
65
 
  signal_received = signum;
 
21
 
 
22
volatile bool quit_now = false;
 
23
 
 
24
void termination_handler(int signum){
 
25
  quit_now = true;
66
26
}
67
27
 
68
28
int main(int argc, char **argv){
69
 
  ssize_t ret;
 
29
  ssize_t ret = -1;
70
30
  size_t n;
71
31
  struct termios t_new, t_old;
72
32
  char *buffer = NULL;
73
 
  char *prefix = NULL;
74
33
  int status = EXIT_SUCCESS;
75
34
  struct sigaction old_action,
76
35
    new_action = { .sa_handler = termination_handler,
77
36
                   .sa_flags = 0 };
78
 
  {
79
 
    struct argp_option options[] = {
80
 
      { .name = "prefix", .key = 'p',
81
 
        .arg = "PREFIX", .flags = 0,
82
 
        .doc = "Prefix shown before the prompt", .group = 2 },
83
 
      { .name = "debug", .key = 128,
84
 
        .doc = "Debug mode", .group = 3 },
85
 
      { .name = NULL }
86
 
    };
87
 
    
88
 
    error_t parse_opt (int key, char *arg, struct argp_state *state){
89
 
      switch (key){
90
 
      case 'p':
91
 
        prefix = arg;
92
 
        break;
93
 
      case 128:
94
 
        debug = true;
95
 
        break;
96
 
      case ARGP_KEY_ARG:
97
 
        argp_usage(state);
98
 
        break;
99
 
      case ARGP_KEY_END:
100
 
        break;
101
 
      default:
102
 
        return ARGP_ERR_UNKNOWN;
103
 
      }
104
 
      return 0;
105
 
    }
106
 
    
107
 
    struct argp argp = { .options = options, .parser = parse_opt,
108
 
                         .args_doc = "",
109
 
                         .doc = "Mandos password-prompt -- Read and"
110
 
                         " output a password" };
111
 
    ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
112
 
    if(ret == ARGP_ERR_UNKNOWN){
113
 
      fprintf(stderr, "Unknown error while parsing arguments\n");
114
 
      return EXIT_FAILURE;
115
 
    }
116
 
  }
117
 
  
118
 
  if(debug){
119
 
    fprintf(stderr, "Starting %s\n", argv[0]);
120
 
  }
121
 
  if(debug){
122
 
    fprintf(stderr, "Storing current terminal attributes\n");
123
 
  }
124
 
  
125
 
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
126
 
    perror("tcgetattr");
 
37
  
 
38
  if (tcgetattr(STDIN_FILENO, &t_old) != 0){
127
39
    return EXIT_FAILURE;
128
40
  }
129
41
  
130
42
  sigemptyset(&new_action.sa_mask);
131
 
  ret = sigaddset(&new_action.sa_mask, SIGINT);
132
 
  if(ret == -1){
133
 
    perror("sigaddset");
134
 
    return EXIT_FAILURE;
135
 
  }
136
 
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
137
 
  if(ret == -1){
138
 
    perror("sigaddset");
139
 
    return EXIT_FAILURE;
140
 
  }
141
 
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
142
 
  if(ret == -1){
143
 
    perror("sigaddset");
144
 
    return EXIT_FAILURE;
145
 
  }
146
 
  /* Need to check if the handler is SIG_IGN before handling:
147
 
     | [[info:libc:Initial Signal Actions]] |
148
 
     | [[info:libc:Basic Signal Handling]]  |
149
 
  */
150
 
  ret = sigaction(SIGINT, NULL, &old_action);
151
 
  if(ret == -1){
152
 
    perror("sigaction");
153
 
    return EXIT_FAILURE;
154
 
  }
155
 
  if(old_action.sa_handler != SIG_IGN){
156
 
    ret = sigaction(SIGINT, &new_action, NULL);
157
 
    if(ret == -1){
158
 
      perror("sigaction");
159
 
      return EXIT_FAILURE;
160
 
    }
161
 
  }
162
 
  ret = sigaction(SIGHUP, NULL, &old_action);
163
 
  if(ret == -1){
164
 
    perror("sigaction");
165
 
    return EXIT_FAILURE;
166
 
  }
167
 
  if(old_action.sa_handler != SIG_IGN){
168
 
    ret = sigaction(SIGHUP, &new_action, NULL);
169
 
    if(ret == -1){
170
 
      perror("sigaction");
171
 
      return EXIT_FAILURE;
172
 
    }
173
 
  }
174
 
  ret = sigaction(SIGTERM, NULL, &old_action);
175
 
  if(ret == -1){
176
 
    perror("sigaction");
177
 
    return EXIT_FAILURE;
178
 
  }
179
 
  if(old_action.sa_handler != SIG_IGN){
180
 
    ret = sigaction(SIGTERM, &new_action, NULL);
181
 
    if(ret == -1){
182
 
      perror("sigaction");
183
 
      return EXIT_FAILURE;
184
 
    }
185
 
  }
186
 
  
187
 
  
188
 
  if(debug){
189
 
    fprintf(stderr, "Removing echo flag from terminal attributes\n");
190
 
  }
 
43
  sigaddset(&new_action.sa_mask, SIGINT);
 
44
  sigaddset(&new_action.sa_mask, SIGQUIT);
 
45
  sigaddset(&new_action.sa_mask, SIGHUP);
 
46
  sigaddset(&new_action.sa_mask, SIGTERM);
 
47
  sigaction(SIGINT, NULL, &old_action);
 
48
  if (old_action.sa_handler != SIG_IGN)
 
49
    sigaction(SIGINT, &new_action, NULL);
 
50
  sigaction(SIGQUIT, NULL, &old_action);
 
51
  if (old_action.sa_handler != SIG_IGN)
 
52
    sigaction(SIGQUIT, &new_action, NULL);
 
53
  sigaction(SIGHUP, NULL, &old_action);
 
54
  if (old_action.sa_handler != SIG_IGN)
 
55
    sigaction(SIGHUP, &new_action, NULL);
 
56
  sigaction(SIGTERM, NULL, &old_action);
 
57
  if (old_action.sa_handler != SIG_IGN)
 
58
    sigaction(SIGTERM, &new_action, NULL);
191
59
  
192
60
  t_new = t_old;
193
 
  t_new.c_lflag &= ~(tcflag_t)ECHO;
194
 
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
 
61
  t_new.c_lflag &= ~ECHO;
 
62
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
195
63
    perror("tcsetattr-echo");
196
64
    return EXIT_FAILURE;
197
65
  }
198
 
 
199
 
  if(debug){
200
 
    fprintf(stderr, "Waiting for input from stdin \n");
201
 
  }
 
66
  
202
67
  while(true){
203
 
    if(quit_now){
204
 
      if(debug){
205
 
        fprintf(stderr, "Interrupted by signal, exiting.\n");
206
 
      }
 
68
    if (quit_now){
207
69
      status = EXIT_FAILURE;
208
70
      break;
209
71
    }
210
 
 
211
 
    if(prefix){
212
 
      fprintf(stderr, "%s ", prefix);
213
 
    }
214
 
    {
215
 
      const char *cryptsource = getenv("cryptsource");
216
 
      const char *crypttarget = getenv("crypttarget");
217
 
      const char *const prompt
218
 
        = "Enter passphrase to unlock the disk";
219
 
      if(cryptsource == NULL){
220
 
        if(crypttarget == NULL){
221
 
          fprintf(stderr, "%s: ", prompt);
222
 
        } else {
223
 
          fprintf(stderr, "%s (%s): ", prompt, crypttarget);
224
 
        }
225
 
      } else {
226
 
        if(crypttarget == NULL){
227
 
          fprintf(stderr, "%s %s: ", prompt, cryptsource);
228
 
        } else {
229
 
          fprintf(stderr, "%s %s (%s): ", prompt, cryptsource,
230
 
                  crypttarget);
231
 
        }
232
 
      }
233
 
    }
 
72
    fprintf(stderr, "Password: ");
234
73
    ret = getline(&buffer, &n, stdin);
235
 
    if(ret > 0){
 
74
    if (ret > 0){
 
75
      fprintf(stdout, "%s", buffer);
236
76
      status = EXIT_SUCCESS;
237
 
      /* Make n = data size instead of allocated buffer size */
238
 
      n = (size_t)ret;
239
 
      /* Strip final newline */
240
 
      if(n>0 and buffer[n-1] == '\n'){
241
 
        buffer[n-1] = '\0';     /* not strictly necessary */
242
 
        n--;
243
 
      }
244
 
      size_t written = 0;
245
 
      while(written < n){
246
 
        ret = write(STDOUT_FILENO, buffer + written, n - written);
247
 
        if(ret < 0){
248
 
          perror("write");
249
 
          status = EXIT_FAILURE;
250
 
          break;
251
 
        }
252
 
        written += (size_t)ret;
253
 
      }
254
77
      break;
255
78
    }
256
 
    if(ret < 0){
257
 
      if(errno != EINTR and not feof(stdin)){
 
79
    // ret == 0 makes no other sence than to retry to read from stdin
 
80
    if (ret < 0){
 
81
      if (errno != EINTR and not feof(stdin)){
258
82
        perror("getline");
259
83
        status = EXIT_FAILURE;
260
84
        break;
261
85
      }
262
86
    }
263
 
    /* if(ret == 0), then the only sensible thing to do is to retry to
264
 
       read from stdin */
265
87
    fputc('\n', stderr);
266
 
    if(debug and not quit_now){
267
 
      /* If quit_now is nonzero, we were interrupted by a signal, and
268
 
         will print that later, so no need to show this too. */
269
 
      fprintf(stderr, "getline() returned 0, retrying.\n");
270
 
    }
271
 
  }
272
 
  
273
 
  free(buffer);
274
 
  
275
 
  if(debug){
276
 
    fprintf(stderr, "Restoring terminal attributes\n");
277
 
  }
278
 
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
 
88
  }
 
89
 
 
90
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
279
91
    perror("tcsetattr+echo");
280
92
  }
281
93
  
282
 
  if(quit_now){
283
 
    sigemptyset(&old_action.sa_mask);
284
 
    old_action.sa_handler = SIG_DFL;
285
 
    ret = sigaction(signal_received, &old_action, NULL);
286
 
    if(ret == -1){
287
 
      perror("sigaction");
288
 
    }
289
 
    raise(signal_received);
290
 
  }
291
 
  
292
 
  if(debug){
293
 
    fprintf(stderr, "%s is exiting with status %d\n", argv[0],
294
 
            status);
295
 
  }
296
 
  if(status == EXIT_SUCCESS){
297
 
    fputc('\n', stderr);
298
 
  }
299
 
  
300
94
  return status;
301
95
}