/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/plymouth.c

First version of a somewhat complete D-Bus server interface.  Also
change user/group name to "_mandos".

* debian/mandos.postinst: Rename old "mandos" user and group to
                          "_mandos"; create "_mandos" user and group
                          if none exist.
* debian/mandos-client.postinst: - '' -

* initramfs-tools-hook: Try "_mandos" before "mandos" as user and
                        group name.

* mandos (_datetime_to_dbus_struct): New; was previously local.
  (Client.started): Renamed to "last_started".  All users changed.
  (Client.started): New; boolean.
  (Client.dbus_object_path): New.
  (Client.check_command): Renamed to "checker_command".  All users
                          changed.
  (Client.__init__): Set and use "self.dbus_object_path".  Set
                     "self.started".
  (Client.start): Update "self.started".  Emit "self.PropertyChanged"
                  signals for both "started" and "last_started".
  (Client.stop): Update "self.started".  Emit "self.PropertyChanged"
                 signal for "started".
  (Client.checker_callback): Take additional "command" argument.  All
                             callers changed. Emit
                             "self.PropertyChanged" signal.
  (Client.bump_timeout): Emit "self.PropertyChanged" signal for
                         "last_checked_ok".
  (Client.start_checker): Emit "self.PropertyChanged" signal for
                          "checker_running".
  (Client.stop_checker): Emit "self.PropertyChanged" signal for
                         "checker_running".
  (Client.still_valid): Bug fix: use "getattr(self, started, False)"
                        instead of "self.started" in case this client
                        object is so new that the "started" attribute
                        has not been created yet.
  (Client.IntervalChanged, Client.CheckerIsRunning, Client.GetChecker,
  Client.GetCreated, Client.GetFingerprint, Client.GetHost,
  Client.GetInterval, Client.GetName, Client.GetStarted,
  Client.GetTimeout, Client.StateChanged, Client.TimeoutChanged):
  Removed; all callers changed.
  (Client.CheckerCompleted): Add "condition" and "command" arguments.
                             All callers changed.
  (Client.GetAllProperties, Client.PropertyChanged): New.
  (Client.StillValid): Renamed to "IsStillValid".
  (Client.StartChecker): Changed to its own function to avoid the
                         return value from "Client.start_checker()".
  (Client.Stop): Changed to its own function to avoid the return value
                 from "Client.stop()".
  (main): Try "_mandos" before "mandos" as user and group name.
          Removed inner function "remove_from_clients".  New inner
          class "MandosServer".

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  -*- coding: utf-8 -*- */
2
 
/*
3
 
 * Plymouth - Read a password from Plymouth and output it
4
 
 * 
5
 
 * Copyright © 2010-2018 Teddy Hogeborn
6
 
 * Copyright © 2010-2018 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
16
 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
17
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18
 
 * General Public License for more details.
19
 
 * 
20
 
 * You should have received a copy of the GNU General Public License
21
 
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
22
 
 * 
23
 
 * Contact the authors at <mandos@recompile.se>.
24
 
 */
25
 
 
26
 
#define _GNU_SOURCE             /* asprintf(), TEMP_FAILURE_RETRY() */
27
 
#include <signal.h>             /* sig_atomic_t, struct sigaction,
28
 
                                   sigemptyset(), sigaddset(), SIGINT,
29
 
                                   SIGHUP, SIGTERM, sigaction(),
30
 
                                   kill(), SIG_IGN */
31
 
#include <stdbool.h>            /* bool, false, true */
32
 
#include <fcntl.h>              /* open(), O_RDONLY */
33
 
#include <iso646.h>             /* and, or, not*/
34
 
#include <sys/types.h>          /* size_t, ssize_t, pid_t, struct
35
 
                                   dirent, waitpid() */
36
 
#include <sys/wait.h>           /* waitpid() */
37
 
#include <stddef.h>             /* NULL */
38
 
