/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: 2016-06-03 17:27:03 UTC
  • Revision ID: teddy@recompile.se-20160603172703-mc6tjor6rhq4xy74
mandos: Bug fix: Do multiprocessing cleanup correctly on exit

* mandos (main): Save module "multiprocessing" and open file "wnull"
                 as scope variables accessible by function cleanup(),
                 since the module and global variable may not be
                 accessible when the cleanup() function is run as
                 scheduled by atexit().

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,2009 Teddy Hogeborn
6
 
 * Copyright © 2008,2009 Björn Påhlsson
 
5
 * Copyright © 2008-2016 Teddy Hogeborn
 
6
 * Copyright © 2008-2016 Björn Påhlsson
7
7
 * 
8
8
 * This program is free software: you can redistribute it and/or
9
9
 * modify it under the terms of the GNU General Public License as
19
19
 * along with this program.  If not, see
20
20
 * <http://www.gnu.org/licenses/>.
21
21
 * 
22
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
22
 * Contact the authors at <mandos@recompile.se>.
23
23
 */
24
24
 
25
 
#define _GNU_SOURCE             /* getline() */
 
25
#define _GNU_SOURCE             /* getline(), asprintf() */
26
26
 
27
 
#include <termios.h>            /* struct termios, tcsetattr(),
 
27
#include <termios.h>            /* struct termios, tcsetattr(),
28
28
                                   TCSAFLUSH, tcgetattr(), ECHO */
29
29
#include <unistd.h>             /* struct termios, tcsetattr(),
30
30
                                   STDIN_FILENO, TCSAFLUSH,
31
 
                                   tcgetattr(), ECHO */
 
31
                                   tcgetattr(), ECHO, readlink() */
32
32
#include <signal.h>             /* sig_atomic_t, raise(), struct
33
33
                                   sigaction, sigemptyset(),
34
34
                                   sigaction(), sigaddset(), SIGINT,
35
35
                                   SIGQUIT, SIGHUP, SIGTERM,
36
36
                                   raise() */
37
37
#include <stddef.h>             /* NULL, size_t, ssize_t */
38
 
#include <sys/types.h>          /* ssize_t */
 
38
#include <sys/types.h>          /* ssize_t, struct dirent, pid_t,
 
39
                                   ssize_t, open() */
39
40
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
40
 
                                   getenv() */
 
41
                                   getenv(), free() */
 
42
#include <dirent.h>             /* scandir(), alphasort() */
41
43
#include <stdio.h>              /* fprintf(), stderr, getline(),
42
 
                                   stdin, feof(), perror(), fputc()
43
 
                                */
 
44
                                   stdin, feof(), fputc(), vfprintf(),
 
45
                                   vasprintf() */
44
46
#include <errno.h>              /* errno, EBADF, ENOTTY, EINVAL,
45
47
                                   EFAULT, EFBIG, EIO, ENOSPC, EINTR
46
48
                                */
 
49
#include <error.h>              /* error() */
47
50
#include <iso646.h>             /* or, not */
48
51
#include <stdbool.h>            /* bool, false, true */
49
 
#include <string.h>             /* strlen, rindex, strncmp, strcmp */
 
52
#include <inttypes.h>           /* strtoumax() */
 
53
#include <sys/stat.h>           /* struct stat, lstat(), open() */
 
54
#include <string.h>             /* strlen, rindex, memcmp, strerror()
 
55
                                 */
50
56
#include <argp.h>               /* struct argp_option, struct
51
57
                                   argp_state, struct argp,
52
58
                                   argp_parse(), error_t,
54
60
                                   ARGP_ERR_UNKNOWN */
55
61
#include <sysexits.h>           /* EX_SOFTWARE, EX_OSERR,
56
62
                                   EX_UNAVAILABLE, EX_IOERR, EX_OK */
 
63
#include <fcntl.h>              /* open() */
 
64
#include <stdarg.h>             /* va_list, va_start(), ... */
57
65
 
58
66
volatile sig_atomic_t quit_now = 0;
59
67
int signal_received;
60
68
bool debug = false;
61
69
const char *argp_program_version = "password-prompt " VERSION;
62
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
70
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
71
 
 
72
/* Needed for conflict resolution */
 
73
const char plymouth_name[] = "plymouthd";
 
74
 
 
75
/* Function to use when printing errors */
 
76
__attribute__((format (gnu_printf, 3, 4)))
 
