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