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