/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk
208 by Teddy Hogeborn
* Makefile (PLUGINS): Added "plugins.d/usplash".
1
#define _GNU_SOURCE		/* asprintf() */
2
#include <signal.h>		/* sig_atomic_t, struct sigaction,
3
				   sigemptyset(), sigaddset(),
4
				   sigaction, SIGINT, SIG_IGN, SIGHUP,
5
				   SIGTERM, kill(), SIGKILL */
6
#include <stddef.h>		/* NULL */
7
#include <stdlib.h>		/* getenv() */
8
#include <stdio.h>		/* asprintf(), perror() */
9
#include <stdlib.h>		/* EXIT_FAILURE, EXIT_SUCCESS,
10
				   strtoul(), free() */
11
#include <sys/types.h>		/* pid_t, DIR, struct dirent,
12
				   ssize_t */
13
#include <dirent.h>		/* opendir(), readdir(), closedir() */
14
#include <unistd.h>		/* readlink(), fork(), execl(),
15
				   _exit */
16
#include <string.h>		/* memcmp() */
17
#include <iso646.h>		/* and */
18
#include <stdbool.h>		/* bool, false, true */
19
#include <errno.h>		/* errno */
20
#include <sys/wait.h>		/* waitpid(), WIFEXITED(),
21
				   WEXITSTATUS() */
22
#include <fcntl.h>		/* open(), O_RDONLY */
23
24
sig_atomic_t interrupted_by_signal = 0;
25
26
static void termination_handler(__attribute__((unused))int signum){
27
  interrupted_by_signal = 1;
28
}
29
30
int main(__attribute__((unused))int argc,
31
	 __attribute__((unused))char **argv){
32
  int ret = 0;
33
  ssize_t sret;
34
  bool an_error_occured = false;
35
  
36
  /* Create prompt string */
37
  char *prompt = NULL;
38
  {
39
    const char *const cryptsource = getenv("cryptsource");
40
    const char *const crypttarget = getenv("crypttarget");
41
    const char *const prompt_start = "Enter passphrase to unlock the disk";
42
    
43
    if(cryptsource == NULL){
44
      if(crypttarget == NULL){
45
	ret = asprintf(&prompt, "%s: ", prompt_start);
46
      } else {
47
	ret = asprintf(&prompt, "%s (%s): ", prompt_start,
48
		       crypttarget);
49
      }
50
    } else {
51
      if(crypttarget == NULL){
52
	ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource);
53
      } else {
54
	ret = asprintf(&prompt, "%s %s (%s): ", prompt_start,
55
		       cryptsource, crypttarget);
56
      }
57
    }
58
    if(ret == -1){
59
      return EXIT_FAILURE;
60
    }
61
  }
62
  
63
  /* Find usplash process */
64
  pid_t usplash_pid = 0;
65
  char *cmdline = NULL;
66
  size_t cmdline_len = 0;
