/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: Björn Påhlsson
  • Date: 2008-07-20 02:52:20 UTC
  • Revision ID: belorn@braxen-20080720025220-r5u0388uy9iu23h6
Added following support:
Pluginbased client handler
rewritten Mandos client
       Avahi instead of udp server discovery
       openpgp encrypted key support
Passprompt stand alone application for direct console input
Added logging for Mandos server

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