/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/splashy.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
 
 * Splashy - Read a password from splashy 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 <mandos@fukt.bsnet.se>.
23
 
 */
24
 
 
25
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
26
 
#include <signal.h>             /* sig_atomic_t, struct sigaction,
27
 
                                   sigemptyset(), sigaddset(), SIGINT,
28
 
                                   SIGHUP, SIGTERM, sigaction,
29
 
                                   SIG_IGN, kill(), SIGKILL */
30
 
#include <stddef.h>             /* NULL */
31
 
#include <stdlib.h>             /* getenv() */
32
 
#include <stdio.h>              /* asprintf() */
33
 
#include <stdlib.h>             /* EXIT_FAILURE, free(),
34
 
                                   EXIT_SUCCESS */
35
 
#include <sys/types.h>          /* pid_t, DIR, struct dirent,
36
 
                                   ssize_t */
37
 
#include <dirent.h>             /* opendir(), readdir(), closedir() */
38
 
#include <inttypes.h>           /* intmax_t, strtoimax() */
39
 
#include <sys/stat.h>           /* struct stat, lstat(), S_ISLNK */
40
 
#include <iso646.h>             /* not, or, and */
41
 
#include <unistd.h>             /* readlink(), fork(), execl(),
42
 
                                   sleep(), dup2() STDERR_FILENO,
43
 
                                   STDOUT_FILENO, _exit(),
44
 
                                   pause() */
45
 
#include <string.h>             /* memcmp() */
46
 
#include <errno.h>              /* errno, EACCES, ENOTDIR, ELOOP,
47
 
                                   ENOENT, ENAMETOOLONG, EMFILE,
48
 
                                   ENFILE, ENOMEM, ENOEXEC, EINVAL,
49
 
                                   E2BIG, EFAULT, EIO, ETXTBSY,
50
 
                                   EISDIR, ELIBBAD, EPERM, EINTR,
51
 
                                   ECHILD */
52
 
#include <error.h>              /* error() */
53
 
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
54
 
                                   WEXITSTATUS() */
55
 
#include <sysexits.h>           /* EX_OSERR, EX_OSFILE,
56
 
                                   EX_UNAVAILABLE */
57
 
 
58
 
sig_atomic_t interrupted_by_signal = 0;
59
 
int signal_received;
60
 
 
61
 
static void termination_handler(int signum){
62
 
  if(interrupted_by_signal){
63
 
    return;
64
 
  }
65
 
  interrupted_by_signal = 1;
66
 
  signal_received = signum;
67
 
}
68
 
 
69
 
int main(__attribute__((unused))int argc,
70
 
         __attribute__((unused))char **argv){
71
 
  int ret = 0;
72
 
  char *prompt = NULL;
73
 
  DIR *proc_dir = NULL;
74
 
  pid_t splashy_pid = 0;
75
 
  pid_t splashy_command_pid = 0;
76
 
  int exitstatus = EXIT_FAILURE;
77
 
  
78
 
  /* Create prompt string */
79
 
  {
80
 
    const char *const cryptsource = getenv("cryptsource");
81
 
    const char *const crypttarget = getenv("crypttarget");
82
 
    const char *const prompt_start = "getpass "
83
 
      "Enter passphrase to unlock the disk";
84
 
    
85
 
    if(cryptsource == NULL){
86
 
      if(crypttarget == NULL){
87
 
        ret = asprintf(&prompt, "%s: ", prompt_start);
88
 
      } else {
89
 
        ret = asprintf(&prompt, "%s (%s): ", prompt_start,
90
 
                       crypttarget);
91
 
      }
92
 
    } else {
93
 
      if(crypttarget == NULL){
94
 
        ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource);
95
 
      } else {
96
 
        ret = asprintf(&prompt, "%s %s (%s): ", prompt_start,
97
 
                       cryptsource, crypttarget);
98
 
      }
99
 
    }
100
 
    if(ret == -1){
101
 
      prompt = NULL;
102
 
      exitstatus = EX_OSERR;
103
 
      goto failure;
104
 
    }
105
 
  }
106
 
  
107
 
  /* Find splashy process */
