/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/usplash.c

  • Committer: teddy at recompile
  • Date: 2020-04-05 21:30:59 UTC
  • Revision ID: teddy@recompile.se-20200405213059-fb2a61ckqynrmatk
Fix file descriptor leak in mandos-client

When the local network has Mandos servers announcing themselves using
real, globally reachable, IPv6 addresses (i.e. not link-local
addresses), but there is no router on the local network providing IPv6
RA (Router Advertisement) packets, the client cannot reach the server
by normal means, since the client only has a link-local IPv6 address,
and has no usable route to reach the server's global IPv6 address.
(This is not a common situation, and usually only happens when the
router itself reboots and runs a Mandos client, since it cannot then
give RA packets to itself.)  The client code has a solution for
this, which consists of adding a temporary local route to reach the
address of the server during communication, and removing this
temporary route afterwards.

This solution with a temporary route works, but has a file descriptor
leak; it leaks one file descriptor for each addition and for each
removal of a route.  If one server requiring an added route is present
on the network, but no servers gives a password, making the client
retry after the default ten seconds, and we furthermore assume a
default 1024 open files limit, the client runs out of file descriptors
after about 90 minutes, after which time the client process will be
useless and fail to retrieve any passwords, necessitating manual
password entry via the keyboard.

Fix this by eliminating the file descriptor leak in the client.

* plugins.d/mandos-client.c (add_delete_local_route): Do
  close(devnull) also in parent process, also if fork() fails, and on
  any failure in child process.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
/*
3
3
 * Usplash - Read a password from usplash and output it
4
4
 * 
5
 
 * Copyright © 2008,2009 Teddy Hogeborn
6
 
 * Copyright © 2008,2009 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
 
5
 * Copyright © 2008-2018 Teddy Hogeborn
 
6
 * Copyright © 2008-2018 Björn Påhlsson
 
7
 * 
 
8
 * This file is part of Mandos.
 
9
 * 
 
10
 * Mandos is free software: you can redistribute it and/or modify it
 
11
 * under the terms of the GNU General Public License as published by
 
12
 * the Free Software Foundation, either version 3 of the License, or
 
13
 * (at your option) any later version.
 
14
 * 
 
15
 * Mandos is distributed in the hope that it will be useful, but
14
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of
15
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
18
 * General Public License for more details.
17
19
 * 
18
20
 * 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
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
21
22
 * 
22
 
 * Contact the authors at <https://www.fukt.bsnet.se/~belorn/> and
23
 
 * <https://www.fukt.bsnet.se/~teddy/>.
 
23
 * Contact the authors at <mandos@recompile.se>.
24
24
 */
25
25
 
26
 
#define _GNU_SOURCE             /* asprintf() */
 
26
#define _GNU_SOURCE             /* asprintf(), TEMP_FAILURE_RETRY() */
27
27
#include <signal.h>             /* sig_atomic_t, struct sigaction,
28
28
                                   sigemptyset(), sigaddset(), SIGINT,
29
29
                                   SIGHUP, SIGTERM, sigaction(),
32
32
#include <fcntl.h>              /* open(), O_WRONLY, O_RDONLY */
33
33
#include <iso646.h>             /* and, or, not*/
34
34
#include <errno.h>              /* errno, EINTR */
 
35
#include <error.h>
35
36
#include <sys/types.h>          /* size_t, ssize_t, pid_t, DIR, struct
36
37
                                   dirent */
37
38
#include <stddef.h>             /* NULL */
38
 
#include <string.h>             /* strlen(), memcmp() */
39
 
#include <stdio.h>              /* asprintf(), perror() */
 
39
#include <string.h>             /* strlen(), memcmp(), strerror() */
 
40
#include <stdio.h>              /* asprintf(), vasprintf(), vprintf(),
 
41
                                   fprintf() */
40
42
#include <unistd.h>             /* close(), write(), readlink(),
41
43
                                   read(), STDOUT_FILENO, sleep(),
42
44
                                   fork(), setuid(), geteuid(),
43
45
                                   setsid(), chdir(), dup2(),
44
46
                                   STDERR_FILENO, execv() */
45
 
#include <stdlib.h>             /* free(), EXIT_FAILURE, strtoul(),
46
 
                                   realloc(), EXIT_SUCCESS, malloc(),
47
 
                                   _exit() */
48
 
#include <stdlib.h>             /* getenv() */
 
47
#include <stdlib.h>             /* free(), EXIT_FAILURE, realloc(),
 
48
                                   EXIT_SUCCESS, malloc(), _exit(),
 
49
                                   getenv() */
49
50
#include <dirent.h>             /* opendir(), readdir(), closedir() */
 
51
#include <inttypes.h>           /* intmax_t, strtoimax() */
50
52
#include <sys/stat.h>           /* struct stat, lstat(), S_ISLNK */
 
53
#include <sysexits.h>           /* EX_OSERR, EX_UNAVAILABLE */
 
54
#include <argz.h>               /* argz_count(), argz_extract() */
 
55
#include <stdarg.h>             /* va_list, va_start(), ... */
51
56
 
52
57
sig_atomic_t interrupted_by_signal = 0;
53
 
 
54
 
