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