#include <string.h>             /* strchr(), memcmp() */
39
 
#include <stdio.h>              /* asprintf(), perror(), fopen(),
40
 
                                   fscanf(), vasprintf(), fprintf(),
41
 
                                   vfprintf() */
42
 
#include <unistd.h>             /* close(), readlink(), read(),
43
 
                                   fork(), setsid(), chdir(), dup2(),
44
 
                                   STDERR_FILENO, execv(), access() */
45
 
#include <stdlib.h>             /* free(), EXIT_FAILURE, realloc(),
46
 
                                   EXIT_SUCCESS, malloc(), _exit(),
47
 
                                   getenv() */
48
 
#include <dirent.h>             /* scandir(), alphasort() */
49
 
#include <inttypes.h>           /* intmax_t, strtoumax(), SCNuMAX */
50
 
#include <sys/stat.h>           /* struct stat, lstat() */
51
 
#include <sysexits.h>           /* EX_OSERR, EX_UNAVAILABLE */
52
 
#include <error.h>              /* error() */
53
 
#include <errno.h>              /* TEMP_FAILURE_RETRY */
54
 
#include <argz.h>               /* argz_count(), argz_extract() */
55
 
#include <stdarg.h>             /* va_list, va_start(), ... */
56
 
 
57
 
sig_atomic_t interrupted_by_signal = 0;
58
 
 
59
 
/* Used by Ubuntu 11.04 (Natty Narwahl) */
60
 
const char plymouth_old_old_pid[] = "/dev/.initramfs/plymouth.pid";
61
 
/* Used by Ubuntu 11.10 (Oneiric Ocelot) */
62
 
const char plymouth_old_pid[] = "/run/initramfs/plymouth.pid";
63
 
/* Used by Debian 9 (stretch) */
64
 
const char plymouth_pid[] = "/run/plymouth/pid";
65
 
 
66
 
const char plymouth_path[] = "/bin/plymouth";
67
 
const char plymouthd_path[] = "/sbin/plymouthd";
68
 
const char *plymouthd_default_argv[] = {"/sbin/plymouthd",
69
 
                                        "--mode=boot",
70
 
                                        "--attach-to-session",
71
 
                                        NULL };
72
 
 
73
 
static void termination_handler(__attribute__((unused))int signum){
74
 
  if(interrupted_by_signal){
75
 
    return;
76
 
  }
77
 
  interrupted_by_signal = 1;
78
 
}
79
 
 
80
 
/* Function to use when printing errors */
81
 
__attribute__((format (gnu_printf, 3, 4)))
82
 
void error_plus(int status, int errnum, const char *formatstring,
83
 
                ...){
84
 
  va_list ap;
85
 
  char *text;
86
 
  int ret;
87
 
  
88
 
  va_start(ap, formatstring);
89
 
  ret = vasprintf(&text, formatstring, ap);
90
 
  if(ret == -1){
91
 
    fprintf(stderr, "Mandos plugin %s: ",
92
 
            program_invocation_short_name);
93
 
    vfprintf(stderr, formatstring, ap);
94
 
    fprintf(stderr, ": ");
95
 
    fprintf(stderr, "%s\n", strerror(errnum));
96
 
    error(status, errno, "vasprintf while printing error");
97
 
    return;
98
 
  }
99
 
  fprintf(stderr, "Mandos plugin ");
100
 
  error(status, errnum, "%s", text);
101
 
  free(text);
102
 
}
103
 
 
104
 
/* Create prompt string */
105
 
