/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-12-10 01:26:02 UTC
  • mfrom: (237.1.2 mandos)
  • Revision ID: teddy@fukt.bsnet.se-20081210012602-vhz3h75xkj24t340
First version of a somewhat complete D-Bus server interface.  Also
change user/group name to "_mandos".

* debian/mandos.postinst: Rename old "mandos" user and group to
                          "_mandos"; create "_mandos" user and group
                          if none exist.
* debian/mandos-client.postinst: - '' -

* initramfs-tools-hook: Try "_mandos" before "mandos" as user and
                        group name.

* mandos (_datetime_to_dbus_struct): New; was previously local.
  (Client.started): Renamed to "last_started".  All users changed.
  (Client.started): New; boolean.
  (Client.dbus_object_path): New.
  (Client.check_command): Renamed to "checker_command".  All users
                          changed.
  (Client.__init__): Set and use "self.dbus_object_path".  Set
                     "self.started".
  (Client.start): Update "self.started".  Emit "self.PropertyChanged"
                  signals for both "started" and "last_started".
  (Client.stop): Update "self.started".  Emit "self.PropertyChanged"
                 signal for "started".
  (Client.checker_callback): Take additional "command" argument.  All
                             callers changed. Emit
                             "self.PropertyChanged" signal.
  (Client.bump_timeout): Emit "self.PropertyChanged" signal for
                         "last_checked_ok".
  (Client.start_checker): Emit "self.PropertyChanged" signal for
                          "checker_running".
  (Client.stop_checker): Emit "self.PropertyChanged" signal for
                         "checker_running".
  (Client.still_valid): Bug fix: use "getattr(self, started, False)"
                        instead of "self.started" in case this client
                        object is so new that the "started" attribute
                        has not been created yet.
  (Client.IntervalChanged, Client.CheckerIsRunning, Client.GetChecker,
  Client.GetCreated, Client.GetFingerprint, Client.GetHost,
  Client.GetInterval, Client.GetName, Client.GetStarted,
  Client.GetTimeout, Client.StateChanged, Client.TimeoutChanged):
  Removed; all callers changed.
  (Client.CheckerCompleted): Add "condition" and "command" arguments.
                             All callers changed.
  (Client.GetAllProperties, Client.PropertyChanged): New.
  (Client.StillValid): Renamed to "IsStillValid".
  (Client.StartChecker): Changed to its own function to avoid the
                         return value from "Client.start_checker()".
  (Client.Stop): Changed to its own function to avoid the return value
                 from "Client.stop()".
  (main): Try "_mandos" before "mandos" as user and group name.
          Removed inner function "remove_from_clients".  New inner
          class "MandosServer".

Show diffs side-by-side

added added

removed removed

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