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