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