/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-2016 Teddy Hogeborn
6
 
 * Copyright © 2010-2016 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
 
__attribute__((nonnull (2, 3)))
158
 
bool exec_and_wait(pid_t *pid_return, const char *path,
159
 
                   const char * const *argv, bool interruptable,
160
 
                   bool daemonize){
161
 
  int status;
162
 
  int ret;
163
 
  pid_t pid;
164
 
  pid = fork();
165
 
  if(pid == -1){
166
 
    error_plus(0, errno, "fork");
167
 
    return false;
168
 
  }
169
 
  if(pid == 0){
170
 
    /* Child */
171
 
    if(daemonize){
172
 
      if(not become_a_daemon()){
173
 
        _exit(EX_OSERR);
174
 
      }
175
 
    }
176
 
    
177
 
    char **new_argv = malloc(sizeof(const char *));
178
 
    if(new_argv == NULL){
179
 
      error_plus(0, errno, "malloc");
180
 
      _exit(EX_OSERR);
181
 
    }
182
 
    char **tmp;
183
 
    int i = 0;
184
 
    for (; argv[i]!=NULL; i++){
185
 
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 2));
186
 
      if(tmp == NULL){
187
 
        error_plus(0, errno, "realloc");
188
 
        free(new_argv);
189
 
        _exit(EX_OSERR);
190
 
      }
191
 
      new_argv = tmp;
192
 
      new_argv[i] = strdup(argv[i]);
193
 
    }
194
 
    new_argv[i] = NULL;
195
 
    
196
 
    execv(path, (char *const *)new_argv);
197
 
    error_plus(0, errno, "execv");
198
 
    _exit(EXIT_FAILURE);
199
 
  }
200
 
  if(pid_return != NULL){
201
 
    *pid_return = pid;
202
 
  }
203
 
  do {
204
 
    ret = waitpid(pid, &status, 0);
205
 
  } while(ret == -1 and errno == EINTR
206
 
          and ((not interrupted_by_signal)
207
 
               or (not interruptable)));
208
 
  if(interrupted_by_signal and interruptable){
209
 
    return false;
210
 
  }
211
 
  if(ret == -1){
212
 
    error_plus(0, errno, "waitpid");
213
 
    return false;
214
 
  }
215
 
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
216
 
    return true;
217
 
  }
218
 
  return false;
219
 
}
220
 
 
221
 
__attribute__((nonnull))
222
 
int is_plymouth(const struct dirent *proc_entry){
223
 
  int ret;
224
 
  {
225
 
    uintmax_t proc_id;
226
 
    char *tmp;
227
 
    errno = 0;
228
 
    proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
229
 
 
230
 
    if(errno != 0 or *tmp != '\0'
231
 
       or proc_id != (uintmax_t)((pid_t)proc_id)){
232
 
      return 0;
233
 
    }
234
 
  }
235
 
  char exe_target[sizeof(plymouthd_path)];
236
 
  char *exe_link;
237
 
  ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name);
238
 
  if(ret == -1){
239
 
    error_plus(0, errno, "asprintf");
240
 
    return 0;
241
 
  }
242
 
  
243
 
  struct stat exe_stat;
244
 
  ret = lstat(exe_link, &exe_stat);
245
 
  if(ret == -1){
246
 
    free(exe_link);
247
 
    if(errno != ENOENT){
248
 
      error_plus(0, errno, "lstat");
249
 
    }
250
 
    return 0;
251
 
  }
252
 
  
253
 
  if(not S_ISLNK(exe_stat.st_mode)
254
 
     or exe_stat.st_uid != 0
255
 
     or exe_stat.st_gid != 0){
256
 
    free(exe_link);
257
 
    return 0;
258
 
  }
259
 
  
260
 
  ssize_t sret = readlink(exe_link, exe_target, sizeof(exe_target));
261
 
  free(exe_link);
262
 
  if((sret != (ssize_t)sizeof(plymouthd_path)-1) or
263
 
      (memcmp(plymouthd_path, exe_target,
264
 
              sizeof(plymouthd_path)-1) != 0)){
265
 
    return 0;
266
 
  }
267
 
  return 1;
268
 
}
269
 
 
270
 