108
 
  {
109
 
    const char splashy_name[] = "/sbin/splashy";
110
 
    proc_dir = opendir("/proc");
111
 
    if(proc_dir == NULL){
112
 
      int e = errno;
113
 
      error(0, errno, "opendir");
114
 
      switch(e){
115
 
      case EACCES:
116
 
      case ENOTDIR:
117
 
      case ELOOP:
118
 
      case ENOENT:
119
 
      default:
120
 
        exitstatus = EX_OSFILE;
121
 
        break;
122
 
      case ENAMETOOLONG:
123
 
      case EMFILE:
124
 
      case ENFILE:
125
 
      case ENOMEM:
126
 
        exitstatus = EX_OSERR;
127
 
        break;
128
 
      }
129
 
      goto failure;
130
 
    }
131
 
    for(struct dirent *proc_ent = readdir(proc_dir);
132
 
        proc_ent != NULL;
133
 
        proc_ent = readdir(proc_dir)){
134
 
      pid_t pid;
135
 
      {
136
 
        intmax_t tmpmax;
137
 
        char *tmp;
138
 
        errno = 0;
139
 
        tmpmax = strtoimax(proc_ent->d_name, &tmp, 10);
140
 
        if(errno != 0 or tmp == proc_ent->d_name or *tmp != '\0'
141
 
           or tmpmax != (pid_t)tmpmax){
142
 
          /* Not a process */
143
 
          continue;
144
 
        }
145
 
        pid = (pid_t)tmpmax;
146
 
      }
147
 
      /* Find the executable name by doing readlink() on the
148
 
         /proc/<pid>/exe link */
149
 
      char exe_target[sizeof(splashy_name)];
150
 
      ssize_t sret;
151
 
      {
152
 
        char *exe_link;
153
 
        ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
154
 
        if(ret == -1){
155
 
          error(0, errno, "asprintf");
156
 
          exitstatus = EX_OSERR;
157
 
          goto failure;
158
 
        }
159
 
        
160
 
        /* Check that it refers to a symlink owned by root:root */
161
 
        struct stat exe_stat;
162
 
        ret = lstat(exe_link, &exe_stat);
163
 
        if(ret == -1){
164
 
          if(errno == ENOENT){
165
 
            free(exe_link);
166
 
            continue;
167
 
          }
168
 
          int e = errno;
169
 
          error(0, errno, "lstat");
170
 
          free(exe_link);
171
 
          switch(e){
172
 
          case EACCES:
173
 
          case ENOTDIR:
174
 
          case ELOOP:
175
 
          default:
176
 
            exitstatus = EX_OSFILE;
177
 
            break;
178
 
          case ENAMETOOLONG:
179
 
            exitstatus = EX_OSERR;
180
 
            break;
181
 
          }
182
 
          goto failure;
183
 
        }
184
 
        if(not S_ISLNK(exe_stat.st_mode)
185
 
           or exe_stat.st_uid != 0
186
 
           or exe_stat.st_gid != 0){
187
 
          free(exe_link);
188
 
          continue;
189
 
        }
190
 
        
191
 
        sret = readlink(exe_link, exe_target, sizeof(exe_target));
192
 
        free(exe_link);
193
 
      }
194
 
      if((sret == ((ssize_t)sizeof(exe_target)-1))
195
 
         and (memcmp(splashy_name, exe_target,
196
 
                     sizeof(exe_target)-1) == 0)){
197
 
        splashy_pid = pid;
198
 
        break;
199
 
      }
200
 
    }
201
 
    closedir(proc_dir);
202
 
    proc_dir = NULL;
203
 
  }
204
 
  if(splashy_pid == 0){
205
 
    exitstatus = EX_UNAVAILABLE;
206
 
    goto failure;
207
 
  }
208
 
  
209
 
  /* Set up the signal handler */