char *makeprompt(void){
106
 
  int ret = 0;
107
 
  char *prompt;
108
 
  const char *const cryptsource = getenv("cryptsource");
109
 
  const char *const crypttarget = getenv("crypttarget");
110
 
  const char prompt_start[] = "Unlocking the disk";
111
 
  const char prompt_end[] = "Enter passphrase";
112
 
  
113
 
  if(cryptsource == NULL){
114
 
    if(crypttarget == NULL){
115
 
      ret = asprintf(&prompt, "%s\n%s", prompt_start, prompt_end);
116
 
    } else {
117
 
      ret = asprintf(&prompt, "%s (%s)\n%s", prompt_start,
118
 
                     crypttarget, prompt_end);
119
 
    }
120
 
  } else {
121
 
    if(crypttarget == NULL){
122
 
      ret = asprintf(&prompt, "%s %s\n%s", prompt_start, cryptsource,
123
 
                     prompt_end);
124
 
    } else {
125
 
      ret = asprintf(&prompt, "%s %s (%s)\n%s", prompt_start,
126
 
                     cryptsource, crypttarget, prompt_end);
127
 
    }
128
 
  }
129
 
  if(ret == -1){
130
 
    return NULL;
131
 
  }
132
 
  return prompt;
133
 
}
134
 
 
135
 
void kill_and_wait(pid_t pid){
136
 
  TEMP_FAILURE_RETRY(kill(pid, SIGTERM));
137
 
  TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0));
138
 
}
139
 
 
140
 
bool become_a_daemon(void){
141
 
  int ret = setuid(geteuid());
142
 
  if(ret == -1){
143
 
    error_plus(0, errno, "setuid");
144
 
  }
145
 
    
146
 
  setsid();
147
 
  ret = chdir("/");
148
 
  if(ret == -1){
149
 
    error_plus(0, errno, "chdir");
150
 
    return false;
151
 
  }
152
 
  ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
153
 
  if(ret == -1){
154
 
    error_plus(0, errno, "dup2");
155
 
    return false;
156
 
  }
157
 
  return true;
158
 
}
159
 
 
160
 
__attribute__((nonnull (2, 3)))
161
 
bool exec_and_wait(pid_t *pid_return, const char *path,
162
 
                   const char * const *argv, bool interruptable,
163
 
                   bool daemonize){
164
 
  int status;
165
 
  int ret;
166
 
  pid_t pid;
167
 
  pid = fork();
168
 
  if(pid == -1){
169
 
    error_plus(0, errno, "fork");
170
 
    return false;
171
 
  }
172
 
  if(pid == 0){
173
 
    /* Child */
174
 
    if(daemonize){
175
 
      if(not become_a_daemon()){
176
 
        _exit(EX_OSERR);
177
 
      }
178
 
    }
179
 
    
180
 
    char **new_argv = malloc(sizeof(const char *));
181
 
    if(new_argv == NULL){
182
 
      error_plus(0, errno, "malloc");
183
 
      _exit(EX_OSERR);
184
 
    }
185
 
    char **tmp;
186
 
    int i = 0;
187
 
    for (; argv[i] != NULL; i++){
188
 
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 2));
189
 
      if(tmp == NULL){
190
 
        error_plus(0, errno, "realloc");
191
 
        free(new_argv);
192
 
        _exit(EX_OSERR);
193
 
      }
194
 
      new_argv = tmp;
195
 
      new_argv[i] = strdup(argv[i]);
196
 
    }
197
 
    new_argv[i] = NULL;
198
 
    
199
 
    execv(path, (char *const *)new_argv);
200
 
    error_plus(0, errno, "execv");
201
 
    _exit(EXIT_FAILURE);
202
 
  }
203
 
  if(pid_return != NULL){
204
 
    *pid_return = pid;
205
 
  }
206
 
  do {
207
 
    ret = waitpid(pid, &status, 0);
208
 
  } while(ret == -1 and errno == EINTR
209
 
          and ((not interrupted_by_signal)
210
 
               or (not interruptable)));
211
 
  if(interrupted_by_signal and interruptable){
212
 
    return false;
213
 
  }
214
 
  if(ret == -1){
215
 
    error_plus(0, errno, "waitpid");
216
 
    return false;
217
 
  }
218
 
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
219
 
    return true;
220
 
  }
221
 
  return false;
