/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 Hogeborn
  • Date: 2019-08-05 21:14:05 UTC
  • Revision ID: teddy@recompile.se-20190805211405-9m6hecekaihpttz9
Override lintian warnings about upgrading from old versions

There are some really things which are imperative that we fix in case
someone were to upgrade from a really old version.  We want to keep
these fixes in the postinst maintainer scripts, even though lintian
complains about such old upgrades not being supported by Debian in
general.  We prefer the code being there, for the sake of the users.

* debian/mandos-client.lintian-overrides
  (maintainer-script-supports-ancient-package-version): New.
  debian/mandos.lintian-overrides
  (maintainer-script-supports-ancient-package-version): - '' -

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
/*
3
3
 * Password-prompt - Read a password from the terminal and print it
4
4
 * 
5
 
 * Copyright © 2008-2010 Teddy Hogeborn
6
 
 * Copyright © 2008-2010 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
 
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
14
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of
15
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
18
 * General Public License for more details.
17
19
 * 
18
20
 * You should have received a copy of the GNU General Public License
19
 
 * along with this program.  If not, see
20
 
 * <http://www.gnu.org/licenses/>.
 
21
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
21
22
 * 
22
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
23
 * Contact the authors at <mandos@recompile.se>.
23
24
 */
24
25
 
25
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, readlink() */
 
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
37
                                   SIGQUIT, SIGHUP, SIGTERM,
36
38
                                   raise() */
37
39
#include <stddef.h>             /* NULL, size_t, ssize_t */
38
 
#include <sys/types.h>          /* ssize_t, struct dirent, pid_t, ssize_t */
 
40
#include <sys/types.h>          /* ssize_t, struct dirent, pid_t,
 
41
                                   ssize_t, open() */
39
42
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
40
43
                                   getenv(), free() */
41
44
#include <dirent.h>             /* scandir(), alphasort() */
42
45
#include <stdio.h>              /* fprintf(), stderr, getline(),
43
 
                                   stdin, feof(), fputc()
44
 
                                */
 
46
                                   stdin, feof(), fputc(), vfprintf(),
 
47
                                   vasprintf() */
45
48
#include <errno.h>              /* errno, EBADF, ENOTTY, EINVAL,
46
49
                                   EFAULT, EFBIG, EIO, ENOSPC, EINTR
47
50
                                */
49
52
#include <iso646.h>             /* or, not */
50
53
#include <stdbool.h>            /* bool, false, true */
51
54
#include <inttypes.h>           /* strtoumax() */
52
 
#include <sys/stat.h>           /* struct stat, lstat() */
53
 
#include <string.h>             /* strlen, rindex, memcmp */
 
55
#include <sys/stat.h>           /* struct stat, lstat(), open() */
 
56
#include <string.h>             /* strlen, rindex, memcmp, strerror()
 
57
                                 */
54
58
#include <argp.h>               /* struct argp_option, struct
55
59
                                   argp_state, struct argp,
56
60
                                   argp_parse(), error_t,
58
62
                                   ARGP_ERR_UNKNOWN */
59
63
#include <sysexits.h>           /* EX_SOFTWARE, EX_OSERR,
60
64
                                   EX_UNAVAILABLE, EX_IOERR, EX_OK */
 
65
#include <fcntl.h>              /* open() */
 
66
#include <stdarg.h>             /* va_list, va_start(), ... */
61
67
 
62
68
volatile sig_atomic_t quit_now = 0;
63
69
int signal_received;
64
70
bool debug = false;
65
71
const char *argp_program_version = "password-prompt " VERSION;
66
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
67
 
 
68
 
/* Needed for conflic resolution */
69
 
const char plymouthd_path[] = "/sbin/plymouth";
70
 
 
 
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
}
71
99
 
