/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: Teddy Hogeborn
  • Date: 2008-08-02 10:48:24 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080802104824-fx0miwp9o4g9r31e
* plugbasedclient.c (struct process): New fields "eof", "completed",
                                      and "status".
  (handle_sigchld): New function.
  (main): Initialize "dir" to NULL to only closedir() it if necessary.
          Move "process_list" to be a global variable to be accessible
          by "handle_sigchld".  Make "handle_sigchld" handle SIGCHLD.
          Remove redundant check for NULL "dir".  Free "filename" when
          no longer used.  Block SIGCHLD around fork()/exec().
          Restore normal signals in child.  Only loop while running
          processes exist.  Print process buffer when the process is
          done and it has emitted EOF, not when it only emits EOF.
          Remove processes from list which exit non-cleanly.  In
          cleaning up, closedir() if necessary.  Bug fix: set next
          pointer correctly when freeing process list.

* plugins.d/passprompt.c (main): Do not ignore SIGQUIT.

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