222
 
}
223
 
 
224
 
__attribute__((nonnull))
225
 
int is_plymouth(const struct dirent *proc_entry){
226
 
  int ret;
227
 
  {
228
 
    uintmax_t proc_id;
229
 
    char *tmp;
230
 
    errno = 0;
231
 
    proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
232
 
 
233
 
    if(errno != 0 or *tmp != '\0'
234
 
       or proc_id != (uintmax_t)((pid_t)proc_id)){
235
 
      return 0;
236
 
    }
237
 
  }
238
 
  char exe_target[sizeof(plymouthd_path)];
239
 
  char *exe_link;
240
 
  ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name);
241
 
  if(ret == -1){
242
 
    error_plus(0, errno, "asprintf");
243
 
    return 0;
244
 
  }
245
 
  
246
 
  struct stat exe_stat;
247
 
  ret = lstat(exe_link, &exe_stat);
248
 
  if(ret == -1){
249
 
    free(exe_link);
250
 
    if(errno != ENOENT){
251
 
      error_plus(0, errno, "lstat");
252
 
    }
253
 
    return 0;
254
 
  }
255
 
  
256
 
  if(not S_ISLNK(exe_stat.st_mode)
257
 
     or exe_stat.st_uid != 0
258
 
     or exe_stat.st_gid != 0){
259
 
    free(exe_link);
260
 
    return 0;
261
 
  }
262
 
  
263
 
  ssize_t sret = readlink(exe_link, exe_target, sizeof(exe_target));
264
 
  free(exe_link);
265
 
  if((sret != (ssize_t)sizeof(plymouthd_path)-1) or
266
 
      (memcmp(plymouthd_path, exe_target,
267
 
              sizeof(plymouthd_path)-1) != 0)){
268
 
    return 0;
269
 
  }
270
 
  return 1;
271
 
}
272
 
 
273
 
pid_t get_pid(void){
274
 
  int ret;
275
 
  uintmax_t proc_id = 0;
276
 
  FILE *pidfile = fopen(plymouth_pid, "r");
277
 
  /* Try the new pid file location */
278
 
  if(pidfile != NULL){
279
 
    ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
280
 
    if(ret != 1){
281
 
      proc_id = 0;
282
 
    }
283
 
    fclose(pidfile);
284
 
  }
285
 
  /* Try the old pid file location */
286
 
  if(proc_id == 0){
287
 
    pidfile = fopen(plymouth_old_pid, "r");
288
 
    if(pidfile != NULL){
289
 
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
290
 
      if(ret != 1){
291
 
        proc_id = 0;
292
 
      }
293
 
      fclose(pidfile);
294
 
    }
295
 
  }
296
 
  /* Try the old old pid file location */
297
 
  if(proc_id == 0){
298
 
    pidfile = fopen(plymouth_old_old_pid, "r");
299
 
    if(pidfile != NULL){
300
 
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
301
 
      if(ret != 1){
302
 
        proc_id = 0;
303
 
      }
304
 
      fclose(pidfile);
305
 
    }
306
 
  }
307
 
  /* Look for a plymouth process */
308
 
  if(proc_id == 0){
309
 
    struct dirent **direntries = NULL;
310
 
    ret = scandir("/proc", &direntries, is_plymouth, alphasort);
311
 
    if(ret == -1){
312
 
      error_plus(0, errno, "scandir");
313
 
    }
314
 
    if(ret > 0){
315
 
      for(int i = ret-1; i >= 0; i--){
316
 
        if(proc_id == 0){
317
 
          ret = sscanf(direntries[i]->d_name, "%" SCNuMAX, &proc_id);
318
 
          if(ret < 0){
319
 
            error_plus(0, errno, "sscanf");
320
 
          }
321
 
        }
322
 
        free(direntries[i]);
323
 
      }
324
 
    }
325
 
    /* scandir might preallocate for this variable (man page unclear).
326
 
       even if ret == 0, therefore we need to free it. */
327
 
    free(direntries);
328
 
  }
329
 
  pid_t pid;
330
 
  pid = (pid_t)proc_id;
331
 
  if((uintmax_t)pid == proc_id){
332
 
    return pid;
333
 
  }
334
 
  
335
 
  return 0;
336
 
}
337
 
 
338
 