72
100
static void termination_handler(int signum){
73
101
  if(quit_now){
79
107
 
80
108
bool conflict_detection(void){
81
109
 
82
 
  /* plymouth conflicts with password-promt since both want to control the
83
 
     associated terminal. Password-prompt exit since plymouth perferms the same
84
 
     functionallity.
 
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.
85
113
   */
 
114
  if(access("/run/plymouth/pid", R_OK) == 0){
 
115
    return true;
 
116
  }
 
117
  
 
118
  __attribute__((nonnull))
86
119
  int is_plymouth(const struct dirent *proc_entry){
87
120
    int ret;
 
121
    int cl_fd;
88
122
    {
89
 
      uintmax_t maxvalue;
 
123
      uintmax_t proc_id;
90
124
      char *tmp;
91
125
      errno = 0;
92
 
      maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
126
      proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
93
127
      
94
128
      if(errno != 0 or *tmp != '\0'
95
 
         or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
129
         or proc_id != (uintmax_t)((pid_t)proc_id)){
96
130
        return 0;
97
131
      }
98
132
    }
99
 
    char exe_target[sizeof(plymouthd_path)];
100
 
    char *exe_link;
101
 
    ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name);
 
133
    
 
134
    char *cmdline_filename;
 
135
    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
 
136
                   proc_entry->d_name);
102
137
    if(ret == -1){
103
 
      error(0, errno, "asprintf");
 
138
      error_plus(0, errno, "asprintf");
104
139
      return 0;
105
140
    }
106
 
  
107
 
    struct stat exe_stat;
108
 
    ret = lstat(exe_link, &exe_stat);
109
 
    if(ret == -1){
110
 
      free(exe_link);
 
141
    
 
142
    /* Open /proc/<pid>/cmdline */
 
143
    cl_fd = open(cmdline_filename, O_RDONLY);
 
144
    free(cmdline_filename);
 
145
    if(cl_fd == -1){
111
146
      if(errno != ENOENT){
112
 
        error(0, errno, "lstat");
113
 
      }
114
 
      return 0;
115
 
    }
116
 
  
117
 
    if(not S_ISLNK(exe_stat.st_mode)
118
 
       or exe_stat.st_uid != 0
119
 
       or exe_stat.st_gid != 0){
120
 
      free(exe_link);
121
 
      return 0;
122
 
    }
123
 
  
124
 
    ssize_t sret = readlink(exe_link, exe_target, sizeof(exe_target));
125
 
    free(exe_link);
126
 
    if((sret != (ssize_t)sizeof(plymouthd_path)-1) or
127
 
       (memcmp(plymouthd_path, exe_target,
128
 
               sizeof(plymouthd_path)-1) != 0)){
129
 
      return 0;
130
 
    }
 
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);
131
215
    return 1;
132
216
  }
133
 
 
134
 
  struct dirent **direntries;
 
217
  
 
218
  struct dirent **direntries = NULL;
135
219
  int ret;
136
220
  ret = scandir("/proc", &direntries, is_plymouth, alphasort);
137
 
  if (ret == -1){
138
 
    error(1, errno, "scandir");
139
 
  }
140
 
  if (ret < 0){
141
 
    return 1;
142
 
  } else {
143
 
    return 0;
144
 
  }
 
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;
145
232
}
146
233
 
147
234
 
152
239
  struct termios t_new, t_old;
153
240
  char *buffer = NULL;
154
241
  char *prefix = NULL;
 
242
  char *prompt = NULL;
155
243
  int status = EXIT_SUCCESS;
156
244
  struct sigaction old_action,
