/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: 2021-02-03 23:10:42 UTC
  • Revision ID: teddy@recompile.se-20210203231042-2z3egrvpo1zt7nej
mandos-ctl: Fix bad test for command.Remove and related minor issues

The test for command.Remove removes all clients from the spy server,
and then loops over all clients, looking for the corresponding Remove
command as recorded by the spy server.  But since since there aren't
any clients left after they were removed, no assertions are made, and
the test therefore does nothing.  Fix this.

In tests for command.Approve and command.Deny, add checks that clients
were not somehow removed by the command (in which case, likewise, no
assertions are made).

Add related checks to TestPropertySetterCmd.runTest; i.e. test that a
sequence is not empty before looping over it and making assertions.

* mandos-ctl (TestBaseCommands.test_Remove): Save a copy of the
  original "clients" dict, and loop over those instead.  Add assertion
  that all clients were indeed removed.  Also fix the code which looks
  for the Remove command, which now needs to actually work.
  (TestBaseCommands.test_Approve, TestBaseCommands.test_Deny): Add
  assertion that there are still clients before looping over them.
  (TestPropertySetterCmd.runTest): Add assertion that the list of
  values to get is not empty before looping over them.  Also add check
  that there are still clients before looping over clients.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
/*
3
3
 * Splashy - Read a password from splashy and output it
4
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
 
5
 * Copyright © 2008-2018, 2021 Teddy Hogeborn
 
6
 * Copyright © 2008-2018, 2021 Björn Påhlsson
 
7
 * 
 
8
 * This file is part of Mandos.
 
9
 * 
 
10
 * Mandos is free software: you can redistribute it and/or modify it
 
11
 * under the terms of the GNU General Public License as published by
 
12
 * the Free Software Foundation, either version 3 of the License, or
 
13
 * (at your option) any later version.
 
14
 * 
 
15
 * Mandos is distributed in the hope that it will be useful, but
14
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of
15
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
18
 * General Public License for more details.
17
19
 * 
18
20
 * 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
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
21
22
 * 
22
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
23
 * Contact the authors at <mandos@recompile.se>.
23
24
 */
24
25
 
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 */
 
26
#define _GNU_SOURCE             /* vasprintf(),
 
27
                                   program_invocation_short_name,
 
28
                                   asprintf(), TEMP_FAILURE_RETRY() */
 
29
#include <sys/types.h>          /* sig_atomic_t, pid_t, setuid(),
 
30
                                   geteuid(), setsid() */
 
31
#include <stdarg.h>             /* va_list, va_start(), vfprintf() */
 
32
#include <stdio.h>              /* vasprintf(), fprintf(), stderr,
 
33
                                   vfprintf(), asprintf() */
 
34
#include <errno.h>              /* program_invocation_short_name,
 
35
                                   errno, EACCES, ENOTDIR, ELOOP,
 
36
                                   ENOENT, ENAMETOOLONG, EMFILE,
 
37
                                   ENFILE, ENOMEM, ENOEXEC, EINVAL,
 
38
                                   E2BIG, EFAULT, EIO, ETXTBSY,
 
39
                                   EISDIR, ELIBBAD, EPERM, EINTR,
 
40
                                   ECHILD */
 
41
#include <string.h>             /* strerror(), memcmp() */
 
42
#include <error.h>              /* error() */
 
43
#include <stdlib.h>             /* free(), EXIT_FAILURE, getenv(),
 
44
                                   EXIT_SUCCESS, abort() */
30
45
#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() */
 
46
#include <dirent.h>             /* DIR, opendir(), struct dirent,
 
47
                                   readdir(), closedir() */
 
48
#include <sysexits.h>           /* EX_OSERR, EX_OSFILE,
 
49
                                   EX_UNAVAILABLE */
38
50
#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 */
 
51
#include <iso646.h>             /* or, not, and */
 
52
#include <unistd.h>             /* ssize_t, readlink(), fork(),
 
53
                                   execl(), _exit(),
 
54
                                   TEMP_FAILURE_RETRY(), sleep(),
 
55
                                   setuid(), geteuid(), setsid(),
 
