/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
 
#define _GNU_SOURCE             /* asprintf(), TEMP_FAILURE_RETRY() */
2
 
#include <signal.h>             /* sig_atomic_t, struct sigaction,
3
 
                                   sigemptyset(), sigaddset(), SIGINT,
4
 
                                   SIGHUP, SIGTERM, sigaction(),
5
 
                                   kill(), SIG_IGN */
6
 
#include <stdbool.h>            /* bool, false, true */
7
 
#include <fcntl.h>              /* open(), O_RDONLY */
8
 
#include <iso646.h>             /* and, or, not*/
9
 
#include <sys/types.h>          /* size_t, ssize_t, pid_t, struct dirent,
10
 
                                   waitpid() */
11
 
#include <sys/wait.h>           /* waitpid() */
12
 
#include <stddef.h>             /* NULL */
13
 
#include <string.h>             /* strchr(), memcmp() */
14
 
#include <stdio.h>              /* asprintf(), perror(), fopen(), fscanf() */
15
 
#include <unistd.h>             /* close(), readlink(), read(), fork()
16
 
                                   setsid(), chdir(), dup2()
17
 
                                   STDERR_FILENO, execv(), access() */
18
 
#include <stdlib.h>             /* free(), EXIT_FAILURE, realloc(),
19
 
                                   EXIT_SUCCESS, malloc(), _exit(),
20
 
                                   getenv() */
21
 
#include <dirent.h>             /* scandir(), alphasort() */
22
 
#include <inttypes.h>           /* intmax_t, strtoumax(), SCNuMAX */
23
 
#include <sys/stat.h>           /* struct stat, lstat() */
24
 
#include <sysexits.h>           /* EX_OSERR */
25
 
#include <error.h>              /* error() */
26
 
#include <errno.h>              /* TEMP_FAILURE_RETRY */
27
 
#include <argz.h>               /* argz_count(), argz_extract() */
28
 
 
29
 
sig_atomic_t interrupted_by_signal = 0;
30
 
const char plymouth_pid[] = "/dev/.initramfs/plymouth.pid";
31
 
const char plymouth_path[] = "/bin/plymouth";
32
 
const char plymouthd_path[] = "/sbin/plymouthd";
33
 
const char *plymouthd_default_argv[] = {"/sbin/plymouthd", "--mode=boot",
34
 
                                        "--attach-to-session",
35
 
                                        "--pid-file=/dev/.initramfs/plymouth.pid",
36
 
                                        NULL };
37
 
 
38
 
static void termination_handler(__attribute__((unused))int signum){
39
 
  if(interrupted_by_signal){
40
 
    return;
41
 
  }
42
 
  interrupted_by_signal = 1;
43
 
}
44
 
 
45
 
/* Create prompt string */
46
 
char *makeprompt(void){
47
 
  int ret = 0;
48
 
  char *prompt;
49
 
  const char *const cryptsource = getenv("cryptsource");
50
 
  const char *const crypttarget = getenv("crypttarget");
51
 
  const char prompt_start[] = "Enter passphrase to unlock the disk";
52
 
  
53
 
  if(cryptsource == NULL){
54
 
    if(crypttarget == NULL){
55
 
      ret = asprintf(&prompt, "%s: ", prompt_start);
56
 
    } else {
57
 
      ret = asprintf(&prompt, "%s (%s): ", prompt_start,
58
 
                     crypttarget);
59
 
    }
60
 
  } else {
61
 
    if(crypttarget == NULL){
62
 
      ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource);
63
 
    } else {
64
 
      ret = asprintf(&prompt, "%s %s (%s): ", prompt_start,
65
 
                     cryptsource, crypttarget);
66
 
    }
67
 
  }
68
 
  if(ret == -1){
69
 
    return NULL;
70
 
  }
71
 
  return prompt;
72
 
}
73
 
 
74
 
void kill_and_wait(pid_t pid){
75
 
  TEMP_FAILURE_RETRY(kill(pid, SIGTERM));
76
 
  TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0));
77
 
}
78
 
 
79
 
