/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/splashy.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
 * Splashy - Read a password from splashy 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(), asprintf() */
29
29
                                   SIG_IGN, kill(), SIGKILL */
30
30
#include <stddef.h>             /* NULL */
31
31
#include <stdlib.h>             /* getenv() */
32
 
#include <stdio.h>              /* asprintf(), perror() */
 
32
#include <stdio.h>              /* asprintf(), vasprintf(), vprintf(),
 
33
                                   fprintf() */
33
34
#include <stdlib.h>             /* EXIT_FAILURE, free(),
34
35
                                   EXIT_SUCCESS */
35
36
#include <sys/types.h>          /* pid_t, DIR, struct dirent,
42
43
                                   sleep(), dup2() STDERR_FILENO,
43
44
                                   STDOUT_FILENO, _exit(),
44
45
                                   pause() */
45
 
#include <string.h>             /* memcmp() */
46
 
#include <errno.h>              /* errno */
 
46
#include <string.h>             /* memcmp(), strerror() */
 
47
#include <errno.h>              /* errno, EACCES, ENOTDIR, ELOOP,
 
48
                                   ENOENT, ENAMETOOLONG, EMFILE,
 
49
                                   ENFILE, ENOMEM, ENOEXEC, EINVAL,
 
50
                                   E2BIG, EFAULT, EIO, ETXTBSY,
 
51
                                   EISDIR, ELIBBAD, EPERM, EINTR,
 
52
                                   ECHILD */
 
53
#include <error.h>              /* error() */
47
54
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
48
55
                                   WEXITSTATUS() */
 
56
#include <sysexits.h>           /* EX_OSERR, EX_OSFILE,
 
57
                                   EX_UNAVAILABLE */
 
58
#include <stdarg.h>             /* va_list, va_start(), ... */
49
59
 
50
60
sig_atomic_t interrupted_by_signal = 0;
51
61
int signal_received;
52
62
 
 
63
/* Function to use when printing errors */
 
64
__attribute__((format (gnu_printf, 3, 4)))
 
65
void error_plus(int status, int errnum, const char *formatstring,
 
66
                ...){
 
67
  va_list ap;
 
68
  char *text;
 
69
  int ret;
 
70
  
 
71
  va_start(ap, formatstring);
 
72
  ret = vasprintf(&text, formatstring, ap);
 
73
  if(ret == -1){
 
74
    fprintf(stderr, "Mandos plugin %s: ",
 
75
            program_invocation_short_name);
 
76
    vfprintf(stderr, formatstring, ap);
 
77
    fprintf(stderr, ": ");
 
78
    fprintf(stderr, "%s\n", strerror(errnum));
 
79
    error(status, errno, "vasprintf while printing error");
 
80
    return;
 
81
  }
 
82
  fprintf(stderr, "Mandos plugin ");
 
83
  error(status, errnum, "%s", text);
 
84
  free(text);
 
85
}
 
86
 
 
87
 
53
88
static void termination_handler(int signum){
54
89
  if(interrupted_by_signal){
55
90
    return;
65
100
  DIR *proc_dir = NULL;
66
101
  pid_t splashy_pid = 0;
67
102
  pid_t splashy_command_pid = 0;
 
103
  int exitstatus = EXIT_FAILURE;
68
104
  
69
105
  /* Create prompt string */
70
106
  {
90
126
    }
91
127
    if(ret == -1){
92
128
      prompt = NULL;
 
129
      exitstatus = EX_OSERR;
93
130
      goto failure;
94
131
    }
95
132
  }
99
136
    const char splashy_name[] = "/sbin/splashy";
100
137
    proc_dir = opendir("/proc");
101
138
    if(proc_dir == NULL){
102
 
      perror("opendir");
 
139
      int e = errno;
 
140
      error_plus(0, errno, "opendir");
 
141
      switch(e){
 
142
      case EACCES:
 
143
      case ENOTDIR:
 
144
      case ELOOP:
 
145
      case ENOENT:
 
146
      default:
 
147
        exitstatus = EX_OSFILE;
 
148
        break;
 
149
      case ENAMETOOLONG:
 
150
      case EMFILE:
 
151
      case ENFILE:
 
152
      case ENOMEM:
 
153
        exitstatus = EX_OSERR;
 
154
        break;
 
155
      }
103
156
      goto failure;
104
157
    }
105
158
    for(struct dirent *proc_ent = readdir(proc_dir);
126
179
        char *exe_link;
127
180
        ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
128
181
        if(ret == -1){
129
 
          perror("asprintf");
 
182
          error_plus(0, errno, "asprintf");
 
183
          exitstatus = EX_OSERR;
130
184
          goto failure;
131
185
        }
132
186
        
138
192
            free(exe_link);
139
193
            continue;
140
194
          }
141
 
          perror("lstat");
 
195
          int e = errno;
 
196
          error_plus(0, errno, "lstat");
142
197
          free(exe_link);
 
198
          switch(e){
 
199
          case EACCES:
 
200
          case ENOTDIR:
 
201
          case ELOOP:
 
202
          default:
 
203
            exitstatus = EX_OSFILE;
 
204
            break;
 
205
          case ENAMETOOLONG:
 
206
            exitstatus = EX_OSERR;
 
207
            break;
 
208
          }
143
209
          goto failure;
144
210
        }
