/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: 2021-02-03 23:10:42 UTC
  • mto: This revision was merged to the branch mainline in revision 406.
  • Revision ID: teddy@recompile.se-20210203231042-2z3egrvpo1zt7nej
mandos-ctl: Fix bad test for command.Remove and related minor issues

The test for command.Remove removes all clients from the spy server,
and then loops over all clients, looking for the corresponding Remove
command as recorded by the spy server.  But since since there aren't
any clients left after they were removed, no assertions are made, and
the test therefore does nothing.  Fix this.

In tests for command.Approve and command.Deny, add checks that clients
were not somehow removed by the command (in which case, likewise, no
assertions are made).

Add related checks to TestPropertySetterCmd.runTest; i.e. test that a
sequence is not empty before looping over it and making assertions.

* mandos-ctl (TestBaseCommands.test_Remove): Save a copy of the
  original "clients" dict, and loop over those instead.  Add assertion
  that all clients were indeed removed.  Also fix the code which looks
  for the Remove command, which now needs to actually work.
  (TestBaseCommands.test_Approve, TestBaseCommands.test_Deny): Add
  assertion that there are still clients before looping over them.
  (TestPropertySetterCmd.runTest): Add assertion that the list of
  values to get is not empty before looping over them.  Also add check
  that there are still clients before looping over clients.

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, 2021 Teddy Hogeborn
 
6
 * Copyright © 2008-2019, 2021 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
 
#define _GNU_SOURCE             /* getline(), asprintf() */
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, readlink() */
32
 
#include <signal.h>             /* sig_atomic_t, raise(), struct
33
 
                                   sigaction, sigemptyset(),
34
 
                                   sigaction(), sigaddset(), SIGINT,
35
 
                                   SIGQUIT, SIGHUP, SIGTERM,
 
26
#define _GNU_SOURCE             /* vasprintf(),
 
27
                                   program_invocation_short_name,
 
28
                                   asprintf(), getline() */
 
29
#include <sys/types.h>          /* sig_atomic_t, pid_t */
 
30
#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,
36
69
                                   raise() */
37
 
#include <stddef.h>             /* NULL, size_t, ssize_t */
38
 
#include <sys/types.h>          /* ssize_t, struct dirent, pid_t,
39
 
                                   ssize_t, open() */
40
 
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
41
 
                                   getenv(), free() */
42
 
#include <dirent.h>             /* scandir(), alphasort() */
43
 
#include <stdio.h>              /* fprintf(), stderr, getline(),
44
 
                                   stdin, feof(), fputc(), vfprintf(),
45
 
                                   vasprintf() */
46
 
#include <errno.h>              /* errno, EBADF, ENOTTY, EINVAL,
47
 
                                   EFAULT, EFBIG, EIO, ENOSPC, EINTR
48
 
                                */
49
 
#include <error.h>              /* error() */
50
 
#include <iso646.h>             /* or, not */
51
 
#include <stdbool.h>            /* bool, false, true */
52
 
#include <inttypes.h>           /* strtoumax() */
53
 
#include <sys/stat.h>           /* struct stat, lstat(), open() */
54
 
#include <string.h>             /* strlen, rindex, memcmp, strerror()
55
 
                                 */
56
 
#include <argp.h>               /* struct argp_option, struct
57
 
                                   argp_state, struct argp,
58
 
                                   argp_parse(), error_t,
59
 
                                   ARGP_KEY_ARG, ARGP_KEY_END,
60
 
                                   ARGP_ERR_UNKNOWN */
61
 
#include <sysexits.h>           /* EX_SOFTWARE, EX_OSERR,
62
 
                                   EX_UNAVAILABLE, EX_IOERR, EX_OK */
63
 
#include <fcntl.h>              /* open() */
64
 
#include <stdarg.h>             /* va_list, va_start(), ... */
 
70
#include <sysexits.h>           /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE,
 
71
                                   EX_IOERR, EX_OSFILE, EX_OK */
65
72
 
