/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

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
1
/*  -*- coding: utf-8 -*- */
2
2
/*
3
 
 * Splashy - Read a password from splashy and output it
 
3
 * Passprompt - Read a password from splashy and output it
4
4
 * 
5
 
 * Copyright © 2008,2009 Teddy Hogeborn
6
 
 * Copyright © 2008,2009 Björn Påhlsson
 
5
 * Copyright © 2008 Teddy Hogeborn
 
6
 * Copyright © 2008 Björn Påhlsson
7
7
 * 
8
8
 * This program is free software: you can redistribute it and/or
9
9
 * modify it under the terms of the GNU General Public License as
19
19
 * along with this program.  If not, see
20
20
 * <http://www.gnu.org/licenses/>.
21
21
 * 
22
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
22
 * Contact the authors at <https://www.fukt.bsnet.se/~belorn/> and
 
23
 * <https://www.fukt.bsnet.se/~teddy/>.
23
24
 */
24
25
 
25
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
 
26
#define _GNU_SOURCE             /* asprintf() */
26
27
#include <signal.h>             /* sig_atomic_t, struct sigaction,
27
28
                                   sigemptyset(), sigaddset(), SIGINT,
28
29
                                   SIGHUP, SIGTERM, sigaction,
30
31
#include <stddef.h>             /* NULL */
31
32
#include <stdlib.h>             /* getenv() */
32
33
#include <stdio.h>              /* asprintf(), perror() */
33
 
#include <stdlib.h>             /* EXIT_FAILURE, free(),
 
34
#include <stdlib.h>             /* EXIT_FAILURE, free(), strtoul(),
34
35
                                   EXIT_SUCCESS */
35
36
#include <sys/types.h>          /* pid_t, DIR, struct dirent,
36
37
                                   ssize_t */
37
38
#include <dirent.h>             /* opendir(), readdir(), closedir() */
38
 
#include <inttypes.h>           /* intmax_t, strtoimax() */
39
39
#include <sys/stat.h>           /* struct stat, lstat(), S_ISLNK */
40
40
#include <iso646.h>             /* not, or, and */
41
41
#include <unistd.h>             /* readlink(), fork(), execl(),
42
42
                                   sleep(), dup2() STDERR_FILENO,
43
 
                                   STDOUT_FILENO, _exit(),
44
 
                                   pause() */
 
43
                                   STDOUT_FILENO, _exit() */
45
44
#include <string.h>             /* memcmp() */
46
45
#include <errno.h>              /* errno */
47
46
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
48
47
                                   WEXITSTATUS() */
49
48
 
50
49
sig_atomic_t interrupted_by_signal = 0;
51
 
int signal_received;
52
50
 
53
 
static void termination_handler(int signum){
54
 
  if(interrupted_by_signal){
55
 
    return;
56
 
  }
 
51
static void termination_handler(__attribute__((unused))int signum){
57
52
  interrupted_by_signal = 1;
58
 
  signal_received = signum;
59
53
}
60
54
 
61
55
int main(__attribute__((unused))int argc,
62
56
         __attribute__((unused))char **argv){
63
57
  int ret = 0;
 
58
  
 
59
  /* Create prompt string */
64
60
  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
61
  {
71
62
    const char *const cryptsource = getenv("cryptsource");
72
63
    const char *const crypttarget = getenv("crypttarget");
89
80
      }
90
81
    }
91
82
    if(ret == -1){
92
 
      prompt = NULL;
93
 
      goto failure;
 
83
      return EXIT_FAILURE;
94
84
    }
95
85
  }
96
86
  
97
87
  /* Find splashy process */
 
88
  pid_t splashy_pid = 0;
98
89
  {
99
90
    const char splashy_name[] = "/sbin/splashy";
100
 
    proc_dir = opendir("/proc");
 
91
    DIR *proc_dir = opendir("/proc");
101
92
    if(proc_dir == NULL){
 
93
      free(prompt);
102
94
      perror("opendir");
103
 
      goto failure;
 
95
      return EXIT_FAILURE;
104
96
    }
105
97
    for(struct dirent *proc_ent = readdir(proc_dir);
106
98
        proc_ent != NULL;
107
99
        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;
 
100
      pid_t pid = (pid_t) strtoul(proc_ent->d_name, NULL, 10);
 
101
      if(pid == 0){
 
102
        /* Not a process */
 
103
        continue;
120
104
      }
121
105
      /* Find the executable name by doing readlink() on the
122
106
         /proc/<pid>/exe link */
127
111
        ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
128
112
        if(ret == -1){
129
113
          perror("asprintf");
130
 
          goto failure;
 
114
          free(prompt);
 
115
          closedir(proc_dir);
 
116
          return EXIT_FAILURE;
131
117
        }
132
118
        
133
119
        /* Check that it refers to a symlink owned by root:root */
134
120
        struct stat exe_stat;
135
121
        ret = lstat(exe_link, &exe_stat);
136
122
        if(ret == -1){
137
 
          if(errno == ENOENT){
138
 
            free(exe_link);
139
 
            continue;
140
 
          }
141
123
          perror("lstat");
142
124
          free(exe_link);
143
 
          goto failure;
 
125
          free(prompt);
 
126
          closedir(proc_dir);
 
127
          return EXIT_FAILURE;
144
128
        }
145
129
        if(not S_ISLNK(exe_stat.st_mode)
146
130
           or exe_stat.st_uid != 0
160
144
      }
161
145
    }
162
146
    closedir(proc_dir);
163
 
    proc_dir = NULL;
164
147
  }
