/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: 2016-03-17 20:40:55 UTC
  • Revision ID: teddy@recompile.se-20160317204055-bhsh5xsidq7w5cxu
Client: Fix plymouth agent; broken since 1.7.2.

Fix an very old memory bug in the plymouth agent (which has been
present since its apperance in version 1.2), but which was only
recently detected at run time due to the new -fsanitize=address
compile- time flag, which has been used since version 1.7.2.  This
detection of a memory access violation causes the program to abort,
making the Plymouth graphical boot system unable to accept interactive
input of passwords when using the Mandos client.

* plugins.d/plymouth.c (exec_and_wait): Fix memory allocation bug when
  allocating new_argv.  Also tolerate a zero-length argv.

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-2016 Teddy Hogeborn
 
6
 * Copyright © 2010-2016 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,
12
36
#include <stddef.h>             /* NULL */
13
37
#include <string.h>             /* strchr(), memcmp() */
14
38
#include <stdio.h>              /* asprintf(), perror(), fopen(),
15
 
                                   fscanf() */
 
39
                                   fscanf(), vasprintf(), fprintf(),
 
40
                                   vfprintf() */
16
41
#include <unistd.h>             /* close(), readlink(), read(),
17
42
                                   fork(), setsid(), chdir(), dup2(),
18
43
                                   STDERR_FILENO, execv(), access() */
26
51
#include <error.h>              /* error() */
27
52
#include <errno.h>              /* TEMP_FAILURE_RETRY */
28
53
#include <argz.h>               /* argz_count(), argz_extract() */
 
54
#include <stdarg.h>             /* va_list, va_start(), ... */
29
55
 
30
56
sig_atomic_t interrupted_by_signal = 0;
31
 
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
 
32
63
const char plymouth_path[] = "/bin/plymouth";
33
64
const char plymouthd_path[] = "/sbin/plymouthd";
34
65
const char *plymouthd_default_argv[] = {"/sbin/plymouthd",
35
66
                                        "--mode=boot",
36
67
                                        "--attach-to-session",
37
 
                                        "--pid-file="
38
 
                                        "/dev/.initramfs/"
39
 
                                        "plymouth.pid",
40
68
                                        NULL };
41
69
 
42
70
static void termination_handler(__attribute__((unused))int signum){
46
74
  interrupted_by_signal = 1;
47
75
}
48
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
 
49
101
/* Create prompt string */
50
102
char *makeprompt(void){
51
103
  int ret = 0;
52
104
  char *prompt;
53
105
  const char *const cryptsource = getenv("cryptsource");
54
106
  const char *const crypttarget = getenv("crypttarget");
55
 
  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";
56
109
  
57
110
  if(cryptsource == NULL){
58
111
    if(crypttarget == NULL){
59
 
      ret = asprintf(&prompt, "%s: ", prompt_start);
 
112
      ret = asprintf(&prompt, "%s\n%s", prompt_start, prompt_end);
60
113
    } else {
61
 
      ret = asprintf(&prompt, "%s (%s): ", prompt_start,
62
 
                     crypttarget);
 
114
      ret = asprintf(&prompt, "%s (%s)\n%s", prompt_start,
 
115
                     crypttarget, prompt_end);
63
116
    }
64
117
  } else {
65
118
    if(crypttarget == NULL){
66
 
      ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource);
 
119
      ret = asprintf(&prompt, "%s %s\n%s", prompt_start, cryptsource,
 
120
                     prompt_end);
67
121
    } else {
68
 
      ret = asprintf(&prompt, "%s %s (%s): ", prompt_start,
69
 
                     cryptsource, crypttarget);
 
122
      ret = asprintf(&prompt, "%s %s (%s)\n%s", prompt_start,
 
123
                     cryptsource, crypttarget, prompt_end);
70
124
    }
71
125
  }
72
126
  if(ret == -1){
83
137
bool become_a_daemon(void){
84
138
  int ret = setuid(geteuid());
85
139
  if(ret == -1){
86
 
    error(0, errno, "setuid");
 
140
    error_plus(0, errno, "setuid");
87
141
  }
88
142
    
89
143
  setsid();
90
144
  ret = chdir("/");
91
145
  if(ret == -1){
92
 
    error(0, errno, "chdir");
 
146
    error_plus(0, errno, "chdir");
93
147
    return false;
94
148
  }
95
149
  ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
96
150
  if(ret == -1){
97
 
    error(0, errno, "dup2");
 
151
    error_plus(0, errno, "dup2");
98
152
    return false;
99
153
  }
100
154
  return true;
101
155
}
102
156
 
 
157
__attribute__((nonnull (2, 3)))
103
158
bool exec_and_wait(pid_t *pid_return, const char *path,
104
 
                   const char **argv, bool interruptable,
 
159
                   const char * const *argv, bool interruptable,
105
160
                   bool daemonize){
106
161
  int status;
107
162
  int ret;
108
163
  pid_t pid;
109
164
  pid = fork();
110
165
  if(pid == -1){
111
 
    error(0, errno, "fork");
 
166
    error_plus(0, errno, "fork");
112
167
    return false;
113
168
  }
114
169
  if(pid == 0){
118
173
        _exit(EX_OSERR);
119
174
      }
120
175
    }
121
 
 
122
 
    char **new_argv = NULL;
123
 
    char *tmp;
 
176
    
 
177
    char **new_argv = malloc(sizeof(const char *));
 
178
    if(new_argv == NULL){
 
179
      error_plus(0, errno, "malloc");
 
180
      _exit(EX_OSERR);
 
181
    }
 
182
    char **tmp;
124
183
    int i = 0;
125
 
    for (; argv[i]!=(char *)NULL; i++){
126
 
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 1));
127
 
      if (tmp == NULL){
128
 
        error(0, errno, "realloc");
 
184
    for (; argv[i]!=NULL; i++){
 
185
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 2));
 
