/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-03-18 22:29:25 UTC
  • Revision ID: teddy@recompile.se-20190318222925-jvhek84dgcfgj6g3
mandos-ctl: Refactor tests

* mandos-ctl: Where the clients names "foo" and "barbar" do not refer
              to the actual mock clients in the TestCommand class,
              change all occurrences of these names to "client1" and
              "client2" (or just "client" when only one is used) .
              Also change all test doubles to use correct terminology;
              some things called mocks are actually stubs or spies,
              and rename all true mocks to have "mock" in their names.
              Also eliminate duplicate values in tests; derive values
              from previously defined values whenever possible.

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
30
#include <unistd.h>             /* struct termios, tcsetattr(),
30
31
                                   STDIN_FILENO, TCSAFLUSH,
41
42
                                   getenv(), free() */
42
43
#include <dirent.h>             /* scandir(), alphasort() */
43
44
#include <stdio.h>              /* fprintf(), stderr, getline(),
44
 
                                   stdin, feof(), fputc()
45
 
                                */
 
45
                                   stdin, feof(), fputc(), vfprintf(),
 
46
                                   vasprintf() */
46
47
#include <errno.h>              /* errno, EBADF, ENOTTY, EINVAL,
47
48
                                   EFAULT, EFBIG, EIO, ENOSPC, EINTR
48
49
                                */
50
51
#include <iso646.h>             /* or, not */
51
52
#include <stdbool.h>            /* bool, false, true */
52
53
#include <inttypes.h>           /* strtoumax() */
53
 
#include <sys/stat.h>           /* struct stat, lstat(), open() */
54
 
#include <string.h>             /* strlen, rindex, memcmp */
 
54
#include <sys/stat.h>           /* struct stat, lstat(), open() */
 
55
#include <string.h>             /* strlen, rindex, memcmp, strerror()
 
56
                                 */
55
57
#include <argp.h>               /* struct argp_option, struct
56
58
                                   argp_state, struct argp,
57
59
                                   argp_parse(), error_t,
60
62
#include <sysexits.h>           /* EX_SOFTWARE, EX_OSERR,
61
63
                                   EX_UNAVAILABLE, EX_IOERR, EX_OK */
62
64
#include <fcntl.h>              /* open() */
 
65
#include <stdarg.h>             /* va_list, va_start(), ... */
63
66
 
64
67
volatile sig_atomic_t quit_now = 0;
65
68
int signal_received;
66
69
bool debug = false;
67
70
const char *argp_program_version = "password-prompt " VERSION;
68
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
71
const char *argp_program_bug_address = "<mandos@recompile.se>";
69
72
 
70
73
/* Needed for conflict resolution */
71
74
const char plymouth_name[] = "plymouthd";
72
 
const char plymouth_alt_name[] = "plymouthd";
73
75
 
 
76
/* Function to use when printing errors */
 
77
__attribute__((format (gnu_printf, 3, 4)))
 
78
void error_plus(int status, int errnum, const char *formatstring,
 
79
                ...){
 
80
  va_list ap;
 
81
  char *text;
 
82
  int ret;
 
83
  
 
84
  va_start(ap, formatstring);
 
85
  ret = vasprintf(&text, formatstring, ap);
 
86
  if(ret == -1){
 
87
    fprintf(stderr, "Mandos plugin %s: ",
 
88
            program_invocation_short_name);
 
89
    vfprintf(stderr, formatstring, ap);
 
90
    fprintf(stderr, ": %s\n", strerror(errnum));
 
91
    error(status, errno, "vasprintf while printing error");
 
92
    return;
 
93
  }
 
94
  fprintf(stderr, "Mandos plugin ");
 
95
  error(status, errnum, "%s", text);
 
96
  free(text);
 
97
}
74
98
 