157
245
    new_action = { .sa_handler = termination_handler,
161
249
      { .name = "prefix", .key = 'p',
162
250
        .arg = "PREFIX", .flags = 0,
163
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 },
164
255
      { .name = "debug", .key = 128,
165
256
        .doc = "Debug mode", .group = 3 },
166
257
      /*
175
266
      { .name = NULL }
176
267
    };
177
268
    
 
269
    __attribute__((nonnull(3)))
178
270
    error_t parse_opt (int key, char *arg, struct argp_state *state){
179
271
      errno = 0;
180
272
      switch (key){
181
 
      case 'p':
 
273
      case 'p':                 /* --prefix */
182
274
        prefix = arg;
183
275
        break;
184
 
      case 128:
 
276
      case 128:                 /* --debug */
185
277
        debug = true;
186
278
        break;
 
279
      case 129:                 /* --prompt */
 
280
        prompt = arg;
 
281
        break;
187
282
        /*
188
283
         * These reproduce what we would get without ARGP_NO_HELP
189
284
         */
191
286
        argp_state_help(state, state->out_stream,
192
287
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
193
288
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
289
        __builtin_unreachable();
194
290
      case -3:                  /* --usage */
195
291
        argp_state_help(state, state->out_stream,
196
292
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
293
        __builtin_unreachable();
197
294
      case 'V':                 /* --version */
198
295
        fprintf(state->out_stream, "%s\n", argp_program_version);
199
296
        exit(argp_err_exit_status);
216
313
    case ENOMEM:
217
314
    default:
218
315
      errno = ret;
219
 
      error(0, errno, "argp_parse");
 
316
      error_plus(0, errno, "argp_parse");
220
317
      return EX_OSERR;
221
318
    case EINVAL:
222
319
      return EX_USAGE;
227
324
    fprintf(stderr, "Starting %s\n", argv[0]);
228
325
  }
229
326
 
230
 
  if (conflict_detection()){
 
327
  if(conflict_detection()){
231
328
    if(debug){
232
 
      fprintf(stderr, "Stopping %s because of conflict", argv[0]);
 
329
      fprintf(stderr, "Stopping %s because of conflict\n", argv[0]);
233
330
    }
234
331
    return EXIT_FAILURE;
235
332
  }
240
337
  
241
338
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
242
339
    int e = errno;
243
 
    error(0, errno, "tcgetattr");
 
340
    error_plus(0, errno, "tcgetattr");
244
341
    switch(e){
245
342
    case EBADF:
246
343
    case ENOTTY:
253
350
  sigemptyset(&new_action.sa_mask);
254
351
  ret = sigaddset(&new_action.sa_mask, SIGINT);
255
352
  if(ret == -1){
256
 
    error(0, errno, "sigaddset");
 
353
    error_plus(0, errno, "sigaddset");
257
354
    return EX_OSERR;
258
355
  }
259
356
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
260
357
  if(ret == -1){
261
 
    error(0, errno, "sigaddset");
 
358
    error_plus(0, errno, "sigaddset");
262
359
    return EX_OSERR;
263
360
  }
264
361
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
265
362
  if(ret == -1){
266
 
    error(0, errno, "sigaddset");
 
363
    error_plus(0, errno, "sigaddset");
267
364
    return EX_OSERR;
268
365
  }
269
366
  /* Need to check if the handler is SIG_IGN before handling:
272
369
  */
273
370
  ret = sigaction(SIGINT, NULL, &old_action);
274
371
  if(ret == -1){
275
 
    error(0, errno, "sigaction");
 
372
    error_plus(0, errno, "sigaction");
276
373
    return EX_OSERR;
277
374
  }
278
375
  if(old_action.sa_handler != SIG_IGN){
279
376
    ret = sigaction(SIGINT, &new_action, NULL);
280
377
    if(ret == -1){
281
 
      error(0, errno, "sigaction");
 
378
      error_plus(0, errno, "sigaction");
282
379
      return EX_OSERR;
283
380
    }
284
381
  }
285
382
  ret = sigaction(SIGHUP, NULL, &old_action);
286
383
  if(ret == -1){
287
 
    error(0, errno, "sigaction");
 
384
    error_plus(0, errno, "sigaction");
288
385
    return EX_OSERR;
289
386
  }
290
387
  if(old_action.sa_handler != SIG_IGN){
291
388
    ret = sigaction(SIGHUP, &new_action, NULL);
292
389
    if(ret == -1){
293
 
      error(0, errno, "sigaction");
 
390
      error_plus(0, errno, "sigaction");
294
391
      return EX_OSERR;
295
392
    }
296
393
  }
297
394
  ret = sigaction(SIGTERM, NULL, &old_action);
298
395
  if(ret == -1){
299
 
    error(0, errno, "sigaction");
 
396
    error_plus(0, errno, "sigaction");
300
397
    return EX_OSERR;
301
398
  }
302
399
  if(old_action.sa_handler != SIG_IGN){
303
400
    ret = sigaction(SIGTERM, &new_action, NULL);
304
401
    if(ret == -1){
305
 
      error(0, errno, "sigaction");
 
402
      error_plus(0, errno, "sigaction");
306
403
      return EX_OSERR;
307
404
    }
308
405
  }
316
413
  t_new.c_lflag &= ~(tcflag_t)ECHO;
317
414
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
318
415
    int e = errno;
319
 
    error(0, errno, "tcsetattr-echo");
 
416
    error_plus(0, errno, "tcsetattr-echo");
320
417
    switch(e){
321
418
    case EBADF:
322
419
    case ENOTTY:
342
439
    if(prefix){
343
440
      fprintf(stderr, "%s ", prefix);
344
441
    }
345
 
    {
 
442
    if(prompt != NULL){
 
443
      fprintf(stderr, "%s: ", prompt);
 
444
    } else {
346
445
      const char *cryptsource = getenv("CRYPTTAB_SOURCE");
347
446
      const char *crypttarget = getenv("CRYPTTAB_NAME");
348
447
      /* Before cryptsetup 1.1.0~rc2 */
386
485
        sret = write(STDOUT_FILENO, buffer + written, n - written);
387
486
        if(sret < 0){
388
487
          int e = errno;
389
 
          error(0, errno, "write");
 
488
          error_plus(0, errno, "write");
390
489
          switch(e){
391
490
          case EBADF:
392
491
          case EFAULT:
408
507
      sret = close(STDOUT_FILENO);
409
508
      if(sret == -1){
410
509
        int e = errno;
411
 
        error(0, errno, "close");
 
510
        error_plus(0, errno, "close");
412
511
        switch(e){
413
512
        case EBADF:
414
513
          status = EX_OSFILE;
423
522
    }
424
523
    if(sret < 0){
425
524
      int e = errno;
426
 
      if(errno != EINTR and not feof(stdin)){
427
 
        error(0, errno, "getline");
428
 
        switch(e){
429
 
        case EBADF:
430
 
          status = EX_UNAVAILABLE;
431
 
        case EIO:
432
 
        case EINVAL:
433
 
        default:
434
 
          status = EX_IOERR;
 
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
          }
435
538
          break;
 
539
        } else {
 
540
          clearerr(stdin);
436
541
        }
437
 
        break;
438
542
      }
439
543
    }
440
 
    /* if(sret == 0), then the only sensible thing to do is to retry to
441
 
       read from stdin */
 
544
    /* if(sret == 0), then the only sensible thing to do is to retry
 
545
       to read from stdin */
442
546
    fputc('\n', stderr);
443
547
    if(debug and not quit_now){
444
548
      /* If quit_now is nonzero, we were interrupted by a signal, and
453
557
    fprintf(stderr, "Restoring terminal attributes\n");
454
558
  }
455
559
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
456
 
    error(0, errno, "tcsetattr+echo");
 
560
    error_plus(0, errno, "tcsetattr+echo");
457
561
  }
458
562
  
459
563
  if(quit_now){
461
565
    old_action.sa_handler = SIG_DFL;
462
566
    ret = sigaction(signal_received, &old_action, NULL);
463
567
    if(ret == -1){
464
 
      error(0, errno, "sigaction");
 
568
      error_plus(0, errno, "sigaction");
465
569
    }
466
570
    raise(signal_received);
467
571
  }