56
                                   chdir(), dup2(), STDERR_FILENO,
 
57
                                   STDOUT_FILENO, pause() */
 
58
#include <sys/stat.h>           /* struct stat, lstat(), S_ISLNK() */
 
59
#include <signal.h>             /* struct sigaction, sigemptyset(),
 
60
                                   sigaddset(), SIGINT, SIGHUP,
 
61
                                   SIGTERM, SIG_IGN, kill(), SIGKILL,
 
62
                                   SIG_DFL, raise() */
47
63
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
48
64
                                   WEXITSTATUS() */
49
65
 
50
66
sig_atomic_t interrupted_by_signal = 0;
51
67
int signal_received;
52
68
 
 
69
/* Function to use when printing errors */
 
70
__attribute__((format (gnu_printf, 3, 4)))
 
71
void error_plus(int status, int errnum, const char *formatstring,
 
72
                ...){
 
73
  va_list ap;
 
74
  char *text;
 
75
  int ret;
 
76
  
 
77
  va_start(ap, formatstring);
 
78
  ret = vasprintf(&text, formatstring, ap);
 
79
  if(ret == -1){
 
80
    fprintf(stderr, "Mandos plugin %s: ",
 
81
            program_invocation_short_name);
 
82
    vfprintf(stderr, formatstring, ap);
 
83
    fprintf(stderr, ": ");
 
84
    fprintf(stderr, "%s\n", strerror(errnum));
 
85
    error(status, errno, "vasprintf while printing error");
 
86
    return;
 
87
  }
 
88
  fprintf(stderr, "Mandos plugin ");
 
89
  error(status, errnum, "%s", text);
 
90
  free(text);
 
91
}
 
92
 
 
93
 
53
94
static void termination_handler(int signum){
54
95
  if(interrupted_by_signal){
55
96
    return;
65
106
  DIR *proc_dir = NULL;
66
107
  pid_t splashy_pid = 0;
67
108
  pid_t splashy_command_pid = 0;
 
109
  int exitstatus = EXIT_FAILURE;
68
110
  
69
111
  /* Create prompt string */
70
112
  {
90
132
    }
91
133
    if(ret == -1){
92
134
      prompt = NULL;
 
135
      exitstatus = EX_OSERR;
93
136
      goto failure;
94
137
    }
95
138
  }
99
142
    const char splashy_name[] = "/sbin/splashy";
100
143
    proc_dir = opendir("/proc");
101
144
    if(proc_dir == NULL){
102
 
      perror("opendir");
 
145
      int e = errno;
 
146
      error_plus(0, errno, "opendir");
 
147
      switch(e){
 
148
      case EACCES:
 
149
      case ENOTDIR:
 
150
      case ELOOP:
 
151
      case ENOENT:
 
152
      default:
 
153
        exitstatus = EX_OSFILE;
 
154
        break;
 
155
      case ENAMETOOLONG:
 
156
      case EMFILE:
 
157
      case ENFILE:
 
158
      case ENOMEM:
 
159
        exitstatus = EX_OSERR;
 
160
        break;
 
161
      }
103
162
      goto failure;
104
163
    }
105
164
    for(struct dirent *proc_ent = readdir(proc_dir);
126
185
        char *exe_link;
127
186
        ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
128
187
        if(ret == -1){
129
 
          perror("asprintf");
 
188
          error_plus(0, errno, "asprintf");
 
189
          exitstatus = EX_OSERR;
130
190
          goto failure;
131
191
        }
132
192
        
138
198
            free(exe_link);
139
199
            continue;
140
200
          }
141
 
          perror("lstat");
 
201
          int e = errno;
 
202
          error_plus(0, errno, "lstat");
142
203
          free(exe_link);
 
204
          switch(e){
 
205
          case EACCES:
 
206
          case ENOTDIR:
 
207
          case ELOOP:
 
208
          default:
 
209
            exitstatus = EX_OSFILE;
 
210
            break;
 
211
          case ENAMETOOLONG:
 
212
            exitstatus = EX_OSERR;
 
213
            break;
 
214
          }
143
215
          goto failure;
144
216
        }
