/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to plugins.d/plymouth.c

  • Committer: Teddy Hogeborn
  • Date: 2008-08-02 10:48:24 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080802104824-fx0miwp9o4g9r31e
* plugbasedclient.c (struct process): New fields "eof", "completed",
                                      and "status".
  (handle_sigchld): New function.
  (main): Initialize "dir" to NULL to only closedir() it if necessary.
          Move "process_list" to be a global variable to be accessible
          by "handle_sigchld".  Make "handle_sigchld" handle SIGCHLD.
          Remove redundant check for NULL "dir".  Free "filename" when
          no longer used.  Block SIGCHLD around fork()/exec().
          Restore normal signals in child.  Only loop while running
          processes exist.  Print process buffer when the process is
          done and it has emitted EOF, not when it only emits EOF.
          Remove processes from list which exit non-cleanly.  In
          cleaning up, closedir() if necessary.  Bug fix: set next
          pointer correctly when freeing process list.

* plugins.d/passprompt.c (main): Do not ignore SIGQUIT.

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 Teddy Hogeborn
6
 
 * Copyright © 2010 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(plymouth_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(plymouth_path)-1) or
229
 
      (memcmp(plymouth_path, exe_target,
230
 
              sizeof(plymouth_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
 
    sscanf(direntries[0]->d_name, "%" SCNuMAX, &maxvalue);
251
 
  }
252
 
  pid_t pid;
253
 
  pid = (pid_t)maxvalue;
254
 
  if((uintmax_t)pid == maxvalue){
255
 
    return pid;
256
 
  }
257
 
  
258
 
  return 0;
259
 
}
260
 
 
261
 
const char **getargv(pid_t pid){
262
 
  int cl_fd;
263
 
  char *cmdline_filename;
264
 
  ssize_t sret;
265
 
  int ret;
266
 
  
267
 
  ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline",
268
 
                 (uintmax_t)pid);
269
 
  if(ret == -1){
270
 
    error(0, errno, "asprintf");
271
 
    return NULL;
272
 
  }
273
 
  
274
 
  /* Open /proc/<pid>/cmdline  */
275
 
  cl_fd = open(cmdline_filename, O_RDONLY);
276
 
  free(cmdline_filename);
277
 
  if(cl_fd == -1){
278
 
    error(0, errno, "open");
279
 
    return NULL;
280
 
  }
281
 
  
282
 
  size_t cmdline_allocated = 0;
283
 
  size_t cmdline_len = 0;
284
 
  char *cmdline = NULL;
285
 
  char *tmp;
286
 
  const size_t blocksize = 1024;
287
 
  do {
288
 
    /* Allocate more space? */
289
 
    if(cmdline_len + blocksize > cmdline_allocated){
290
 
      tmp = realloc(cmdline, cmdline_allocated + blocksize);
291
 
      if(tmp == NULL){
292
 
        error(0, errno, "realloc");
293
 
        free(cmdline);
294
 
        close(cl_fd);
295
 
        return NULL;
296
 
      }
297
 
      cmdline = tmp;
298
 
      cmdline_allocated += blocksize;
299
 
    }
300
 
    
301
 
    /* Read data */
302
 
    sret = read(cl_fd, cmdline + cmdline_len,
303
 
                cmdline_allocated - cmdline_len);
304
 
    if(sret == -1){
305
 
      error(0, errno, "read");
306
 
      free(cmdline);
307
 
      close(cl_fd);
308
 
      return NULL;
309
 
    }
310
 
    cmdline_len += (size_t)sret;
311
 
  } while(sret != 0);
312
 
  ret = close(cl_fd);
313
 
  if(ret == -1){
314
 
    error(0, errno, "close");
315
 
    free(cmdline);
316
 
    return NULL;
317
 
  }
318
 
  
319
 
  /* we got cmdline and cmdline_len, ignore rest... */
320
 
  char **argv = malloc((argz_count(cmdline, cmdline_len) + 1)
321
 
                       * sizeof(char *)); /* Get number of args */
322
 
  if(argv == NULL){
323
 
    error(0, errno, "argv = malloc()");
324
 
    free(cmdline);
325
 
    return NULL;
326
 
  }
327
 
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
328
 
  return (const char **)argv;
329
 
}
330
 
 
331
 