66
73
volatile sig_atomic_t quit_now = 0;
67
74
int signal_received;
68
75
bool debug = false;
69
76
const char *argp_program_version = "password-prompt " VERSION;
70
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
77
const char *argp_program_bug_address = "<mandos@recompile.se>";
71
78
 
72
79
/* Needed for conflict resolution */
73
80
const char plymouth_name[] = "plymouthd";
74
81
 
75
82
/* Function to use when printing errors */
 
83
__attribute__((format (gnu_printf, 3, 4)))
76
84
void error_plus(int status, int errnum, const char *formatstring,
77
85
                ...){
78
86
  va_list ap;
81
89
  
82
90
  va_start(ap, formatstring);
83
91
  ret = vasprintf(&text, formatstring, ap);
84
 
  if (ret == -1){
 
92
  if(ret == -1){
85
93
    fprintf(stderr, "Mandos plugin %s: ",
86
94
            program_invocation_short_name);
87
95
    vfprintf(stderr, formatstring, ap);
88
 
    fprintf(stderr, ": ");
89
 
    fprintf(stderr, "%s\n", strerror(errnum));
 
96
    fprintf(stderr, ": %s\n", strerror(errnum));
90
97
    error(status, errno, "vasprintf while printing error");
91
98
    return;
92
99
  }
109
116
     from the terminal.  Password-prompt will exit if it detects
110
117
     plymouth since plymouth performs the same functionality.
111
118
   */
 
119
  if(access("/run/plymouth/pid", R_OK) == 0){
 
120
    return true;
 
121
  }
 
122
  
 
123
  __attribute__((nonnull))
112
124
  int is_plymouth(const struct dirent *proc_entry){
113
125
    int ret;
114
126
    int cl_fd;
115
127
    {
116
 
      uintmax_t maxvalue;
 
128
      uintmax_t proc_id;
117
129
      char *tmp;
118
130
      errno = 0;
119
 
      maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
131
      proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
120
132
      
121
133
      if(errno != 0 or *tmp != '\0'
122
 
         or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
134
         or proc_id != (uintmax_t)((pid_t)proc_id)){
123
135
        return 0;
124
136
      }
125
137
    }
128
140
    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
129
141
                   proc_entry->d_name);
130
142
    if(ret == -1){
131
 
      error(0, errno, "asprintf");
 
143
      error_plus(0, errno, "asprintf");
132
144
      return 0;
133
145
    }
134
146
    
137
149
    free(cmdline_filename);
138
150
    if(cl_fd == -1){
139
151
      if(errno != ENOENT){
140
 
        error(0, errno, "open");
 
152
        error_plus(0, errno, "open");
141
153
      }
142
154
      return 0;
143
155
    }
154
166
        if(cmdline_len + blocksize + 1 > cmdline_allocated){
155
167
          tmp = realloc(cmdline, cmdline_allocated + blocksize + 1);
156
168
          if(tmp == NULL){
157
 
            error(0, errno, "realloc");
 
169
            error_plus(0, errno, "realloc");
158
170
            free(cmdline);
159
171
            close(cl_fd);
160
172
            return 0;
167
179
        sret = read(cl_fd, cmdline + cmdline_len,
168
180
                    cmdline_allocated - cmdline_len);
169
181
        if(sret == -1){
170
 
          error(0, errno, "read");
 
182
          error_plus(0, errno, "read");
171
183
          free(cmdline);
172
184
          close(cl_fd);
173
185
          return 0;
176
188
      } while(sret != 0);
177
189
      ret = close(cl_fd);
178
190
      if(ret == -1){
179
 
        error(0, errno, "close");
 
191
        error_plus(0, errno, "close");
180
192
        free(cmdline);
181
193
        return 0;
182
194
      }
208
220
    return 1;
209
221
  }
210
222
  
211
 
  struct dirent **direntries;
 
223
  struct dirent **direntries = NULL;
212
224
  int ret;
213
225
  ret = scandir("/proc", &direntries, is_plymouth, alphasort);
214
 
  if (ret == -1){
215
 
    error(1, errno, "scandir");
216
 
  }
 
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);
217
236
  return ret > 0;
218
237
}
219
238
 
225
244
  struct termios t_new, t_old;
226
245
  char *buffer = NULL;
227
246
  char *prefix = NULL;
 
247
  char *prompt = NULL;
228
248
  int status = EXIT_SUCCESS;
229
249
  struct sigaction old_action,
230
250
    new_action = { .sa_handler = termination_handler,
234
254
      { .name = "prefix", .key = 'p',
235
255
        .arg = "PREFIX", .flags = 0,
236
256
        .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 },
237
260
      { .name = "debug", .key = 128,
238
261
        .doc = "Debug mode", .group = 3 },
239
262
      /*
248
271
      { .name = NULL }
249
272
    };
250
273
    
 
274
    __attribute__((nonnull(3)))
251
275
    error_t parse_opt (int key, char *arg, struct argp_state *state){
252
276
      errno = 0;
253
277
      switch (key){
254
 
      case 'p':
 
278
      case 'p':                 /* --prefix */
255
279
        prefix = arg;
256
280
        break;
257
 
      case 128:
 
281
      case 128:                 /* --debug */
258
282
        debug = true;
259
283
        break;
 
284
      case 129:                 /* --prompt */
 
285
        prompt = arg;
 
286
        break;
260
287
        /*
261
288
         * These reproduce what we would get without ARGP_NO_HELP
262
289
         */
264
291
        argp_state_help(state, state->out_stream,
265
292
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
266
293
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
294
        __builtin_unreachable();
267
295
      case -3:                  /* --usage */
268
296
        argp_state_help(state, state->out_stream,
269
297
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
298
        __builtin_unreachable();
270
299
      case 'V':                 /* --version */
271
300
        fprintf(state->out_stream, "%s\n", argp_program_version);
272
301
        exit(argp_err_exit_status);
289
318
    case ENOMEM:
290
319
    default:
291
320
      errno = ret;
292
 
      error(0, errno, "argp_parse");
 
321
      error_plus(0, errno, "argp_parse");
293
322
      return EX_OSERR;
294
323
    case EINVAL:
295
324
      return EX_USAGE;
300
329
    fprintf(stderr, "Starting %s\n", argv[0]);
301
330
  }
302
331
 
303
 
  if (conflict_detection()){
 
332
  if(conflict_detection()){
304
333
    if(debug){
305
334
      fprintf(stderr, "Stopping %s because of conflict\n", argv[0]);
306
335
    }
313
342
  
314
343
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
315
344
    int e = errno;
316
 
    error(0, errno, "tcgetattr");
 
345
    error_plus(0, errno, "tcgetattr");
317
346
    switch(e){
318
347
    case EBADF:
319
348
    case ENOTTY:
326
355
  sigemptyset(&new_action.sa_mask);
327
356
  ret = sigaddset(&new_action.sa_mask, SIGINT);
328
357
  if(ret == -1){
329
 
    error(0, errno, "sigaddset");
 
358
    error_plus(0, errno, "sigaddset");
330
359
    return EX_OSERR;
331
360
  }
332
361
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
333
362
  if(ret == -1){
334
 
    error(0, errno, "sigaddset");
 
363
    error_plus(0, errno, "sigaddset");
335
364
    return EX_OSERR;
336
365
  }
337
366
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
338
367
  if(ret == -1){
339
 
    error(0, errno, "sigaddset");
 
368
    error_plus(0, errno, "sigaddset");
340
369
    return EX_OSERR;
341
370
  }
342
371
  /* Need to check if the handler is SIG_IGN before handling:
345
374
  */
346
375
  ret = sigaction(SIGINT, NULL, &old_action);
347
376
  if(ret == -1){
348
 
    error(0, errno, "sigaction");
 
377
    error_plus(0, errno, "sigaction");
349
378
    return EX_OSERR;
350
379
  }
351
380
  if(old_action.sa_handler != SIG_IGN){
352
381
    ret = sigaction(SIGINT, &new_action, NULL);
353
382
    if(ret == -1){
354
 
      error(0, errno, "sigaction");
 
383
      error_plus(0, errno, "sigaction");
355
384
      return EX_OSERR;
356
385
    }
357
386
  }
358
387
  ret = sigaction(SIGHUP, NULL, &old_action);
359
388
  if(ret == -1){
360
 
    error(0, errno, "sigaction");
 
389
    error_plus(0, errno, "sigaction");
361
390
    return EX_OSERR;
362
391
  }
363
392
  if(old_action.sa_handler != SIG_IGN){
364
393
    ret = sigaction(SIGHUP, &new_action, NULL);
365
394
    if(ret == -1){
366
 
      error(0, errno, "sigaction");
 
395
      error_plus(0, errno, "sigaction");
367
396
      return EX_OSERR;
368
397
    }
369
398
  }
370
399
  ret = sigaction(SIGTERM, NULL, &old_action);
371
400
  if(ret == -1){
372
 
    error(0, errno, "sigaction");
 
401
    error_plus(0, errno, "sigaction");
373
402
    return EX_OSERR;
374
403
  }
375
404
  if(old_action.sa_handler != SIG_IGN){
376
405
    ret = sigaction(SIGTERM, &new_action, NULL);
377
406
    if(ret == -1){
378
 
      error(0, errno, "sigaction");
 
407
      error_plus(0, errno, "sigaction");
379
408
      return EX_OSERR;
380
409
    }
381
410
  }
389
418
  t_new.c_lflag &= ~(tcflag_t)ECHO;
390
419
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
391
420
    int e = errno;
392
 
    error(0, errno, "tcsetattr-echo");
 
421
    error_plus(0, errno, "tcsetattr-echo");
393
422
    switch(e){
394
423
    case EBADF:
395
424
    case ENOTTY:
415
444
    if(prefix){
416
445
      fprintf(stderr, "%s ", prefix);
417
446
    }
418
 
    {
 
447
    if(prompt != NULL){
 
448
      fprintf(stderr, "%s: ", prompt);
 
449
    } else {
419
450
      const char *cryptsource = getenv("CRYPTTAB_SOURCE");
420
451
      const char *crypttarget = getenv("CRYPTTAB_NAME");
421
452
      /* Before cryptsetup 1.1.0~rc2 */
459
490
        sret = write(STDOUT_FILENO, buffer + written, n - written);
460
491
        if(sret < 0){
461
492
          int e = errno;
462
 
          error(0, errno, "write");
 
493
          error_plus(0, errno, "write");
463
494
          switch(e){
464
495
          case EBADF:
465
496
          case EFAULT:
481
512
      sret = close(STDOUT_FILENO);
482
513
      if(sret == -1){
483
514
        int e = errno;
484
 
        error(0, errno, "close");
 
515
        error_plus(0, errno, "close");
485
516
        switch(e){
486
517
        case EBADF:
487
518
          status = EX_OSFILE;
496
527
    }
497
528
    if(sret < 0){
498
529
      int e = errno;
499
 
      if(errno != EINTR and not feof(stdin)){
500
 
        error(0, errno, "getline");
501
 
        switch(e){
502
 
        case EBADF:
503
 
          status = EX_UNAVAILABLE;
504
 
        case EIO:
505
 
        case EINVAL:
506
 
        default:
507
 
          status = EX_IOERR;
 
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
          }
508
543
          break;
 
544
        } else {
 
545
          clearerr(stdin);
509
546
        }
510
 
        break;
511
547
      }
512
548
    }
513
549
    /* if(sret == 0), then the only sensible thing to do is to retry
526
562
    fprintf(stderr, "Restoring terminal attributes\n");
527
563
  }
528
564
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
529
 
    error(0, errno, "tcsetattr+echo");
 
565
    error_plus(0, errno, "tcsetattr+echo");
530
566
  }
531
567
  
532
568
  if(quit_now){
534
570
    old_action.sa_handler = SIG_DFL;
535
571
    ret = sigaction(signal_received, &old_action, NULL);
536
572
    if(ret == -1){
537
 
      error(0, errno, "sigaction");
 
573
      error_plus(0, errno, "sigaction");
538
574
    }
539
575
    raise(signal_received);
540
576
  }