/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/password-prompt.c

  • Committer: teddy at recompile
  • Date: 2020-04-05 21:30:59 UTC
  • Revision ID: teddy@recompile.se-20200405213059-fb2a61ckqynrmatk
Fix file descriptor leak in mandos-client

When the local network has Mandos servers announcing themselves using
real, globally reachable, IPv6 addresses (i.e. not link-local
addresses), but there is no router on the local network providing IPv6
RA (Router Advertisement) packets, the client cannot reach the server
by normal means, since the client only has a link-local IPv6 address,
and has no usable route to reach the server's global IPv6 address.
(This is not a common situation, and usually only happens when the
router itself reboots and runs a Mandos client, since it cannot then
give RA packets to itself.)  The client code has a solution for
this, which consists of adding a temporary local route to reach the
address of the server during communication, and removing this
temporary route afterwards.

This solution with a temporary route works, but has a file descriptor
leak; it leaks one file descriptor for each addition and for each
removal of a route.  If one server requiring an added route is present
on the network, but no servers gives a password, making the client
retry after the default ten seconds, and we furthermore assume a
default 1024 open files limit, the client runs out of file descriptors
after about 90 minutes, after which time the client process will be
useless and fail to retrieve any passwords, necessitating manual
password entry via the keyboard.

Fix this by eliminating the file descriptor leak in the client.

* plugins.d/mandos-client.c (add_delete_local_route): Do
  close(devnull) also in parent process, also if fork() fails, and on
  any failure in child process.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  -*- coding: utf-8 -*- */
 
1
/*  -*- coding: utf-8; mode: c; mode: orgtbl -*- */
2
2
/*
3
 
 * Passprompt - Read a password from the terminal and print it
4
 
 *
5
 
 * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
6
 
 * 
7
 
 * This program is free software: you can redistribute it and/or
8
 
 * modify it under the terms of the GNU General Public License as
9
 
 * published by the Free Software Foundation, either version 3 of the
10
 
 * License, or (at your option) any later version.
11
 
 * 
12
 
 * This program is distributed in the hope that it will be useful, but
 
3
 * Password-prompt - Read a password from the terminal and print it
 
4
 * 
 
5
 * Copyright © 2008-2019 Teddy Hogeborn
 
6
 * Copyright © 2008-2019 Björn Påhlsson
 
7
 * 
 
8
 * This file is part of Mandos.
 
9
 * 
 
10
 * Mandos is free software: you can redistribute it and/or modify it
 
11
 * under the terms of the GNU General Public License as published by
 
12
 * the Free Software Foundation, either version 3 of the License, or
 
13
 * (at your option) any later version.
 
14
 * 
 
15
 * Mandos is distributed in the hope that it will be useful, but
13
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of
14
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
18
 * General Public License for more details.
16
19
 * 
17
20
 * You should have received a copy of the GNU General Public License
18
 
 * along with this program.  If not, see
19
 
 * <http://www.gnu.org/licenses/>.
 
21
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
20
22
 * 
21
 
 * Contact the authors at <https://www.fukt.bsnet.se/~belorn/> and
22
 
 * <https://www.fukt.bsnet.se/~teddy/>.
 
23
 * Contact the authors at <mandos@recompile.se>.
23
24
 */
24
25
 
25
 
#define _GNU_SOURCE             /* getline() */
 
26
#define _GNU_SOURCE             /* getline(), asprintf() */
26
27
 
27
 
#include <termios.h>            /* struct termios, tcsetattr(),
 
28
#include <termios.h>            /* struct termios, tcsetattr(),
28
29
                                   TCSAFLUSH, tcgetattr(), ECHO */
29
 
#include <unistd.h>             /* struct termios, tcsetattr(),
30
 
                                   STDIN_FILENO, TCSAFLUSH,
31
 
                                   tcgetattr(), ECHO */
 
30
#include <unistd.h>             /* access(), struct termios,
 
31
                                   tcsetattr(), STDIN_FILENO,
 
32
                                   TCSAFLUSH, tcgetattr(), ECHO,
 
33
                                   readlink() */
32
34
#include <signal.h>             /* sig_atomic_t, raise(), struct
33
35
                                   sigaction, sigemptyset(),
34
36
                                   sigaction(), sigaddset(), SIGINT,
