/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« 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 <https://www.fukt.bsnet.se/~belorn/> and
23
 
 * <https://www.fukt.bsnet.se/~teddy/>.
24
 
 */
25
 
 
26
 
#define _GNU_SOURCE             /* asprintf() */
27
 
#include <signal.h>             /* sig_atomic_t, struct sigaction,
28
 
                                   sigemptyset(), sigaddset(), SIGINT,
29
 
                                   SIGHUP, SIGTERM, sigaction,
30
 
                                   SIG_IGN, kill(), SIGKILL */
31
 
#include <stddef.h>             /* NULL */
32
 
#include <stdlib.h>             /* getenv() */
33
 
#include <stdio.h>              /* asprintf(), perror() */
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
 
#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
 
  
65
 
  /* Create prompt string */
66
 
  char *prompt = NULL;
67
 
  {
68
 
    const char *const cryptsource = getenv("cryptsource");
69
 
    const char *const crypttarget = getenv("crypttarget");
70
 
    const char *const prompt_start = "getpass "
71
 
      "Enter passphrase to unlock the disk";
72
 
    
73
 
    if(cryptsource == NULL){
74
 
      if(crypttarget == NULL){
75
 
        ret = asprintf(&prompt, "%s: ", prompt_start);
76
 
      } else {
77
 
        ret = asprintf(&prompt, "%s (%s): ", prompt_start,
78
 
                       crypttarget);
79
 
      }
80
 
    } else {
81
 
      if(crypttarget == NULL){
82
 
        ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource);
83
 
      } else {
84
 
        ret = asprintf(&prompt, "%s %s (%s): ", prompt_start,
85
 
                       cryptsource, crypttarget);
86
 
      }
87
 
    }
88
 
    if(ret == -1){
89
 
      return EXIT_FAILURE;
90
 
    }
91
 
  }
92
 
  
93
 
  /* Find splashy process */
94
 
  pid_t splashy_pid = 0;
95
 
  {
96
 
    const char splashy_name[] = "/sbin/splashy";
97
 
    DIR *proc_dir = opendir("/proc");
98
 
    if(proc_dir == NULL){
99
 
      free(prompt);
100
 
      perror("opendir");
101
 
      return EXIT_FAILURE;
102
 
    }
103
 
    for(struct dirent *proc_ent = readdir(proc_dir);
104
 
        proc_ent != NULL;
105
 
        proc_ent = readdir(proc_dir)){
106
 
      pid_t pid;
107
 
      {
108
 
        intmax_t tmpmax;
109
 
        char *tmp;
110
 
        errno = 0;
111
 
        tmpmax = strtoimax(proc_ent->d_name, &tmp, 10);
112
 
        if(errno != 0 or tmp == proc_ent->d_name or *tmp != '\0'
113
 
           or tmpmax != (pid_t)tmpmax){
114
 
          /* Not a process */
115
 
          continue;
116
 
        }
117
 
        pid = (pid_t)tmpmax;
118
 
      }
119
 
      /* Find the executable name by doing readlink() on the
120
 
         /proc/<pid>/exe link */
121
 
      char exe_target[sizeof(splashy_name)];
122
 
      ssize_t sret;
123
 
      {
124
 
        char *exe_link;
125
 
        ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
126
 
        if(ret == -1){
127
 
          perror("asprintf");
128
 
          free(prompt);
129
 
          closedir(proc_dir);
130
 
          return EXIT_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
 
          free(prompt);
144
 
          closedir(proc_dir);
145
 
          return EXIT_FAILURE;
146
 
        }
147
 
        if(not S_ISLNK(exe_stat.st_mode)
148
 
           or exe_stat.st_uid != 0
149
 
           or exe_stat.st_gid != 0){
150
 
          free(exe_link);
151
 
          continue;
152
 
        }
153
 
        
154
 
        sret = readlink(exe_link, exe_target, sizeof(exe_target));
155
 
        free(exe_link);
156
 
      }
157
 
      if((sret == ((ssize_t)sizeof(exe_target)-1))
158
 
         and (memcmp(splashy_name, exe_target,
159
 
                     sizeof(exe_target)-1) == 0)){
160
 
        splashy_pid = pid;
161
 
        break;
162
 
      }
163
 
    }
164
 
    closedir(proc_dir);
165
 
  }
166
 
  if(splashy_pid == 0){
167
 
    free(prompt);
168
 
    return EXIT_FAILURE;
169
 
  }
170
 
  
171
 
  /* Set up the signal handler */
