/mandos/release

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

« back to all changes in this revision

Viewing changes to plugins.d/password-prompt.c

  • Committer: Teddy Hogeborn
  • Date: 2008-08-29 05:53:59 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080829055359-wkdasnyxtylmnxus
* mandos.xml (EXAMPLE): Replaced all occurences of command name with
                        "&COMMANDNAME;".

* plugins.d/password-prompt.c (main): Improved some documentation
                                      strings.  Do perror() of
                                      tcgetattr() fails.  Add debug
                                      output if interrupted by signal.
                                      Loop over write() instead of
                                      using fwrite() when outputting
                                      password.  Add debug output if
                                      getline() returns 0, unless it
                                      was caused by a signal.  Add
                                      exit status code to debug
                                      output.

* plugins.d/password-prompt.xml: Changed all single quotes to double
                                 quotes for consistency.  Removed
                                 <?xml-stylesheet>.
  (ENTITY TIMESTAMP): New.  Automatically updated by Emacs time-stamp
                      by using Emacs local variables.
  (/refentry/refentryinfo/title): Changed to "Mandos Manual".
  (/refentry/refentryinfo/productname): Changed to "Mandos".
  (/refentry/refentryinfo/date): New; set to "&TIMESTAMP;".
  (/refentry/refentryinfo/copyright): Split copyright holders.
  (/refentry/refnamediv/refpurpose): Improved wording.
  (SYNOPSIS): Fix to use correct markup.  Add short options.
  (DESCRIPTION, OPTIONS): Improved wording.
  (OPTIONS): Improved wording.  Use more correct markup.  Document
             short options.
  (EXIT STATUS): Add text.
  (ENVIRONMENT): Document use of "cryptsource" and "crypttarget".
  (FILES): REMOVED.
  (BUGS): Add text.
  (EXAMPLE): Added some examples.
  (SECURITY): Added text.
  (SEE ALSO): Remove reference to mandos(8).  Add reference to
              crypttab(5).

Show diffs side-by-side

added added

removed removed

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