pid_t get_pid(void){
271
 
  int ret;
272
 
  uintmax_t proc_id = 0;
273
 
  FILE *pidfile = fopen(plymouth_pid, "r");
274
 
  /* Try the new pid file location */
275
 
  if(pidfile != NULL){
276
 
    ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
277
 
    if(ret != 1){
278
 
      proc_id = 0;
279
 
    }
280
 
    fclose(pidfile);
281
 
  }
282
 
  /* Try the old pid file location */
283
 
  if(proc_id == 0){
284
 
    pidfile = fopen(plymouth_pid, "r");
285
 
    if(pidfile != NULL){
286
 
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
287
 
      if(ret != 1){
288
 
        proc_id = 0;
289
 
      }
290
 
      fclose(pidfile);
291
 
    }
292
 
  }
293
 
  /* Look for a plymouth process */
294
 
  if(proc_id == 0){
295
 
    struct dirent **direntries = NULL;
296
 
    ret = scandir("/proc", &direntries, is_plymouth, alphasort);
297
 
    if(ret == -1){
298
 
      error_plus(0, errno, "scandir");
299
 
    }
300
 
    if(ret > 0){
301
 
      ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &proc_id);
302
 
      if(ret < 0){
303
 
        error_plus(0, errno, "sscanf");
304
 
      }
305
 
    }
306
 
    /* scandir might preallocate for this variable (man page unclear).
307
 
       even if ret == 0, therefore we need to free it. */
308
 
    free(direntries);
309
 
  }
310
 
  pid_t pid;
311
 
  pid = (pid_t)proc_id;
312
 
  if((uintmax_t)pid == proc_id){
313
 
    return pid;
314
 
  }
315
 
  
316
 
  return 0;
317
 
}
318
 
 
319
 
const char * const * getargv(pid_t pid){
320
 
  int cl_fd;
321
 
  char *cmdline_filename;
322
 
  ssize_t sret;
323
 
  int ret;
324
 
  
325
 
  ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline",
326
 
                 (uintmax_t)pid);
327
 
  if(ret == -1){
328
 
    error_plus(0, errno, "asprintf");
329
 
    return NULL;
330
 
  }
331
 
  
332
 
  /* Open /proc/<pid>/cmdline  */
333
 
  cl_fd = open(cmdline_filename, O_RDONLY);
334
 
  free(cmdline_filename);
335
 
  if(cl_fd == -1){
336
 
    error_plus(0, errno, "open");
337
 
    return NULL;
338
 
  }
339
 
  
340
 
  size_t cmdline_allocated = 0;
341
 
  size_t cmdline_len = 0;
342
 
  char *cmdline = NULL;
343
 
  char *tmp;
344
 
  const size_t blocksize = 1024;
345
 
  do {
346
 
    /* Allocate more space? */
347
 
    if(cmdline_len + blocksize > cmdline_allocated){
348
 
      tmp = realloc(cmdline, cmdline_allocated + blocksize);
349
 
      if(tmp == NULL){
350
 
        error_plus(0, errno, "realloc");
351
 
        free(cmdline);
352
 
        close(cl_fd);
353
 
        return NULL;
354
 
      }
355
 
      cmdline = tmp;
356
 
      cmdline_allocated += blocksize;
357
 
    }
358
 
    
359
 
    /* Read data */
360
 
    sret = read(cl_fd, cmdline + cmdline_len,
361
 
                cmdline_allocated - cmdline_len);
362
 
    if(sret == -1){
363
 
      error_plus(0, errno, "read");
364
 
      free(cmdline);
365
 
      close(cl_fd);
366
 
      return NULL;
367
 
    }
368
 
    cmdline_len += (size_t)sret;
369
 
  } while(sret != 0);
370
 
  ret = close(cl_fd);
371
 
  if(ret == -1){
372
 
    error_plus(0, errno, "close");
373
 
    free(cmdline);
374
 
    return NULL;
375
 
  }
376
 
  
377
 
  /* we got cmdline and cmdline_len, ignore rest... */
378
 
  char **argv = malloc((argz_count(cmdline, cmdline_len) + 1)
379
 
                       * sizeof(char *)); /* Get number of args */
380
 
  if(argv == NULL){
381
 
    error_plus(0, errno, "argv = malloc()");
382
 
    free(cmdline);
383
 
    return NULL;
384
 
  }
385
 
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
386
 
  return (const char * const *)argv;
387
 
}
388
 
 
389
 
