/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-08-29 05:53:59 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080829055359-wkdasnyxtylmnxus
* mandos.xml (EXAMPLE): Replaced all occurences of command name with
                        "&COMMANDNAME;".

* plugins.d/password-prompt.c (main): Improved some documentation
                                      strings.  Do perror() of
                                      tcgetattr() fails.  Add debug
                                      output if interrupted by signal.
                                      Loop over write() instead of
                                      using fwrite() when outputting
                                      password.  Add debug output if
                                      getline() returns 0, unless it
                                      was caused by a signal.  Add
                                      exit status code to debug
                                      output.

* plugins.d/password-prompt.xml: Changed all single quotes to double
                                 quotes for consistency.  Removed
                                 <?xml-stylesheet>.
  (ENTITY TIMESTAMP): New.  Automatically updated by Emacs time-stamp
                      by using Emacs local variables.
  (/refentry/refentryinfo/title): Changed to "Mandos Manual".
  (/refentry/refentryinfo/productname): Changed to "Mandos".
  (/refentry/refentryinfo/date): New; set to "&TIMESTAMP;".
  (/refentry/refentryinfo/copyright): Split copyright holders.
  (/refentry/refnamediv/refpurpose): Improved wording.
  (SYNOPSIS): Fix to use correct markup.  Add short options.
  (DESCRIPTION, OPTIONS): Improved wording.
  (OPTIONS): Improved wording.  Use more correct markup.  Document
             short options.
  (EXIT STATUS): Add text.
  (ENVIRONMENT): Document use of "cryptsource" and "crypttarget".
  (FILES): REMOVED.
  (BUGS): Add text.
  (EXAMPLE): Added some examples.
  (SECURITY): Added text.
  (SEE ALSO): Remove reference to mandos(8).  Add reference to
              crypttab(5).

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_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
 
      ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &proc_id);
303
 
      if(ret < 0){
304
 
        error_plus(0, errno, "sscanf");
305
 
      }
306
 
    }
307
 
    /* scandir might preallocate for this variable (man page unclear).
308
 
       even if ret == 0, therefore we need to free it. */
309
 
    free(direntries);
310
 
  }
311
 
  pid_t pid;
312
 
  pid = (pid_t)proc_id;
313
 
  if((uintmax_t)pid == proc_id){
314
 
    return pid;
315
 
  }
316
 
  
317
 
  return 0;
318
 
}
319
 
 
320
 
const char * const * getargv(pid_t pid){
321
 
  int cl_fd;
322
 
  char *cmdline_filename;
323
 
  ssize_t sret;
324
 
  int ret;
325
 
  
326
 
  ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline",
327
 
                 (uintmax_t)pid);
328
 
  if(ret == -1){
329
 
    error_plus(0, errno, "asprintf");
330
 
    return NULL;
331
 
  }
332
 
  
333
 
  /* Open /proc/<pid>/cmdline  */
334
 
  cl_fd = open(cmdline_filename, O_RDONLY);
335
 
  free(cmdline_filename);
336
 
  if(cl_fd == -1){
337
 
    error_plus(0, errno, "open");
338
 
    return NULL;
339
 
  }
340
 
  
341
 
  size_t cmdline_allocated = 0;
342
 
  size_t cmdline_len = 0;
343
 
  char *cmdline = NULL;
344
 
  char *tmp;
345
 
  const size_t blocksize = 1024;
346
 
  do {
347
 
    /* Allocate more space? */
348
 
    if(cmdline_len + blocksize > cmdline_allocated){
349
 
      tmp = realloc(cmdline, cmdline_allocated + blocksize);
350
 
      if(tmp == NULL){
351
 
        error_plus(0, errno, "realloc");
352
 
        free(cmdline);
353
 
        close(cl_fd);
354
 
        return NULL;
355
 
      }
356
 
      cmdline = tmp;
357
 
      cmdline_allocated += blocksize;
358
 
    }
359
 
    
360
 
    /* Read data */
361
 
    sret = read(cl_fd, cmdline + cmdline_len,
362
 
                cmdline_allocated - cmdline_len);
363
 
    if(sret == -1){
364
 
      error_plus(0, errno, "read");
365
 
      free(cmdline);
366
 
      close(cl_fd);
367
 
      return NULL;
368
 
    }
369
 
    cmdline_len += (size_t)sret;
370
 
  } while(sret != 0);
371
 
  ret = close(cl_fd);
372
 
  if(ret == -1){
373
 
    error_plus(0, errno, "close");
374
 
    free(cmdline);
375
 
    return NULL;
376
 
  }
377
 
  
378
 
  /* we got cmdline and cmdline_len, ignore rest... */