77
void error_plus(int status, int errnum, const char *formatstring,
 
78
                ...){
 
79
  va_list ap;
 
80
  char *text;
 
81
  int ret;
 
82
  
 
83
  va_start(ap, formatstring);
 
84
  ret = vasprintf(&text, formatstring, ap);
 
85
  if(ret == -1){
 
86
    fprintf(stderr, "Mandos plugin %s: ",
 
87
            program_invocation_short_name);
 
88
    vfprintf(stderr, formatstring, ap);
 
89
    fprintf(stderr, ": %s\n", strerror(errnum));
 
90
    error(status, errno, "vasprintf while printing error");
 
91
    return;
 
92
  }
 
93
  fprintf(stderr, "Mandos plugin ");
 
94
  error(status, errnum, "%s", text);
 
95
  free(text);
 
96
}
63
97
 
64
98
static void termination_handler(int signum){
65
99
  if(quit_now){
69
103
  signal_received = signum;
70
104
}
71
105
 
 
106
bool conflict_detection(void){
 
107
 
 
108
  /* plymouth conflicts with password-prompt since both want to read
 
109
     from the terminal.  Password-prompt will exit if it detects
 
110
     plymouth since plymouth performs the same functionality.
 
111
   */
 
112
  __attribute__((nonnull))
 
113
  int is_plymouth(const struct dirent *proc_entry){
 
114
    int ret;
 
115
    int cl_fd;
 
116
    {
 
117
      uintmax_t proc_id;
 
118
      char *tmp;
 
119
      errno = 0;
 
120
      proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
 
121
      
 
122
      if(errno != 0 or *tmp != '\0'
 
123
         or proc_id != (uintmax_t)((pid_t)proc_id)){
 
124
        return 0;
 
125
      }
 
126
    }
 
127
    
 
128
    char *cmdline_filename;
 
129
    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
 
130
                   proc_entry->d_name);
 
131
    if(ret == -1){
 
132
      error_plus(0, errno, "asprintf");
 
133
      return 0;
 
134
    }
 
135
    
 
136
    /* Open /proc/<pid>/cmdline */
 
137
    cl_fd = open(cmdline_filename, O_RDONLY);
 
138
    free(cmdline_filename);
 
139
    if(cl_fd == -1){
 
140
      if(errno != ENOENT){
 
141
        error_plus(0, errno, "open");
 
142
      }
 
143
      return 0;
 
144
    }
 
145
    
 
146
    char *cmdline = NULL;
 
147
    {
 
148
      size_t cmdline_len = 0;
 
149
      size_t cmdline_allocated = 0;
 
150
      char *tmp;
 
151
      const size_t blocksize = 1024;
 
152
      ssize_t sret;
 
153
      do {
 
154
        /* Allocate more space? */
 
155
        if(cmdline_len + blocksize + 1 > cmdline_allocated){
 
156
          tmp = realloc(cmdline, cmdline_allocated + blocksize + 1);
 
157
          if(tmp == NULL){
 
158
            error_plus(0, errno, "realloc");
 
159
            free(cmdline);
 
160
            close(cl_fd);
 
161
            return 0;
 
162
          }
 
163
          cmdline = tmp;
 
164
          cmdline_allocated += blocksize;
 
165
        }
 
166
        
 
167
        /* Read data */
 
168
        sret = read(cl_fd, cmdline + cmdline_len,
 
169
                    cmdline_allocated - cmdline_len);
 
170
        if(sret == -1){
 
171
          error_plus(0, errno, "read");
 
172
          free(cmdline);
 
173
          close(cl_fd);
 
174
          return 0;
 
175
        }
 
176
        cmdline_len += (size_t)sret;
 
177
      } while(sret != 0);
 
178
      ret = close(cl_fd);
 
179
      if(ret == -1){
 
180
        error_plus(0, errno, "close");
 
181
        free(cmdline);
 
182
        return 0;
 
183
      }
 
184
      cmdline[cmdline_len] = '\0'; /* Make sure it is terminated */
 
185
    }
 
186
    /* we now have cmdline */
 
187
    
 
188
    /* get basename */
 
189
    char *cmdline_base = strrchr(cmdline, '/');
 
190
    if(cmdline_base != NULL){
 
191
      cmdline_base += 1;                /* skip the slash */
 
192
    } else {
 
193
      cmdline_base = cmdline;
 
194
    }
 
195
    
 
196
    if(strcmp(cmdline_base, plymouth_name) != 0){
 
197
      if(debug){
 
198
        fprintf(stderr, "\"%s\" is not \"%s\"\n", cmdline_base,
 
199
                plymouth_name);
 
200
      }
 
201
      free(cmdline);
 
202
      return 0;
 
203
    }
 
204
    if(debug){
 
205
      fprintf(stderr, "\"%s\" equals \"%s\"\n", cmdline_base,
 
206
              plymouth_name);
 
207
    }
 
208
    free(cmdline);
 
209
    return 1;
 
210
  }
 
211
  
 
212
  struct dirent **direntries = NULL;
 
213
  int ret;
 
214
  ret = scandir("/proc", &direntries, is_plymouth, alphasort);
 
215
  if(ret == -1){
 
216
    error_plus(1, errno, "scandir");
 
217
  }
 
218
  free(direntries);
 
219
  return ret > 0;
 
220
}
 