210
 
  {
211
 
    struct sigaction old_action,
212
 
      new_action = { .sa_handler = termination_handler,
213
 
                     .sa_flags = 0 };
214
 
    sigemptyset(&new_action.sa_mask);
215
 
    ret = sigaddset(&new_action.sa_mask, SIGINT);
216
 
    if(ret == -1){
217
 
      error(0, errno, "sigaddset");
218
 
      exitstatus = EX_OSERR;
219
 
      goto failure;
220
 
    }
221
 
    ret = sigaddset(&new_action.sa_mask, SIGHUP);
222
 
    if(ret == -1){
223
 
      error(0, errno, "sigaddset");
224
 
      exitstatus = EX_OSERR;
225
 
      goto failure;
226
 
    }
227
 
    ret = sigaddset(&new_action.sa_mask, SIGTERM);
228
 
    if(ret == -1){
229
 
      error(0, errno, "sigaddset");
230
 
      exitstatus = EX_OSERR;
231
 
      goto failure;
232
 
    }
233
 
    ret = sigaction(SIGINT, NULL, &old_action);
234
 
    if(ret == -1){
235
 
      error(0, errno, "sigaction");
236
 
      exitstatus = EX_OSERR;
237
 
      goto failure;
238
 
    }
239
 
    if(old_action.sa_handler != SIG_IGN){
240
 
      ret = sigaction(SIGINT, &new_action, NULL);
241
 
      if(ret == -1){
242
 
        error(0, errno, "sigaction");
243
 
        exitstatus = EX_OSERR;
244
 
        goto failure;
245
 
      }
246
 
    }
247
 
    ret = sigaction(SIGHUP, NULL, &old_action);
248
 
    if(ret == -1){
249
 
      error(0, errno, "sigaction");
250
 
      exitstatus = EX_OSERR;
251
 
      goto failure;
252
 
    }
253
 
    if(old_action.sa_handler != SIG_IGN){
254
 
      ret = sigaction(SIGHUP, &new_action, NULL);
255
 
      if(ret == -1){
256
 
        error(0, errno, "sigaction");
257
 
        exitstatus = EX_OSERR;
258
 
        goto failure;
259
 
      }
260
 
    }
261
 
    ret = sigaction(SIGTERM, NULL, &old_action);
262
 
    if(ret == -1){
263
 
      error(0, errno, "sigaction");
264
 
      exitstatus = EX_OSERR;
265
 
      goto failure;
266
 
    }
267
 
    if(old_action.sa_handler != SIG_IGN){
268
 
      ret = sigaction(SIGTERM, &new_action, NULL);
269
 
      if(ret == -1){
270
 
        error(0, errno, "sigaction");
271
 
        exitstatus = EX_OSERR;
272
 
        goto failure;
273
 
      }
274
 
    }
275
 
  }
276
 
  
277
 
  if(interrupted_by_signal){
278
 
    goto failure;
279
 
  }
280
 
  
281
 
  /* Fork off the splashy command to prompt for password */
282
 
  splashy_command_pid = fork();
283
 
  if(splashy_command_pid != 0 and interrupted_by_signal){
284
 
    goto failure;
285
 
  }
286
 
  if(splashy_command_pid == -1){
287
 
    error(0, errno, "fork");
288
 
    exitstatus = EX_OSERR;
289
 
    goto failure;
290
 
  }
291
 
  /* Child */
292
 
  if(splashy_command_pid == 0){
293
 
    if(not interrupted_by_signal){
294
 
      const char splashy_command[] = "/sbin/splashy_update";
295
 
      execl(splashy_command, splashy_command, prompt, (char *)NULL);
296
 
      int e = errno;
297
 
      error(0, errno, "execl");
298
 
      switch(e){
299
 
      case EACCES:
300
 
      case ENOENT:
301
 
      case ENOEXEC:
302
 
      case EINVAL:
303
 
        _exit(EX_UNAVAILABLE);
304
 
      case ENAMETOOLONG:
305
 
      case E2BIG:
306
 
      case ENOMEM:
307
 
      case EFAULT:
308
 
      case EIO:
309
 
      case EMFILE:
310
 
      case ENFILE:
311
 
      case ETXTBSY:
312
 
      default:
313
 
        _exit(EX_OSERR);
314
 
      case ENOTDIR:
315
 
      case ELOOP:
316
 
      case EISDIR:
317
 
      case ELIBBAD:
318
 
      case EPERM:
319
 
        _exit(EX_OSFILE);
320
 
      }
321
 
    }
322
 
    free(prompt);
323
 
    _exit(EXIT_FAILURE);
324
 
  }
325
 
  
326
 
  /* Parent */
327
 
  free(prompt);
328
 
  prompt = NULL;
329
 
  
330
 
  if(interrupted_by_signal){
331
 
    goto failure;
332
 
  }
