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