/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/askpass-fifo.c

  • Committer: Teddy Hogeborn
  • Date: 2015-05-23 20:18:34 UTC
  • mto: This revision was merged to the branch mainline in revision 756.
  • Revision ID: teddy@recompile.se-20150523201834-e89ex4ito93yni8x
mandos: Use multiprocessing module to run checkers.

For a long time, the Mandos server has occasionally logged the message
"ERROR: Child process vanished".  This was never a fatal error, but it
has been annoying and slightly worrying, since a definite cause was
not found.  One potential cause could be the "multiprocessing" and
"subprocess" modules conflicting w.r.t. SIGCHLD.  To avoid this,
change the running of checkers from using subprocess.Popen
asynchronously to instead first create a multiprocessing.Process()
(which is asynchronous) calling a function, and have that function
then call subprocess.call() (which is synchronous).  In this way, the
only thing using any asynchronous subprocesses is the multiprocessing
module.

This makes it necessary to change one small thing in the D-Bus API,
since the subprocesses.call() function does not expose the raw wait(2)
status value.

DBUS-API (CheckerCompleted): Change the second value provided by this
                             D-Bus signal from the raw wait(2) status
                             to the actual terminating signal number.
mandos (subprocess_call_pipe): New function to be called by
                               multiprocessing.Process (starting a
                               separate process).
(Client.last_checker signal): New attribute for signal which
                              terminated last checker.  Like
                              last_checker_status, only not accessible
                              via D-Bus.
(Client.checker_callback): Take new "connection" argument and use it
                           to get returncode; set last_checker_signal.
                           Return False so gobject does not call this
                           callback again.
(Client.start_checker): Start checker using a multiprocessing.Process
                        instead of a subprocess.Popen.
(ClientDBus.checker_callback): Take new "connection" argument.        Call
                               Client.checker_callback early to have
                               it set last_checker_status and
                               last_checker_signal; use those.  Change
                               second value provided to D-Bus signal
                               CheckerCompleted to use
                               last_checker_signal if checker was
                               terminated by signal.
mandos-monitor: Update to reflect DBus API change.
(MandosClientWidget.checker_completed): Take "signal" instead of
                                        "condition" argument.  Use it
                                        accordingly.  Remove dead code
                                        (os.WCOREDUMP case).

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
/*
3
3
 * Askpass-FIFO - Read a password from a FIFO and output it
4
4
 * 
5
 
 * Copyright © 2008,2009 Teddy Hogeborn
6
 
 * Copyright © 2008,2009 Björn Påhlsson
 
5
 * Copyright © 2008-2014 Teddy Hogeborn
 
6
 * Copyright © 2008-2014 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
25
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY() */
26
 
#include <sys/types.h>          /* ssize_t */
 
26
#include <sys/types.h>          /* uid_t, gid_t, ssize_t */
27
27
#include <sys/stat.h>           /* mkfifo(), S_IRUSR, S_IWUSR */
28
28
#include <iso646.h>             /* and */
29
29
#include <errno.h>              /* errno, EACCES, ENOTDIR, ELOOP,
31
31
                                   ENOENT, EEXIST, EFAULT, EMFILE,
32
32
                                   ENFILE, ENOMEM, EBADF, EINVAL, EIO,
33
33
                                   EISDIR, EFBIG */
34
 
#include <stdio.h>              /* perror() */
 
34
#include <error.h>              /* error() */
 
35
#include <stdio.h>              /* fprintf(), vfprintf(),
 
36
                                   vasprintf() */
35
37
#include <stdlib.h>             /* EXIT_FAILURE, NULL, size_t, free(),
36
38
                                   realloc(), EXIT_SUCCESS */
37
39
#include <fcntl.h>              /* open(), O_RDONLY */
39
41
                                   STDOUT_FILENO */
40
42
#include <sysexits.h>           /* EX_OSERR, EX_OSFILE,
41
43
                                   EX_UNAVAILABLE, EX_IOERR */
42
 
 
 
44
#include <string.h>             /* strerror() */
 
45
#include <stdarg.h>             /* va_list, va_start(), ... */
 
46
 
 
47
uid_t uid = 65534;
 
48
gid_t gid = 65534;
 
49
 
 
50
/* Function to use when printing errors */
 
51
__attribute__((format (gnu_printf, 3, 4)))
 