145
211
        if(not S_ISLNK(exe_stat.st_mode)
163
229
    proc_dir = NULL;
164
230
  }
165
231
  if(splashy_pid == 0){
 
232
    exitstatus = EX_UNAVAILABLE;
166
233
    goto failure;
167
234
  }
168
235
  
174
241
    sigemptyset(&new_action.sa_mask);
175
242
    ret = sigaddset(&new_action.sa_mask, SIGINT);
176
243
    if(ret == -1){
177
 
      perror("sigaddset");
 
244
      error_plus(0, errno, "sigaddset");
 
245
      exitstatus = EX_OSERR;
178
246
      goto failure;
179
247
    }
180
248
    ret = sigaddset(&new_action.sa_mask, SIGHUP);
181
249
    if(ret == -1){
182
 
      perror("sigaddset");
 
250
      error_plus(0, errno, "sigaddset");
 
251
      exitstatus = EX_OSERR;
183
252
      goto failure;
184
253
    }
185
254
    ret = sigaddset(&new_action.sa_mask, SIGTERM);
186
255
    if(ret == -1){
187
 
      perror("sigaddset");
 
256
      error_plus(0, errno, "sigaddset");
 
257
      exitstatus = EX_OSERR;
188
258
      goto failure;
189
259
    }
190
260
    ret = sigaction(SIGINT, NULL, &old_action);
191
261
    if(ret == -1){
192
 
      perror("sigaction");
 
262
      error_plus(0, errno, "sigaction");
 
263
      exitstatus = EX_OSERR;
193
264
      goto failure;
194
265
    }
195
266
    if(old_action.sa_handler != SIG_IGN){
196
267
      ret = sigaction(SIGINT, &new_action, NULL);
197
268
      if(ret == -1){
198
 
        perror("sigaction");
 
269
        error_plus(0, errno, "sigaction");
 
270
        exitstatus = EX_OSERR;
199
271
        goto failure;
200
272
      }
201
273
    }
202
274
    ret = sigaction(SIGHUP, NULL, &old_action);
203
275
    if(ret == -1){
204
 
      perror("sigaction");
 
276
      error_plus(0, errno, "sigaction");
 
277
      exitstatus = EX_OSERR;
205
278
      goto failure;
206
279
    }
207
280
    if(old_action.sa_handler != SIG_IGN){
208
281
      ret = sigaction(SIGHUP, &new_action, NULL);
209
282
      if(ret == -1){
210
 
        perror("sigaction");
 
283
        error_plus(0, errno, "sigaction");
 
284
        exitstatus = EX_OSERR;
211
285
        goto failure;
212
286
      }
213
287
    }
214
288
    ret = sigaction(SIGTERM, NULL, &old_action);
215
289
    if(ret == -1){
216
 
      perror("sigaction");
 
290
      error_plus(0, errno, "sigaction");
 
291
      exitstatus = EX_OSERR;
217
292
      goto failure;
218
293
    }
219
294
    if(old_action.sa_handler != SIG_IGN){
220
295
      ret = sigaction(SIGTERM, &new_action, NULL);
221
296
      if(ret == -1){
222
 
        perror("sigaction");
 
297
        error_plus(0, errno, "sigaction");
 
298
        exitstatus = EX_OSERR;
223
299
        goto failure;
224
300
      }
225
301
    }
