/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-16 03:29:08 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080816032908-ihw7c05r2mnyk389
Add feature to specify custom environment variables for plugins.

* plugin-runner.c (plugin): New members "environ" and "envc" to
                            contain possible custom environment.
  (getplugin): Return NULL on failure instead of doing exit(); all
               callers changed.
  (add_to_char_array): New helper function for "add_argument" and
                       "add_environment".
  (addargument): Renamed to "add_argument".  Return bool.  Call
                 "add_to_char_array" to actually do things.
  (add_environment): New; analogous to "add_argument".
  (addcustomargument): Renamed to "add_to_argv" to avoid confusion
                       with "add_argument".
  (main): New options "--global-envs" and "--envs-for" to specify
          custom environment for plugins.  Print environment for
          plugins in debug mode.  Use asprintf instead of strcpy and
          strcat.  Use execve() for plugins with custom environments.
          Free environment for plugin when freeing plugin list.

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