67
  {
68
    const char usplash_name[] = "/sbin/usplash";
69
    DIR *proc_dir = opendir("/proc");
70
    if(proc_dir == NULL){
71
      free(prompt);
72
      perror("opendir");
73
      return EXIT_FAILURE;
74
    }
75
    for(struct dirent *proc_ent = readdir(proc_dir);
76
	proc_ent != NULL;
77
	proc_ent = readdir(proc_dir)){
78
      pid_t pid = (pid_t) strtoul(proc_ent->d_name, NULL, 10);
79
      if(pid == 0){
80
	/* Not a process */
81
	continue;
82
      }
83
      /* Find the executable name by doing readlink() on the
84
	 /proc/<pid>/exe link */
85
      char exe_target[sizeof(usplash_name)];
86
      {
87
	char *exe_link;
88
	ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
89
	if(ret == -1){
90
	  perror("asprintf");
91
	  free(prompt);
92
	  closedir(proc_dir);
93
	  return EXIT_FAILURE;
94
	}
95
	sret = readlink(exe_link, exe_target, sizeof(exe_target));
96
	free(exe_link);
97
      }
98
      if((sret == ((ssize_t)sizeof(exe_target)-1))
99
	 and (memcmp(usplash_name, exe_target,
100
		     sizeof(exe_target)-1) == 0)){
101
	usplash_pid = pid;
102
	/* Read and save the command line of usplash in "cmdline" */
103
	{
104
	  /* Open /proc/<pid>/cmdline  */
105
	  int cl_fd;
106
	  {
107
	    char *cmdline_filename;
108
	    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
109
			   proc_ent->d_name);
110
	    if(ret == -1){
111
	      perror("asprintf");
112
	      free(prompt);
113
	      closedir(proc_dir);
114
	      return EXIT_FAILURE;
115
	    }
116
	    cl_fd = open(cmdline_filename, O_RDONLY);
117
	    if(cl_fd == -1){
118
	      perror("open");
119
	      free(cmdline_filename);
120
	      free(prompt);
121
	      closedir(proc_dir);
122
	      return EXIT_FAILURE;
123
	    }
124
	    free(cmdline_filename);
125
	  }
126
	  size_t cmdline_allocated = 0;
127
	  char *tmp;
128
	  const size_t blocksize = 1024;
129
	  do{
130
	    if(cmdline_len + blocksize > cmdline_allocated){
131
	      tmp = realloc(cmdline, cmdline_allocated + blocksize);
132
	      if(tmp == NULL){
133
		perror("realloc");
134
		free(cmdline);
135
		free(prompt);
136
		closedir(proc_dir);
137
		return EXIT_FAILURE;
138
	      }
139
	      cmdline = tmp;
140
	      cmdline_allocated += blocksize;
141
	    }
142
	    sret = read(cl_fd, cmdline + cmdline_len,
143
			cmdline_allocated - cmdline_len);
144
	    if(sret == -1){
145
	      perror("read");
146
	      free(cmdline);
147
	      free(prompt);
148
	      closedir(proc_dir);
149
	      return EXIT_FAILURE;
150
	    }
151
	    cmdline_len += (size_t)sret;
152
	  } while(sret != 0);
153
	  close(cl_fd);
154
	}
155
	break;
156
      }
157
    }
158
    closedir(proc_dir);
159
  }
160
  if(usplash_pid == 0){
161
    free(prompt);
162
    return EXIT_FAILURE;
163
  }
164
  
165
  /* Set up the signal handler */
166
  {
167
    struct sigaction old_action,
168
      new_action = { .sa_handler = termination_handler,
169
		     .sa_flags = 0 };
170
    sigemptyset(&new_action.sa_mask);
171
    sigaddset(&new_action.sa_mask, SIGINT);
172
    sigaddset(&new_action.sa_mask, SIGHUP);
173
    sigaddset(&new_action.sa_mask, SIGTERM);
174
    ret = sigaction(SIGINT, NULL, &old_action);
175
    if(ret == -1){
176
      perror("sigaction");
177
      free(prompt);
178
      return EXIT_FAILURE;
179
    }
180
    if (old_action.sa_handler != SIG_IGN){
181
      ret = sigaction(SIGINT, &new_action, NULL);
182
      if(ret == -1){
183
	perror("sigaction");
184
	free(prompt);
185
	return EXIT_FAILURE;
186
      }
187
    }
188
    ret = sigaction(SIGHUP, NULL, &old_action);
189
    if(ret == -1){
190
      perror("sigaction");
191
      free(prompt);
192
      return EXIT_FAILURE;
193
    }
194
    if (old_action.sa_handler != SIG_IGN){
195
      ret = sigaction(SIGHUP, &new_action, NULL);
196
      if(ret == -1){
197
	perror("sigaction");
198
	free(prompt);
199
	return EXIT_FAILURE;
200
      }
201
    }
202
    ret = sigaction(SIGTERM, NULL, &old_action);
203
    if(ret == -1){
204
      perror("sigaction");
205
      free(prompt);
206
      return EXIT_FAILURE;
207
    }
208
    if (old_action.sa_handler != SIG_IGN){
209
      ret = sigaction(SIGTERM, &new_action, NULL);
210
      if(ret == -1){
211
	perror("sigaction");
212
	free(prompt);
213
	return EXIT_FAILURE;
214
      }
215
    }
216
  }
217
  
218
  /* Write command to FIFO */