379
 
  char **argv = malloc((argz_count(cmdline, cmdline_len) + 1)
380
 
                       * sizeof(char *)); /* Get number of args */
381
 
  if(argv == NULL){
382
 
    error_plus(0, errno, "argv = malloc()");
383
 
    free(cmdline);
384
 
    return NULL;
385
 
  }
386
 
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
387
 
  return (const char * const *)argv;
388
 
}
389
 
 
390
 
int main(__attribute__((unused))int argc,
391
 
         __attribute__((unused))char **argv){
392
 
  char *prompt;
393
 
  char *prompt_arg;
394
 
  pid_t plymouth_command_pid;
395
 
  int ret;
396
 
  bool bret;
397
 
 
398
 
  /* test -x /bin/plymouth */
399
 
  ret = access(plymouth_path, X_OK);
400
 
  if(ret == -1){
401
 
    /* Plymouth is probably not installed.  Don't print an error
402
 
       message, just exit. */
403
 
    exit(EX_UNAVAILABLE);
404
 
  }
405
 
  
406
 
  { /* Add signal handlers */
407
 
    struct sigaction old_action,
408
 
      new_action = { .sa_handler = termination_handler,
409
 
                     .sa_flags = 0 };
410
 
    sigemptyset(&new_action.sa_mask);
411
 
    for(int *sig = (int[]){ SIGINT, SIGHUP, SIGTERM, 0 };
412
 
        *sig != 0; sig++){
413
 
      ret = sigaddset(&new_action.sa_mask, *sig);
414
 
      if(ret == -1){
415
 
        error_plus(EX_OSERR, errno, "sigaddset");
416
 
      }
417
 
      ret = sigaction(*sig, NULL, &old_action);
418
 
      if(ret == -1){
419
 
        error_plus(EX_OSERR, errno, "sigaction");
420
 
      }
421
 
      if(old_action.sa_handler != SIG_IGN){
422
 
        ret = sigaction(*sig, &new_action, NULL);
423
 
        if(ret == -1){
424
 
          error_plus(EX_OSERR, errno, "sigaction");
425
 
        }
426
 
      }
427
 
    }
428
 
  }
429
 
  
430
 
  /* plymouth --ping */
431
 
  bret = exec_and_wait(&plymouth_command_pid, plymouth_path,
432
 
                       (const char *[])
433
 
                       { plymouth_path, "--ping", NULL },
434
 
                       true, false);
435
 
  if(not bret){
436
 
    if(interrupted_by_signal){
437
 
      kill_and_wait(plymouth_command_pid);
438
 
      exit(EXIT_FAILURE);
439
 
    }
440
 
    /* Plymouth is probably not running.  Don't print an error
441
 
       message, just exit. */
442
 
    exit(EX_UNAVAILABLE);
443
 
  }
444
 
  
445
 
  prompt = makeprompt();
446
 
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
447
 
  free(prompt);
448
 
  if(ret == -1){
449
 
    error_plus(EX_OSERR, errno, "asprintf");
450
 
  }
451
 
  
452
 
  /* plymouth ask-for-password --prompt="$prompt" */
453
 
  bret = exec_and_wait(&plymouth_command_pid,
454
 
                       plymouth_path, (const char *[])
455
 
                       { plymouth_path, "ask-for-password",
456
 
                           prompt_arg, NULL },
457
 
                       true, false);
458
 
  free(prompt_arg);
459
 
  if(bret){
460
 
    exit(EXIT_SUCCESS);
461
 
  }
462
 
  if(not interrupted_by_signal){
463
 
    /* exec_and_wait failed for some other reason */
464
 
    exit(EXIT_FAILURE);
465
 
  }
466
 
  kill_and_wait(plymouth_command_pid);
467
 
  
468
 
  const char * const *plymouthd_argv;
469
 
  pid_t pid = get_pid();
470
 
  if(pid == 0){
471
 
    error_plus(0, 0, "plymouthd pid not found");
472
 
    plymouthd_argv = plymouthd_default_argv;
473
 
  } else {
474
 
    plymouthd_argv = getargv(pid);
475
 
  }
476
 
  
477
 
  bret = exec_and_wait(NULL, plymouth_path, (const char *[])
478
 
                       { plymouth_path, "quit", NULL },
479
 
                       false, false);
480
 
  if(not bret){
481
 
    exit(EXIT_FAILURE);
482
 
  }
483
 
  bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv,
484
 
                       false, true);
485
 
  if(not bret){
486
 
    exit(EXIT_FAILURE);
487
 
  }
488
 
  exec_and_wait(NULL, plymouth_path, (const char *[])
489
 
                { plymouth_path, "show-splash", NULL },
490
 
                false, false);
491
 
  exit(EXIT_FAILURE);
492
 
}