165
148
  if(splashy_pid == 0){
166
 
    goto failure;
 
149
    free(prompt);
 
150
    return EXIT_FAILURE;
167
151
  }
168
152
  
169
153
  /* Set up the signal handler */
173
157
                     .sa_flags = 0 };
174
158
    sigemptyset(&new_action.sa_mask);
175
159
    sigaddset(&new_action.sa_mask, SIGINT);
176
 
    if(ret == -1){
177
 
      perror("sigaddset");
178
 
      goto failure;
179
 
    }
180
160
    sigaddset(&new_action.sa_mask, SIGHUP);
181
 
    if(ret == -1){
182
 
      perror("sigaddset");
183
 
      goto failure;
184
 
    }
185
161
    sigaddset(&new_action.sa_mask, SIGTERM);
186
 
    if(ret == -1){
187
 
      perror("sigaddset");
188
 
      goto failure;
189
 
    }
190
162
    ret = sigaction(SIGINT, NULL, &old_action);
191
163
    if(ret == -1){
192
164
      perror("sigaction");
193
 
      goto failure;
 
165
      free(prompt);
 
166
      return EXIT_FAILURE;
194
167
    }
195
168
    if(old_action.sa_handler != SIG_IGN){
196
169
      ret = sigaction(SIGINT, &new_action, NULL);
197
170
      if(ret == -1){
198
171
        perror("sigaction");
199
 
        goto failure;
 
172
        free(prompt);
 
173
        return EXIT_FAILURE;
200
174
      }
201
175
    }
202
176
    ret = sigaction(SIGHUP, NULL, &old_action);
203
177
    if(ret == -1){
204
178
      perror("sigaction");
205
 
      goto failure;
 
179
      free(prompt);
 
180
      return EXIT_FAILURE;
206
181
    }
207
182
    if(old_action.sa_handler != SIG_IGN){
208
183
      ret = sigaction(SIGHUP, &new_action, NULL);
209
184
      if(ret == -1){
210
185
        perror("sigaction");
211
 
        goto failure;
 
186
        free(prompt);
 
187
        return EXIT_FAILURE;
212
188
      }
213
189
    }
214
190
    ret = sigaction(SIGTERM, NULL, &old_action);
215
191
    if(ret == -1){
216
192
      perror("sigaction");
217
 
      goto failure;
 
193
      free(prompt);
 
194
      return EXIT_FAILURE;
218
195
    }
219
196
    if(old_action.sa_handler != SIG_IGN){
220
197
      ret = sigaction(SIGTERM, &new_action, NULL);
221
198
      if(ret == -1){
222
199
        perror("sigaction");
223
 
        goto failure;
 
200
        free(prompt);
 
201
        return EXIT_FAILURE;
224
202
      }
225
203
    }
226
204
  }
227
205
  
228
 
  if(interrupted_by_signal){
229
 
    goto failure;
230
 
  }
231
 
  
232
206
  /* 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){
 
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){
244
218
      const char splashy_command[] = "/sbin/splashy_update";
245
 
      execl(splashy_command, splashy_command, prompt, (char *)NULL);
246
 
      perror("execl");
 
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);
247
226
    }
248
 
    free(prompt);
249
 
    _exit(EXIT_FAILURE);
250
227
  }
251
228
  
252
229
  /* Parent */
253
230
  free(prompt);
254
 
  prompt = NULL;
255
 
  
256
 
  if(interrupted_by_signal){
257
 
    goto failure;
258
 
  }
259
231
  
260
232
  /* Wait for command to complete */
261
 
  {
 
233
  if(not interrupted_by_signal and splashy_command_pid != 0){
262
234
    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
 
    }
 
235
    ret = waitpid(splashy_command_pid, &status, 0);
270
236
    if(ret == -1){
271
 
      perror("waitpid");
 
237
      if(errno != EINTR){
 
238
        perror("waitpid");
 
239
      }
272
240
      if(errno == ECHILD){
273
241
        splashy_command_pid = 0;
274
242
      }
275
243
    } else {
276
244
      /* The child process has exited */
277
245
      splashy_command_pid = 0;
278
 
      if(WIFEXITED(status) and WEXITSTATUS(status) == 0){
 
246
      if(not interrupted_by_signal and WIFEXITED(status)
 
247
         and WEXITSTATUS(status)==0){
279
248
        return EXIT_SUCCESS;
280
249
      }
281
250
    }
282
251
  }
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);
 
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){
327
285
      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());
 
286
    }
 
287
    _exit(EXIT_FAILURE);
349
288
  }
350
289
  
351
290
  return EXIT_FAILURE;