static void termination_handler(__attribute__((unused))int signum){
 
58
int signal_received;
 
59
const char usplash_name[] = "/sbin/usplash";
 
60
 
 
61
/* Function to use when printing errors */
 
62
__attribute__((format (gnu_printf, 3, 4)))
 
63
void error_plus(int status, int errnum, const char *formatstring,
 
64
                ...){
 
65
  va_list ap;
 
66
  char *text;
 
67
  int ret;
 
68
  
 
69
  va_start(ap, formatstring);
 
70
  ret = vasprintf(&text, formatstring, ap);
 
71
  if(ret == -1){
 
72
    fprintf(stderr, "Mandos plugin %s: ",
 
73
            program_invocation_short_name);
 
74
    vfprintf(stderr, formatstring, ap);
 
75
    fprintf(stderr, ": ");
 
76
    fprintf(stderr, "%s\n", strerror(errnum));
 
77
    error(status, errno, "vasprintf while printing error");
 
78
    return;
 
79
  }
 
80
  fprintf(stderr, "Mandos plugin ");
 
81
  error(status, errnum, "%s", text);
 
82
  free(text);
 
83
}
 
84
 
 
85
static void termination_handler(int signum){
 
86
  if(interrupted_by_signal){
 
87
    return;
 
88
  }
55
89
  interrupted_by_signal = 1;
 
90
  signal_received = signum;
56
91
}
57
92
 
58
 
static bool usplash_write(const char *cmd, const char *arg){
 
93
static bool usplash_write(int *fifo_fd_r,
 
94
                          const char *cmd, const char *arg){
59
95
  /* 
60
 
   * usplash_write("TIMEOUT", "15") will write "TIMEOUT 15\0"
61
 
   * usplash_write("PULSATE", NULL) will write "PULSATE\0"
 
96
   * usplash_write(&fd, "TIMEOUT", "15") will write "TIMEOUT 15\0"
 
97
   * usplash_write(&fd, "PULSATE", NULL) will write "PULSATE\0"
62
98
   * SEE ALSO
63
99
   *         usplash_write(8)
64
100
   */
65
101
  int ret;
66
 
  int fifo_fd;
67
 
  do{
68
 
    fifo_fd = open("/dev/.initramfs/usplash_fifo", O_WRONLY);
69
 
    if(fifo_fd == -1 and (errno != EINTR or interrupted_by_signal)){
 
102
  if(*fifo_fd_r == -1){
 
103
    ret = open("/dev/.initramfs/usplash_fifo", O_WRONLY);
 
104
    if(ret == -1){
70
105
      return false;
71
106
    }
72
 
  }while(fifo_fd == -1);
 
107
    *fifo_fd_r = ret;
 
108
  }
73
109
  
74
110
  const char *cmd_line;
75
111
  size_t cmd_line_len;
76
112
  char *cmd_line_alloc = NULL;
77
113
  if(arg == NULL){
78
114
    cmd_line = cmd;
79
 
    cmd_line_len = strlen(cmd);
80
 
  }else{
81
 
    do{
 
115
    cmd_line_len = strlen(cmd) + 1;
 
116
  } else {
 
117
    do {
82
118
      ret = asprintf(&cmd_line_alloc, "%s %s", cmd, arg);
83
 
      if(ret == -1 and (errno != EINTR or interrupted_by_signal)){
 
119
      if(ret == -1){
84
120
        int e = errno;
85
 
        close(fifo_fd);
 
121
        close(*fifo_fd_r);
86
122
        errno = e;
87
123
        return false;
88
124
      }
89
 
    }while(ret == -1);
 
125
    } while(ret == -1);
90
126
    cmd_line = cmd_line_alloc;
91
127
    cmd_line_len = (size_t)ret + 1;
92
128
  }
93
129
  
94
130
  size_t written = 0;
95
131
  ssize_t sret = 0;
96
 
  while(not interrupted_by_signal and written < cmd_line_len){
97
 
    sret = write(fifo_fd, cmd_line + written,
 
132
  while(written < cmd_line_len){
 
133
    sret = write(*fifo_fd_r, cmd_line + written,
98
134
                 cmd_line_len - written);
99
135
    if(sret == -1){
100
 
      if(errno != EINTR or interrupted_by_signal){
101
 
        int e = errno;
102
 
        close(fifo_fd);
103
 
        free(cmd_line_alloc);
104
 
        errno = e;
105
 
        return false;
106
 
      } else {
107
 
        continue;
108
 
      }
 
136
      int e = errno;
 
137
      close(*fifo_fd_r);
 
138
      free(cmd_line_alloc);
 
139
      errno = e;
 
140
      return false;
109
141
    }
110
142
    written += (size_t)sret;
111
143
  }
112
144
  free(cmd_line_alloc);
113
 
  do{
114
 
    ret = close(fifo_fd);
115
 
    if(ret == -1 and (errno != EINTR or interrupted_by_signal)){
116
 
      return false;
117
 
    }
118
 
  }while(ret == -1);
119
 
  if(interrupted_by_signal){
120
 
    return false;
121
 
  }
 
145
  
122
146
  return true;
123
147
}
124
148
 
 
149
/* Create prompt string */
 
150
char *makeprompt(void){
 
151
  int ret = 0;
 
152
  char *prompt;
 
153
  const char *const cryptsource = getenv("cryptsource");
 
154
  const char *const crypttarget = getenv("crypttarget");
 
155
  const char prompt_start[] = "Enter passphrase to unlock the disk";
 
156
  
 
157
  if(cryptsource == NULL){
 
158
    if(crypttarget == NULL){
 
159
      ret = asprintf(&prompt, "%s: ", prompt_start);
 
160
    } else {
 
161
      ret = asprintf(&prompt, "%s (%s): ", prompt_start,
 
162
                     crypttarget);
 
163
    }
 
164
  } else {
 
165
    if(crypttarget == NULL){
 
166
      ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource);
 
167
    } else {
 
168
      ret = asprintf(&prompt, "%s %s (%s): ", prompt_start,
 
169
                     cryptsource, crypttarget);
 
170
    }
 
171
  }
 
172
  if(ret == -1){
 
173
    return NULL;
 
174
  }
 
175
  return prompt;
 
176
}
 
177
 
 
178
pid_t find_usplash(char **cmdline_r, size_t *cmdline_len_r){
 
179
  int ret = 0;
 
180
  ssize_t sret = 0;
 
181
  char *cmdline = NULL;
 
182
  size_t cmdline_len = 0;
 
183
  DIR *proc_dir = opendir("/proc");
 
184
  if(proc_dir == NULL){
 
185
    error_plus(0, errno, "opendir");
 
186
    return -1;
 
187
  }
 
188
  errno = 0;
 
189
  for(struct dirent *proc_ent = readdir(proc_dir);
 
190
      proc_ent != NULL;
 
191
      proc_ent = readdir(proc_dir)){
 
192
    pid_t pid;
 
193
    {
 
194
      intmax_t tmpmax;
 
195
      char *tmp;
 
196
      tmpmax = strtoimax(proc_ent->d_name, &tmp, 10);
 
197
      if(errno != 0 or tmp == proc_ent->d_name or *tmp != '\0'
 
198
         or tmpmax != (pid_t)tmpmax){
 
199
        /* Not a process */
 
200
        errno = 0;
 
201
        continue;
 
202
      }
 
203
      pid = (pid_t)tmpmax;
 
204
    }
 
205
    /* Find the executable name by doing readlink() on the
 
206
       /proc/<pid>/exe link */
 
207
    char exe_target[sizeof(usplash_name)];
 
208
    {
 
209
      /* create file name string */
 
210
      char *exe_link;
 
211
      ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
 
212
      if(ret == -1){
 
213
        error_plus(0, errno, "asprintf");
 
214
        goto fail_find_usplash;
 
215
      }
 
216
      
 
217
      /* Check that it refers to a symlink owned by root:root */
 
218
      struct stat exe_stat;
 
219
      ret = lstat(exe_link, &exe_stat);
 
220
      if(ret == -1){
 
221
        if(errno == ENOENT){
 
222
          free(exe_link);
 
223
          continue;
 
224
        }
 
225
        error_plus(0, errno, "lstat");
 
226
        free(exe_link);
 
227
        goto fail_find_usplash;
 
228
      }
 
229
      if(not S_ISLNK(exe_stat.st_mode)
 
230
         or exe_stat.st_uid != 0
 
231
         or exe_stat.st_gid != 0){
 
232
        free(exe_link);
 
233
        continue;
 
234
      }
 
235
        
 
236
      sret = readlink(exe_link, exe_target, sizeof(exe_target));
 
237
      free(exe_link);
 
238
    }
 
239
    /* Compare executable name */
 
240
    if((sret != ((ssize_t)sizeof(exe_target)-1))
 
241
       or (memcmp(usplash_name, exe_target,
 
242
                  sizeof(exe_target)-1) != 0)){
 
243
      /* Not it */
 
244
      continue;
 
245
    }
 
246
    /* Found usplash */
 
247
    /* Read and save the command line of usplash in "cmdline" */
 
248
    {
 
249
      /* Open /proc/<pid>/cmdline  */
 
250
      int cl_fd;
 
251
      {
 
252
        char *cmdline_filename;
 
253
        ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
 
254
                       proc_ent->d_name);
 
255
        if(ret == -1){
 
256
          error_plus(0, errno, "asprintf");
 
257
          goto fail_find_usplash;
 
258
        }
 
259
        cl_fd = open(cmdline_filename, O_RDONLY);
 
260
        free(cmdline_filename);
 
261
        if(cl_fd == -1){
 
262
          error_plus(0, errno, "open");
 
263
          goto fail_find_usplash;
 
264
        }
 
265
      }
 
266
      size_t cmdline_allocated = 0;
 
267
      char *tmp;
 
268
      const size_t blocksize = 1024;
 
269
      do {
 
270
        /* Allocate more space? */
 
271
        if(cmdline_len + blocksize > cmdline_allocated){
 
272
          tmp = realloc(cmdline, cmdline_allocated + blocksize);
 
273
          if(tmp == NULL){
 
274
            error_plus(0, errno, "realloc");
 
275
            close(cl_fd);
 
276
            goto fail_find_usplash;
 
277
          }
 
278
          cmdline = tmp;
 
279
          cmdline_allocated += blocksize;
 
280
        }
 
281
        /* Read data */
 
282
        sret = read(cl_fd, cmdline + cmdline_len,
 
283
                    cmdline_allocated - cmdline_len);
 
284
        if(sret == -1){
 
285
          error_plus(0, errno, "read");
 
286
          close(cl_fd);
 
287
          goto fail_find_usplash;
 
288
        }
 
289
        cmdline_len += (size_t)sret;
 
290
      } while(sret != 0);
 
291
      ret = close(cl_fd);
 
292
      if(ret == -1){
 
293
        error_plus(0, errno, "close");
 
294
        goto fail_find_usplash;
 
295
      }
 
296
    }
 
297
    /* Close directory */
 
298
    ret = closedir(proc_dir);
 
299
    if(ret == -1){
 
300
      error_plus(0, errno, "closedir");
 
301
      goto fail_find_usplash;
 
302
    }
 
303
    /* Success */
 
304
    *cmdline_r = cmdline;
 
305
    *cmdline_len_r = cmdline_len;
 
306
    return pid;
 
307
  }
 
308
  
 
309
 fail_find_usplash:
 
310
  
 
311
  free(cmdline);
 
312
  if(proc_dir != NULL){
 
313
    int e = errno;
 
314
    closedir(proc_dir);
 
315
    errno = e;
 
316
  }
 
317
  return 0;
 
318
}
 
319
 
125
320
int main(__attribute__((unused))int argc,
126
321
         __attribute__((unused))char **argv){
127
322
  int ret = 0;
128
323
  ssize_t sret;
129
 
  bool an_error_occured = false;
 
324
  int fifo_fd = -1;
 
325
  int outfifo_fd = -1;
 
326
  char *buf = NULL;
 
327
  size_t buf_len = 0;
 
328
  pid_t usplash_pid = -1;
 
329
  bool usplash_accessed = false;
 
330
  int status = EXIT_FAILURE;    /* Default failure exit status */
130
331
  
131
 
  /* Create prompt string */
132
 
  char *prompt = NULL;
133
 
  {
134
 
    const char *const cryptsource = getenv("cryptsource");
135
 
    const char *const crypttarget = getenv("crypttarget");
136
 
    const char prompt_start[] = "Enter passphrase to unlock the disk";
137
 
    
138
 
    if(cryptsource == NULL){
139
 
      if(crypttarget == NULL){
140
 
        ret = asprintf(&prompt, "%s: ", prompt_start);
141
 
      } else {
142
 
        ret = asprintf(&prompt, "%s (%s): ", prompt_start,
143
 
                       crypttarget);
144
 
      }
145
 
    } else {
146
 
      if(crypttarget == NULL){
147
 
        ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource);
148
 
      } else {
149
 
        ret = asprintf(&prompt, "%s %s (%s): ", prompt_start,
150
 
                       cryptsource, crypttarget);
151
 
      }
152
 
    }
153
 
    if(ret == -1){
154
 
      return EXIT_FAILURE;
155
 
    }
 
332
  char *prompt = makeprompt();
 
333
  if(prompt == NULL){
 
334
    status = EX_OSERR;
 
335
    goto failure;
156
336
  }
157
337
  
158
338
  /* Find usplash process */
159
 
  pid_t usplash_pid = 0;
160
339
  char *cmdline = NULL;
161
340
  size_t cmdline_len = 0;
162
 
  const char usplash_name[] = "/sbin/usplash";
163
 
  {
164
 
    DIR *proc_dir = opendir("/proc");
165
 
    if(proc_dir == NULL){
166
 
      free(prompt);
167
 
      perror("opendir");
168
 
      return EXIT_FAILURE;
169
 
    }
170
 
    for(struct dirent *proc_ent = readdir(proc_dir);
171
 
        proc_ent != NULL;
172
 
        proc_ent = readdir(proc_dir)){
173
 
      pid_t pid = (pid_t) strtoul(proc_ent->d_name, NULL, 10);
174
 
      if(pid == 0){
175
 
        /* Not a process */
176
 
        continue;
177
 
      }
178
 
      /* Find the executable name by doing readlink() on the
179
 
         /proc/<pid>/exe link */
180
 
      char exe_target[sizeof(usplash_name)];
181
 
      {
182
 
        /* create file name string */
183
 
        char *exe_link;
184
 
        ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
185
 
        if(ret == -1){
186
 
          perror("asprintf");
187
 
          free(prompt);
188
 
          closedir(proc_dir);
189
 
          return EXIT_FAILURE;
190
 
        }
191
 
        
192
 
        /* Check that it refers to a symlink owned by root:root */
193
 
        struct stat exe_stat;
194
 
        ret = lstat(exe_link, &exe_stat);
195
 
        if(ret == -1){
196
 
          if(errno == ENOENT){
197
 
            free(exe_link);
198
 
            continue;
199
 
          }
200
 
          perror("lstat");
201
 
          free(exe_link);
202
 
          free(prompt);
203
 
          closedir(proc_dir);
204
 
          return EXIT_FAILURE;
205
 
        }
206
 
        if(not S_ISLNK(exe_stat.st_mode)
207
 
           or exe_stat.st_uid != 0
208
 
           or exe_stat.st_gid != 0){
209
 
          free(exe_link);
210
 
          continue;
211
 
        }
212
 
        
213
 
        sret = readlink(exe_link, exe_target, sizeof(exe_target));
214
 
        free(exe_link);
215
 
      }
216
 
      if((sret == ((ssize_t)sizeof(exe_target)-1))
217
 
         and (memcmp(usplash_name, exe_target,
218
 
                     sizeof(exe_target)-1) == 0)){
219
 
        usplash_pid = pid;
220
 
        /* Read and save the command line of usplash in "cmdline" */
221
 
        {
222
 
          /* Open /proc/<pid>/cmdline  */
223
 
          int cl_fd;
224
 
          {
225
 
            char *cmdline_filename;
226
 
            ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
227
 
                           proc_ent->d_name);
228
 
            if(ret == -1){
229
 
              perror("asprintf");
230
 
              free(prompt);
231
 
              closedir(proc_dir);
232
 
              return EXIT_FAILURE;
233
 
            }
234
 
            cl_fd = open(cmdline_filename, O_RDONLY);
235
 
            if(cl_fd == -1){
236
 
              perror("open");
237
 
              free(cmdline_filename);
238
 
              free(prompt);
239
 
              closedir(proc_dir);
240
 
              return EXIT_FAILURE;
241
 
            }
242
 
            free(cmdline_filename);
243
 
          }
244
 
          size_t cmdline_allocated = 0;
245
 
          char *tmp;
246
 
          const size_t blocksize = 1024;
247
 
          do{
248
 
            if(cmdline_len + blocksize > cmdline_allocated){
249
 
              tmp = realloc(cmdline, cmdline_allocated + blocksize);
250
 
              if(tmp == NULL){
251
 
                perror("realloc");
252
 
                free(cmdline);
253
 
                free(prompt);
254
 
                closedir(proc_dir);
255
 
                return EXIT_FAILURE;
256
 
              }
257
 
              cmdline = tmp;
258
 
              cmdline_allocated += blocksize;
259
 
            }
260
 
            sret = read(cl_fd, cmdline + cmdline_len,
261
 
                        cmdline_allocated - cmdline_len);
262
 
            if(sret == -1){
263
 
              perror("read");
264
 
              free(cmdline);
265
 
              free(prompt);
266
 
              closedir(proc_dir);
267
 
              return EXIT_FAILURE;
268
 
            }
269
 
            cmdline_len += (size_t)sret;
270
 
          } while(sret != 0);
271
 
          close(cl_fd);
272
 
        }
273
 
        break;
274
 
      }
275
 
    }
276
 
    closedir(proc_dir);
277
 
  }
 
341
  usplash_pid = find_usplash(&cmdline, &cmdline_len);
278
342
  if(usplash_pid == 0){
279
 
    free(prompt);
280
 
    return EXIT_FAILURE;
 
343
    status = EX_UNAVAILABLE;
 
344
    goto failure;
281
345
  }
282
346
  
283
347
  /* Set up the signal handler */
286
350
      new_action = { .sa_handler = termination_handler,
287
351
                     .sa_flags = 0 };
288
352
    sigemptyset(&new_action.sa_mask);
289
 
    sigaddset(&new_action.sa_mask, SIGINT);
290
 
    sigaddset(&new_action.sa_mask, SIGHUP);
291
 
    sigaddset(&new_action.sa_mask, SIGTERM);
 
353
    ret = sigaddset(&new_action.sa_mask, SIGINT);
 
354
    if(ret == -1){
 
355
      error_plus(0, errno, "sigaddset");
 
356
      status = EX_OSERR;
 
357
      goto failure;
 
358
    }
 
359
    ret = sigaddset(&new_action.sa_mask, SIGHUP);
 
360
    if(ret == -1){
 
361
      error_plus(0, errno, "sigaddset");
 
362
      status = EX_OSERR;
 
363
      goto failure;
 
364
    }
 
365
    ret = sigaddset(&new_action.sa_mask, SIGTERM);
 
366
    if(ret == -1){
 
367
      error_plus(0, errno, "sigaddset");
 
368
      status = EX_OSERR;
 
369
      goto failure;
 
370
    }
292
371
    ret = sigaction(SIGINT, NULL, &old_action);
293
372
    if(ret == -1){
294
 
      perror("sigaction");
295
 
      free(prompt);
296
 
      return EXIT_FAILURE;
 
373
      if(errno != EINTR){
 
374
        error_plus(0, errno, "sigaction");
 
375
        status = EX_OSERR;
 
376
      }
 
377
      goto failure;
297
378
    }
298
379
    if(old_action.sa_handler != SIG_IGN){
299
380
      ret = sigaction(SIGINT, &new_action, NULL);
300
381
      if(ret == -1){
301
 
        perror("sigaction");
302
 
        free(prompt);
303
 
        return EXIT_FAILURE;
 
382
        if(errno != EINTR){
 
383
          error_plus(0, errno, "sigaction");
 
384
          status = EX_OSERR;
 
385
        }
 
386
        goto failure;
304
387
      }
305
388
    }
306
389
    ret = sigaction(SIGHUP, NULL, &old_action);
307
390
    if(ret == -1){
308
 
      perror("sigaction");
309
 
      free(prompt);
310
 
      return EXIT_FAILURE;
 
391
      if(errno != EINTR){
 
392
        error_plus(0, errno, "sigaction");
 
393
        status = EX_OSERR;
 
394
      }
 
395
      goto failure;
311
396
    }
312
397
    if(old_action.sa_handler != SIG_IGN){
313
398
      ret = sigaction(SIGHUP, &new_action, NULL);
314
399
      if(ret == -1){
315
 
        perror("sigaction");
316
 
        free(prompt);
317
 
        return EXIT_FAILURE;
 
400
        if(errno != EINTR){
 
401
          error_plus(0, errno, "sigaction");
 
402
          status = EX_OSERR;
 
403
        }
 
404
        goto failure;
318
405
      }
319
406
    }
320
407
    ret = sigaction(SIGTERM, NULL, &old_action);
321
408
    if(ret == -1){
322
 
      perror("sigaction");
323
 
      free(prompt);
324
 
      return EXIT_FAILURE;
 
409
      if(errno != EINTR){
 
410
        error_plus(0, errno, "sigaction");
 
411
        status = EX_OSERR;
 
412
      }
 
413
      goto failure;
325
414
    }
326
415
    if(old_action.sa_handler != SIG_IGN){
327
416
      ret = sigaction(SIGTERM, &new_action, NULL);
328
417
      if(ret == -1){
329
 
        perror("sigaction");
330
 
        free(prompt);
331
 
        return EXIT_FAILURE;
 
418
        if(errno != EINTR){
 
419
          error_plus(0, errno, "sigaction");
 
420
          status = EX_OSERR;
 
421
        }
 
422
        goto failure;
332
423
      }
333
424
    }
334
425
  }
335
426
  
 
427
  usplash_accessed = true;
336
428
  /* Write command to FIFO */
337
 
  if(not interrupted_by_signal){
338
 
    if(not usplash_write("TIMEOUT", "0")
339
 
       and (errno != EINTR)){
340
 
      perror("usplash_write");
341
 
      an_error_occured = true;
342
 
    }
343
 
  }
344
 
  if(not interrupted_by_signal and not an_error_occured){
345
 
    if(not usplash_write("INPUTQUIET", prompt)
346
 
       and (errno != EINTR)){
347
 
      perror("usplash_write");
348
 
      an_error_occured = true;
349
 
    }
350
 
  }
 
429
  if(not usplash_write(&fifo_fd, "TIMEOUT", "0")){
 
430
    if(errno != EINTR){
 
431
      error_plus(0, errno, "usplash_write");
 
432
      status = EX_OSERR;
 
433
    }
 
434
    goto failure;
 
435
  }
 
436
  
 
437
  if(interrupted_by_signal){
 
438
    goto failure;
 
439
  }
 
440
  
 
441
  if(not usplash_write(&fifo_fd, "INPUTQUIET", prompt)){
 
442
    if(errno != EINTR){
 
443
      error_plus(0, errno, "usplash_write");
 
444
      status = EX_OSERR;
 
445
    }
 
446
    goto failure;
 
447
  }
 
448
  
 
449
  if(interrupted_by_signal){
 
450
    goto failure;
 
451
  }
 
452
  
351
453
  free(prompt);
352
 
  
353
 
  /* This is not really a loop; while() is used to be able to "break"
354
 
     out of it; those breaks are marked "Big" */
355
 
  while(not interrupted_by_signal and not an_error_occured){
356
 
    char *buf = NULL;
357
 
    size_t buf_len = 0;
358
 
    
359
 
    /* Open FIFO */
360
 
    int fifo_fd;
361
 
    do{
362
 
      fifo_fd = open("/dev/.initramfs/usplash_outfifo", O_RDONLY);
363
 
      if(fifo_fd == -1){
364
 
        if(errno != EINTR){
365
 
          perror("open");
366
 
          an_error_occured = true;
367
 
          break;
368
 
        }
369
 
        if(interrupted_by_signal){
370
 
          break;
371
 
        }
372
 
      }
373
 
    }while(fifo_fd == -1);
374
 
    if(interrupted_by_signal or an_error_occured){
375
 
      break;                    /* Big */
376
 
    }
377
 
    
378
 
    /* Read from FIFO */
379
 
    size_t buf_allocated = 0;
380
 
    const size_t blocksize = 1024;
381
 
    do{
382
 
      if(buf_len + blocksize > buf_allocated){
383
 
        char *tmp = realloc(buf, buf_allocated + blocksize);
384
 
        if(tmp == NULL){
385
 
          perror("realloc");
386
 
          an_error_occured = true;
387
 
          break;
388
 
        }
389
 
        buf = tmp;
390
 
        buf_allocated += blocksize;
391
 
      }
392
 
      do{
393
 
        sret = read(fifo_fd, buf + buf_len, buf_allocated - buf_len);
394
 
        if(sret == -1){
395
 
          if(errno != EINTR){
396
 
            perror("read");
397
 
            an_error_occured = true;
398
 
            break;
399
 
          }
400
 
          if(interrupted_by_signal){
401
 
            break;
402
 
          }
403
 
        }
404
 
      }while(sret == -1);
405
 
      if(interrupted_by_signal or an_error_occured){
406
 
        break;
407
 
      }
408
 
      
409
 
      buf_len += (size_t)sret;
410
 
    }while(sret != 0);
411
 
    close(fifo_fd);
412
 
    if(interrupted_by_signal or an_error_occured){
413
 
      break;                    /* Big */
414
 
    }
415
 
    
416
 
    if(not usplash_write("TIMEOUT", "15")
417
 
       and (errno != EINTR)){
418
 
        perror("usplash_write");
419
 
        an_error_occured = true;
420
 
    }
421
 
    if(interrupted_by_signal or an_error_occured){
422
 
      break;                    /* Big */
423
 
    }
424
 
    
425
 
    /* Print password to stdout */
426
 
    size_t written = 0;
427
 
    while(written < buf_len){
428
 
      do{
429
 
        sret = write(STDOUT_FILENO, buf + written, buf_len - written);
430
 
        if(sret == -1){
431
 
          if(errno != EINTR){
432
 
            perror("write");
433
 
            an_error_occured = true;
434
 
            break;
435
 
          }
436
 
          if(interrupted_by_signal){
437
 
            break;
438
 
          }
439
 
        }
440
 
      }while(sret == -1);
441
 
      if(interrupted_by_signal or an_error_occured){
442
 
        break;
443
 
      }
444
 
      written += (size_t)sret;
445
 
    }
446
 
    free(buf);
447
 
    if(not interrupted_by_signal and not an_error_occured){
448
 
      free(cmdline);
449
 
      return EXIT_SUCCESS;
450
 
    }
451
 
    break;                      /* Big */
452
 
  }                             /* end of non-loop while() */
453
 
  
454
 
  /* If we got here, an error or interrupt must have happened */
455
 
  
456
 
  /* Create argc and argv for new usplash*/
457
 
  int cmdline_argc = 0;
458
 
  char **cmdline_argv = malloc(sizeof(char *));
459
 
  {
460
 
    size_t position = 0;
461
 
    while(position < cmdline_len){
462
 
      char **tmp = realloc(cmdline_argv,
463
 
                           (sizeof(char *)
464
 
                            * (size_t)(cmdline_argc + 2)));
 
454
  prompt = NULL;
 
455
  
 
456
  /* Read reply from usplash */
 
457
  /* Open FIFO */
 
458
  outfifo_fd = open("/dev/.initramfs/usplash_outfifo", O_RDONLY);
 
459
  if(outfifo_fd == -1){
 
460
    if(errno != EINTR){
 
461
      error_plus(0, errno, "open");
 
462
      status = EX_OSERR;
 
463
    }
 
464
    goto failure;
 
465
  }
 
466
  
 
467
  if(interrupted_by_signal){
 
468
    goto failure;
 
469
  }
 
470
  
 
471
  /* Read from FIFO */
 
472
  size_t buf_allocated = 0;
 
473
  const size_t blocksize = 1024;
 
474
  do {
 
475
    /* Allocate more space */
 
476
    if(buf_len + blocksize > buf_allocated){
 
477
      char *tmp = realloc(buf, buf_allocated + blocksize);
465
478
      if(tmp == NULL){
466
 
        perror("realloc");
467
 
        free(cmdline_argv);
468
 
        return EXIT_FAILURE;
469
 
      }
470
 
      cmdline_argv = tmp;
471
 
      cmdline_argv[cmdline_argc] = cmdline + position;
472
 
      cmdline_argc++;
473
 
      position += strlen(cmdline + position) + 1;
474
 
    }
475
 
    cmdline_argv[cmdline_argc] = NULL;
476
 
  }
 
479
        if(errno != EINTR){
 
480
          error_plus(0, errno, "realloc");
 
481
          status = EX_OSERR;
 
482
        }
 
483
        goto failure;
 
484
      }
 
485
      buf = tmp;
 
486
      buf_allocated += blocksize;
 
487
    }
 
488
    sret = read(outfifo_fd, buf + buf_len,
 
489
                buf_allocated - buf_len);
 
490
    if(sret == -1){
 
491
      if(errno != EINTR){
 
492
        error_plus(0, errno, "read");
 
493
        status = EX_OSERR;
 
494
      }
 
495
      close(outfifo_fd);
 
496
      goto failure;
 
497
    }
 
498
    if(interrupted_by_signal){
 
499
      break;
 
500
    }
 
501
    
 
502
    buf_len += (size_t)sret;
 
503
  } while(sret != 0);
 
504
  ret = close(outfifo_fd);
 
505
  if(ret == -1){
 
506
    if(errno != EINTR){
 
507
      error_plus(0, errno, "close");
 
508
      status = EX_OSERR;
 
509
    }
 
510
    goto failure;
 
511
  }
 
512
  outfifo_fd = -1;
 
513
  
 
514
  if(interrupted_by_signal){
 
515
    goto failure;
 
516
  }
 
517
  
 
518
  if(not usplash_write(&fifo_fd, "TIMEOUT", "15")){
 
519
    if(errno != EINTR){
 
520
      error_plus(0, errno, "usplash_write");
 
521
      status = EX_OSERR;
 
522
    }
 
523
    goto failure;
 
524
  }
 
525
  
 
526
  if(interrupted_by_signal){
 
527
    goto failure;
 
528
  }
 
529
  
 
530
  ret = close(fifo_fd);
 
531
  if(ret == -1){
 
532
    if(errno != EINTR){
 
533
      error_plus(0, errno, "close");
 
534
      status = EX_OSERR;
 
535
    }
 
536
    goto failure;
 
537
  }
 
538
  fifo_fd = -1;
 
539
  
 
540
  /* Print password to stdout */
 
541
  size_t written = 0;
 
542
  while(written < buf_len){
 
543
    do {
 
544
      sret = write(STDOUT_FILENO, buf + written, buf_len - written);
 
545
      if(sret == -1){
 
546
        if(errno != EINTR){
 
547
          error_plus(0, errno, "write");
 
548
          status = EX_OSERR;
 
549
        }
 
550
        goto failure;
 
551
      }
 
552
    } while(sret == -1);
 
553
    
 
554
    if(interrupted_by_signal){
 
555
      goto failure;
 
556
    }
 
557
    written += (size_t)sret;
 
558
  }
 
559
  free(buf);
 
560
  buf = NULL;
 
561
  
 
562
  if(interrupted_by_signal){
 
563
    goto failure;
 
564
  }
 
565
  
 
566
  free(cmdline);
 
567
  return EXIT_SUCCESS;
 
568
  
 
569
 failure:
 
570
  
 
571
  free(buf);
 
572
  
 
573
  free(prompt);
 
574
  
 
575
  /* If usplash was never accessed, we can stop now */
 
576
  if(not usplash_accessed){
 
577
    return status;
 
578
  }
 
579
  
 
580
  /* Close FIFO */
 
581
  if(fifo_fd != -1){
 
582
    ret = close(fifo_fd);
 
583
    if(ret == -1 and errno != EINTR){
 
584
      error_plus(0, errno, "close");
 
585
    }
 
586
    fifo_fd = -1;
 
587
  }
 
588
  
 
589
  /* Close output FIFO */
 
590
  if(outfifo_fd != -1){
 
591
    ret = close(outfifo_fd);
 
592
    if(ret == -1){
 
593
      error_plus(0, errno, "close");
 
594
    }
 
595
  }
 
596
  
 
597
  /* Create argv for new usplash*/
 
598
  char **cmdline_argv = malloc((argz_count(cmdline, cmdline_len) + 1)
 
599
                               * sizeof(char *)); /* Count args */
 
600
  if(cmdline_argv == NULL){
 
601
    error_plus(0, errno, "malloc");
 
602
    return status;
 
603
  }
 
604
  argz_extract(cmdline, cmdline_len, cmdline_argv); /* Create argv */
 
605
  
477
606
  /* Kill old usplash */
478
607
  kill(usplash_pid, SIGTERM);
479
608
  sleep(2);
481
610
    kill(usplash_pid, SIGKILL);
482
611
    sleep(1);
483
612
  }
 
613
  
484
614
  pid_t new_usplash_pid = fork();
485
615
  if(new_usplash_pid == 0){
486
616
    /* Child; will become new usplash process */
487
617
    
488
618
    /* Make the effective user ID (root) the only user ID instead of
489
 
       the real user ID (mandos) */
 
619
       the real user ID (_mandos) */
490
620
    ret = setuid(geteuid());
491
621
    if(ret == -1){
492
 
      perror("setuid");
 
622
      error_plus(0, errno, "setuid");
493
623
    }
494
624
    
495
625
    setsid();
496
626
    ret = chdir("/");
 
627
    if(ret == -1){
 
628
      error_plus(0, errno, "chdir");
 
629
      _exit(EX_OSERR);
 
630
    }
497
631
/*     if(fork() != 0){ */
498
632
/*       _exit(EXIT_SUCCESS); */
499
633
/*     } */
500
634
    ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
501
635
    if(ret == -1){
502
 
      perror("dup2");
503
 
      _exit(EXIT_FAILURE);
 
636
      error_plus(0, errno, "dup2");
 
637
      _exit(EX_OSERR);
504
638
    }
505
639
    
506
640
    execv(usplash_name, cmdline_argv);
507
641
    if(not interrupted_by_signal){
508
 
      perror("execv");
 
642
      error_plus(0, errno, "execv");
509
643
    }
510
644
    free(cmdline);
511
645
    free(cmdline_argv);
512
 
    _exit(EXIT_FAILURE);
 
646
    _exit(EX_OSERR);
513
647
  }
514
648
  free(cmdline);
515
649
  free(cmdline_argv);
516
650
  sleep(2);
517
 
  if(not usplash_write("PULSATE", NULL)
518
 
     and (errno != EINTR)){
519
 
    perror("usplash_write");
520
 
  }
521
 
  
522
 
  return EXIT_FAILURE;
 
651
  if(not usplash_write(&fifo_fd, "PULSATE", NULL)){
 
652
    if(errno != EINTR){
 
653
      error_plus(0, errno, "usplash_write");
 
654
    }
 
655
  }
 
656
  
 
657
  /* Close FIFO (again) */
 
658
  if(fifo_fd != -1){
 
659
    ret = close(fifo_fd);
 
660
    if(ret == -1 and errno != EINTR){
 
661
      error_plus(0, errno, "close");
 
662
    }
 
663
    fifo_fd = -1;
 
664
  }
 
665
  
 
666
  if(interrupted_by_signal){
 
667
    struct sigaction signal_action = { .sa_handler = SIG_DFL };
 
668
    sigemptyset(&signal_action.sa_mask);
 
669
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
 
670
                                            &signal_action, NULL));
 
671
    if(ret == -1){
 
672
      error_plus(0, errno, "sigaction");
 
673
    }
 
674
    do {
 
675
      ret = raise(signal_received);
 
676
    } while(ret != 0 and errno == EINTR);
 
677
    if(ret != 0){
 
678
      error_plus(0, errno, "raise");
 
679
      abort();
 
680
    }
 
681
    TEMP_FAILURE_RETRY(pause());
 
682
  }
 
683
  
 
684
  return status;
523
685
}