/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: Teddy Hogeborn
  • Date: 2015-03-10 18:03:38 UTC
  • Revision ID: teddy@recompile.se-20150310180338-pcxw6r2qmw9k6br9
Add ":!RSA" to GnuTLS priority string, to disallow non-DHE kx.

If Mandos was somehow made to use a non-ephemeral Diffie-Hellman key
exchange algorithm in the TLS handshake, any saved network traffic
could then be decrypted later if the Mandos client key was obtained.
By default, Mandos uses ephemeral DH key exchanges which does not have
this problem, but a non-ephemeral key exchange algorithm was still
enabled by default.  The simplest solution is to simply turn that off,
which ensures that Mandos will always use ephemeral DH key exchanges.

There is a "PFS" priority string specifier, but we can't use it because:

1. Security-wise, it is a mix between "NORMAL" and "SECURE128" - it
   enables a lot more algorithms than "SECURE256".

2. It is only available since GnuTLS 3.2.4.

Thanks to Andreas Fischer <af@bantuX.org> for reporting this issue.

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