bool become_a_daemon(void){
80
 
  int ret = setuid(geteuid());
81
 
  if(ret == -1){
82
 
    error(0, errno, "setuid");
83
 
  }
84
 
    
85
 
  setsid();
86
 
  ret = chdir("/");
87
 
  if(ret == -1){
88
 
    error(0, errno, "chdir");
89
 
    return false;
90
 
  }
91
 
  ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
92
 
  if(ret == -1){
93
 
    error(0, errno, "dup2");
94
 
    return false;
95
 
  }
96
 
  return true;
97
 
}
98
 
 
99
 
bool exec_and_wait(pid_t *pid_return, const char *path,
100
 
                   const char **argv, bool interruptable,
101
 
                   bool daemonize){
102
 
  int status;
103
 
  int ret;
104
 
  pid_t pid;
105
 
  pid = fork();
106
 
  if(pid == -1){
107
 
    error(0, errno, "fork");
108
 
    return false;
109
 
  }
110
 
  if(pid == 0){
111
 
    /* Child */
112
 
    if(daemonize){
113
 
      if(not become_a_daemon()){
114
 
        _exit(EX_OSERR);
115
 
      }
116
 
    }
117
 
 
118
 
    char **new_argv = NULL;
119
 
    char *tmp;
120
 
    int i = 0;
121
 
    for (; argv[i]!=(char *)NULL; i++){
122
 
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 1));
123
 
      if (tmp == NULL){
124
 
        error(0, errno, "realloc");
125
 
        free(new_argv);
126
 
        _exit(EXIT_FAILURE);
127
 
      }
128
 
      new_argv = (char **)tmp;
129
 
      new_argv[i] = strdup(argv[i]);
130
 
    }
131
 
    new_argv[i] = (char *) NULL;
132
 
    
133
 
    execv(path, (char *const *)new_argv);
134
 
    error(0, errno, "execv");
135
 
    _exit(EXIT_FAILURE);
136
 
  }
137
 
  if(pid_return != NULL){
138
 
    *pid_return = pid;
139
 
  }
140
 
  do {
141
 
    ret = waitpid(pid, &status, 0);
142
 
  } while(ret == -1 and errno == EINTR
143
 
          and ((not interrupted_by_signal)
144
 
               or (not interruptable)));
145
 
  if(interrupted_by_signal and interruptable){
146
 
    return false;
147
 
  }
148
 
  if(ret == -1){
149
 
    error(0, errno, "waitpid");
150
 
    return false;
151
 
  }
152
 
  if(WIFEXITED(status) and WEXITSTATUS(status) == 0){
153
 
    return true;
154
 
  }
155
 
  return false;
156
 
}
157
 
 
158
 
int is_plymouth(const struct dirent *proc_entry){
159
 
  int ret;
160
 
  {
161
 
    uintmax_t maxvalue;
162
 
    char *tmp;
163
 
    errno = 0;
164
 
    maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
165
 
 
166
 
    if(errno != 0 or *tmp != '\0' or maxvalue != (uintmax_t)((pid_t)maxvalue)){
167
 
      return 0;
168
 
    }
169
 
  }
170
 
  char exe_target[sizeof(plymouth_path)];
171
 
  char *exe_link;
172
 
  ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name);
173
 
  if(ret == -1){
174
 
    error(0, errno, "asprintf");
175
 
    return 0;
176
 
  }
177
 
 
178
 
  struct stat exe_stat;
179
 
  ret = lstat(exe_link, &exe_stat);
180
 
  if(ret == -1){
181
 
    free(exe_link);
182
 
    if(errno != ENOENT){
183
 
      error(0, errno, "lstat");
184
 
    }
185
 
    return 0;
186
 
  }
187
 
  
188
 
  if(not S_ISLNK(exe_stat.st_mode)
189
 
     or exe_stat.st_uid != 0
190
 
     or exe_stat.st_gid != 0){
191
 
    free(exe_link);
192
 
    return 0;
193
 
  }
194
 
 
195
 
  ssize_t sret = readlink(exe_link, exe_target, sizeof(exe_target));
196
 
  free(exe_link);
