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