char **getargv(pid_t pid){
339
 
  int cl_fd;
340
 
  char *cmdline_filename;
341
 
  ssize_t sret;
342
 
  int ret;
343
 
  
344
 
  ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline",
345
 
                 (uintmax_t)pid);
346
 
  if(ret == -1){
347
 
    error_plus(0, errno, "asprintf");
348
 
    return NULL;
349
 
  }
350
 
  
351
 
  /* Open /proc/<pid>/cmdline  */
352
 
  cl_fd = open(cmdline_filename, O_RDONLY);
353
 
  free(cmdline_filename);
354
 
  if(cl_fd == -1){
355
 
    error_plus(0, errno, "open");
356
 
    return NULL;
357
 
  }
358
 
  
359
 
  size_t cmdline_allocated = 0;
360
 
  size_t cmdline_len = 0;
361
 
  char *cmdline = NULL;
362
 
  char *tmp;
363
 
  const size_t blocksize = 1024;
364
 
  do {
365
 
    /* Allocate more space? */
366
 
    if(cmdline_len + blocksize > cmdline_allocated){
367
 
      tmp = realloc(cmdline, cmdline_allocated + blocksize);
368
 
      if(tmp == NULL){
369
 
        error_plus(0, errno, "realloc");
370
 
        free(cmdline);
371
 
        close(cl_fd);
372
 
        return NULL;
373
 
      }
374
 
      cmdline = tmp;
375
 
      cmdline_allocated += blocksize;
376
 
    }
377
 
    
378
 
    /* Read data */
379
 
    sret = read(cl_fd, cmdline + cmdline_len,
380
 
                cmdline_allocated - cmdline_len);
381
 
    if(sret == -1){
382
 
      error_plus(0, errno, "read");
383
 
      free(cmdline);
384
 
      close(cl_fd);
385
 
      return NULL;
386
 
    }
387
 
    cmdline_len += (size_t)sret;
388
 
  } while(sret != 0);
389
 
  ret = close(cl_fd);
390
 
  if(ret == -1){
391
 
    error_plus(0, errno, "close");
392
 
    free(cmdline);
393
 
    return NULL;
394
 
  }
395
 
  
396
 
  /* we got cmdline and cmdline_len, ignore rest... */
397
 
  char **argv = malloc((argz_count(cmdline, cmdline_len) + 1)
398
 
                       * sizeof(char *)); /* Get number of args */
399
 
  if(argv == NULL){
400
 
    error_plus(0, errno, "argv = malloc()");
401
 
    free(cmdline);
402
 
    return NULL;
403
 
  }
404
 
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
405
 
  return argv;
406
 
}
407
 
 
408
 