35
 
                                   SIGQUIT, SIGHUP, SIGTERM */
 
37
                                   SIGQUIT, SIGHUP, SIGTERM,
 
38
                                   raise() */
36
39
#include <stddef.h>             /* NULL, size_t, ssize_t */
37
 
#include <sys/types.h>          /* ssize_t */
 
40
#include <sys/types.h>          /* ssize_t, struct dirent, pid_t,
 
41
                                   ssize_t, open() */
38
42
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
39
 
                                   getopt_long, getenv() */
 
43
                                   getenv(), free() */
 
44
#include <dirent.h>             /* scandir(), alphasort() */
40
45
#include <stdio.h>              /* fprintf(), stderr, getline(),
41
 
                                   stdin, feof(), perror(), fputc(),
42
 
                                   stdout, getopt_long */
43
 
#include <errno.h>              /* errno, EINVAL */
 
46
                                   stdin, feof(), fputc(), vfprintf(),
 
47
                                   vasprintf() */
 
48
#include <errno.h>              /* errno, EBADF, ENOTTY, EINVAL,
 
49
                                   EFAULT, EFBIG, EIO, ENOSPC, EINTR
 
50
                                */
 
51
#include <error.h>              /* error() */
44
52
#include <iso646.h>             /* or, not */
45
53
#include <stdbool.h>            /* bool, false, true */
46
 
#include <string.h>             /* strlen, rindex, strncmp, strcmp */
 
54
#include <inttypes.h>           /* strtoumax() */
 
55
#include <sys/stat.h>           /* struct stat, lstat(), open() */
 
56
#include <string.h>             /* strlen, rindex, memcmp, strerror()
 
57
                                 */
47
58
#include <argp.h>               /* struct argp_option, struct
48
59
                                   argp_state, struct argp,
49
60
                                   argp_parse(), error_t,
50
61
                                   ARGP_KEY_ARG, ARGP_KEY_END,
51
62
                                   ARGP_ERR_UNKNOWN */
 
63
#include <sysexits.h>           /* EX_SOFTWARE, EX_OSERR,
 
64
                                   EX_UNAVAILABLE, EX_IOERR, EX_OK */
 
65
#include <fcntl.h>              /* open() */
 
66
#include <stdarg.h>             /* va_list, va_start(), ... */
52
67
 
53
 
volatile bool quit_now = false;
 
68
volatile sig_atomic_t quit_now = 0;
 
69
int signal_received;
54
70
bool debug = false;
55
 
const char *argp_program_version = "password-prompt 1.0";
56
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
57
 
 
58
 
static void termination_handler(__attribute__((unused))int signum){
59
 
  quit_now = true;
60
 
}
 
71
const char *argp_program_version = "password-prompt " VERSION;
 
72
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
73
 
 
74
/* Needed for conflict resolution */
 
75
const char plymouth_name[] = "plymouthd";
 
76
 
 
77
/* Function to use when printing errors */
 
78
__attribute__((format (gnu_printf, 3, 4)))
 
79
void error_plus(int status, int errnum, const char *formatstring,
 
80
                ...){
 
81
  va_list ap;
 
82
  char *text;
 
83
  int ret;
 
84
  
 
85
  va_start(ap, formatstring);
 
86
  ret = vasprintf(&text, formatstring, ap);
 
87
  if(ret == -1){
 
88
    fprintf(stderr, "Mandos plugin %s: ",
 
89
            program_invocation_short_name);
 
90
    vfprintf(stderr, formatstring, ap);
 
91
    fprintf(stderr, ": %s\n", strerror(errnum));
 
92
    error(status, errno, "vasprintf while printing error");
 
93
    return;
 
94
  }
 
95
  fprintf(stderr, "Mandos plugin ");
 
96
  error(status, errnum, "%s", text);
 
97
  free(text);
 
98
}
 
99
 
 
100
static void termination_handler(int signum){
 
101
  if(quit_now){
 
102
    return;
 
103
  }
 
104
  quit_now = 1;
 
105
  signal_received = signum;
 
106
}
 
