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

  • Committer: Teddy Hogeborn
  • Date: 2008-12-10 01:26:02 UTC
  • mfrom: (237.1.2 mandos)
  • Revision ID: teddy@fukt.bsnet.se-20081210012602-vhz3h75xkj24t340
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 <stdarg.h>
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
 
  const char **argv = NULL;
290
 
  size_t argv_size = 0;
291
 
  for(char *arg = cmdline; arg-cmdline < (ssize_t)cmdline_len;
292
 
      arg = strchr(arg, '\0')+1){
293
 
    tmp = realloc(argv, ((++argv_size)+1)*sizeof(char *));
294
 
    if(tmp == NULL){
295
 
      error(0, errno, "realloc");
296
 
      free(argv);
297
 
      return NULL;
298
 
    }
299
 
    argv = (const char **)tmp;
300
 
    argv[argv_size-1] = arg;
301
 
  }
302
 
  argv[argv_size] = NULL;
303
 
  return argv;
304
 
}
305
 
 
306
 
int main(__attribute__((unused))int argc,
307
 
         __attribute__((unused))char **argv){
308
 
  char *prompt;
309
 
  char *prompt_arg;
310
 
  pid_t plymouth_command_pid;
311
 
  int ret;
312
 
  bool bret;
313
 
 
314
 
  /* test -x /bin/plymouth */
315
 
  ret = access(plymouth_path, X_OK);
316
 
  if(ret == -1){
317
 
    exit(EXIT_FAILURE);
318
 
  }
319
 
 
320
 
  { /* Add signal handlers */
321
 
    struct sigaction old_action,
322
 
      new_action = { .sa_handler = termination_handler,
323
 
                     .sa_flags = 0 };
324
 
    sigemptyset(&new_action.sa_mask);
325
 
    for(int *sig = (int[]){ SIGINT, SIGHUP, SIGTERM, 0 }; *sig != 0; sig++){
326
 
      ret = sigaddset(&new_action.sa_mask, *sig);
327
 
      if(ret == -1){
328
 
        error(0, errno, "sigaddset");
329
 
        exit(EX_OSERR);
330
 
      }
331
 
      ret = sigaction(*sig, NULL, &old_action);
332
 
      if(ret == -1){
333
 
        error(0, errno, "sigaction");
334
 
        exit(EX_OSERR);
335
 
      }
336
 
      if(old_action.sa_handler != SIG_IGN){
337
 
        ret = sigaction(*sig, &new_action, NULL);
338
 
        if(ret == -1){
339
 
          error(0, errno, "sigaction");
340
 
          exit(EX_OSERR);
341
 
        }
342
 
      }
343
 
    }
344
 
  }
345
 
    
346
 
  /* plymouth --ping */
347
 
  bret = exec_and_wait(&plymouth_command_pid, plymouth_path,
348
 
                       (const char *[]){ (const char *)plymouth_path, (const char *)"--ping", (const char *)NULL},
349
 
                       true, false);
350
 
  if(not bret){
351
 
    if(interrupted_by_signal){
352
 
      kill_and_wait(plymouth_command_pid);
353
 
    }
354
 
    exit(EXIT_FAILURE);
355
 
  }
356
 
  
357
 
  prompt = makeprompt();
358
 
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
359
 
  free(prompt);
360
 
  if(ret == -1){
361
 
    error(0, errno, "asprintf");
362
 
    exit(EXIT_FAILURE);
363
 
  }
364
 
  
365
 
  /* plymouth ask-for-password --prompt="$prompt" */
366
 
  bret = exec_and_wait(&plymouth_command_pid, plymouth_path,
367
 
                       (const char *[]){plymouth_path, "ask-for-password", prompt_arg, NULL},
368
 
                       true, false);
369
 
  free(prompt_arg);
370
 
  if(not bret){
371
 
    if(interrupted_by_signal){
372
 
      kill_and_wait(plymouth_command_pid);
373
 
    } else {
374
 
      exit(EXIT_FAILURE);
375
 
    }
376
 
  }
377
 
  
378
 
  if(bret){
379
 
    exit(EXIT_SUCCESS);
380
 
  }
381
 
  
382
 
  const char **plymouthd_argv = NULL;
383
 
  pid_t pid = get_pid();
384
 
  if(pid == 0){
385
 
    error(0, 0, "plymouthd pid not found");
386
 
  } else {
387
 
    plymouthd_argv = getargv(pid);
388
 
  }
389
 
  if(plymouthd_argv == NULL){
390
 
    plymouthd_argv = plymouthd_default_argv;
391
 
  }
392
 
  
393
 
  bret = exec_and_wait(NULL, plymouth_path,
394
 
                       (const char *[]){plymouth_path, "quit", NULL}, false, false);
395
 
  if(not bret){
396
 
    exit(EXIT_FAILURE);
397
 
  }
398
 
  bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv, false, true);
399
 
  if(not bret){
400
 
    exit(EXIT_FAILURE);
401
 
  }
402
 
  exec_and_wait(NULL, plymouth_path,
403
 
                (const char *[]){ plymouth_path, "show-splash", NULL }, false, false);
404
 
  exit(EXIT_FAILURE);
405
 
}