197
 
  if((sret != (ssize_t)sizeof(plymouth_path)-1) or
198
 
      (memcmp(plymouth_path, exe_target,
199
 
              sizeof(plymouth_path)-1) != 0)){
200
 
    return 0;
201
 
  }
202
 
  return 1;
203
 
}
204
 
 
205
 
pid_t get_pid(void){
206
 
  int ret;
207
 
  FILE *pidfile = fopen(plymouth_pid, "r");
208
 
  uintmax_t maxvalue = 0;
209
 
  if(pidfile != NULL){
210
 
    ret = fscanf(pidfile, "%" SCNuMAX, &maxvalue);
211
 
    if(ret != 1){
212
 
      maxvalue = 0;
213
 
    }
214
 
    fclose(pidfile);
215
 
  }
216
 
  if(maxvalue == 0){
217
 
    struct dirent **direntries;
218
 
    ret = scandir("/proc", &direntries, is_plymouth, alphasort);
219
 
    sscanf(direntries[0]->d_name, "%" SCNuMAX, &maxvalue);
220
 
  }
221
 
  pid_t pid;
222
 
  pid = (pid_t)maxvalue;
223
 
  if((uintmax_t)pid == maxvalue){
224
 
    return pid;
225
 
  }
226
 
  
227
 
  return 0;
228
 
}
229
 
 
230
 
const char **getargv(pid_t pid){
231
 
  int cl_fd;
232
 
  char *cmdline_filename;
233
 
  ssize_t sret;
234
 
  int ret;
235
 
  
236
 
  ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline",
237
 
                 (uintmax_t)pid);
238
 
  if(ret == -1){
239
 
    error(0, errno, "asprintf");
240
 
    return NULL;
241
 
  }
242
 
  
243
 
  /* Open /proc/<pid>/cmdline  */
244
 
  cl_fd = open(cmdline_filename, O_RDONLY);
245
 
  free(cmdline_filename);
246
 
  if(cl_fd == -1){
247
 
    error(0, errno, "open");
248
 
    return NULL;
249
 
  }
250
 
  
251
 
  size_t cmdline_allocated = 0;
252
 
  size_t cmdline_len = 0;
253
 
  char *cmdline = NULL;
254
 
  char *tmp;
255
 
  const size_t blocksize = 1024;
256
 
  do {
257
 
    /* Allocate more space? */
258
 
    if(cmdline_len + blocksize > cmdline_allocated){
259
 
      tmp = realloc(cmdline, cmdline_allocated + blocksize);
260
 
      if(tmp == NULL){
261
 
        error(0, errno, "realloc");
262
 
        free(cmdline);
263
 
        close(cl_fd);
264
 
        return NULL;
265
 
      }
266
 
      cmdline = tmp;
267
 
      cmdline_allocated += blocksize;
268
 
    }
269
 
    
270
 
    /* Read data */
271
 
    sret = read(cl_fd, cmdline + cmdline_len,
272
 
                cmdline_allocated - cmdline_len);
273
 
    if(sret == -1){
274
 
      error(0, errno, "read");
275
 
      free(cmdline);
276
 
      close(cl_fd);
277
 
      return NULL;
278
 
    }
279
 
    cmdline_len += (size_t)sret;
280
 
  } while(sret != 0);
281
 
  ret = close(cl_fd);
282
 
  if(ret == -1){
283
 
    error(0, errno, "close");
284
 
    free(cmdline);
285
 
    return NULL;
286
 
  }
287
 
  
288
 
  /* we got cmdline and cmdline_len, ignore rest... */
289
 
  char **argv = malloc((argz_count(cmdline, cmdline_len) + 1)
290
 
                       * sizeof(char *)); /* Get number of args */
291
 
  if(argv == NULL){
292
 
    error(0, errno, "argv = malloc()");
293
 
    free(cmdline);
294
 
    return NULL;
295
 
  }
296
 
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
297
 
  return (const char **)argv;
298
 
}
299
 
 
300
 