219
  if(not interrupted_by_signal){
220
    int fifo_fd = open("/dev/.initramfs/usplash_fifo", O_WRONLY);
221
    if(fifo_fd == -1){
222
      perror("open");
223
      free(prompt);
224
      return EXIT_FAILURE;
225
    }
226
    char *command;
227
    ret = asprintf(&command, "INPUTQUIET %s", prompt);
228
    if(ret == -1){
229
      perror("asprintf");
230
      free(prompt);
231
      return EXIT_FAILURE;
232
    }
233
    free(prompt);
234
    
235
    size_t command_len = (size_t)ret + 1;
236
    size_t written = 0;
237
    while(not interrupted_by_signal and written < command_len){
238
      ret = write(fifo_fd, command + written, command_len - written);
239
      if(ret == -1){
240
	if(interrupted_by_signal){
241
	  break;
242
	}
243
	perror("write");
244
	if(written == 0){
245
	  free(command);
246
	  return EXIT_FAILURE;
247
	}
248
	an_error_occured = true;
249
	break;
250
      }
251
      written += (size_t)ret;
252
    }
253
    ret = close(fifo_fd);
254
    if(ret == -1 and not interrupted_by_signal){
255
      an_error_occured = true;
256
    }
257
    free(command);
258
  }else{
259
    free(prompt);
260
  }
261
  
262
  {
263
    char *buf = NULL;
264
    size_t buf_len = 0;
265
    
266
    /* Read from FIFO */
267
    if(not interrupted_by_signal and not an_error_occured){
268
      int fifo_fd = open("/dev/.initramfs/usplash_outfifo", O_RDONLY);
269
      if(fifo_fd == -1 and not interrupted_by_signal){
270
	perror("open");
271
	return EXIT_FAILURE;
272
      }
273
      size_t buf_allocated = 0;
274
      const int blocksize = 1024;
275
      do{
276
	if(buf_len + blocksize > buf_allocated){
277
	  char *tmp = realloc(buf, buf_allocated + blocksize);
278
	  if(tmp == NULL){
279
	    perror("realloc");
280
	    an_error_occured = true;
281
	    break;
282
	  }
283
	  buf = tmp;
284
	  buf_allocated += blocksize;
285
	}
286
	sret = read(fifo_fd, buf + buf_len, buf_allocated - buf_len);
287
	if(sret == -1){
288
	  perror("read");
289
	  an_error_occured = true;
290
	  break;
291
	}
292
	buf_len += (size_t)sret;
293
      }while(not interrupted_by_signal and sret != 0);
294
      close(fifo_fd);
295
    }
296
  
297
    /* Print password to stdout */
298
    if(not interrupted_by_signal and not an_error_occured){
299
      size_t written = 0;
300
      do{
301
	sret = write(STDOUT_FILENO, buf + written, buf_len - written);
302
	if(sret == -1 and not interrupted_by_signal){
303
	  perror("write");
304
	  an_error_occured = true;
305
	  break;
306
	}
307
	written += (size_t)sret;
308
      }while(written < buf_len);
309
      if(not interrupted_by_signal and not an_error_occured){
310
	return EXIT_SUCCESS;
311
      }
312
    }
313
  }
314
  
315
  kill(usplash_pid, SIGTERM);
316
  
317
  int cmdline_argc = 0;
318
  char **cmdline_argv = malloc(sizeof(char *));
319
  /* Create argv and argc for new usplash*/
320
  {
321
    ptrdiff_t position = 0;
322
    while((size_t)position < cmdline_len){
323
      char **tmp = realloc(cmdline_argv,
324
			   (sizeof(char *) * (size_t)(cmdline_argc + 2)));
325
      if(tmp == NULL){
326
	perror("realloc");
327
	free(cmdline_argv);
328
	return EXIT_FAILURE;
329
      }
330
      cmdline_argv = tmp;
331
      cmdline_argv[cmdline_argc] = cmdline + position;
332
      cmdline_argc++;
333
      position = (char *)rawmemchr(cmdline + position, '\0')
334
	- cmdline + 1;
335
    }
336
    cmdline_argv[cmdline_argc] = NULL;
337
  }
338
  pid_t new_usplash_pid = fork();
339
  if(new_usplash_pid == 0){
340
    /* Child; will become new usplash process */
341
    while(kill(usplash_pid, 0)){
342
      sleep(2);
343
      kill(usplash_pid, SIGKILL);
344
      sleep(1);
345
    }
346
    ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
347
    if(ret == -1){
348
      perror("dup2");
349
      _exit(EXIT_FAILURE);
350
    }
351
    execv("/sbin/usplash", cmdline_argv);
352
  }
353
  
354
  return EXIT_FAILURE;
355
}