/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 Hogeborn
  • Date: 2008-08-29 05:53:59 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080829055359-wkdasnyxtylmnxus
* mandos.xml (EXAMPLE): Replaced all occurences of command name with
                        "&COMMANDNAME;".

* plugins.d/password-prompt.c (main): Improved some documentation
                                      strings.  Do perror() of
                                      tcgetattr() fails.  Add debug
                                      output if interrupted by signal.
                                      Loop over write() instead of
                                      using fwrite() when outputting
                                      password.  Add debug output if
                                      getline() returns 0, unless it
                                      was caused by a signal.  Add
                                      exit status code to debug
                                      output.

* plugins.d/password-prompt.xml: Changed all single quotes to double
                                 quotes for consistency.  Removed
                                 <?xml-stylesheet>.
  (ENTITY TIMESTAMP): New.  Automatically updated by Emacs time-stamp
                      by using Emacs local variables.
  (/refentry/refentryinfo/title): Changed to "Mandos Manual".
  (/refentry/refentryinfo/productname): Changed to "Mandos".
  (/refentry/refentryinfo/date): New; set to "&TIMESTAMP;".
  (/refentry/refentryinfo/copyright): Split copyright holders.
  (/refentry/refnamediv/refpurpose): Improved wording.
  (SYNOPSIS): Fix to use correct markup.  Add short options.
  (DESCRIPTION, OPTIONS): Improved wording.
  (OPTIONS): Improved wording.  Use more correct markup.  Document
             short options.
  (EXIT STATUS): Add text.
  (ENVIRONMENT): Document use of "cryptsource" and "crypttarget".
  (FILES): REMOVED.
  (BUGS): Add text.
  (EXAMPLE): Added some examples.
  (SECURITY): Added text.
  (SEE ALSO): Remove reference to mandos(8).  Add reference to
              crypttab(5).

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 © 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
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 <https://www.fukt.bsnet.se/~belorn/> and
23
 
 * <https://www.fukt.bsnet.se/~teddy/>.
24
 
 */
25
 
 
26
 
#define _GNU_SOURCE             /* asprintf() */
27
 
#include <signal.h>             /* sig_atomic_t, struct sigaction,
28
 
                                   sigemptyset(), sigaddset(), SIGINT,
29
 
                                   SIGHUP, SIGTERM, sigaction(),
30
 
                                   SIG_IGN, kill(), SIGKILL */
31
 
#include <stdbool.h>            /* bool, false, true */
32
 
#include <fcntl.h>              /* open(), O_WRONLY, O_RDONLY */
33
 
#include <iso646.h>             /* and, or, not*/
34
 
#include <errno.h>              /* errno, EINTR */
35
 
#include <sys/types.h>          /* size_t, ssize_t, pid_t, DIR, struct
36
 
                                   dirent */
37
 
#include <stddef.h>             /* NULL */
38
 
#include <string.h>             /* strlen(), memcmp() */
39
 
#include <stdio.h>              /* asprintf(), perror() */
40
 
#include <unistd.h>             /* close(), write(), readlink(),
41
 
                                   read(), STDOUT_FILENO, sleep(),
42
 
                                   fork(), setuid(), geteuid(),
43
 
                                   setsid(), chdir(), dup2(),
44
 
                                   STDERR_FILENO, execv() */
45
 
#include <stdlib.h>             /* free(), EXIT_FAILURE, strtoul(),
46
 
                                   realloc(), EXIT_SUCCESS, malloc(),
47
 
                                   _exit() */
48
 
#include <stdlib.h>             /* getenv() */
49
 
#include <dirent.h>             /* opendir(), readdir(), closedir() */
50
 
#include <sys/stat.h>           /* struct stat, lstat(), S_ISLNK */
51
 
 
52
 
sig_atomic_t interrupted_by_signal = 0;
53
 
 
54
 
static void termination_handler(__attribute__((unused))int signum){
55
 
  interrupted_by_signal = 1;
56
 
}
57
 
 
58
 
static bool usplash_write(const char *cmd, const char *arg){
59
 
  /* 
60
 
   * usplash_write("TIMEOUT", "15") will write "TIMEOUT 15\0"
61
 
   * usplash_write("PULSATE", NULL) will write "PULSATE\0"
62
 
   * SEE ALSO
63
 
   *         usplash_write(8)
64
 
   */
65
 
  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)){
70
 
      return false;
71
 
    }
72
 
  }while(fifo_fd == -1);
73
 
  
74
 
  const char *cmd_line;
75
 
  size_t cmd_line_len;
76
 
  char *cmd_line_alloc = NULL;
77
 
  if(arg == NULL){
78
 
    cmd_line = cmd;
79
 
    cmd_line_len = strlen(cmd);
80
 
  }else{
81
 
    do{
82
 
      ret = asprintf(&cmd_line_alloc, "%s %s", cmd, arg);
83
 
      if(ret == -1 and (errno != EINTR or interrupted_by_signal)){
84
 
        int e = errno;
85
 
        close(fifo_fd);
86
 
        errno = e;
87
 
        return false;
88
 
      }
89
 
    }while(ret == -1);
90
 
    cmd_line = cmd_line_alloc;
91
 
    cmd_line_len = (size_t)ret + 1;
92
 
  }