int main(__attribute__((unused))int argc,
409
 
         __attribute__((unused))char **argv){
410
 
  char *prompt;
411
 
  char *prompt_arg;
412
 
  pid_t plymouth_command_pid;
413
 
  int ret;
414
 
  bool bret;
415
 
 
416
 
  /* test -x /bin/plymouth */
417
 
  ret = access(plymouth_path, X_OK);
418
 
  if(ret == -1){
419
 
    /* Plymouth is probably not installed.  Don't print an error
420
 
       message, just exit. */
421
 
    exit(EX_UNAVAILABLE);
422
 
  }
423
 
  
424
 
  { /* Add signal handlers */
425
 
    struct sigaction old_action,
426
 
      new_action = { .sa_handler = termination_handler,
427
 
                     .sa_flags = 0 };
428
 
    sigemptyset(&new_action.sa_mask);
429
 
    for(int *sig = (int[]){ SIGINT, SIGHUP, SIGTERM, 0 };
430
 
        *sig != 0; sig++){
431
 
      ret = sigaddset(&new_action.sa_mask, *sig);
432
 
      if(ret == -1){
433
 
        error_plus(EX_OSERR, errno, "sigaddset");
434
 
      }
435
 
      ret = sigaction(*sig, NULL, &old_action);
436
 
      if(ret == -1){
437
 
        error_plus(EX_OSERR, errno, "sigaction");
438
 
      }
439
 
      if(old_action.sa_handler != SIG_IGN){
440
 
        ret = sigaction(*sig, &new_action, NULL);
441
 
        if(ret == -1){
442
 
          error_plus(EX_OSERR, errno, "sigaction");
443
 
        }
444
 
      }
445
 
    }
446
 
  }
447
 
  
448
 
  /* plymouth --ping */
449
 
  bret = exec_and_wait(&plymouth_command_pid, plymouth_path,
450
 
                       (const char *[])
451
 
                       { plymouth_path, "--ping", NULL },
452
 
                       true, false);
453
 
  if(not bret){
454
 
    if(interrupted_by_signal){
455
 
      kill_and_wait(plymouth_command_pid);
456
 
      exit(EXIT_FAILURE);
457
 
    }
458
 
    /* Plymouth is probably not running.  Don't print an error
459
 
       message, just exit. */
460
 
    exit(EX_UNAVAILABLE);
461
 
  }
462
 
  
463
 
  prompt = makeprompt();
464
 
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
465
 
  free(prompt);
466
 
  if(ret == -1){
467
 
    error_plus(EX_OSERR, errno, "asprintf");
468
 
  }
469
 
  
470
 
  /* plymouth ask-for-password --prompt="$prompt" */
471
 
  bret = exec_and_wait(&plymouth_command_pid,
472
 
                       plymouth_path, (const char *[])
473
 
                       { plymouth_path, "ask-for-password",
474
 
                           prompt_arg, NULL },
475
 
                       true, false);
476
 
  free(prompt_arg);
477
 
  if(bret){
478
 
    exit(EXIT_SUCCESS);
479
 
  }
480
 
  if(not interrupted_by_signal){
481
 
    /* exec_and_wait failed for some other reason */
482
 
    exit(EXIT_FAILURE);
483
 
  }
484
 
  kill_and_wait(plymouth_command_pid);
485
 
  
486
 
  char **plymouthd_argv = NULL;
487
 
  pid_t pid = get_pid();
488
 
  if(pid == 0){
489
 
    error_plus(0, 0, "plymouthd pid not found");
490
 
  } else {
491
 
    plymouthd_argv = getargv(pid);
492
 
  }
493
 
  
494
 
  bret = exec_and_wait(NULL, plymouth_path, (const char *[])
495
 
                       { plymouth_path, "quit", NULL },
496
 
                       false, false);
497
 
  if(not bret){
498
 
    if(plymouthd_argv != NULL){
499
 
      free(*plymouthd_argv);
500
 
      free(plymouthd_argv);
501
 
    }
502
 
    exit(EXIT_FAILURE);
503
 
  }
504
 
  bret = exec_and_wait(NULL, plymouthd_path,
505
 
                       (plymouthd_argv != NULL)
506
 
                       ? (const char * const *)plymouthd_argv
507
 
                       : plymouthd_default_argv,
508
 
                       false, true);
509
 
  if(plymouthd_argv != NULL){
510
 
    free(*plymouthd_argv);
511
 
    free(plymouthd_argv);
512
 
  }
513
 
  if(not bret){
514
 
    exit(EXIT_FAILURE);
515
 
  }
516
 
  exec_and_wait(NULL, plymouth_path, (const char *[])
517
 
                { plymouth_path, "show-splash", NULL },
518
 
                false, false);
519
 
  exit(EXIT_FAILURE);
520
 
}