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