52
void error_plus(int status, int errnum, const char *formatstring,
 
53
                ...){
 
54
  va_list ap;
 
55
  char *text;
 
56
  int ret;
 
57
  
 
58
  va_start(ap, formatstring);
 
59
  ret = vasprintf(&text, formatstring, ap);
 
60
  if(ret == -1){
 
61
    fprintf(stderr, "Mandos plugin %s: ",
 
62
            program_invocation_short_name);
 
63
    vfprintf(stderr, formatstring, ap);
 
64
    fprintf(stderr, ": ");
 
65
    fprintf(stderr, "%s\n", strerror(errnum));
 
66
    error(status, errno, "vasprintf while printing error");
 
67
    return;
 
68
  }
 
69
  fprintf(stderr, "Mandos plugin ");
 
70
  error(status, errnum, "%s", text);
 
71
  free(text);
 
72
}
43
73
 
44
74
int main(__attribute__((unused))int argc,
45
75
         __attribute__((unused))char **argv){
46
76
  int ret = 0;
47
77
  ssize_t sret;
48
78
  
 
79
  uid = getuid();
 
80
  gid = getgid();
 
81
  
49
82
  /* Create FIFO */
50
83
  const char passfifo[] = "/lib/cryptsetup/passfifo";
51
84
  ret = mkfifo(passfifo, S_IRUSR | S_IWUSR);
52
85
  if(ret == -1){
53
86
    int e = errno;
54
 
    perror("mkfifo");
55
87
    switch(e){
56
88
    case EACCES:
57
89
    case ENOTDIR:
58
90
    case ELOOP:
59
 
      return EX_OSFILE;
 
91
      error_plus(EX_OSFILE, errno, "mkfifo");
60
92
    case ENAMETOOLONG:
61
93
    case ENOSPC:
62
94
    case EROFS:
63
95
    default:
64
 
      return EX_OSERR;
 
96
      error_plus(EX_OSERR, errno, "mkfifo");
65
97
    case ENOENT:
66
 
      return EX_UNAVAILABLE;    /* no "/lib/cryptsetup"? */
 
98
      /* no "/lib/cryptsetup"? */
 
99
      error_plus(EX_UNAVAILABLE, errno, "mkfifo");
67
100
    case EEXIST:
68
101
      break;                    /* not an error */
69
102
    }
73
106
  int fifo_fd = open(passfifo, O_RDONLY);
74
107
  if(fifo_fd == -1){
75
108
    int e = errno;
76
 
    perror("open");
 
109
    error_plus(0, errno, "open");
77
110
    switch(e){
78
111
    case EACCES:
79
112
    case ENOENT:
91
124
    }
92
125
  }
93
126
  
 
127
  /* Lower group privileges  */
 
128
  if(setgid(gid) == -1){
 
129
    error_plus(0, errno, "setgid");
 
130
  }
 
131
  
 
132
  /* Lower user privileges */
 
133
  if(setuid(uid) == -1){
 
134
    error_plus(0, errno, "setuid");
 
135
  }
 
136
  
94
137
  /* Read from FIFO */
95
138
  char *buf = NULL;
96
139
  size_t buf_len = 0;
101
144
      if(buf_len + blocksize > buf_allocated){
102
145
        char *tmp = realloc(buf, buf_allocated + blocksize);
103
146
        if(tmp == NULL){
104
 
          perror("realloc");
 
147
          error_plus(0, errno, "realloc");
105
148
          free(buf);
106
149
          return EX_OSERR;
107
150
        }
113
156
        int e = errno;
114
157
        free(buf);
115
158
        errno = e;
116
 
        perror("read");
 
159
        error_plus(0, errno, "read");
117
160
        switch(e){
118
161
        case EBADF:
119
162
        case EFAULT:
141
184
      int e = errno;
142
185
      free(buf);
143
186
      errno = e;
144
 
      perror("write");
 
187
      error_plus(0, errno, "write");
145
188
      switch(e){
146
189
      case EBADF:
147
190
      case EFAULT:
161
204
  ret = close(STDOUT_FILENO);
162
205
  if(ret == -1){
163
206
    int e = errno;
164
 
    perror("close");
 
207
    error_plus(0, errno, "close");
165
208
    switch(e){
166
209
    case EBADF:
167
210
      return EX_OSFILE;