/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: teddy at recompile
  • Date: 2020-12-03 20:30:45 UTC
  • Revision ID: teddy@recompile.se-20201203203045-iqd6nq9y5nwalh1x
Minor fix of a test function

In dracut-module/password-agent, the test function
test_send_password_to_socket_EMSGSIZE() (which tests that the
send_password_to_socket() task function aborts properly when getting
EMSGSIZE when writing to the password socket), part of the test code
is supposed to find a message size which definitely does trigger
EMSGSIZE when send()ing to a socket.  Without a "break" in the proper
place, however, the size given is always exactly 1024 bytes too large.

This is very probably not a problem, since a too large message will
still be too large if it is increased by 1024 bytes, and send(2) in
practice checks the size before reading the buffer.  The biggest issue
would be if some version of send(2) would try to look at the last 1024
bytes of the message buffer before checking the message size; this
would then lead to a buffer over-read when running this test function.
(But even then there would be no security implications since the tests
are not run in the normal operation of the program.)

* dracut-module/password-agent.c
  (test_send_password_to_socket_EMSGSIZE): Break out early when ssret
  < 0 and errno == EMSGSIZE; don't allow loop to increase message_size
  again.

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,
41
43
                                   getenv(), free() */
42
44
#include <dirent.h>             /* scandir(), alphasort() */
43
45
#include <stdio.h>              /* fprintf(), stderr, getline(),
44
 
                                   stdin, feof(), fputc()
45
 
                                */
 
46
                                   stdin, feof(), fputc(), vfprintf(),
 
47
                                   vasprintf() */
46
48
#include <errno.h>              /* errno, EBADF, ENOTTY, EINVAL,
47
49
                                   EFAULT, EFBIG, EIO, ENOSPC, EINTR
48
50
                                */
50
52
#include <iso646.h>             /* or, not */
51
53
#include <stdbool.h>            /* bool, false, true */
52
54
#include <inttypes.h>           /* strtoumax() */
53
 
#include <sys/stat.h>           /* struct stat, lstat(), open() */
54
 
#include <string.h>             /* strlen, rindex, memcmp */
 
55
#include <sys/stat.h>           /* struct stat, lstat(), open() */
 
56
#include <string.h>             /* strlen, rindex, memcmp, strerror()
 
57
                                 */
55
58
#include <argp.h>               /* struct argp_option, struct
56
59
                                   argp_state, struct argp,
57
60
                                   argp_parse(), error_t,
60
63
#include <sysexits.h>           /* EX_SOFTWARE, EX_OSERR,
61
64
                                   EX_UNAVAILABLE, EX_IOERR, EX_OK */
62
65
#include <fcntl.h>              /* open() */
 
66
#include <stdarg.h>             /* va_list, va_start(), ... */
63
67
 
64
68
volatile sig_atomic_t quit_now = 0;
65
69
int signal_received;
66
70
bool debug = false;
67
71
const char *argp_program_version = "password-prompt " VERSION;
68
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
69
 
 
70
 
/* Needed for conflic resolution */
71
 
const char plymouthd_name[] = "plymouthd";
72
 
 
 
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
}
73
99
 
74
100
static void termination_handler(int signum){
75
101
  if(quit_now){
85
111
     from the terminal.  Password-prompt will exit if it detects
86
112
     plymouth since plymouth performs the same functionality.
87
113
   */
 
114
  if(access("/run/plymouth/pid", R_OK) == 0){
 
115
    return true;
 
116
  }
 
117
  
 
118
  __attribute__((nonnull))
88
119
  int is_plymouth(const struct dirent *proc_entry){
89
120
    int ret;
90
121
    int cl_fd;
91
122
    {
92
 
      uintmax_t maxvalue;
 
123
      uintmax_t proc_id;
93
124
      char *tmp;
94
125
      errno = 0;
95
 
      maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
126
      proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
96
127
      
97
128
      if(errno != 0 or *tmp != '\0'
98
 
         or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
129
         or proc_id != (uintmax_t)((pid_t)proc_id)){
99
130
        return 0;
100
131
      }
101
132
    }
102
133
    
103
134
    char *cmdline_filename;
104
 
    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline", proc_entry->d_name);
 
135
    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
 
136
                   proc_entry->d_name);
105
137
    if(ret == -1){
106
 
      error(0, errno, "asprintf");
 
138
      error_plus(0, errno, "asprintf");
107
139
      return 0;
108
140
    }
109
141
    
110
 
    /* Open /proc/<pid>/cmdline  */
 
142
    /* Open /proc/<pid>/cmdline */
111
143
    cl_fd = open(cmdline_filename, O_RDONLY);
112
144
    free(cmdline_filename);
113
145
    if(cl_fd == -1){
114
 
      error(0, errno, "open");
 
146
      if(errno != ENOENT){
 
147
        error_plus(0, errno, "open");
 
148
      }
115
149
      return 0;
116
150
    }
117
151
    
127
161
        if(cmdline_len + blocksize + 1 > cmdline_allocated){
128
162
          tmp = realloc(cmdline, cmdline_allocated + blocksize + 1);
129
163
          if(tmp == NULL){
130
 
            error(0, errno, "realloc");
 
164
            error_plus(0, errno, "realloc");
131
165
            free(cmdline);
132
166
            close(cl_fd);
133
167
            return 0;
140
174
        sret = read(cl_fd, cmdline + cmdline_len,
141
175
                    cmdline_allocated - cmdline_len);
142
176
        if(sret == -1){
143
 
          error(0, errno, "read");
 
177
          error_plus(0, errno, "read");
144
178
          free(cmdline);
145
179
          close(cl_fd);
146
180
          return 0;
149
183
      } while(sret != 0);
150
184
      ret = close(cl_fd);
151
185
      if(ret == -1){
152
 
        error(0, errno, "close");
 
186
        error_plus(0, errno, "close");
153
187
        free(cmdline);
154
188
        return 0;
155
189
      }
165
199
      cmdline_base = cmdline;
166
200
    }
167
201
    
168
 
    if(strcmp(cmdline_base, plymouthd_name) != 0){
 
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
      }
169
207
      free(cmdline);
170
208
      return 0;
171
209
    }
 
210
    if(debug){
 
211
      fprintf(stderr, "\"%s\" equals \"%s\"\n", cmdline_base,
 
212
              plymouth_name);
 
213
    }
172
214
    free(cmdline);
173
215
    return 1;
174
216
  }
175
 
 
176
 
  struct dirent **direntries;
 
217
  
 
218
  struct dirent **direntries = NULL;
177
219
  int ret;
178
220
  ret = scandir("/proc", &direntries, is_plymouth, alphasort);
179
 
  if (ret == -1){
180
 
    error(1, errno, "scandir");
181
 
  }
 
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);
182
231
  return ret > 0;