221
 
 
222
 
72
223
int main(int argc, char **argv){
73
 
  ssize_t ret;
 
224
  ssize_t sret;
 
225
  int ret;
74
226
  size_t n;
75
227
  struct termios t_new, t_old;
76
228
  char *buffer = NULL;
98
250
      { .name = NULL }
99
251
    };
100
252
    
 
253
    __attribute__((nonnull(3)))
101
254
    error_t parse_opt (int key, char *arg, struct argp_state *state){
102
255
      errno = 0;
103
256
      switch (key){
139
292
    case ENOMEM:
140
293
    default:
141
294
      errno = ret;
142
 
      perror("argp_parse");
 
295
      error_plus(0, errno, "argp_parse");
143
296
      return EX_OSERR;
144
297
    case EINVAL:
145
298
      return EX_USAGE;
149
302
  if(debug){
150
303
    fprintf(stderr, "Starting %s\n", argv[0]);
151
304
  }
 
305
 
 
306
  if(conflict_detection()){
 
307
    if(debug){
 
308
      fprintf(stderr, "Stopping %s because of conflict\n", argv[0]);
 
309
    }
 
310
    return EXIT_FAILURE;
 
311
  }
 
312
  
152
313
  if(debug){
153
314
    fprintf(stderr, "Storing current terminal attributes\n");
154
315
  }
155
316
  
156
317
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
157
318
    int e = errno;
158
 
    perror("tcgetattr");
 
319
    error_plus(0, errno, "tcgetattr");
159
320
    switch(e){
160
321
    case EBADF:
161
322
    case ENOTTY:
168
329
  sigemptyset(&new_action.sa_mask);
169
330
  ret = sigaddset(&new_action.sa_mask, SIGINT);
170
331
  if(ret == -1){
171
 
    perror("sigaddset");
 
332
    error_plus(0, errno, "sigaddset");
172
333
    return EX_OSERR;
173
334
  }
174
335
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
175
336
  if(ret == -1){
176
 
    perror("sigaddset");
 
337
    error_plus(0, errno, "sigaddset");
177
338
    return EX_OSERR;
178
339
  }
179
340
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
180
341
  if(ret == -1){
181
 
    perror("sigaddset");
 
342
    error_plus(0, errno, "sigaddset");
182
343
    return EX_OSERR;
183
344
  }
184
345
  /* Need to check if the handler is SIG_IGN before handling:
187
348
  */
188
349
  ret = sigaction(SIGINT, NULL, &old_action);
189
350
  if(ret == -1){
190
 
    perror("sigaction");
 
351
    error_plus(0, errno, "sigaction");
191
352
    return EX_OSERR;
192
353
  }
193
354
  if(old_action.sa_handler != SIG_IGN){
194
355
    ret = sigaction(SIGINT, &new_action, NULL);
195
356
    if(ret == -1){
196
 
      perror("sigaction");
 
357
      error_plus(0, errno, "sigaction");
197
358
      return EX_OSERR;
198
359
    }
199
360
  }
200
361
  ret = sigaction(SIGHUP, NULL, &old_action);
201
362
  if(ret == -1){
202
 
    perror("sigaction");
 
363
    error_plus(0, errno, "sigaction");
203
364
    return EX_OSERR;
204
365
  }
205
366
  if(old_action.sa_handler != SIG_IGN){
206
367
    ret = sigaction(SIGHUP, &new_action, NULL);
207
368
    if(ret == -1){
208
 
      perror("sigaction");
 
369
      error_plus(0, errno, "sigaction");
209
370
      return EX_OSERR;
210
371
    }
211
372
  }
212
373
  ret = sigaction(SIGTERM, NULL, &old_action);
213
374
  if(ret == -1){
214
 
    perror("sigaction");
 
375
    error_plus(0, errno, "sigaction");
215
376
    return EX_OSERR;
216
377
  }
217
378
  if(old_action.sa_handler != SIG_IGN){
218
379
    ret = sigaction(SIGTERM, &new_action, NULL);
219
380
    if(ret == -1){
220
 
      perror("sigaction");
 
381
      error_plus(0, errno, "sigaction");
221
382
      return EX_OSERR;
222
383
    }
223
384
  }
231
392
  t_new.c_lflag &= ~(tcflag_t)ECHO;
232
393
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
233
394
    int e = errno;
234
 
    perror("tcsetattr-echo");
 
395
    error_plus(0, errno, "tcsetattr-echo");
235
396
    switch(e){
236
397
    case EBADF:
237
398
    case ENOTTY:
286
447
        }
287
448
      }
288
449
    }
289
 
    ret = getline(&buffer, &n, stdin);
290
 
    if(ret > 0){
 
450
    sret = getline(&buffer, &n, stdin);
 
451
    if(sret > 0){
291
452
      status = EXIT_SUCCESS;
292
453
      /* Make n = data size instead of allocated buffer size */
293
 
      n = (size_t)ret;
 
454
      n = (size_t)sret;
294
455
      /* Strip final newline */
295
456
      if(n > 0 and buffer[n-1] == '\n'){
296
457
        buffer[n-1] = '\0';     /* not strictly necessary */
298
459
      }
299
460
      size_t written = 0;
300
461
      while(written < n){
301
 
        ret = write(STDOUT_FILENO, buffer + written, n - written);
302
 
        if(ret < 0){
 
462
        sret = write(STDOUT_FILENO, buffer + written, n - written);
 
463
        if(sret < 0){
303
464
          int e = errno;
304
 
          perror("write");
 
465
          error_plus(0, errno, "write");
305
466
          switch(e){
306
467
          case EBADF:
307
468
          case EFAULT:
318
479
          }
319
480
          break;
320
481
        }
321
 
        written += (size_t)ret;
 
482
        written += (size_t)sret;
322
483
      }
323
 
      ret = close(STDOUT_FILENO);
324
 
      if(ret == -1){
 
484
      sret = close(STDOUT_FILENO);
 
485
      if(sret == -1){
325
486
        int e = errno;
326
 
        perror("close");
 
487
        error_plus(0, errno, "close");
327
488
        switch(e){
328
489
        case EBADF:
329
490
          status = EX_OSFILE;
336
497
      }
337
498
      break;
338
499
    }
339
 
    if(ret < 0){
 
500
    if(sret < 0){
340
501
      int e = errno;
341
502
      if(errno != EINTR and not feof(stdin)){
342
 
        perror("getline");
 
503
        error_plus(0, errno, "getline");
343
504
        switch(e){
344
505
        case EBADF:
345
506
          status = EX_UNAVAILABLE;
 
507
          break;
346
508
        case EIO:
347
509
        case EINVAL:
348
510
        default:
352
514
        break;
353
515
      }
354
516
    }
355
 
    /* if(ret == 0), then the only sensible thing to do is to retry to
356
 
       read from stdin */
 
517
    /* if(sret == 0), then the only sensible thing to do is to retry
 
518
       to read from stdin */
357
519
    fputc('\n', stderr);
358
520
    if(debug and not quit_now){
359
521
      /* If quit_now is nonzero, we were interrupted by a signal, and
368
530
    fprintf(stderr, "Restoring terminal attributes\n");
369
531
  }
370
532
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
371
 
    perror("tcsetattr+echo");
 
533
    error_plus(0, errno, "tcsetattr+echo");
372
534
  }
373
535
  
374
536
  if(quit_now){
376
538
    old_action.sa_handler = SIG_DFL;
377
539
    ret = sigaction(signal_received, &old_action, NULL);
378
540
    if(ret == -1){
379
 
      perror("sigaction");
 
541
      error_plus(0, errno, "sigaction");
380
542
    }
381
543
    raise(signal_received);
382
544
  }