186
      if(tmp == NULL){
 
187
        error_plus(0, errno, "realloc");
129
188
        free(new_argv);
130
189
        _exit(EX_OSERR);
131
190
      }
132
 
      new_argv = (char **)tmp;
 
191
      new_argv = tmp;
133
192
      new_argv[i] = strdup(argv[i]);
134
193
    }
135
 
    new_argv[i] = (char *) NULL;
 
194
    new_argv[i] = NULL;
136
195
    
137
196
    execv(path, (char *const *)new_argv);
138
 
    error(0, errno, "execv");
 
197
    error_plus(0, errno, "execv");
139
198
    _exit(EXIT_FAILURE);
140
199
  }
141
200
  if(pid_return != NULL){
150
209
    return false;
151
210
  }
152
211
  if(ret == -1){
153
 
    error(0, errno, "waitpid");
 
212
    error_plus(0, errno, "waitpid");
154
213
    return false;
155
214
  }
156
215
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
159
218
  return false;
160
219
}
161
220
 
 
221
__attribute__((nonnull))
162
222
int is_plymouth(const struct dirent *proc_entry){
163
223
  int ret;
164
224
  {
165
 
    uintmax_t maxvalue;
 
225
    uintmax_t proc_id;
166
226
    char *tmp;
167
227
    errno = 0;
168
 
    maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
228
    proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
169
229
 
170
230
    if(errno != 0 or *tmp != '\0'
171
 
       or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
231
       or proc_id != (uintmax_t)((pid_t)proc_id)){
172
232
      return 0;
173
233
    }
174
234
  }
175
 
  char exe_target[sizeof(plymouth_path)];
 
235
  char exe_target[sizeof(plymouthd_path)];
176
236
  char *exe_link;
177
237
  ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name);
178
238
  if(ret == -1){
179
 
    error(0, errno, "asprintf");
 
239
    error_plus(0, errno, "asprintf");
180
240
    return 0;
181
241
  }
182
242
  
185
245
  if(ret == -1){
186
246
    free(exe_link);
187
247
    if(errno != ENOENT){
188
 
      error(0, errno, "lstat");
 
248
      error_plus(0, errno, "lstat");
189
249
    }
190
250
    return 0;
191
251
  }
199
259
  
200
260
  ssize_t sret = readlink(exe_link, exe_target, sizeof(exe_target));
201
261
  free(exe_link);