235
311
    goto failure;
236
312
  }
237
313
  if(splashy_command_pid == -1){
238
 
    perror("fork");
 
314
    error_plus(0, errno, "fork");
 
315
    exitstatus = EX_OSERR;
239
316
    goto failure;
240
317
  }
241
318
  /* Child */
243
320
    if(not interrupted_by_signal){
244
321
      const char splashy_command[] = "/sbin/splashy_update";
245
322
      execl(splashy_command, splashy_command, prompt, (char *)NULL);
246
 
      perror("execl");
 
323
      int e = errno;
 
324
      error_plus(0, errno, "execl");
 
325
      switch(e){
 
326
      case EACCES:
 
327
      case ENOENT:
 
328
      case ENOEXEC:
 
329
      case EINVAL:
 
330
        _exit(EX_UNAVAILABLE);
 
331
      case ENAMETOOLONG:
 
332
      case E2BIG:
 
333
      case ENOMEM:
 
334
      case EFAULT:
 
335
      case EIO:
 
336
      case EMFILE:
 
337
      case ENFILE:
 
338
      case ETXTBSY:
 
339
      default:
 
340
        _exit(EX_OSERR);
 
341
      case ENOTDIR:
 
342
      case ELOOP:
 
343
      case EISDIR:
 
344
#ifdef ELIBBAD
 
345
      case ELIBBAD:             /* Linux only */
 
346
#endif
 
347
      case EPERM:
 
348
        _exit(EX_OSFILE);
 
349
      }
247
350
    }
248
351
    free(prompt);
249
352
    _exit(EXIT_FAILURE);
268
371
      goto failure;
269
372
    }
270
373
    if(ret == -1){
271
 
      perror("waitpid");
 
374
      error_plus(0, errno, "waitpid");
272
375
      if(errno == ECHILD){
273
376
        splashy_command_pid = 0;
274
377
      }
306
409
         the real user ID (_mandos) */
307
410
      ret = setuid(geteuid());
308
411
      if(ret == -1){
309
 
        perror("setuid");
 
412
        error_plus(0, errno, "setuid");
310
413
      }
311
414
      
312
415
      setsid();
313
416
      ret = chdir("/");
314
417
      if(ret == -1){
315
 
        perror("chdir");
 
418
        error_plus(0, errno, "chdir");
316
419
      }
317
420
/*       if(fork() != 0){ */
318
421
/*      _exit(EXIT_SUCCESS); */
319
422
/*       } */
320
423
      ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace stdout */
321
424
      if(ret == -1){
322
 
        perror("dup2");
323
 
        _exit(EXIT_FAILURE);
 
425
        error_plus(0, errno, "dup2");
 
426
        _exit(EX_OSERR);
324
427
      }
325
 
    
 
428
      
326
429
      execl("/sbin/splashy", "/sbin/splashy", "boot", (char *)NULL);
327
 
      perror("execl");
328
 
      _exit(EXIT_FAILURE);
 
430
      {
 
431
        int e = errno;
 
432
        error_plus(0, errno, "execl");
 
433
        switch(e){
 
434
        case EACCES:
 
435
        case ENOENT:
 
436
        case ENOEXEC:
 
437
        default:
 
438
          _exit(EX_UNAVAILABLE);
 
439
        case ENAMETOOLONG:
 
440
        case E2BIG:
 
441
        case ENOMEM:
 
442
          _exit(EX_OSERR);
 
443
        case ENOTDIR:
 
444
        case ELOOP:
 
445
          _exit(EX_OSFILE);
 
446
        }
 
447
      }
329
448
    }
330
449
  }
331
450
  
336
455
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
337
456
                                            &signal_action, NULL));
338
457
    if(ret == -1){
339
 
      perror("sigaction");
 
458
      error_plus(0, errno, "sigaction");
340
459
    }
341
460
    do {
342
461
      ret = raise(signal_received);
343
462
    } while(ret != 0 and errno == EINTR);
344
463
    if(ret != 0){
345
 
      perror("raise");
 
464
      error_plus(0, errno, "raise");
346
465
      abort();
347
466
    }
348
467
    TEMP_FAILURE_RETRY(pause());
349
468
  }
350
469
  
351
 
  return EXIT_FAILURE;
 
470
  return exitstatus;
352
471
}