145
217
        if(not S_ISLNK(exe_stat.st_mode)
163
235
    proc_dir = NULL;
164
236
  }
165
237
  if(splashy_pid == 0){
 
238
    exitstatus = EX_UNAVAILABLE;
166
239
    goto failure;
167
240
  }
168
241
  
172
245
      new_action = { .sa_handler = termination_handler,
173
246
                     .sa_flags = 0 };
174
247
    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");
 
248
    ret = sigaddset(&new_action.sa_mask, SIGINT);
 
249
    if(ret == -1){
 
250
      error_plus(0, errno, "sigaddset");
 
251
      exitstatus = EX_OSERR;
 
252
      goto failure;
 
253
    }
 
254
    ret = sigaddset(&new_action.sa_mask, SIGHUP);
 
255
    if(ret == -1){
 
256
      error_plus(0, errno, "sigaddset");
 
257
      exitstatus = EX_OSERR;
 
258
      goto failure;
 
259
    }
 
260
    ret = sigaddset(&new_action.sa_mask, SIGTERM);
 
261
    if(ret == -1){
 
262
      error_plus(0, errno, "sigaddset");
 
263
      exitstatus = EX_OSERR;
188
264
      goto failure;
189
265
    }
190
266
    ret = sigaction(SIGINT, NULL, &old_action);
191
267
    if(ret == -1){
192
 
      perror("sigaction");
 
268
      error_plus(0, errno, "sigaction");
 
269
      exitstatus = EX_OSERR;
193
270
      goto failure;
194
271
    }
195
272
    if(old_action.sa_handler != SIG_IGN){
196
273
      ret = sigaction(SIGINT, &new_action, NULL);
197
274
      if(ret == -1){
198
 
        perror("sigaction");
 
275
        error_plus(0, errno, "sigaction");
 
276
        exitstatus = EX_OSERR;
199
277
        goto failure;
200
278
      }
201
279
    }
202
280
    ret = sigaction(SIGHUP, NULL, &old_action);
203
281
    if(ret == -1){
204
 
      perror("sigaction");
 
282
      error_plus(0, errno, "sigaction");
 
283
      exitstatus = EX_OSERR;
205
284
      goto failure;
206
285
    }
207
286
    if(old_action.sa_handler != SIG_IGN){
208
287
      ret = sigaction(SIGHUP, &new_action, NULL);
209
288
      if(ret == -1){
210
 
        perror("sigaction");
 
289
        error_plus(0, errno, "sigaction");
 
290
        exitstatus = EX_OSERR;
211
291
        goto failure;
212
292
      }
213
293
    }
214
294
    ret = sigaction(SIGTERM, NULL, &old_action);
215
295
    if(ret == -1){
216
 
      perror("sigaction");
 
296
      error_plus(0, errno, "sigaction");
 
297
      exitstatus = EX_OSERR;
217
298
      goto failure;
218
299
    }
219
300
    if(old_action.sa_handler != SIG_IGN){
220
301
      ret = sigaction(SIGTERM, &new_action, NULL);
221
302
      if(ret == -1){
222
 
        perror("sigaction");
 
303
        error_plus(0, errno, "sigaction");
 
304
        exitstatus = EX_OSERR;
223
305
        goto failure;
224
306
      }
225
307
    }
235
317
    goto failure;
236
318
  }
237
319
  if(splashy_command_pid == -1){
238
 
    perror("fork");
 
320
    error_plus(0, errno, "fork");
 
321
    exitstatus = EX_OSERR;
239
322
    goto failure;
240
323
  }
241
324
  /* Child */
243
326
    if(not interrupted_by_signal){
244
327
      const char splashy_command[] = "/sbin/splashy_update";
245
328
      execl(splashy_command, splashy_command, prompt, (char *)NULL);
246
 
      perror("execl");
 
329
      int e = errno;
 
330
      error_plus(0, errno, "execl");
 
331
      switch(e){
 
332
      case EACCES:
 
333
      case ENOENT:
 
334
      case ENOEXEC:
 
335
      case EINVAL:
 
336
        _exit(EX_UNAVAILABLE);
 
337
      case ENAMETOOLONG:
 
338
      case E2BIG:
 
339
      case ENOMEM:
 
340
      case EFAULT:
 
341
      case EIO:
 
342
      case EMFILE:
 
343
      case ENFILE:
 
344
      case ETXTBSY:
 
345
      default:
 
346
        _exit(EX_OSERR);
 
347
      case ENOTDIR:
 
348
      case ELOOP:
 
349
      case EISDIR:
 
350
#ifdef ELIBBAD
 
351
      case ELIBBAD:             /* Linux only */
 
352
#endif
 
353
      case EPERM:
 
354
        _exit(EX_OSFILE);
 
355
      }
247
356
    }
