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