333
 
  
334
 
  /* Wait for command to complete */
335
 
  {
336
 
    int status;
337
 
    do {
338
 
      ret = waitpid(splashy_command_pid, &status, 0);
339
 
    } while(ret == -1 and errno == EINTR
340
 
            and not interrupted_by_signal);
341
 
    if(interrupted_by_signal){
342
 
      goto failure;
343
 
    }
344
 
    if(ret == -1){
345
 
      error(0, errno, "waitpid");
346
 
      if(errno == ECHILD){
347
 
        splashy_command_pid = 0;
348
 
      }
349
 
    } else {
350
 
      /* The child process has exited */
351
 
      splashy_command_pid = 0;
352
 
      if(WIFEXITED(status) and WEXITSTATUS(status) == 0){
353
 
        return EXIT_SUCCESS;
354
 
      }
355
 
    }
356
 
  }
357
 
  
358
 
 failure:
359
 
  
360
 
  free(prompt);
361
 
  
362
 
  if(proc_dir != NULL){
363
 
    TEMP_FAILURE_RETRY(closedir(proc_dir));
364
 
  }
365
 
  
366
 
  if(splashy_command_pid != 0){
367
 
    TEMP_FAILURE_RETRY(kill(splashy_command_pid, SIGTERM));
368
 
    
369
 
    TEMP_FAILURE_RETRY(kill(splashy_pid, SIGTERM));
370
 
    sleep(2);
371
 
    while(TEMP_FAILURE_RETRY(kill(splashy_pid, 0)) == 0){
372
 
      TEMP_FAILURE_RETRY(kill(splashy_pid, SIGKILL));
373
 
      sleep(1);
374
 
    }
375
 
    pid_t new_splashy_pid = (pid_t)TEMP_FAILURE_RETRY(fork());
376
 
    if(new_splashy_pid == 0){
377
 
      /* Child; will become new splashy process */
378
 
      
379
 
      /* Make the effective user ID (root) the only user ID instead of
380
 
         the real user ID (_mandos) */
381
 
      ret = setuid(geteuid());
382
 
      if(ret == -1){
383
 
        error(0, errno, "setuid");
384
 
      }
385
 
      
386
 
      setsid();
387
 
      ret = chdir("/");
388
 
      if(ret == -1){
389
 
        error(0, errno, "chdir");
390
 
      }
391
 
/*       if(fork() != 0){ */
392
 
/*      _exit(EXIT_SUCCESS); */
393
 
/*       } */
394
 
      ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace stdout */
395
 
      if(ret == -1){
396
 
        error(0, errno, "dup2");
397
 
        _exit(EX_OSERR);
398
 
      }
399
 
      
400
 
      execl("/sbin/splashy", "/sbin/splashy", "boot", (char *)NULL);
401
 
      {
402
 
        int e = errno;
403
 
        error(0, errno, "execl");
404
 
        switch(e){
405
 
        case EACCES:
406
 
        case ENOENT:
407
 
        case ENOEXEC:
408
 
        default:
409
 
          _exit(EX_UNAVAILABLE);
410
 
        case ENAMETOOLONG:
411
 
        case E2BIG:
412
 
        case ENOMEM:
413
 
          _exit(EX_OSERR);
414
 
        case ENOTDIR:
415
 
        case ELOOP:
416
 
          _exit(EX_OSFILE);
417
 
        }
418
 
      }
419
 
    }
420
 
  }
421
 
  
422
 
  if(interrupted_by_signal){
423
 
    struct sigaction signal_action;
424
 
    sigemptyset(&signal_action.sa_mask);
425
 
    signal_action.sa_handler = SIG_DFL;
426
 
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
427
 
                                            &signal_action, NULL));
428
 
    if(ret == -1){
429
 
      error(0, errno, "sigaction");
430
 
    }
431
 
    do {
432
 
      ret = raise(signal_received);
433
 
    } while(ret != 0 and errno == EINTR);
434
 
    if(ret != 0){
435
 
      error(0, errno, "raise");
436
 
      abort();
437
 
    }
438
 
    TEMP_FAILURE_RETRY(pause());
439
 
  }
440
 
  
441
 
  return exitstatus;
442
 
}