int main(__attribute__((unused))int argc,
332
 
         __attribute__((unused))char **argv){
333
 
  char *prompt;
334
 
  char *prompt_arg;
335
 
  pid_t plymouth_command_pid;
336
 
  int ret;
337
 
  bool bret;
338
 
 
339
 
  /* test -x /bin/plymouth */
340
 
  ret = access(plymouth_path, X_OK);
341
 
  if(ret == -1){
342
 
    /* Plymouth is probably not installed.  Don't print an error
343
 
       message, just exit. */
344
 
    exit(EX_UNAVAILABLE);
345
 
  }
346
 
  
347
 
  { /* Add signal handlers */
348
 
    struct sigaction old_action,
349
 
      new_action = { .sa_handler = termination_handler,
350
 
                     .sa_flags = 0 };
351
 
    sigemptyset(&new_action.sa_mask);
352
 
    for(int *sig = (int[]){ SIGINT, SIGHUP, SIGTERM, 0 };
353
 
        *sig != 0; sig++){
354
 
      ret = sigaddset(&new_action.sa_mask, *sig);
355
 
      if(ret == -1){
356
 
        error(EX_OSERR, errno, "sigaddset");
357
 
      }
358
 
      ret = sigaction(*sig, NULL, &old_action);
359
 
      if(ret == -1){
360
 
        error(EX_OSERR, errno, "sigaction");
361
 
      }
362
 
      if(old_action.sa_handler != SIG_IGN){
363
 
        ret = sigaction(*sig, &new_action, NULL);
364
 
        if(ret == -1){
365
 
          error(EX_OSERR, errno, "sigaction");
366
 
        }
367
 
      }
368
 
    }
369
 
  }
370
 
  
371
 
  /* plymouth --ping */
372
 
  bret = exec_and_wait(&plymouth_command_pid, plymouth_path,
373
 
                       (const char *[])
374
 
                       { plymouth_path, "--ping", NULL },
375
 
                       true, false);
376
 
  if(not bret){
377
 
    if(interrupted_by_signal){
378
 
      kill_and_wait(plymouth_command_pid);
379
 
      exit(EXIT_FAILURE);
380
 
    }
381
 
    /* Plymouth is probably not running.  Don't print an error
382
 
       message, just exit. */
383
 
    exit(EX_UNAVAILABLE);
384
 
  }
385
 
  
386
 
  prompt = makeprompt();
387
 
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
388
 
  free(prompt);
389
 
  if(ret == -1){
390
 
    error(EX_OSERR, errno, "asprintf");
391
 
  }
392
 
  
393
 
  /* plymouth ask-for-password --prompt="$prompt" */
394
 
  bret = exec_and_wait(&plymouth_command_pid,
395
 
                       plymouth_path, (const char *[])
396
 
                       { plymouth_path, "ask-for-password",
397
 
                           prompt_arg, NULL },
398
 
                       true, false);
399
 
  free(prompt_arg);
400
 
  if(bret){
401
 
    exit(EXIT_SUCCESS);
402
 
  }
403
 
  if(not interrupted_by_signal){
404
 
    /* exec_and_wait failed for some other reason */
405
 
    exit(EXIT_FAILURE);
406
 
  }
407
 
  kill_and_wait(plymouth_command_pid);
408
 
  
409
 
  const char **plymouthd_argv;
410
 
  pid_t pid = get_pid();
411
 
  if(pid == 0){
412
 
    error(0, 0, "plymouthd pid not found");
413
 
    plymouthd_argv = plymouthd_default_argv;
414
 
  } else {
415
 
    plymouthd_argv = getargv(pid);
416
 
  }
417
 
  
418
 
  bret = exec_and_wait(NULL, plymouth_path, (const char *[])
419
 
                       { plymouth_path, "quit", NULL },
420
 
                       false, false);
421
 
  if(not bret){
422
 
    exit(EXIT_FAILURE);
423
 
  }
424
 
  bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv,
425
 
                       false, true);
426
 
  if(not bret){
427
 
    exit(EXIT_FAILURE);
428
 
  }
429
 
  exec_and_wait(NULL, plymouth_path, (const char *[])
430
 
                { plymouth_path, "show-splash", NULL },
431
 
                false, false);
432
 
  exit(EXIT_FAILURE);
433
 
}