75
99
static void termination_handler(int signum){
76
100
  if(quit_now){
86
110
     from the terminal.  Password-prompt will exit if it detects
87
111
     plymouth since plymouth performs the same functionality.
88
112
   */
 
113
  __attribute__((nonnull))
89
114
  int is_plymouth(const struct dirent *proc_entry){
90
115
    int ret;
91
116
    int cl_fd;
92
117
    {
93
 
      uintmax_t maxvalue;
 
118
      uintmax_t proc_id;
94
119
      char *tmp;
95
120
      errno = 0;
96
 
      maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
121
      proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
97
122
      
98
123
      if(errno != 0 or *tmp != '\0'
99
 
         or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
124
         or proc_id != (uintmax_t)((pid_t)proc_id)){
100
125
        return 0;
101
126
      }
102
127
    }
103
128
    
104
129
    char *cmdline_filename;
105
 
    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline", proc_entry->d_name);
 
130
    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
 
131
                   proc_entry->d_name);
106
132
    if(ret == -1){
107
 
      error(0, errno, "asprintf");
 
133
      error_plus(0, errno, "asprintf");
108
134
      return 0;
109
135
    }
110
136
    
111
 
    /* Open /proc/<pid>/cmdline  */
 
137
    /* Open /proc/<pid>/cmdline */
112
138
    cl_fd = open(cmdline_filename, O_RDONLY);
113
139
    free(cmdline_filename);
114
140
    if(cl_fd == -1){
115
 
      error(0, errno, "open");
 
141
      if(errno != ENOENT){
 
142
        error_plus(0, errno, "open");
 
143
      }
116
144
      return 0;
117
145
    }
118
146
    
128
156
        if(cmdline_len + blocksize + 1 > cmdline_allocated){
129
157
          tmp = realloc(cmdline, cmdline_allocated + blocksize + 1);
130
158
          if(tmp == NULL){
131
 
            error(0, errno, "realloc");
 
159
            error_plus(0, errno, "realloc");
132
160
            free(cmdline);
133
161
            close(cl_fd);
134
162
            return 0;
141
169
        sret = read(cl_fd, cmdline + cmdline_len,
142
170
                    cmdline_allocated - cmdline_len);
143
171
        if(sret == -1){
144
 
          error(0, errno, "read");
 
172
          error_plus(0, errno, "read");
145
173
          free(cmdline);
146
174
          close(cl_fd);
147
175
          return 0;
150
178
      } while(sret != 0);
151
179
      ret = close(cl_fd);
152
180
      if(ret == -1){
153
 
        error(0, errno, "close");
 
181
        error_plus(0, errno, "close");
154
182
        free(cmdline);
155
183
        return 0;
156
184
      }
166
194
      cmdline_base = cmdline;
167
195
    }
168
196
    
169
 
    if((strcmp(cmdline_base, plymouth_name) != 0)
170
 
       and (strcmp(cmdline_base, plymouth_alt_name) != 0)){
 
197
    if(strcmp(cmdline_base, plymouth_name) != 0){
 
198
      if(debug){
 
199
        fprintf(stderr, "\"%s\" is not \"%s\"\n", cmdline_base,
 
200
                plymouth_name);
 
201
      }
171
202
      free(cmdline);
172
203
      return 0;
173
204
    }
 
205
    if(debug){
 
206
      fprintf(stderr, "\"%s\" equals \"%s\"\n", cmdline_base,
 
207
              plymouth_name);
 
208
    }
174
209
    free(cmdline);
175
210
    return 1;
176
211
  }
177
 
 
178
 
  struct dirent **direntries;
 
212
  
 
213
  struct dirent **direntries = NULL;
179
214
  int ret;
180
215
  ret = scandir("/proc", &direntries, is_plymouth, alphasort);
181
 
  if (ret == -1){
182
 
    error(1, errno, "scandir");
183
 
  }
 
216
  if(ret == -1){
 
217
    error_plus(1, errno, "scandir");
 
218
  }
 
219
  {
 
220
    int i = ret;
 
221
    while(i--){
 
222
      free(direntries[i]);
 
223
    }
 
224
  }
 
225
  free(direntries);
184
226
  return ret > 0;
185
227
}
186
228
 
215
257
      { .name = NULL }
216
258
    };
217
259
    
 
260
    __attribute__((nonnull(3)))
218
261
    error_t parse_opt (int key, char *arg, struct argp_state *state){
219
262
      errno = 0;
220
263
      switch (key){
231
274
        argp_state_help(state, state->out_stream,
232
275
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
233
276
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
277
        __builtin_unreachable();
234
278
      case -3:                  /* --usage */
235
279
        argp_state_help(state, state->out_stream,
236
280
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
281
        __builtin_unreachable();
237
282
      case 'V':                 /* --version */
238
283
        fprintf(state->out_stream, "%s\n", argp_program_version);
239
284
        exit(argp_err_exit_status);
256
301
    case ENOMEM:
257
302
    default:
258
303
      errno = ret;
259
 
      error(0, errno, "argp_parse");
 
304
      error_plus(0, errno, "argp_parse");
260
305
      return EX_OSERR;
261
306
    case EINVAL:
262
307
      return EX_USAGE;
267
312
    fprintf(stderr, "Starting %s\n", argv[0]);
268
313
  }
269
314
 
270
 
  if (conflict_detection()){
 
315
  if(conflict_detection()){
271
316
    if(debug){
272
 
      fprintf(stderr, "Stopping %s because of conflict", argv[0]);
 
317
      fprintf(stderr, "Stopping %s because of conflict\n", argv[0]);
273
318
    }
274
319
    return EXIT_FAILURE;
275
320
  }
280
325
  
281
326
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
282
327
    int e = errno;
283
 
    error(0, errno, "tcgetattr");
 
328
    error_plus(0, errno, "tcgetattr");
284
329
    switch(e){
285
330
    case EBADF:
286
331
    case ENOTTY:
293
338
  sigemptyset(&new_action.sa_mask);
294
339
  ret = sigaddset(&new_action.sa_mask, SIGINT);
295
340
  if(ret == -1){
296
 
    error(0, errno, "sigaddset");
 
341
    error_plus(0, errno, "sigaddset");
297
342
    return EX_OSERR;
298
343
  }
299
344
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
300
345
  if(ret == -1){
301
 
    error(0, errno, "sigaddset");
 
346
    error_plus(0, errno, "sigaddset");
302
347
    return EX_OSERR;
303
348
  }
304
349
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
305
350
  if(ret == -1){
306
 
    error(0, errno, "sigaddset");
 
351
    error_plus(0, errno, "sigaddset");
307
352
    return EX_OSERR;
308
353
  }
309
354
  /* Need to check if the handler is SIG_IGN before handling:
312
357
  */
313
358
  ret = sigaction(SIGINT, NULL, &old_action);
314
359
  if(ret == -1){
315
 
    error(0, errno, "sigaction");
 
360
    error_plus(0, errno, "sigaction");
316
361
    return EX_OSERR;
317
362
  }
318
363
  if(old_action.sa_handler != SIG_IGN){
319
364
    ret = sigaction(SIGINT, &new_action, NULL);
320
365
    if(ret == -1){
321
 
      error(0, errno, "sigaction");
 
366
      error_plus(0, errno, "sigaction");
322
367
      return EX_OSERR;
323
368
    }
324
369
  }
325
370
  ret = sigaction(SIGHUP, NULL, &old_action);
326
371
  if(ret == -1){
327
 
    error(0, errno, "sigaction");
 
372
    error_plus(0, errno, "sigaction");
328
373
    return EX_OSERR;
329
374
  }
330
375
  if(old_action.sa_handler != SIG_IGN){
331
376
    ret = sigaction(SIGHUP, &new_action, NULL);
332
377
    if(ret == -1){
333
 
      error(0, errno, "sigaction");
 
378
      error_plus(0, errno, "sigaction");
334
379
      return EX_OSERR;
335
380
    }
336
381
  }
337
382
  ret = sigaction(SIGTERM, NULL, &old_action);
338
383
  if(ret == -1){
339
 
    error(0, errno, "sigaction");
 
384
    error_plus(0, errno, "sigaction");
340
385
    return EX_OSERR;
341
386
  }
342
387
  if(old_action.sa_handler != SIG_IGN){
343
388
    ret = sigaction(SIGTERM, &new_action, NULL);
344
389
    if(ret == -1){
345
 
      error(0, errno, "sigaction");
 
390
      error_plus(0, errno, "sigaction");
346
391
      return EX_OSERR;
347
392
    }
348
393
  }
356
401
  t_new.c_lflag &= ~(tcflag_t)ECHO;
357
402
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
358
403
    int e = errno;
359
 
    error(0, errno, "tcsetattr-echo");
 
404
    error_plus(0, errno, "tcsetattr-echo");
360
405
    switch(e){
361
406
    case EBADF:
362
407
    case ENOTTY:
426
471
        sret = write(STDOUT_FILENO, buffer + written, n - written);
427
472
        if(sret < 0){
428
473
          int e = errno;
429
 
          error(0, errno, "write");
 
474
          error_plus(0, errno, "write");
430
475
          switch(e){
431
476
          case EBADF:
432
477
          case EFAULT:
448
493
      sret = close(STDOUT_FILENO);
449
494
      if(sret == -1){
450
495
        int e = errno;
451
 
        error(0, errno, "close");
 
496
        error_plus(0, errno, "close");
452
497
        switch(e){
453
498
        case EBADF:
454
499
          status = EX_OSFILE;
464
509
    if(sret < 0){
465
510
      int e = errno;
466
511
      if(errno != EINTR and not feof(stdin)){
467
 
        error(0, errno, "getline");
 
512
        error_plus(0, errno, "getline");
468
513
        switch(e){
469
514
        case EBADF:
470
515
          status = EX_UNAVAILABLE;
 
516
          break;
471
517
        case EIO:
472
518
        case EINVAL:
473
519
        default:
477
523
        break;
478
524
      }
479
525
    }
480
 
    /* if(sret == 0), then the only sensible thing to do is to retry to
481
 
       read from stdin */
 
526
    /* if(sret == 0), then the only sensible thing to do is to retry
 
527
       to read from stdin */
482
528
    fputc('\n', stderr);
483
529
    if(debug and not quit_now){
484
530
      /* If quit_now is nonzero, we were interrupted by a signal, and
493
539
    fprintf(stderr, "Restoring terminal attributes\n");
494
540
  }
495
541
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
496
 
    error(0, errno, "tcsetattr+echo");
 
542
    error_plus(0, errno, "tcsetattr+echo");
497
543
  }
498
544
  
499
545
  if(quit_now){
501
547
    old_action.sa_handler = SIG_DFL;
502
548
    ret = sigaction(signal_received, &old_action, NULL);
503
549
    if(ret == -1){
504
 
      error(0, errno, "sigaction");
 
550
      error_plus(0, errno, "sigaction");
505
551
    }
506
552
    raise(signal_received);
507
553
  }