/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: Björn Påhlsson
  • Date: 2008-07-20 02:52:20 UTC
  • Revision ID: belorn@braxen-20080720025220-r5u0388uy9iu23h6
Added following support:
Pluginbased client handler
rewritten Mandos client
       Avahi instead of udp server discovery
       openpgp encrypted key support
Passprompt stand alone application for direct console input
Added logging for Mandos server

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