172
 
  {
173
 
    struct sigaction old_action,
174
 
      new_action = { .sa_handler = termination_handler,
175
 
                     .sa_flags = 0 };
176
 
    sigemptyset(&new_action.sa_mask);
177
 
    sigaddset(&new_action.sa_mask, SIGINT);
178
 
    if(ret == -1){
179
 
      perror("sigaddset");
180
 
      free(prompt);
181
 
      return EXIT_FAILURE;
182
 
    }
183
 
    sigaddset(&new_action.sa_mask, SIGHUP);
184
 
    if(ret == -1){
185
 
      perror("sigaddset");
186
 
      free(prompt);
187
 
      return EXIT_FAILURE;
188
 
    }
189
 
    sigaddset(&new_action.sa_mask, SIGTERM);
190
 
    if(ret == -1){
191
 
      perror("sigaddset");
192
 
      free(prompt);
193
 
      return EXIT_FAILURE;
194
 
    }
195
 
    ret = sigaction(SIGINT, NULL, &old_action);
196
 
    if(ret == -1){
197
 
      perror("sigaction");
198
 
      free(prompt);
199
 
      return EXIT_FAILURE;
200
 
    }
201
 
    if(old_action.sa_handler != SIG_IGN){
202
 
      ret = sigaction(SIGINT, &new_action, NULL);
203
 
      if(ret == -1){
204
 
        perror("sigaction");
205
 
        free(prompt);
206
 
        return EXIT_FAILURE;
207
 
      }
208
 
    }
209
 
    ret = sigaction(SIGHUP, NULL, &old_action);
210
 
    if(ret == -1){
211
 
      perror("sigaction");
212
 
      free(prompt);
213
 
      return EXIT_FAILURE;
214
 
    }
215
 
    if(old_action.sa_handler != SIG_IGN){
216
 
      ret = sigaction(SIGHUP, &new_action, NULL);
217
 
      if(ret == -1){
218
 
        perror("sigaction");
219
 
        free(prompt);
220
 
        return EXIT_FAILURE;
221
 
      }
222
 
    }
223
 
    ret = sigaction(SIGTERM, NULL, &old_action);
224
 
    if(ret == -1){
225
 
      perror("sigaction");
226
 
      free(prompt);
227
 
      return EXIT_FAILURE;
228
 
    }
229
 
    if(old_action.sa_handler != SIG_IGN){
230
 
      ret = sigaction(SIGTERM, &new_action, NULL);
231
 
      if(ret == -1){
232
 
        perror("sigaction");
233
 
        free(prompt);
234
 
        return EXIT_FAILURE;
235
 
      }
236
 
    }
237
 
  }
238
 
  
239
 
  /* Fork off the splashy command to prompt for password */
240
 
  pid_t splashy_command_pid = 0;
241
 
  if(not interrupted_by_signal){
242
 
    splashy_command_pid = fork();
243
 
    if(splashy_command_pid == -1){
244
 
      if(not interrupted_by_signal){
245
 
        perror("fork");
246
 
      }
247
 
      return EXIT_FAILURE;
248
 
    }
249
 
    /* Child */
250
 
    if(splashy_command_pid == 0){
251
 
      const char splashy_command[] = "/sbin/splashy_update";
252
 
      ret = execl(splashy_command, splashy_command, prompt,
253
 
                  (char *)NULL);
254
 
      if(not interrupted_by_signal){
255
 
        perror("execl");
256
 
      }
257
 
      free(prompt);
258
 
      _exit(EXIT_FAILURE);
259
 
    }
260
 
  }
261
 
  
262
 
  /* Parent */
263
 
  free(prompt);
264
 
  
265
 
  /* Wait for command to complete */
266
 
  if(not interrupted_by_signal and splashy_command_pid != 0){
267
 
    int status;
268
 
    ret = waitpid(splashy_command_pid, &status, 0);
269
 
    if(ret == -1){
270
 
      if(errno != EINTR){
271
 
        perror("waitpid");
272
 
      }
273
 
      if(errno == ECHILD){
274
 
        splashy_command_pid = 0;
275
 
      }
276
 
    } else {
277
 
      /* The child process has exited */
278
 
      splashy_command_pid = 0;
279
 
      if(not interrupted_by_signal and WIFEXITED(status)
280
 
         and WEXITSTATUS(status)==0){
281
 
        return EXIT_SUCCESS;
282
 
      }
283
 
    }
284
 
  }
285
 
  kill(splashy_pid, SIGTERM);
286
 
  if(interrupted_by_signal and splashy_command_pid != 0){
287
 
    kill(splashy_command_pid, SIGTERM);
288
 
  }
289
 
  sleep(2);
290
 
  while(kill(splashy_pid, 0) == 0){
291
 
    kill(splashy_pid, SIGKILL);
292
 
    sleep(1);
293
 
  }
294
 
  pid_t new_splashy_pid = fork();
295
 
  if(new_splashy_pid == 0){
296
 
    /* Child; will become new splashy process */
297
 
    
298
 
    /* Make the effective user ID (root) the only user ID instead of
299
 
       the real user ID (_mandos) */
300
 
    ret = setuid(geteuid());
301
 
    if(ret == -1){
302
 
      perror("setuid");
303
 
    }
304
 
    
305
 
    setsid();
306
 
    ret = chdir("/");
307
 
/*     if(fork() != 0){ */
308
 
/*       _exit(EXIT_SUCCESS); */
309
 
/*     } */
310
 
    ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
311
 
    if(ret == -1){
312
 
      perror("dup2");
313
 
      _exit(EXIT_FAILURE);
314
 
    }
315
 
    
316
 
    execl("/sbin/splashy", "/sbin/splashy", "boot", (char *)NULL);
317
 
    if(not interrupted_by_signal){
318
 
      perror("execl");
319
 
    }
320
 
    _exit(EXIT_FAILURE);
321
 
  }
322
 
  
323
 
  return EXIT_FAILURE;
324
 
}