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