107
 
 
108
bool conflict_detection(void){
 
109
 
 
110
  /* plymouth conflicts with password-prompt since both want to read
 
111
     from the terminal.  Password-prompt will exit if it detects
 
112
     plymouth since plymouth performs the same functionality.
 
113
   */
 
114
  if(access("/run/plymouth/pid", R_OK) == 0){
 
115
    return true;
 
116
  }
 
117
  
 
118
  __attribute__((nonnull))
 
119
  int is_plymouth(const struct dirent *proc_entry){
 
120
    int ret;
 
121
    int cl_fd;
 
122
    {
 
123
      uintmax_t proc_id;
 
124
      char *tmp;
 
125
      errno = 0;
 
126
      proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
 
127
      
 
128
      if(errno != 0 or *tmp != '\0'
 
129
         or proc_id != (uintmax_t)((pid_t)proc_id)){
 
130
        return 0;
 
131
      }
 
132
    }
 
133
    
 
134
    char *cmdline_filename;
 
135
    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
 
136
                   proc_entry->d_name);
 
137
    if(ret == -1){
 
138
      error_plus(0, errno, "asprintf");
 
139
      return 0;
 
140
    }
 
141
    
 
142
    /* Open /proc/<pid>/cmdline */
 
143
    cl_fd = open(cmdline_filename, O_RDONLY);
 
144
    free(cmdline_filename);
 
145
    if(cl_fd == -1){
 
146
      if(errno != ENOENT){
 
147
        error_plus(0, errno, "open");
 
148
      }
 
149
      return 0;
 
150
    }
 
151
    
 
152
    char *cmdline = NULL;
 
153
    {
 
154
      size_t cmdline_len = 0;
 
155
      size_t cmdline_allocated = 0;
 
156
      char *tmp;
 
157
      const size_t blocksize = 1024;
 
158
      ssize_t sret;
 
159
      do {
 
160
        /* Allocate more space? */
 
161
        if(cmdline_len + blocksize + 1 > cmdline_allocated){
 
162
          tmp = realloc(cmdline, cmdline_allocated + blocksize + 1);
 
163
          if(tmp == NULL){
 
164
            error_plus(0, errno, "realloc");
 
165
            free(cmdline);
 
166
            close(cl_fd);
 
167
            return 0;
 
168
          }
 
169
          cmdline = tmp;
 
170
          cmdline_allocated += blocksize;
 
171
        }
 
172
        
 
173
        /* Read data */
 
174
        sret = read(cl_fd, cmdline + cmdline_len,
 
175
                    cmdline_allocated - cmdline_len);
 
176
        if(sret == -1){
 
177
          error_plus(0, errno, "read");
 
178
          free(cmdline);
 
179
          close(cl_fd);
 
180
          return 0;
 
181
        }
 
182
        cmdline_len += (size_t)sret;
 
183
      } while(sret != 0);
 
184
      ret = close(cl_fd);
 
185
      if(ret == -1){
 
186
        error_plus(0, errno, "close");
 
187
        free(cmdline);
 
188
        return 0;
 
189
      }
 
190
      cmdline[cmdline_len] = '\0'; /* Make sure it is terminated */
 
191
    }
 
192
    /* we now have cmdline */
 
193
    
 
194
    /* get basename */
 
195
    char *cmdline_base = strrchr(cmdline, '/');
 
196
    if(cmdline_base != NULL){
 
197
      cmdline_base += 1;                /* skip the slash */
 
198
    } else {
 
199
      cmdline_base = cmdline;
 
200
    }
 
201
    
 
202
    if(strcmp(cmdline_base, plymouth_name) != 0){
 
203
      if(debug){
 
204
        fprintf(stderr, "\"%s\" is not \"%s\"\n", cmdline_base,
 
205
                plymouth_name);
 
206
      }
 
207
      free(cmdline);
 
208
      return 0;
 
209
    }
 
210
    if(debug){
 
211
      fprintf(stderr, "\"%s\" equals \"%s\"\n", cmdline_base,
 
212
              plymouth_name);
 
213
    }
 
214
    free(cmdline);
 
215
    return 1;
 
216
  }
 
217
  
 
218
  struct dirent **direntries = NULL;
 
219
  int ret;
 
220
  ret = scandir("/proc", &direntries, is_plymouth, alphasort);
 
221
  if(ret == -1){
 
222
    error_plus(1, errno, "scandir");
 
223
  }
 