202
 
  if((sret != (ssize_t)sizeof(plymouth_path)-1) or
203
 
      (memcmp(plymouth_path, exe_target,
204
 
              sizeof(plymouth_path)-1) != 0)){
 
262
  if((sret != (ssize_t)sizeof(plymouthd_path)-1) or
 
263
      (memcmp(plymouthd_path, exe_target,
 
264
              sizeof(plymouthd_path)-1) != 0)){
205
265
    return 0;
206
266
  }
207
267
  return 1;
209
269
 
210
270
pid_t get_pid(void){
211
271
  int ret;
 
272
  uintmax_t proc_id = 0;
212
273
  FILE *pidfile = fopen(plymouth_pid, "r");
213
 
  uintmax_t maxvalue = 0;
 
274
  /* Try the new pid file location */
214
275
  if(pidfile != NULL){
215
 
    ret = fscanf(pidfile, "%" SCNuMAX, &maxvalue);
 
276
    ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
216
277
    if(ret != 1){
217
 
      maxvalue = 0;
 
278
      proc_id = 0;
218
279
    }
219
280
    fclose(pidfile);
220
281
  }
221
 
  if(maxvalue == 0){
222
 
    struct dirent **direntries;
 
282
  /* Try the old pid file location */
 
283
  if(proc_id == 0){
 
284
    pidfile = fopen(plymouth_pid, "r");
 
285
    if(pidfile != NULL){
 
286
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
 
287
      if(ret != 1){
 
288
        proc_id = 0;
 
289
      }
 
290
      fclose(pidfile);
 
291
    }
 
292
  }
 
293
  /* Look for a plymouth process */
 
294
  if(proc_id == 0){
 
295
    struct dirent **direntries = NULL;
223
296
    ret = scandir("/proc", &direntries, is_plymouth, alphasort);
224
 
    sscanf(direntries[0]->d_name, "%" SCNuMAX, &maxvalue);
 
297
    if(ret == -1){
 
298
      error_plus(0, errno, "scandir");
 
299
    }
 
300
    if(ret > 0){
 
301
      ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &proc_id);
 
302
      if(ret < 0){
 
303
        error_plus(0, errno, "sscanf");
 
304
      }
 
305
    }
 
306
    /* scandir might preallocate for this variable (man page unclear).
 
307
       even if ret == 0, therefore we need to free it. */
 
308
    free(direntries);
225
309
  }
226
310
  pid_t pid;
227
 
  pid = (pid_t)maxvalue;
228
 
  if((uintmax_t)pid == maxvalue){
 
311
  pid = (pid_t)proc_id;
 
312
  if((uintmax_t)pid == proc_id){
229
313
    return pid;
230
314
  }
231
315
  
232
316
  return 0;
233
317
}
234
318
 
235
 
const char **getargv(pid_t pid){
 
319
const char * const * getargv(pid_t pid){
236
320
  int cl_fd;
237
321
  char *cmdline_filename;
238
322
  ssize_t sret;
241
325
  ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline",
242
326
                 (uintmax_t)pid);
243
327
  if(ret == -1){
244
 
    error(0, errno, "asprintf");
 
328
    error_plus(0, errno, "asprintf");
245
329
    return NULL;
246
330
  }
247
331
  
249
333
  cl_fd = open(cmdline_filename, O_RDONLY);
250
334
  free(cmdline_filename);
251
335
  if(cl_fd == -1){
252
 
    error(0, errno, "open");
 
336
    error_plus(0, errno, "open");
253
337
    return NULL;
254
338
  }
255
339
  
263
347
    if(cmdline_len + blocksize > cmdline_allocated){
264
348
      tmp = realloc(cmdline, cmdline_allocated + blocksize);
265
349
      if(tmp == NULL){
266
 
        error(0, errno, "realloc");
 
350
        error_plus(0, errno, "realloc");
267
351
        free(cmdline);
268
352
        close(cl_fd);
269
353
        return NULL;
276
360
    sret = read(cl_fd, cmdline + cmdline_len,
277
361
                cmdline_allocated - cmdline_len);
278
362
    if(sret == -1){
279
 
      error(0, errno, "read");
 
363
      error_plus(0, errno, "read");
280
364
      free(cmdline);
281
365
      close(cl_fd);
282
366
      return NULL;
285
369
  } while(sret != 0);
286
370
  ret = close(cl_fd);
287
371
  if(ret == -1){
288
 
    error(0, errno, "close");
 
372
    error_plus(0, errno, "close");
289
373
    free(cmdline);
290
374
    return NULL;
291
375
  }
294
378
  char **argv = malloc((argz_count(cmdline, cmdline_len) + 1)
295
379
                       * sizeof(char *)); /* Get number of args */
296
380
  if(argv == NULL){
297
 
    error(0, errno, "argv = malloc()");
 
381
    error_plus(0, errno, "argv = malloc()");
298
382
    free(cmdline);
299
383
    return NULL;
300
384
  }
301
385
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
302
 
  return (const char **)argv;
 
386
  return (const char * const *)argv;
303
387
}
304
388
 
305
389
int main(__attribute__((unused))int argc,
327
411
        *sig != 0; sig++){
328
412
      ret = sigaddset(&new_action.sa_mask, *sig);
329
413
      if(ret == -1){
330
 
        error(EX_OSERR, errno, "sigaddset");
 
414
        error_plus(EX_OSERR, errno, "sigaddset");
331
415
      }
332
416
      ret = sigaction(*sig, NULL, &old_action);
333
417
      if(ret == -1){
334
 
        error(EX_OSERR, errno, "sigaction");
 
418
        error_plus(EX_OSERR, errno, "sigaction");
335
419
      }
336
420
      if(old_action.sa_handler != SIG_IGN){
337
421
        ret = sigaction(*sig, &new_action, NULL);
338
422
        if(ret == -1){
339
 
          error(EX_OSERR, errno, "sigaction");
 
423
          error_plus(EX_OSERR, errno, "sigaction");
340
424
        }
341
425
      }
342
426
    }
361
445
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
362
446
  free(prompt);
363
447
  if(ret == -1){
364
 
    error(EX_OSERR, errno, "asprintf");
 
448
    error_plus(EX_OSERR, errno, "asprintf");
365
449
  }
366
450
  
367
451
  /* plymouth ask-for-password --prompt="$prompt" */
380
464
  }
381
465
  kill_and_wait(plymouth_command_pid);
382
466
  
383
 
  const char **plymouthd_argv;
 
467
  const char * const *plymouthd_argv;
384
468
  pid_t pid = get_pid();
385
469
  if(pid == 0){
386
 
    error(0, 0, "plymouthd pid not found");
 
470
    error_plus(0, 0, "plymouthd pid not found");
387
471
    plymouthd_argv = plymouthd_default_argv;
388
472
  } else {
389
473
    plymouthd_argv = getargv(pid);