int main(__attribute__((unused))int argc,
390
 
         __attribute__((unused))char **argv){
391
 
  char *prompt;
392
 
  char *prompt_arg;
393
 
  pid_t plymouth_command_pid;
394
 
  int ret;
395
 
  bool bret;
396
 
 
397
 
  /* test -x /bin/plymouth */
398
 
  ret = access(plymouth_path, X_OK);
399
 
  if(ret == -1){
400
 
    /* Plymouth is probably not installed.  Don't print an error
401
 
       message, just exit. */
402
 
    exit(EX_UNAVAILABLE);
403
 
  }
404
 
  
405
 
  { /* Add signal handlers */
406
 
    struct sigaction old_action,
407
 
      new_action = { .sa_handler = termination_handler,
408
 
                     .sa_flags = 0 };
409
 
    sigemptyset(&new_action.sa_mask);
410
 
    for(int *sig = (int[]){ SIGINT, SIGHUP, SIGTERM, 0 };
411
 
        *sig != 0; sig++){
412
 
      ret = sigaddset(&new_action.sa_mask, *sig);
413
 
      if(ret == -1){
414
 
        error_plus(EX_OSERR, errno, "sigaddset");
415
 
      }
416
 
      ret = sigaction(*sig, NULL, &old_action);
417
 
      if(ret == -1){
418
 
        error_plus(EX_OSERR, errno, "sigaction");
419
 
      }
420
 
      if(old_action.sa_handler != SIG_IGN){
421
 
        ret = sigaction(*sig, &new_action, NULL);
422
 
        if(ret == -1){
423
 
          error_plus(EX_OSERR, errno, "sigaction");
424
 
        }
425
 
      }
426
 
    }
427
 
  }
428
 
  
429
 
  /* plymouth --ping */
430
 
  bret = exec_and_wait(&plymouth_command_pid, plymouth_path,
431
 
                       (const char *[])
432
 
                       { plymouth_path, "--ping", NULL },
433
 
                       true, false);
434
 
  if(not bret){
435
 
    if(interrupted_by_signal){
436
 
      kill_and_wait(plymouth_command_pid);
437
 
      exit(EXIT_FAILURE);
438
 
    }
439
 
    /* Plymouth is probably not running.  Don't print an error
440
 
       message, just exit. */
441
 
    exit(EX_UNAVAILABLE);
442
 
  }
443
 
  
444
 
  prompt = makeprompt();
445
 
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
446
 
  free(prompt);
447
 
  if(ret == -1){
448
 
    error_plus(EX_OSERR, errno, "asprintf");
449
 
  }
450
 
  
451
 
  /* plymouth ask-for-password --prompt="$prompt" */
452
 
  bret = exec_and_wait(&plymouth_command_pid,
453
 
                       plymouth_path, (const char *[])
454
 
                       { plymouth_path, "ask-for-password",
455
 
                           prompt_arg, NULL },
456
 
                       true, false);
457
 
  free(prompt_arg);
458
 
  if(bret){
459
 
    exit(EXIT_SUCCESS);
460
 
  }
461
 
  if(not interrupted_by_signal){
462
 
    /* exec_and_wait failed for some other reason */
463
 
    exit(EXIT_FAILURE);
464
 
  }
465
 
  kill_and_wait(plymouth_command_pid);
466
 
  
467
 
  const char * const *plymouthd_argv;
468
 
  pid_t pid = get_pid();
469
 
  if(pid == 0){
470
 
    error_plus(0, 0, "plymouthd pid not found");
471
 
    plymouthd_argv = plymouthd_default_argv;
472
 
  } else {
473
 
    plymouthd_argv = getargv(pid);
474
 
  }
475
 
  
476
 
  bret = exec_and_wait(NULL, plymouth_path, (const char *[])
477
 
                       { plymouth_path, "quit", NULL },
478
 
                       false, false);
479
 
  if(not bret){
480
 
    exit(EXIT_FAILURE);
481
 
  }
482
 
  bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv,
483
 
                       false, true);
484
 
  if(not bret){
485
 
    exit(EXIT_FAILURE);
486
 
  }
487
 
  exec_and_wait(NULL, plymouth_path, (const char *[])
488
 
                { plymouth_path, "show-splash", NULL },
489
 
                false, false);
490
 
  exit(EXIT_FAILURE);
491
 
}