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