int main(__attribute__((unused))int argc,
301
 
         __attribute__((unused))char **argv){
302
 
  char *prompt;
303
 
  char *prompt_arg;
304
 
  pid_t plymouth_command_pid;
305
 
  int ret;
306
 
  bool bret;
307
 
 
308
 
  /* test -x /bin/plymouth */
309
 
  ret = access(plymouth_path, X_OK);
310
 
  if(ret == -1){
311
 
    exit(EXIT_FAILURE);
312
 
  }
313
 
 
314
 
  { /* Add signal handlers */
315
 
    struct sigaction old_action,
316
 
      new_action = { .sa_handler = termination_handler,
317
 
                     .sa_flags = 0 };
318
 
    sigemptyset(&new_action.sa_mask);
319
 
    for(int *sig = (int[]){ SIGINT, SIGHUP, SIGTERM, 0 }; *sig != 0; sig++){
320
 
      ret = sigaddset(&new_action.sa_mask, *sig);
321
 
      if(ret == -1){
322
 
        error(0, errno, "sigaddset");
323
 
        exit(EX_OSERR);
324
 
      }
325
 
      ret = sigaction(*sig, NULL, &old_action);
326
 
      if(ret == -1){
327
 
        error(0, errno, "sigaction");
328
 
        exit(EX_OSERR);
329
 
      }
330
 
      if(old_action.sa_handler != SIG_IGN){
331
 
        ret = sigaction(*sig, &new_action, NULL);
332
 
        if(ret == -1){
333
 
          error(0, errno, "sigaction");
334
 
          exit(EX_OSERR);
335
 
        }
336
 
      }
337
 
    }
338
 
  }
339
 
    
340
 
  /* plymouth --ping */
341
 
  bret = exec_and_wait(&plymouth_command_pid, plymouth_path,
342
 
                       (const char *[]){ (const char *)plymouth_path, (const char *)"--ping", (const char *)NULL},
343
 
                       true, false);
344
 
  if(not bret){
345
 
    if(interrupted_by_signal){
346
 
      kill_and_wait(plymouth_command_pid);
347
 
    }
348
 
    exit(EXIT_FAILURE);
349
 
  }
350
 
  
351
 
  prompt = makeprompt();
352
 
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
353
 
  free(prompt);
354
 
  if(ret == -1){
355
 
    error(0, errno, "asprintf");
356
 
    exit(EXIT_FAILURE);
357
 
  }
358
 
  
359
 
  /* plymouth ask-for-password --prompt="$prompt" */
360
 
  bret = exec_and_wait(&plymouth_command_pid, plymouth_path,
361
 
                       (const char *[]){plymouth_path, "ask-for-password", prompt_arg, NULL},
362
 
                       true, false);
363
 
  free(prompt_arg);
364
 
  if(not bret){
365
 
    if(interrupted_by_signal){
366
 
      kill_and_wait(plymouth_command_pid);
367
 
    } else {
368
 
      exit(EXIT_FAILURE);
369
 
    }
370
 
  }
371
 
  
372
 
  if(bret){
373
 
    exit(EXIT_SUCCESS);
374
 
  }
375
 
  
376
 
  const char **plymouthd_argv = NULL;
377
 
  pid_t pid = get_pid();
378
 
  if(pid == 0){
379
 
    error(0, 0, "plymouthd pid not found");
380
 
  } else {
381
 
    plymouthd_argv = getargv(pid);
382
 
  }
383
 
  if(plymouthd_argv == NULL){
384
 
    plymouthd_argv = plymouthd_default_argv;
385
 
  }
386
 
  
387
 
  bret = exec_and_wait(NULL, plymouth_path,
388
 
                       (const char *[]){plymouth_path, "quit", NULL}, false, false);
389
 
  if(not bret){
390
 
    exit(EXIT_FAILURE);
391
 
  }
392
 
  bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv, false, true);
393
 
  if(not bret){
394
 
    exit(EXIT_FAILURE);
395
 
  }
396
 
  exec_and_wait(NULL, plymouth_path,
397
 
                (const char *[]){ plymouth_path, "show-splash", NULL }, false, false);
398
 
  exit(EXIT_FAILURE);
399
 
}