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