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