248
357
    free(prompt);
249
358
    _exit(EXIT_FAILURE);
268
377
      goto failure;
269
378
    }
270
379
    if(ret == -1){
271
 
      perror("waitpid");
 
380
      error_plus(0, errno, "waitpid");
272
381
      if(errno == ECHILD){
273
382
        splashy_command_pid = 0;
274
383
      }
298
407
      TEMP_FAILURE_RETRY(kill(splashy_pid, SIGKILL));
299
408
      sleep(1);
300
409
    }
301
 
    pid_t new_splashy_pid = TEMP_FAILURE_RETRY(fork());
 
410
    pid_t new_splashy_pid = (pid_t)TEMP_FAILURE_RETRY(fork());
302
411
    if(new_splashy_pid == 0){
303
412
      /* Child; will become new splashy process */
304
413
      
306
415
         the real user ID (_mandos) */
307
416
      ret = setuid(geteuid());
308
417
      if(ret == -1){
309
 
        perror("setuid");
 
418
        error_plus(0, errno, "setuid");
310
419
      }
311
420
      
312
421
      setsid();
313
422
      ret = chdir("/");
314
423
      if(ret == -1){
315
 
        perror("chdir");
 
424
        error_plus(0, errno, "chdir");
316
425
      }
317
426
/*       if(fork() != 0){ */
318
427
/*      _exit(EXIT_SUCCESS); */
319
428
/*       } */
320
429
      ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace stdout */
321
430
      if(ret == -1){
322
 
        perror("dup2");
323
 
        _exit(EXIT_FAILURE);
 
431
        error_plus(0, errno, "dup2");
 
432
        _exit(EX_OSERR);
324
433
      }
325
 
    
 
434
      
326
435
      execl("/sbin/splashy", "/sbin/splashy", "boot", (char *)NULL);
327
 
      perror("execl");
328
 
      _exit(EXIT_FAILURE);
 
436
      {
 
437
        int e = errno;
 
438
        error_plus(0, errno, "execl");
 
439
        switch(e){
 
440
        case EACCES:
 
441
        case ENOENT:
 
442
        case ENOEXEC:
 
443
        default:
 
444
          _exit(EX_UNAVAILABLE);
 
445
        case ENAMETOOLONG:
 
446
        case E2BIG:
 
447
        case ENOMEM:
 
448
          _exit(EX_OSERR);
 
449
        case ENOTDIR:
 
450
        case ELOOP:
 
451
          _exit(EX_OSFILE);
 
452
        }
 
453
      }
329
454
    }
330
455
  }
331
456
  
333
458
    struct sigaction signal_action;
334
459
    sigemptyset(&signal_action.sa_mask);
335
460
    signal_action.sa_handler = SIG_DFL;
336
 
    ret = TEMP_FAILURE_RETRY(sigaction(signal_received,
337
 
                                       &signal_action, NULL));
 
461
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
 
462
                                            &signal_action, NULL));
338
463
    if(ret == -1){
339
 
      perror("sigaction");
 
464
      error_plus(0, errno, "sigaction");
340
465
    }
341
466
    do {
342
467
      ret = raise(signal_received);
343
468
    } while(ret != 0 and errno == EINTR);
344
469
    if(ret != 0){
345
 
      perror("raise");
 
470
      error_plus(0, errno, "raise");
346
471
      abort();
347
472
    }
348
473
    TEMP_FAILURE_RETRY(pause());
349
474
  }
350
475
  
351
 
  return EXIT_FAILURE;
 
476
  return exitstatus;
352
477
}