/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

First version of a somewhat complete D-Bus server interface.  Also
change user/group name to "_mandos".

* debian/mandos.postinst: Rename old "mandos" user and group to
                          "_mandos"; create "_mandos" user and group
                          if none exist.
* debian/mandos-client.postinst: - '' -

* initramfs-tools-hook: Try "_mandos" before "mandos" as user and
                        group name.

* mandos (_datetime_to_dbus_struct): New; was previously local.
  (Client.started): Renamed to "last_started".  All users changed.
  (Client.started): New; boolean.
  (Client.dbus_object_path): New.
  (Client.check_command): Renamed to "checker_command".  All users
                          changed.
  (Client.__init__): Set and use "self.dbus_object_path".  Set
                     "self.started".
  (Client.start): Update "self.started".  Emit "self.PropertyChanged"
                  signals for both "started" and "last_started".
  (Client.stop): Update "self.started".  Emit "self.PropertyChanged"
                 signal for "started".
  (Client.checker_callback): Take additional "command" argument.  All
                             callers changed. Emit
                             "self.PropertyChanged" signal.
  (Client.bump_timeout): Emit "self.PropertyChanged" signal for
                         "last_checked_ok".
  (Client.start_checker): Emit "self.PropertyChanged" signal for
                          "checker_running".
  (Client.stop_checker): Emit "self.PropertyChanged" signal for
                         "checker_running".
  (Client.still_valid): Bug fix: use "getattr(self, started, False)"
                        instead of "self.started" in case this client
                        object is so new that the "started" attribute
                        has not been created yet.
  (Client.IntervalChanged, Client.CheckerIsRunning, Client.GetChecker,
  Client.GetCreated, Client.GetFingerprint, Client.GetHost,
  Client.GetInterval, Client.GetName, Client.GetStarted,
  Client.GetTimeout, Client.StateChanged, Client.TimeoutChanged):
  Removed; all callers changed.
  (Client.CheckerCompleted): Add "condition" and "command" arguments.
                             All callers changed.
  (Client.GetAllProperties, Client.PropertyChanged): New.
  (Client.StillValid): Renamed to "IsStillValid".
  (Client.StartChecker): Changed to its own function to avoid the
                         return value from "Client.start_checker()".
  (Client.Stop): Changed to its own function to avoid the return value
                 from "Client.stop()".
  (main): Try "_mandos" before "mandos" as user and group name.
          Removed inner function "remove_from_clients".  New inner
          class "MandosServer".

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 © 2008 Teddy Hogeborn
 
6
 * Copyright © 2008 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
16
14
 * WITHOUT ANY WARRANTY; without even the implied warranty of
17
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18
16
 * General Public License for more details.
19
17
 * 
20
18
 * You should have received a copy of the GNU General Public License
21
 
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
 
19
 * along with this program.  If not, see
 
20
 * <http://www.gnu.org/licenses/>.
22
21
 * 
23
 
 * Contact the authors at <mandos@recompile.se>.
 
22
 * Contact the authors at <https://www.fukt.bsnet.se/~belorn/> and
 
23
 * <https://www.fukt.bsnet.se/~teddy/>.
24
24
 */
25
25
 
26
 
#define _GNU_SOURCE             /* vasprintf(),
27
 
                                   program_invocation_short_name,
28
 
                                   asprintf(), getline() */
29
 
#include <sys/types.h>          /* sig_atomic_t, pid_t */
 
26
#define _GNU_SOURCE             /* getline() */
 
27
 
 
28
#include <termios.h>            /* struct termios, tcsetattr(),
 
29
                                   TCSAFLUSH, tcgetattr(), ECHO */
 
30
#include <unistd.h>             /* struct termios, tcsetattr(),
 
31
                                   STDIN_FILENO, TCSAFLUSH,
 
32
                                   tcgetattr(), ECHO */
 
33
#include <signal.h>             /* sig_atomic_t, raise(), struct
 
34
                                   sigaction, sigemptyset(),
 
35
                                   sigaction(), sigaddset(), SIGINT,
 
36
                                   SIGQUIT, SIGHUP, SIGTERM */
 
37
#include <stddef.h>             /* NULL, size_t, ssize_t */
 
38
#include <sys/types.h>          /* ssize_t */
 
39
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
 
40
                                   getopt_long, getenv() */
 
41
#include <stdio.h>              /* fprintf(), stderr, getline(),
 
42
                                   stdin, feof(), perror(), fputc(),
 
43
                                   stdout, getopt_long */
 
44
#include <errno.h>              /* errno, EINVAL */
 
45
#include <iso646.h>             /* or, not */
30
46
#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 */
 
47
#include <string.h>             /* strlen, rindex, strncmp, strcmp */
 
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 */
72
53
 
73
 
volatile sig_atomic_t quit_now = 0;
74
 
int signal_received;
 
54
volatile bool quit_now = false;
75
55
bool debug = false;
76
56
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
 
 
 
57
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
58
 
 
59
static void termination_handler(__attribute__((unused))int signum){
 
60
  quit_now = true;
 
61
}
239
62
 
