/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to plugins.d/splashy.c

  • Committer: Björn Påhlsson
  • Date: 2008-07-20 02:52:20 UTC
  • Revision ID: belorn@braxen-20080720025220-r5u0388uy9iu23h6
Added following support:
Pluginbased client handler
rewritten Mandos client
       Avahi instead of udp server discovery
       openpgp encrypted key support
Passprompt stand alone application for direct console input
Added logging for Mandos server

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