183
232
}
184
233
 
190
239
  struct termios t_new, t_old;
191
240
  char *buffer = NULL;
192
241
  char *prefix = NULL;
 
242
  char *prompt = NULL;
193
243
  int status = EXIT_SUCCESS;
194
244
  struct sigaction old_action,
195
245
    new_action = { .sa_handler = termination_handler,
199
249
      { .name = "prefix", .key = 'p',
200
250
        .arg = "PREFIX", .flags = 0,
201
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 },
202
255
      { .name = "debug", .key = 128,
203
256
        .doc = "Debug mode", .group = 3 },
204
257
      /*
213
266
      { .name = NULL }
214
267
    };
215
268
    
 
269
    __attribute__((nonnull(3)))
216
270
    error_t parse_opt (int key, char *arg, struct argp_state *state){
217
271
      errno = 0;
218
272
      switch (key){
219
 
      case 'p':
 
273
      case 'p':                 /* --prefix */
220
274
        prefix = arg;
221
275
        break;
222
 
      case 128:
 
276
      case 128:                 /* --debug */
223
277
        debug = true;
224
278
        break;
 
279
      case 129:                 /* --prompt */
 
280
        prompt = arg;
 
281
        break;
225
282
        /*
226
283
         * These reproduce what we would get without ARGP_NO_HELP
227
284
         */
229
286
        argp_state_help(state, state->out_stream,
230
287
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
231
288
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
289
        __builtin_unreachable();
232
290
      case -3:                  /* --usage */
233
291
        argp_state_help(state, state->out_stream,
234
292
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
293
        __builtin_unreachable();
235
294
      case 'V':                 /* --version */
236
295
        fprintf(state->out_stream, "%s\n", argp_program_version);
237
296
        exit(argp_err_exit_status);
254
313
    case ENOMEM:
255
314
    default:
256
315
      errno = ret;
257
 
      error(0, errno, "argp_parse");
 
316
      error_plus(0, errno, "argp_parse");
258
317
      return EX_OSERR;
259
318
    case EINVAL:
260
319
      return EX_USAGE;
265
324
    fprintf(stderr, "Starting %s\n", argv[0]);
266
325
  }
267
326
 
268
 
  if (conflict_detection()){
 
327
  if(conflict_detection()){
269
328
    if(debug){
270
 
      fprintf(stderr, "Stopping %s because of conflict", argv[0]);
 
329
      fprintf(stderr, "Stopping %s because of conflict\n", argv[0]);
271
330
    }
272
331
    return EXIT_FAILURE;
273
332
  }
278
337
  
279
338
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
280
339
    int e = errno;
281
 
    error(0, errno, "tcgetattr");
 
340
    error_plus(0, errno, "tcgetattr");
282
341
    switch(e){
283
342
    case EBADF:
284
343
    case ENOTTY:
291
350
  sigemptyset(&new_action.sa_mask);
292
351
  ret = sigaddset(&new_action.sa_mask, SIGINT);
293
352
  if(ret == -1){
294
 
    error(0, errno, "sigaddset");
 
353
    error_plus(0, errno, "sigaddset");
295
354
    return EX_OSERR;
296
355
  }
297
356
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
298
357
  if(ret == -1){
299
 
    error(0, errno, "sigaddset");
 
358
    error_plus(0, errno, "sigaddset");
300
359
    return EX_OSERR;
301
360
  }
302
361
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
303
362
  if(ret == -1){
304
 
    error(0, errno, "sigaddset");
 
363
    error_plus(0, errno, "sigaddset");
305
364
    return EX_OSERR;
306
365
  }
307
366
  /* Need to check if the handler is SIG_IGN before handling:
310
369
  */
311
370
  ret = sigaction(SIGINT, NULL, &old_action);
312
371
  if(ret == -1){
313
 
    error(0, errno, "sigaction");
 
372
    error_plus(0, errno, "sigaction");
314
373
    return EX_OSERR;
315
374
  }
316
375
  if(old_action.sa_handler != SIG_IGN){
317
376
    ret = sigaction(SIGINT, &new_action, NULL);
318
377
    if(ret == -1){
319
 
      error(0, errno, "sigaction");
 
378
      error_plus(0, errno, "sigaction");
320
379
      return EX_OSERR;
321
380
    }
322
381
  }
323
382
  ret = sigaction(SIGHUP, NULL, &old_action);
324
383
  if(ret == -1){
325
 
    error(0, errno, "sigaction");
 
384
    error_plus(0, errno, "sigaction");
326
385
    return EX_OSERR;
327
386
  }
328
387
  if(old_action.sa_handler != SIG_IGN){
329
388
    ret = sigaction(SIGHUP, &new_action, NULL);
330
389
    if(ret == -1){
331
 
      error(0, errno, "sigaction");
 
390
      error_plus(0, errno, "sigaction");
332
391
      return EX_OSERR;
333
392
    }
334
393
  }
335
394
  ret = sigaction(SIGTERM, NULL, &old_action);
336
395
  if(ret == -1){
337
 
    error(0, errno, "sigaction");
 
396
    error_plus(0, errno, "sigaction");
338
397
    return EX_OSERR;
339
398
  }
340
399
  if(old_action.sa_handler != SIG_IGN){
341
400
    ret = sigaction(SIGTERM, &new_action, NULL);
342
401
    if(ret == -1){
343
 
      error(0, errno, "sigaction");
 
402
      error_plus(0, errno, "sigaction");
344
403
      return EX_OSERR;
345
404
    }
346
405
  }
354
413
  t_new.c_lflag &= ~(tcflag_t)ECHO;
355
414
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
356
415
    int e = errno;
357
 
    error(0, errno, "tcsetattr-echo");
 
416
    error_plus(0, errno, "tcsetattr-echo");
358
417
    switch(e){
359
418
    case EBADF:
360
419
    case ENOTTY:
380
439
    if(prefix){
381
440
      fprintf(stderr, "%s ", prefix);
382
441
    }
383
 
    {
 
442
    if(prompt != NULL){
 
443
      fprintf(stderr, "%s: ", prompt);
 
444
    } else {
384
445
      const char *cryptsource = getenv("CRYPTTAB_SOURCE");
385
446
      const char *crypttarget = getenv("CRYPTTAB_NAME");
386
447
      /* Before cryptsetup 1.1.0~rc2 */
424
485
        sret = write(STDOUT_FILENO, buffer + written, n - written);
425
486
        if(sret < 0){
426
487
          int e = errno;
427
 
          error(0, errno, "write");
 
488
          error_plus(0, errno, "write");
428
489
          switch(e){
429
490
          case EBADF:
430
491
          case EFAULT:
446
507
      sret = close(STDOUT_FILENO);
447
508
      if(sret == -1){
448
509
        int e = errno;
449
 
        error(0, errno, "close");
 
510
        error_plus(0, errno, "close");
450
511
        switch(e){
451
512
        case EBADF:
452
513
          status = EX_OSFILE;
461
522
    }
462
523
    if(sret < 0){
463
524
      int e = errno;
464
 
      if(errno != EINTR and not feof(stdin)){
465
 
        error(0, errno, "getline");
466
 
        switch(e){
467
 
        case EBADF:
468
 
          status = EX_UNAVAILABLE;
469
 
        case EIO:
470
 
        case EINVAL:
471
 
        default:
472
 
          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
          }
473
538
          break;
 
539
        } else {
 
540
          clearerr(stdin);
474
541
        }
475
 
        break;
476
542
      }
477
543
    }
478
 
    /* if(sret == 0), then the only sensible thing to do is to retry to
479
 
       read from stdin */
 
544
    /* if(sret == 0), then the only sensible thing to do is to retry
 
545
       to read from stdin */
480
546
    fputc('\n', stderr);
481
547
    if(debug and not quit_now){
482
548
      /* If quit_now is nonzero, we were interrupted by a signal, and
491
557
    fprintf(stderr, "Restoring terminal attributes\n");
492
558
  }
493
559
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
494
 
    error(0, errno, "tcsetattr+echo");
 
560
    error_plus(0, errno, "tcsetattr+echo");
495
561
  }
496
562
  
497
563
  if(quit_now){
499
565
    old_action.sa_handler = SIG_DFL;
500
566
    ret = sigaction(signal_received, &old_action, NULL);
501
567
    if(ret == -1){
502
 
      error(0, errno, "sigaction");
 
568
      error_plus(0, errno, "sigaction");
503
569
    }
504
570
    raise(signal_received);
505
571
  }