240
63
int main(int argc, char **argv){
241
 
  ssize_t sret;
242
 
  int ret;
 
64
  ssize_t ret;
243
65
  size_t n;
244
66
  struct termios t_new, t_old;
245
67
  char *buffer = NULL;
246
68
  char *prefix = NULL;
247
 
  char *prompt = NULL;
248
69
  int status = EXIT_SUCCESS;
249
70
  struct sigaction old_action,
250
71
    new_action = { .sa_handler = termination_handler,
254
75
      { .name = "prefix", .key = 'p',
255
76
        .arg = "PREFIX", .flags = 0,
256
77
        .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
78
      { .name = "debug", .key = 128,
261
79
        .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
80
      { .name = NULL }
272
81
    };
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 */
 
82
  
 
83
    error_t parse_opt (int key, char *arg, struct argp_state *state) {
 
84
      /* Get the INPUT argument from `argp_parse', which we know is a
 
85
         pointer to our plugin list pointer. */
 
86
      switch (key) {
 
87
      case 'p':
279
88
        prefix = arg;
280
89
        break;
281
 
      case 128:                 /* --debug */
 
90
      case 128:
282
91
        debug = true;
283
92
        break;
284
 
      case 129:                 /* --prompt */
285
 
        prompt = arg;
 
93
      case ARGP_KEY_ARG:
 
94
        argp_usage (state);
286
95
        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);
 
96
      case ARGP_KEY_END:
302
97
        break;
303
98
      default:
304
99
        return ARGP_ERR_UNKNOWN;
305
100
      }
306
 
      return errno;
 
101
      return 0;
307
102
    }
308
 
    
 
103
  
309
104
    struct argp argp = { .options = options, .parser = parse_opt,
310
105
                         .args_doc = "",
311
106
                         .doc = "Mandos password-prompt -- Read and"
312
107
                         " 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;
 
108
    ret = argp_parse (&argp, argc, argv, 0, 0, NULL);
 
109
    if (ret == ARGP_ERR_UNKNOWN){
 
110
      fprintf(stderr, "Unknown error while parsing arguments\n");
 
111
      return EXIT_FAILURE;
325
112
    }
326
113
  }
327
 
  
328
 
  if(debug){
 
114
    
 
115
  if (debug){
329
116
    fprintf(stderr, "Starting %s\n", argv[0]);
330
117
  }
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){
 
118
  if (debug){
340
119
    fprintf(stderr, "Storing current terminal attributes\n");
341
120
  }
342
121
  
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
 
    }
 
122
  if (tcgetattr(STDIN_FILENO, &t_old) != 0){
 
123
    perror("tcgetattr");
 
124
    return EXIT_FAILURE;
353
125
  }
354
126
  
355
127
  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
 
  */
 
128
  sigaddset(&new_action.sa_mask, SIGINT);
 
129
  sigaddset(&new_action.sa_mask, SIGHUP);
 
130
  sigaddset(&new_action.sa_mask, SIGTERM);
375
131
  ret = sigaction(SIGINT, NULL, &old_action);
376
132
  if(ret == -1){
377
 
    error_plus(0, errno, "sigaction");
378
 
    return EX_OSERR;
 
133
    perror("sigaction");
 
134
    return EXIT_FAILURE;
379
135
  }
380
 
  if(old_action.sa_handler != SIG_IGN){
 
136
  if (old_action.sa_handler != SIG_IGN){
381
137
    ret = sigaction(SIGINT, &new_action, NULL);
382
138
    if(ret == -1){
383
 
      error_plus(0, errno, "sigaction");
384
 
      return EX_OSERR;
 
139
      perror("sigaction");
 
140
      return EXIT_FAILURE;
385
141
    }
386
142
  }
387
143
  ret = sigaction(SIGHUP, NULL, &old_action);
388
144
  if(ret == -1){
389
 
    error_plus(0, errno, "sigaction");
390
 
    return EX_OSERR;
 
145
    perror("sigaction");
 
146
    return EXIT_FAILURE;
391
147
  }
392
 
  if(old_action.sa_handler != SIG_IGN){
 
148
  if (old_action.sa_handler != SIG_IGN){
393
149
    ret = sigaction(SIGHUP, &new_action, NULL);
394
150
    if(ret == -1){
395
 
      error_plus(0, errno, "sigaction");
396
 
      return EX_OSERR;
 
151
      perror("sigaction");
 
152
      return EXIT_FAILURE;
397
153
    }
398
154
  }
399
155
  ret = sigaction(SIGTERM, NULL, &old_action);
400
156
  if(ret == -1){
401
 
    error_plus(0, errno, "sigaction");
402
 
    return EX_OSERR;
 
157
    perror("sigaction");
 
158
    return EXIT_FAILURE;
403
159
  }
404
 
  if(old_action.sa_handler != SIG_IGN){
 
160
  if (old_action.sa_handler != SIG_IGN){
405
161
    ret = sigaction(SIGTERM, &new_action, NULL);
406
162
    if(ret == -1){
407
 
      error_plus(0, errno, "sigaction");
408
 
      return EX_OSERR;
 
163
      perror("sigaction");
 
164
      return EXIT_FAILURE;
409
165
    }
410
166
  }
411
167
  
412
168
  
413
 
  if(debug){
 
169
  if (debug){
414
170
    fprintf(stderr, "Removing echo flag from terminal attributes\n");
415
171
  }
416
172
  
417
173
  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
 
    }
 
174
  t_new.c_lflag &= ~ECHO;
 
175
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
 
176
    perror("tcsetattr-echo");
 
177
    return EXIT_FAILURE;
430
178
  }
431
 
  
432
 
  if(debug){
 
179
 
 
180
  if (debug){
433
181
    fprintf(stderr, "Waiting for input from stdin \n");
434
182
  }
435
183
  while(true){
436
 
    if(quit_now){
 
184
    if (quit_now){
437
185
      if(debug){
438
186
        fprintf(stderr, "Interrupted by signal, exiting.\n");
439
187
      }
444
192
    if(prefix){
445
193
      fprintf(stderr, "%s ", prefix);
446
194
    }
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";
 
195
    {
 
196
      const char *cryptsource = getenv("cryptsource");
 
197
      const char *crypttarget = getenv("crypttarget");
 
198
      const char *const prompt
 
199
        = "Enter passphrase to unlock the disk";
461
200
      if(cryptsource == NULL){
462
201
        if(crypttarget == NULL){
463
 
          fprintf(stderr, "%s to unlock the disk: ", prompt2);
 
202
          fprintf(stderr, "%s: ", prompt);
464
203
        } else {
465
 
          fprintf(stderr, "%s (%s)\n%s: ", prompt1, crypttarget,
466
 
                  prompt2);
 
204
          fprintf(stderr, "%s (%s): ", prompt, crypttarget);
467
205
        }
468
206
      } else {
469
207
        if(crypttarget == NULL){
470
 
          fprintf(stderr, "%s %s\n%s: ", prompt1, cryptsource,
471
 
                  prompt2);
 
208
          fprintf(stderr, "%s %s: ", prompt, cryptsource);
472
209
        } else {
473
 
          fprintf(stderr, "%s %s (%s)\n%s: ", prompt1, cryptsource,
474
 
                  crypttarget, prompt2);
 
210
          fprintf(stderr, "%s %s (%s): ", prompt, cryptsource,
 
211
                  crypttarget);
475
212
        }
476
213
      }
477
214
    }
478
 
    sret = getline(&buffer, &n, stdin);
479
 
    if(sret > 0){
 
215
    ret = getline(&buffer, &n, stdin);
 
216
    if (ret > 0){
480
217
      status = EXIT_SUCCESS;
481
218
      /* Make n = data size instead of allocated buffer size */
482
 
      n = (size_t)sret;
 
219
      n = (size_t)ret;
483
220
      /* Strip final newline */
484
 
      if(n > 0 and buffer[n-1] == '\n'){
 
221
      if(n>0 and buffer[n-1] == '\n'){
485
222
        buffer[n-1] = '\0';     /* not strictly necessary */
486
223
        n--;
487
224
      }
488
225
      size_t written = 0;
489
226
      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
 
        }
 
227
        ret = write(STDOUT_FILENO, buffer + written, n - written);
 
228
        if(ret < 0){
 
229
          perror("write");
 
230
          status = EXIT_FAILURE;
 
231
          break;
 
232
        }
 
233
        written += (size_t)ret;
525
234
      }
526
235
      break;
527
236
    }
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
 
        }
 
237
    if (ret < 0){
 
238
      if (errno != EINTR and not feof(stdin)){
 
239
        perror("getline");
 
240
        status = EXIT_FAILURE;
 
241
        break;
547
242
      }
548
243
    }
549
 
    /* if(sret == 0), then the only sensible thing to do is to retry
550
 
       to read from stdin */
 
244
    /* if(ret == 0), then the only sensible thing to do is to retry to
 
245
       read from stdin */
551
246
    fputc('\n', stderr);
552
247
    if(debug and not quit_now){
553
 
      /* If quit_now is nonzero, we were interrupted by a signal, and
 
248
      /* If quit_now is true, we were interrupted by a signal, and
554
249
         will print that later, so no need to show this too. */
555
250
      fprintf(stderr, "getline() returned 0, retrying.\n");
556
251
    }
557
252
  }
558
 
  
 
253
 
559
254
  free(buffer);
560
255
  
561
 
  if(debug){
 
256
  if (debug){
562
257
    fprintf(stderr, "Restoring terminal attributes\n");
563
258
  }
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){
 
259
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
 
260
    perror("tcsetattr+echo");
 
261
  }
 
262
  
 
263
  if (debug){
579
264
    fprintf(stderr, "%s is exiting with status %d\n", argv[0],
580
265
            status);
581
266
  }
582
 
  if(status == EXIT_SUCCESS or status == EX_OK){
 
267
  if(status == EXIT_SUCCESS){
583
268
    fputc('\n', stderr);
584
269
  }
585
270