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