224
  {
 
225
    int i = ret;
 
226
    while(i--){
 
227
      free(direntries[i]);
 
228
    }
 
229
  }
 
230
  free(direntries);
 
231
  return ret > 0;
 
232
}
 
233
 
61
234
 
62
235
int main(int argc, char **argv){
63
 
  ssize_t ret;
 
236
  ssize_t sret;
 
237
  int ret;
64
238
  size_t n;
65
239
  struct termios t_new, t_old;
66
240
  char *buffer = NULL;
67
241
  char *prefix = NULL;
 
242
  char *prompt = NULL;
68
243
  int status = EXIT_SUCCESS;
69
244
  struct sigaction old_action,
70
245
    new_action = { .sa_handler = termination_handler,
73
248
    struct argp_option options[] = {
74
249
      { .name = "prefix", .key = 'p',
75
250
        .arg = "PREFIX", .flags = 0,
76
 
        .doc = "Prefix used before the passprompt", .group = 2 },
 
251
        .doc = "Prefix shown before the prompt", .group = 2 },
 
252
      { .name = "prompt", .key = 129,
 
253
        .arg = "PROMPT", .flags = 0,
 
254
        .doc = "The prompt to show", .group = 2 },
77
255
      { .name = "debug", .key = 128,
78
256
        .doc = "Debug mode", .group = 3 },
 
257
      /*
 
258
       * These reproduce what we would get without ARGP_NO_HELP
 
259
       */
 
260
      { .name = "help", .key = '?',
 
261
        .doc = "Give this help list", .group = -1 },
 
262
      { .name = "usage", .key = -3,
 
263
        .doc = "Give a short usage message", .group = -1 },
 
264
      { .name = "version", .key = 'V',
 
265
        .doc = "Print program version", .group = -1 },
79
266
      { .name = NULL }
80
267
    };
81
 
  
82
 
    error_t parse_opt (int key, char *arg, struct argp_state *state) {
83
 
      /* Get the INPUT argument from `argp_parse', which we know is a
84
 
         pointer to our plugin list pointer. */
85
 
      switch (key) {
86
 
      case 'p':
 
268
    
 
269
    __attribute__((nonnull(3)))
 
270
    error_t parse_opt (int key, char *arg, struct argp_state *state){
 
271
      errno = 0;
 
272
      switch (key){
 
273
      case 'p':                 /* --prefix */
87
274
        prefix = arg;
88
275
        break;
89
 
      case 128:
 
276
      case 128:                 /* --debug */
90
277
        debug = true;
91
278
        break;
92
 
      case ARGP_KEY_ARG:
93
 
        argp_usage (state);
 
279
      case 129:                 /* --prompt */
 
280
        prompt = arg;
94
281
        break;
95
 
      case ARGP_KEY_END:
 
282
        /*
 
283
         * These reproduce what we would get without ARGP_NO_HELP
 
284
         */
 
285
      case '?':                 /* --help */
 
286
        argp_state_help(state, state->out_stream,
 
287
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
 
288
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
289
        __builtin_unreachable();
 
290
      case -3:                  /* --usage */
 
291
        argp_state_help(state, state->out_stream,
 
292
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
293
        __builtin_unreachable();
 
294
      case 'V':                 /* --version */
 
295
        fprintf(state->out_stream, "%s\n", argp_program_version);
 
296
        exit(argp_err_exit_status);
96
297
        break;
97
298
      default:
98
299
        return ARGP_ERR_UNKNOWN;
99
300
      }
100
 
      return 0;
 
301
      return errno;
101
302
    }
102
 
  
 
303
    
103
304
    struct argp argp = { .options = options, .parser = parse_opt,
104
305
                         .args_doc = "",
105
 
                         .doc = "Mandos Passprompt -- Provides a passprompt" };
106
 
    ret = argp_parse (&argp, argc, argv, 0, 0, NULL);
107
 
    if (ret == ARGP_ERR_UNKNOWN){
108
 
      fprintf(stderr, "Unknown error while parsing arguments\n");
109
 
      return EXIT_FAILURE;
 
306
                         .doc = "Mandos password-prompt -- Read and"
 
307
                         " output a password" };
 
308
    ret = argp_parse(&argp, argc, argv,
 
309
                     ARGP_IN_ORDER | ARGP_NO_HELP, NULL, NULL);
 
310
    switch(ret){
 
311
    case 0:
 
312
      break;
 
313
    case ENOMEM:
 
314
    default:
 
315
      errno = ret;
 
316
      error_plus(0, errno, "argp_parse");
 
317
      return EX_OSERR;
 
318
    case EINVAL:
 
319
      return EX_USAGE;
110
320
    }
111
321
  }
112
 
    
113
 
  if (debug){
 
322
  
 
323
  if(debug){
114
324
    fprintf(stderr, "Starting %s\n", argv[0]);
115
325
  }
116
 
  if (debug){
 
326
 
 
327
  if(conflict_detection()){
 
328
    if(debug){
 
329
      fprintf(stderr, "Stopping %s because of conflict\n", argv[0]);
 
330
    }
 
331
    return EXIT_FAILURE;
 
332
  }
 
333
  
 
334
  if(debug){
117
335
    fprintf(stderr, "Storing current terminal attributes\n");
118
336
  }
119
337
  
120
 
  if (tcgetattr(STDIN_FILENO, &t_old) != 0){
121
 
    return EXIT_FAILURE;
 
338
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
 
339
    int e = errno;
 
340
    error_plus(0, errno, "tcgetattr");
 
341
    switch(e){
 
342
    case EBADF:
 
343
    case ENOTTY:
 
344
      return EX_UNAVAILABLE;
 
345
    default:
 
346
      return EX_OSERR;
 
347
    }
122
348
  }
123
349
  
124
350
  sigemptyset(&new_action.sa_mask);
125
 
  sigaddset(&new_action.sa_mask, SIGINT);
126
 
  sigaddset(&new_action.sa_mask, SIGHUP);
127
 
  sigaddset(&new_action.sa_mask, SIGTERM);
 
351
  ret = sigaddset(&new_action.sa_mask, SIGINT);
 
352
  if(ret == -1){
 
353
    error_plus(0, errno, "sigaddset");
 
354
    return EX_OSERR;
 
355
  }
 
356
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
 
357
  if(ret == -1){
 
358
    error_plus(0, errno, "sigaddset");
 
359
    return EX_OSERR;
 
360
  }
 
361
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
 
362
  if(ret == -1){
 
363
    error_plus(0, errno, "sigaddset");
 
364
    return EX_OSERR;
 
365
  }
 
366
  /* Need to check if the handler is SIG_IGN before handling:
 
367
     | [[info:libc:Initial Signal Actions]] |
 
368
     | [[info:libc:Basic Signal Handling]]  |
 
369
  */
128
370
  ret = sigaction(SIGINT, NULL, &old_action);
129
371
  if(ret == -1){
130
 
    perror("sigaction");
131
 
    return EXIT_FAILURE;
 
372
    error_plus(0, errno, "sigaction");
 
373
    return EX_OSERR;
132
374
  }
133
 
  if (old_action.sa_handler != SIG_IGN){
 
375
  if(old_action.sa_handler != SIG_IGN){
134
376
    ret = sigaction(SIGINT, &new_action, NULL);
135
377
    if(ret == -1){
136
 
      perror("sigaction");
137
 
      return EXIT_FAILURE;
 
378
      error_plus(0, errno, "sigaction");
 
379
      return EX_OSERR;
138
380
    }
139
381
  }
140
382
  ret = sigaction(SIGHUP, NULL, &old_action);
141
383
  if(ret == -1){
142
 
    perror("sigaction");
143
 
    return EXIT_FAILURE;
 
384
    error_plus(0, errno, "sigaction");
 
385
    return EX_OSERR;
144
386
  }
145
 
  if (old_action.sa_handler != SIG_IGN){
 
387
  if(old_action.sa_handler != SIG_IGN){
146
388
    ret = sigaction(SIGHUP, &new_action, NULL);
147
389
    if(ret == -1){
148
 
      perror("sigaction");
149
 
      return EXIT_FAILURE;
 
390
      error_plus(0, errno, "sigaction");
 
391
      return EX_OSERR;
150
392
    }
151
393
  }
152
394
  ret = sigaction(SIGTERM, NULL, &old_action);
153
395
  if(ret == -1){
154
 
    perror("sigaction");
155
 
    return EXIT_FAILURE;
 
396
    error_plus(0, errno, "sigaction");
 
397
    return EX_OSERR;
156
398
  }
157
 
  if (old_action.sa_handler != SIG_IGN){
 
399
  if(old_action.sa_handler != SIG_IGN){
158
400
    ret = sigaction(SIGTERM, &new_action, NULL);
159
401
    if(ret == -1){
160
 
      perror("sigaction");
161
 
      return EXIT_FAILURE;
 
402
      error_plus(0, errno, "sigaction");
 
403
      return EX_OSERR;
162
404
    }
163
405
  }
164
406
  
165
407
  
166
 
  if (debug){
 
408
  if(debug){
167
409
    fprintf(stderr, "Removing echo flag from terminal attributes\n");
168
410
  }
169
411
  
170
412
  t_new = t_old;
171
 
  t_new.c_lflag &= ~ECHO;
172
 
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
173
 
    perror("tcsetattr-echo");
174
 
    return EXIT_FAILURE;
 
413
  t_new.c_lflag &= ~(tcflag_t)ECHO;
 
414
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
 
415
    int e = errno;
 
416
    error_plus(0, errno, "tcsetattr-echo");
 
417
    switch(e){
 
418
    case EBADF:
 
419
    case ENOTTY:
 
420
      return EX_UNAVAILABLE;
 
421
    case EINVAL:
 
422
    default:
 
423
      return EX_OSERR;
 
424
    }
175
425
  }
176
 
 
177
 
  if (debug){
 
426
  
 
427
  if(debug){
178
428
    fprintf(stderr, "Waiting for input from stdin \n");
179
429
  }
180
430
  while(true){
181
 
    if (quit_now){
 
431
    if(quit_now){
 
432
      if(debug){
 
433
        fprintf(stderr, "Interrupted by signal, exiting.\n");
 
434
      }
182
435
      status = EXIT_FAILURE;
183
436
      break;
184
437
    }
186
439
    if(prefix){
187
440
      fprintf(stderr, "%s ", prefix);
188
441
    }
189
 
    {
190
 
      const char *cryptsource = getenv("cryptsource");
191
 
      const char *crypttarget = getenv("crypttarget");
192
 
      const char *const prompt
193
 
        = "Enter passphrase to unlock the disk";
 
442
    if(prompt != NULL){
 
443
      fprintf(stderr, "%s: ", prompt);
 
444
    } else {
 
445
      const char *cryptsource = getenv("CRYPTTAB_SOURCE");
 
446
      const char *crypttarget = getenv("CRYPTTAB_NAME");
 
447
      /* Before cryptsetup 1.1.0~rc2 */
 
448
      if(cryptsource == NULL){
 
449
        cryptsource = getenv("cryptsource");
 
450
      }
 
451
      if(crypttarget == NULL){
 
452
        crypttarget = getenv("crypttarget");
 
453
      }
 
454
      const char *const prompt1 = "Unlocking the disk";
 
455
      const char *const prompt2 = "Enter passphrase";
194
456
      if(cryptsource == NULL){
195
457
        if(crypttarget == NULL){
196
 
          fprintf(stderr, "%s: ", prompt);
 
458
          fprintf(stderr, "%s to unlock the disk: ", prompt2);
197
459
        } else {
198
 
          fprintf(stderr, "%s (%s): ", prompt, crypttarget);
 
460
          fprintf(stderr, "%s (%s)\n%s: ", prompt1, crypttarget,
 
461
                  prompt2);
199
462
        }
200
463
      } else {
201
464
        if(crypttarget == NULL){
202
 
          fprintf(stderr, "%s %s: ", prompt, cryptsource);
 
465
          fprintf(stderr, "%s %s\n%s: ", prompt1, cryptsource,
 
466
                  prompt2);
203
467
        } else {
204
 
          fprintf(stderr, "%s %s (%s): ", prompt, cryptsource,
205
 
                  crypttarget);
 
468
          fprintf(stderr, "%s %s (%s)\n%s: ", prompt1, cryptsource,
 
469
                  crypttarget, prompt2);
206
470
        }
207
471
      }
208
472
    }
209
 
    ret = getline(&buffer, &n, stdin);
210
 
    if (ret > 0){
211
 
      fprintf(stdout, "%s", buffer);
 
473
    sret = getline(&buffer, &n, stdin);
 
474
    if(sret > 0){
212
475
      status = EXIT_SUCCESS;
 
476
      /* Make n = data size instead of allocated buffer size */
 
477
      n = (size_t)sret;
 
478
      /* Strip final newline */
 
479
      if(n > 0 and buffer[n-1] == '\n'){
 
480
        buffer[n-1] = '\0';     /* not strictly necessary */
 
481
        n--;
 
482
      }
 
483
      size_t written = 0;
 
484
      while(written < n){
 
485
        sret = write(STDOUT_FILENO, buffer + written, n - written);
 
486
        if(sret < 0){
 
487
          int e = errno;
 
488
          error_plus(0, errno, "write");
 
489
          switch(e){
 
490
          case EBADF:
 
491
          case EFAULT:
 
492
          case EINVAL:
 
493
          case EFBIG:
 
494
          case EIO:
 
495
          case ENOSPC:
 
496
          default:
 
497
            status = EX_IOERR;
 
498
            break;
 
499
          case EINTR:
 
500
            status = EXIT_FAILURE;
 
501
            break;
 
502
          }
 
503
          break;
 
504
        }
 
505
        written += (size_t)sret;
 
506
      }
 
507
      sret = close(STDOUT_FILENO);
 
508
      if(sret == -1){
 
509
        int e = errno;
 
510
        error_plus(0, errno, "close");
 
511
        switch(e){
 
512
        case EBADF:
 
513
          status = EX_OSFILE;
 
514
          break;
 
515
        case EIO:
 
516
        default:
 
517
          status = EX_IOERR;
 
518
          break;
 
519
        }
 
520
      }
213
521
      break;
214
522
    }
215
 
    if (ret < 0){
216
 
      if (errno != EINTR and not feof(stdin)){
217
 
        perror("getline");
218
 
        status = EXIT_FAILURE;
219
 
        break;
 
523
    if(sret < 0){
 
524
      int e = errno;
 
525
      if(errno != EINTR){
 
526
        if(not feof(stdin)){
 
527
          error_plus(0, errno, "getline");
 
528
          switch(e){
 
529
          case EBADF:
 
530
            status = EX_UNAVAILABLE;
 
531
            break;
 
532
          case EIO:
 
533
          case EINVAL:
 
534
          default:
 
535
            status = EX_IOERR;
 
536
            break;
 
537
          }
 
538
          break;
 
539
        } else {
 
540
          clearerr(stdin);
 
541
        }
220
542
      }
221
543
    }
222
 
    /* if(ret == 0), then the only sensible thing to do is to retry to
223
 
       read from stdin */
 
544
    /* if(sret == 0), then the only sensible thing to do is to retry
 
545
       to read from stdin */
224
546
    fputc('\n', stderr);
 
547
    if(debug and not quit_now){
 
548
      /* If quit_now is nonzero, we were interrupted by a signal, and
 
549
         will print that later, so no need to show this too. */
 
550
      fprintf(stderr, "getline() returned 0, retrying.\n");
 
551
    }
225
552
  }
226
553
  
227
 
  if (debug){
 
554
  free(buffer);
 
555
  
 
556
  if(debug){
228
557
    fprintf(stderr, "Restoring terminal attributes\n");
229
558
  }
230
 
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
231
 
    perror("tcsetattr+echo");
232
 
  }
233
 
  
234
 
  if (debug){
235
 
    fprintf(stderr, "%s is exiting\n", argv[0]);
 
559
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
 
560
    error_plus(0, errno, "tcsetattr+echo");
 
561
  }
 
562
  
 
563
  if(quit_now){
 
564
    sigemptyset(&old_action.sa_mask);
 
565
    old_action.sa_handler = SIG_DFL;
 
566
    ret = sigaction(signal_received, &old_action, NULL);
 
567
    if(ret == -1){
 
568
      error_plus(0, errno, "sigaction");
 
569
    }
 
570
    raise(signal_received);
 
571
  }
 
572
  
 
573
  if(debug){
 
574
    fprintf(stderr, "%s is exiting with status %d\n", argv[0],
 
575
            status);
 
576
  }
 
577
  if(status == EXIT_SUCCESS or status == EX_OK){
 
578
    fputc('\n', stderr);
236
579
  }
237
580
  
238
581
  return status;