93
 
  
94
 
  size_t written = 0;
95
 
  ssize_t sret = 0;
96
 
  while(not interrupted_by_signal and written < cmd_line_len){
97
 
    sret = write(fifo_fd, cmd_line + written,
98
 
                 cmd_line_len - written);
99
 
    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
 
      }
109
 
    }
110
 
    written += (size_t)sret;
111
 
  }
112
 
  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
 
  }
122
 
  return true;
123
 
}
124
 
 
125
 
int main(__attribute__((unused))int argc,
126
 
         __attribute__((unused))char **argv){
127
 
  int ret = 0;
128
 
  ssize_t sret;
129
 
  bool an_error_occured = false;
130
 
  
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
 
    }
156
 
  }
157
 
  
158
 
  /* Find usplash process */
159
 
  pid_t usplash_pid = 0;
160
 
  char *cmdline = NULL;
161
 
  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
 
  }
278
 
  if(usplash_pid == 0){
279
 
    free(prompt);
280
 
    return EXIT_FAILURE;
281
 
  }
282
 
  
283
 
  /* Set up the signal handler */
284
 
  {
285
 
    struct sigaction old_action,
286
 
      new_action = { .sa_handler = termination_handler,
287
 
                     .sa_flags = 0 };
288
 
    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);
292
 
    ret = sigaction(SIGINT, NULL, &old_action);
293
 
    if(ret == -1){
294
 
      perror("sigaction");
295
 
      free(prompt);
296
 
      return EXIT_FAILURE;
297
 
    }
298
 
    if(old_action.sa_handler != SIG_IGN){
299
 
      ret = sigaction(SIGINT, &new_action, NULL);
300
 
      if(ret == -1){
301
 
        perror("sigaction");
302
 
        free(prompt);
303
 
        return EXIT_FAILURE;
304
 
      }
305
 
    }
306
 
    ret = sigaction(SIGHUP, NULL, &old_action);
307
 
    if(ret == -1){
308
 
      perror("sigaction");
309
 
      free(prompt);
310
 
      return EXIT_FAILURE;
311
 
    }
312
 
    if(old_action.sa_handler != SIG_IGN){
313
 
      ret = sigaction(SIGHUP, &new_action, NULL);
314
 
      if(ret == -1){
315
 
        perror("sigaction");
316
 
        free(prompt);
317
 
        return EXIT_FAILURE;
318
 
      }
319
 
    }
320
 
    ret = sigaction(SIGTERM, NULL, &old_action);
321
 
    if(ret == -1){
322
 
      perror("sigaction");
323
 
      free(prompt);
324
 
      return EXIT_FAILURE;
325
 
    }
326
 
    if(old_action.sa_handler != SIG_IGN){
327
 
      ret = sigaction(SIGTERM, &new_action, NULL);
328
 
      if(ret == -1){
329
 
        perror("sigaction");
330
 
        free(prompt);
331
 
        return EXIT_FAILURE;
332
 
      }
333
 
    }
334
 
  }
335
 
  
336
 
  /* 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
 
  }
351
 
  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)));
465
 
      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
 
  }
477
 
  /* Kill old usplash */
478
 
  kill(usplash_pid, SIGTERM);
479
 
  sleep(2);
480
 
  while(kill(usplash_pid, 0) == 0){
481
 
    kill(usplash_pid, SIGKILL);
482
 
    sleep(1);
483
 
  }
484
 
  pid_t new_usplash_pid = fork();
485
 
  if(new_usplash_pid == 0){
486
 
    /* Child; will become new usplash process */
487
 
    
488
 
    /* Make the effective user ID (root) the only user ID instead of
489
 
       the real user ID (mandos) */
490
 
    ret = setuid(geteuid());
491
 
    if(ret == -1){
492
 
      perror("setuid");
493
 
    }
494
 
    
495
 
    setsid();
496
 
    ret = chdir("/");
497
 
/*     if(fork() != 0){ */
498
 
/*       _exit(EXIT_SUCCESS); */
499
 
/*     } */
500
 
    ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
501
 
    if(ret == -1){
502
 
      perror("dup2");
503
 
      _exit(EXIT_FAILURE);
504
 
    }
505
 
    
506
 
    execv(usplash_name, cmdline_argv);
507
 
    if(not interrupted_by_signal){
508
 
      perror("execv");
509
 
    }
510
 
    free(cmdline);
511
 
    free(cmdline_argv);
512
 
    _exit(EXIT_FAILURE);
513
 
  }
514
 
  free(cmdline);
515
 
  free(cmdline_argv);
516
 
  sleep(2);
517
 
  if(not usplash_write("PULSATE", NULL)
518
 
     and (errno != EINTR)){
519
 
    perror("usplash_write");
520
 
  }
521
 
  
522
 
  return EXIT_FAILURE;
523
 
}