/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/mandos-client.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:
9
9
 * "browse_callback", and parts of "main".
10
10
 * 
11
11
 * Everything else is
12
 
 * Copyright © 2008-2016 Teddy Hogeborn
13
 
 * Copyright © 2008-2016 Björn Påhlsson
14
 
 * 
15
 
 * This program is free software: you can redistribute it and/or
16
 
 * modify it under the terms of the GNU General Public License as
17
 
 * published by the Free Software Foundation, either version 3 of the
18
 
 * License, or (at your option) any later version.
19
 
 * 
20
 
 * This program is distributed in the hope that it will be useful, but
 
12
 * Copyright © 2008-2021 Teddy Hogeborn
 
13
 * Copyright © 2008-2021 Björn Påhlsson
 
14
 * 
 
15
 * This file is part of Mandos.
 
16
 * 
 
17
 * Mandos is free software: you can redistribute it and/or modify it
 
18
 * under the terms of the GNU General Public License as published by
 
19
 * the Free Software Foundation, either version 3 of the License, or
 
20
 * (at your option) any later version.
 
21
 * 
 
22
 * Mandos is distributed in the hope that it will be useful, but
21
23
 * WITHOUT ANY WARRANTY; without even the implied warranty of
22
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23
25
 * General Public License for more details.
24
26
 * 
25
27
 * You should have received a copy of the GNU General Public License
26
 
 * along with this program.  If not, see
27
 
 * <http://www.gnu.org/licenses/>.
 
28
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
28
29
 * 
29
30
 * Contact the authors at <mandos@recompile.se>.
30
31
 */
37
38
#define _FILE_OFFSET_BITS 64
38
39
#endif  /* not _FILE_OFFSET_BITS */
39
40
 
40
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
41
 
 
42
 
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
43
 
                                   stdout, ferror() */
44
 
#include <stdint.h>             /* uint16_t, uint32_t, intptr_t */
45
 
#include <stddef.h>             /* NULL, size_t, ssize_t */
46
 
#include <stdlib.h>             /* free(), EXIT_SUCCESS, srand(),
47
 
                                   strtof(), abort() */
 
41
#define _GNU_SOURCE             /* program_invocation_short_name,
 
42
                                   TEMP_FAILURE_RETRY(), O_CLOEXEC,
 
43
                                   scandirat(), asprintf() */
48
44
#include <stdbool.h>            /* bool, false, true */
49
 
#include <string.h>             /* strcmp(), strlen(), strerror(),
50
 
                                   asprintf(), strncpy() */
51
 
#include <sys/ioctl.h>          /* ioctl */
52
 
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
53
 
                                   sockaddr_in6, PF_INET6,
54
 
                                   SOCK_STREAM, uid_t, gid_t, open(),
55
 
                                   opendir(), DIR */
56
 
#include <sys/stat.h>           /* open(), S_ISREG */
57
 
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
58
 
                                   inet_pton(), connect(),
59
 
                                   getnameinfo() */
60
 
#include <fcntl.h>              /* open(), unlinkat(), AT_REMOVEDIR */
61
 
#include <dirent.h>             /* opendir(), struct dirent, readdir()
62
 
                                 */
63
 
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
64
 
                                   strtoimax() */
65
 
#include <errno.h>              /* perror(), errno, EINTR, EINVAL,
66
 
                                   EAI_SYSTEM, ENETUNREACH,
 
45
#include <argp.h>               /* argp_program_version,
 
46
                                   argp_program_bug_address,
 
47
                                   struct argp_option,
 
48
                                   struct argp_state, argp_error(),
 
49
                                   argp_state_help,
 
50
                                   ARGP_HELP_STD_HELP,
 
51
                                   ARGP_HELP_EXIT_ERR,
 
52
                                   ARGP_HELP_EXIT_OK, ARGP_HELP_USAGE,
 
53
                                   argp_err_exit_status,
 
54
                                   ARGP_ERR_UNKNOWN, struct argp,
 
55
                                   argp_parse(), ARGP_IN_ORDER,
 
56
                                   ARGP_NO_HELP */
 
57
#include <stddef.h>             /* NULL, size_t */
 
58
#include <sys/types.h>          /* uid_t, gid_t, sig_atomic_t,
 
59
                                   seteuid(), setuid(), pid_t,
 
60
                                   setgid(), getuid(), getgid() */
 
61
#include <unistd.h>             /* uid_t, gid_t, TEMP_FAILURE_RETRY(),
 
62
                                   seteuid(), setuid(), close(),
 
63
                                   ssize_t, read(), fork(), setgid(),
 
64
                                   _exit(), dup2(), STDIN_FILENO,
 
65
                                   STDERR_FILENO, STDOUT_FILENO,
 
66
                                   fexecve(), write(), getuid(),
 
67
                                   getgid(), fchown(), symlink(),
 
68
                                   sleep(), unlinkat(), pause() */
 
69
#include <netinet/in.h>         /* in_port_t, struct sockaddr_in6,
 
70
                                   sa_family_t, struct sockaddr_in,
 
71
                                   htons(), IN6_IS_ADDR_LINKLOCAL,
 
72
                                   INET_ADDRSTRLEN, INET6_ADDRSTRLEN,
 
73
                                   ntohl(), IPPROTO_IP */
 
74
#include <time.h>               /* struct timespec, clock_gettime(),
 
75
                                   CLOCK_MONOTONIC, time_t, struct tm,
 
76
                                   gmtime_r(), clock_settime(),
 
77
                                   CLOCK_REALTIME, nanosleep() */
 
78
#include <errno.h>              /* errno,
 
79
                                   program_invocation_short_name,
 
80
                                   EINTR, EINVAL, ENETUNREACH,
67
81
                                   EHOSTUNREACH, ECONNREFUSED, EPROTO,
68
 
                                   EIO, ENOENT, ENXIO, ENOMEM, EISDIR,
69
 
                                   ENOTEMPTY,
70
 
                                   program_invocation_short_name */
71
 
#include <time.h>               /* nanosleep(), time(), sleep() */
72
 
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
73
 
                                   SIOCSIFFLAGS, if_indextoname(),
74
 
                                   if_nametoindex(), IF_NAMESIZE */
75
 
#include <netinet/in.h>         /* IN6_IS_ADDR_LINKLOCAL,
76
 
                                   INET_ADDRSTRLEN, INET6_ADDRSTRLEN
77
 
                                */
78
 
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
79
 
                                   getuid(), getgid(), seteuid(),
80
 
                                   setgid(), pause(), _exit(),
81
 
                                   unlinkat() */
82
 
#include <arpa/inet.h>          /* inet_pton(), htons() */
83
 
#include <iso646.h>             /* not, or, and */
84
 
#include <argp.h>               /* struct argp_option, error_t, struct
85
 
                                   argp_state, struct argp,
86
 
                                   argp_parse(), ARGP_KEY_ARG,
87
 
                                   ARGP_KEY_END, ARGP_ERR_UNKNOWN */
88
 
#include <signal.h>             /* sigemptyset(), sigaddset(),
89
 
                                   sigaction(), SIGTERM, sig_atomic_t,
90
 
                                   raise() */
91
 
#include <sysexits.h>           /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE,
92
 
                                   EX_NOHOST, EX_IOERR, EX_PROTOCOL */
 
82
                                   EIO, ENOENT, ENXIO, error_t,
 
83
                                   ENOMEM, EISDIR, ENOTEMPTY */
 
84
#include <stdio.h>              /* fprintf(), stderr, perror(), FILE,
 
85
                                   vfprintf(), off_t, SEEK_SET,
 
86
                                   stdout, fwrite(), ferror(),
 
87
                                   fflush(), asprintf() */
 
88
#include <stdarg.h>             /* va_list, va_start(), vfprintf() */
 
89
#include <stdlib.h>             /* realloc(), free(), malloc(),
 
90
                                   getenv(), EXIT_FAILURE, setenv(),
 
91
                                   EXIT_SUCCESS, strtof(), strtod(),
 
92
                                   srand(), mkdtemp(), abort() */
 
93
#include <string.h>             /* strdup(), strcmp(), strlen(),
 
94
                                   strerror(), strncpy(), strspn(),
 
95
                                   memcpy(), strrchr(), strchr(),
 
96
                                   strsignal() */
 
97
#include <fcntl.h>              /* open(), O_RDONLY, O_DIRECTORY,
 
98
                                   O_PATH, O_CLOEXEC, openat(),
 
99
                                   O_NOFOLLOW, AT_REMOVEDIR */
 
100
#include <iso646.h>             /* or, and, not */
 
101
#include <sys/stat.h>           /* struct stat, fstat(), fstatat(),
 
102
                                   S_ISREG(), S_IXUSR, S_IXGRP,
 
103
                                   S_IXOTH, lstat() */
 
104
#include <net/if.h>             /* IF_NAMESIZE, if_indextoname(),
 
105
                                   if_nametoindex(), SIOCGIFFLAGS,
 
106
                                   IFF_LOOPBACK, IFF_POINTOPOINT,
 
107
                                   IFF_BROADCAST, IFF_NOARP, IFF_UP,
 
108
                                   IFF_RUNNING, SIOCSIFFLAGS */
 
109
#include <sysexits.h>           /* EX_NOPERM, EX_OSERR,
 
110
                                   EX_UNAVAILABLE, EX_USAGE */
 
111
#include <grp.h>                /* setgroups() */
93
112
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
94
 
                                   WEXITSTATUS(), WTERMSIG() */
95
 
#include <grp.h>                /* setgroups() */
96
 
#include <argz.h>               /* argz_add_sep(), argz_next(),
97
 
                                   argz_delete(), argz_append(),
98
 
                                   argz_stringify(), argz_add(),
99
 
                                   argz_count() */
 
113
                                   WEXITSTATUS(), WIFSIGNALED(),
 
114
                                   WTERMSIG() */
 
115
#include <signal.h>             /* kill(), SIGTERM, struct sigaction,
 
116
                                   SIG_DFL, sigemptyset(),
 
117
                                   sigaddset(), SIGINT, SIGHUP,
 
118
                                   SIG_IGN, raise() */
 
119
#include <sys/socket.h>         /* struct sockaddr_storage, AF_INET6,
 
120
                                   PF_INET6, AF_INET, PF_INET,
 
121
                                   socket(), SOCK_STREAM,
 
122
                                   SOCK_CLOEXEC, struct sockaddr,
 
123
                                   connect(), SOCK_DGRAM */
 
124
#include <argz.h>               /* argz_next(), argz_add_sep(),
 
125
                                   argz_delete(), argz_stringify(),
 
126
                                   argz_add(), argz_count() */
 
127
#include <inttypes.h>           /* PRIuMAX, uintmax_t, uint32_t,
 
128
                                   PRIdMAX, PRIu16, intmax_t,
 
129
                                   strtoimax() */
 
130
#include <arpa/inet.h>          /* inet_pton() */
 
131
#include <stdint.h>             /* uint32_t, intptr_t, uint16_t */
100
132
#include <netdb.h>              /* getnameinfo(), NI_NUMERICHOST,
101
133
                                   EAI_SYSTEM, gai_strerror() */
 
134
#include <sys/ioctl.h>          /* ioctl() */
 
135
#include <dirent.h>             /* struct dirent, scandirat(),
 
136
                                   alphasort(), scandir() */
 
137
#include <limits.h>             /* INT_MAX */
102
138
 
103
139
#ifdef __linux__
104
140
#include <sys/klog.h>           /* klogctl() */
117
153
 
118
154
/* GnuTLS */
119
155
#include <gnutls/gnutls.h>      /* All GnuTLS types, constants and
120
 
                                   functions:
121
 
                                   gnutls_*
122
 
                                   init_gnutls_session(),
123
 
                                   GNUTLS_* */
 
156
                                   functions: gnutls_*, GNUTLS_* */
 
157
#if GNUTLS_VERSION_NUMBER < 0x030600
124
158
#include <gnutls/openpgp.h>
125
159
                         /* gnutls_certificate_set_openpgp_key_file(),
126
160
                            GNUTLS_OPENPGP_FMT_BASE64 */
 
161
#elif GNUTLS_VERSION_NUMBER >= 0x030606
 
162
#include <gnutls/x509.h>        /* GNUTLS_PKCS_PLAIN,
 
163
                                   GNUTLS_PKCS_NULL_PASSWORD */
 
164
#endif
127
165
 
128
166
/* GPGME */
129
167
#include <gpgme.h>              /* All GPGME types, constants and
130
168
                                   functions:
131
 
                                   gpgme_*
132
 
                                   GPGME_PROTOCOL_OpenPGP,
133
 
                                   GPG_ERR_NO_* */
 
169
                                   gpgme_*, GPG_ERR_NO_*,
 
170
                                   GPGME_IMPORT_*
 
171
                                   GPGME_PROTOCOL_OpenPGP */
134
172
 
135
173
#define BUFFER_SIZE 256
136
174
 
137
175
#define PATHDIR "/conf/conf.d/mandos"
138
176
#define SECKEY "seckey.txt"
139
177
#define PUBKEY "pubkey.txt"
 
178
#define TLS_PRIVKEY "tls-privkey.pem"
 
179
#define TLS_PUBKEY "tls-pubkey.pem"
140
180
#define HOOKDIR "/lib/mandos/network-hooks.d"
141
181
 
142
182
bool debug = false;
270
310
  return true;
271
311
}
272
312
 
 
313
/* Set effective uid to 0, return errno */
 
314
__attribute__((warn_unused_result))
 
315
int raise_privileges(void){
 
316
  int old_errno = errno;
 
317
  int ret = 0;
 
318
  if(seteuid(0) == -1){
 
319
    ret = errno;
 
320
  }
 
321
  errno = old_errno;
 
322
  return ret;
 
323
}
 
324
 
 
325
/* Set effective and real user ID to 0.  Return errno. */
 
326
__attribute__((warn_unused_result))
 
327
int raise_privileges_permanently(void){
 
328
  int old_errno = errno;
 
329
  int ret = raise_privileges();
 
330
  if(ret != 0){
 
331
    errno = old_errno;
 
332
    return ret;
 
333
  }
 
334
  if(setuid(0) == -1){
 
335
    ret = errno;
 
336
  }
 
337
  errno = old_errno;
 
338
  return ret;
 
339
}
 
340
 
 
341
/* Set effective user ID to unprivileged saved user ID */
 
342
__attribute__((warn_unused_result))
 
343
int lower_privileges(void){
 
344
  int old_errno = errno;
 
345
  int ret = 0;
 
346
  if(seteuid(uid) == -1){
 
347
    ret = errno;
 
348
  }
 
349
  errno = old_errno;
 
350
  return ret;
 
351
}
 
352
 
 
353
/* Lower privileges permanently */
 
354
__attribute__((warn_unused_result))
 
355
int lower_privileges_permanently(void){
 
356
  int old_errno = errno;
 
357
  int ret = 0;
 
358
  if(setuid(uid) == -1){
 
359
    ret = errno;
 
360
  }
 
361
  errno = old_errno;
 
362
  return ret;
 
363
}
 
364
 
273
365
/* 
274
366
 * Initialize GPGME.
275
367
 */
295
387
      return false;
296
388
    }
297
389
    
 
390
    /* Workaround for systems without a real-time clock; see also
 
391
       Debian bug #894495: <https://bugs.debian.org/894495> */
 
392
    do {
 
393
      {
 
394
        time_t currtime = time(NULL);
 
395
        if(currtime != (time_t)-1){
 
396
          struct tm tm;
 
397
          if(gmtime_r(&currtime, &tm) == NULL) {
 
398
            perror_plus("gmtime_r");
 
399
            break;
 
400
          }
 
401
          if(tm.tm_year != 70 or tm.tm_mon != 0){
 
402
            break;
 
403
          }
 
404
          if(debug){
 
405
            fprintf_plus(stderr, "System clock is January 1970");
 
406
          }
 
407
        } else {
 
408
          if(debug){
 
409
            fprintf_plus(stderr, "System clock is invalid");
 
410
          }
 
411
        }
 
412
      }
 
413
      struct stat keystat;
 
414
      ret = fstat(fd, &keystat);
 
415
      if(ret != 0){
 
416
        perror_plus("fstat");
 
417
        break;
 
418
      }
 
419
      ret = raise_privileges();
 
420
      if(ret != 0){
 
421
        errno = ret;
 
422
        perror_plus("Failed to raise privileges");
 
423
        break;
 
424
      }
 
425
      if(debug){
 
426
        fprintf_plus(stderr,
 
427
                     "Setting system clock to key file mtime");
 
428
      }
 
429
      if(clock_settime(CLOCK_REALTIME, &keystat.st_mtim) != 0){
 
430
        perror_plus("clock_settime");
 
431
      }
 
432
      ret = lower_privileges();
 
433
      if(ret != 0){
 
434
        errno = ret;
 
435
        perror_plus("Failed to lower privileges");
 
436
      }
 
437
    } while(false);
 
438
 
298
439
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
299
440
    if(rc != GPG_ERR_NO_ERROR){
300
441
      fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
308
449
                   gpgme_strsource(rc), gpgme_strerror(rc));
309
450
      return false;
310
451
    }
 
452
    {
 
453
      gpgme_import_result_t import_result
 
454
        = gpgme_op_import_result(mc->ctx);
 
455
      if((import_result->imported < 1
 
456
          or import_result->not_imported > 0)
 
457
         and import_result->unchanged == 0){
 
458
        fprintf_plus(stderr, "bad gpgme_op_import_results:\n");
 
459
        fprintf_plus(stderr,
 
460
                     "The total number of considered keys: %d\n",
 
461
                     import_result->considered);
 
462
        fprintf_plus(stderr,
 
463
                     "The number of keys without user ID: %d\n",
 
464
                     import_result->no_user_id);
 
465
        fprintf_plus(stderr,
 
466
                     "The total number of imported keys: %d\n",
 
467
                     import_result->imported);
 
468
        fprintf_plus(stderr, "The number of imported RSA keys: %d\n",
 
469
                     import_result->imported_rsa);
 
470
        fprintf_plus(stderr, "The number of unchanged keys: %d\n",
 
471
                     import_result->unchanged);
 
472
        fprintf_plus(stderr, "The number of new user IDs: %d\n",
 
473
                     import_result->new_user_ids);
 
474
        fprintf_plus(stderr, "The number of new sub keys: %d\n",
 
475
                     import_result->new_sub_keys);
 
476
        fprintf_plus(stderr, "The number of new signatures: %d\n",
 
477
                     import_result->new_signatures);
 
478
        fprintf_plus(stderr, "The number of new revocations: %d\n",
 
479
                     import_result->new_revocations);
 
480
        fprintf_plus(stderr,
 
481
                     "The total number of secret keys read: %d\n",
 
482
                     import_result->secret_read);
 
483
        fprintf_plus(stderr,
 
484
                     "The number of imported secret keys: %d\n",
 
485
                     import_result->secret_imported);
 
486
        fprintf_plus(stderr,
 
487
                     "The number of unchanged secret keys: %d\n",
 
488
                     import_result->secret_unchanged);
 
489
        fprintf_plus(stderr, "The number of keys not imported: %d\n",
 
490
                     import_result->not_imported);
 
491
        for(gpgme_import_status_t import_status
 
492
              = import_result->imports;
 
493
            import_status != NULL;
 
494
            import_status = import_status->next){
 
495
          fprintf_plus(stderr, "Import status for key: %s\n",
 
496
                       import_status->fpr);
 
497
          if(import_status->result != GPG_ERR_NO_ERROR){
 
498
            fprintf_plus(stderr, "Import result: %s: %s\n",
 
499
                         gpgme_strsource(import_status->result),
 
500
                         gpgme_strerror(import_status->result));
 
501
          }
 
502
          fprintf_plus(stderr, "Key status:\n");
 
503
          fprintf_plus(stderr,
 
504
                       import_status->status & GPGME_IMPORT_NEW
 
505
                       ? "The key was new.\n"
 
506
                       : "The key was not new.\n");
 
507
          fprintf_plus(stderr,
 
508
                       import_status->status & GPGME_IMPORT_UID
 
509
                       ? "The key contained new user IDs.\n"
 
510
                       : "The key did not contain new user IDs.\n");
 
511
          fprintf_plus(stderr,
 
512
                       import_status->status & GPGME_IMPORT_SIG
 
513
                       ? "The key contained new signatures.\n"
 
514
                       : "The key did not contain new signatures.\n");
 
515
          fprintf_plus(stderr,
 
516
                       import_status->status & GPGME_IMPORT_SUBKEY
 
517
                       ? "The key contained new sub keys.\n"
 
518
                       : "The key did not contain new sub keys.\n");
 
519
          fprintf_plus(stderr,
 
520
                       import_status->status & GPGME_IMPORT_SECRET
 
521
                       ? "The key contained a secret key.\n"
 
522
                       : "The key did not contain a secret key.\n");
 
523
        }
 
524
        return false;
 
525
      }
 
526
    }
311
527
    
312
528
    ret = close(fd);
313
529
    if(ret == -1){
354
570
  /* Create new GPGME "context" */
355
571
  rc = gpgme_new(&(mc->ctx));
356
572
  if(rc != GPG_ERR_NO_ERROR){
357
 
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
358
 
                 "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
359
 
                 gpgme_strerror(rc));
 
573
    fprintf_plus(stderr, "bad gpgme_new: %s: %s\n",
 
574
                 gpgme_strsource(rc), gpgme_strerror(rc));
360
575
    return false;
361
576
  }
362
577
  
398
613
  /* Create new empty GPGME data buffer for the plaintext */
399
614
  rc = gpgme_data_new(&dh_plain);
400
615
  if(rc != GPG_ERR_NO_ERROR){
401
 
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
402
 
                 "bad gpgme_data_new: %s: %s\n",
 
616
    fprintf_plus(stderr, "bad gpgme_data_new: %s: %s\n",
403
617
                 gpgme_strsource(rc), gpgme_strerror(rc));
404
618
    gpgme_data_release(dh_crypto);
405
619
    return -1;
418
632
      if(result == NULL){
419
633
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
420
634
      } else {
421
 
        fprintf_plus(stderr, "Unsupported algorithm: %s\n",
422
 
                     result->unsupported_algorithm);
423
 
        fprintf_plus(stderr, "Wrong key usage: %u\n",
424
 
                     result->wrong_key_usage);
 
635
        if(result->unsupported_algorithm != NULL) {
 
636
          fprintf_plus(stderr, "Unsupported algorithm: %s\n",
 
637
                       result->unsupported_algorithm);
 
638
        }
 
639
        fprintf_plus(stderr, "Wrong key usage: %s\n",
 
640
                     result->wrong_key_usage ? "Yes" : "No");
425
641
        if(result->file_name != NULL){
426
642
          fprintf_plus(stderr, "File name: %s\n", result->file_name);
427
643
        }
428
 
        gpgme_recipient_t recipient;
429
 
        recipient = result->recipients;
430
 
        while(recipient != NULL){
 
644
 
 
645
        for(gpgme_recipient_t r = result->recipients; r != NULL;
 
646
            r = r->next){
431
647
          fprintf_plus(stderr, "Public key algorithm: %s\n",
432
 
                       gpgme_pubkey_algo_name
433
 
                       (recipient->pubkey_algo));
434
 
          fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
 
648
                       gpgme_pubkey_algo_name(r->pubkey_algo));
 
649
          fprintf_plus(stderr, "Key ID: %s\n", r->keyid);
435
650
          fprintf_plus(stderr, "Secret key available: %s\n",
436
 
                       recipient->status == GPG_ERR_NO_SECKEY
437
 
                       ? "No" : "Yes");
438
 
          recipient = recipient->next;
 
651
                       r->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
439
652
        }
440
653
      }
441
654
    }
523
736
                              const char *dhparamsfilename,
524
737
                              mandos_context *mc){
525
738
  int ret;
526
 
  unsigned int uret;
527
739
  
528
740
  if(debug){
529
741
    fprintf_plus(stderr, "Initializing GnuTLS\n");
546
758
  }
547
759
  
548
760
  if(debug){
549
 
    fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
550
 
                 " secret key %s as GnuTLS credentials\n",
 
761
    fprintf_plus(stderr, "Attempting to use public key %s and"
 
762
                 " private key %s as GnuTLS credentials\n",
551
763
                 pubkeyfilename,
552
764
                 seckeyfilename);
553
765
  }
554
766
  
 
767
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
768
  ret = gnutls_certificate_set_rawpk_key_file
 
769
    (mc->cred, pubkeyfilename, seckeyfilename,
 
770
     GNUTLS_X509_FMT_PEM,       /* format */
 
771
     NULL,                      /* pass */
 
772
     /* key_usage */
 
773
     GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
 
774
     NULL,                      /* names */
 
775
     0,                         /* names_length */
 
776
     /* privkey_flags */
 
777
     GNUTLS_PKCS_PLAIN | GNUTLS_PKCS_NULL_PASSWORD,
 
778
     0);                        /* pkcs11_flags */
 
779
#elif GNUTLS_VERSION_NUMBER < 0x030600
555
780
  ret = gnutls_certificate_set_openpgp_key_file
556
781
    (mc->cred, pubkeyfilename, seckeyfilename,
557
782
     GNUTLS_OPENPGP_FMT_BASE64);
 
783
#else
 
784
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
 
785
#endif
558
786
  if(ret != GNUTLS_E_SUCCESS){
559
787
    fprintf_plus(stderr,
560
 
                 "Error[%d] while reading the OpenPGP key pair ('%s',"
 
788
                 "Error[%d] while reading the key pair ('%s',"
561
789
                 " '%s')\n", ret, pubkeyfilename, seckeyfilename);
562
790
    fprintf_plus(stderr, "The GnuTLS error is: %s\n",
563
791
                 safer_gnutls_strerror(ret));
611
839
        }
612
840
        params.size += (unsigned int)bytes_read;
613
841
      }
 
842
      ret = close(dhpfile);
 
843
      if(ret == -1){
 
844
        perror_plus("close");
 
845
      }
614
846
      if(params.data == NULL){
615
847
        dhparamsfilename = NULL;
616
848
      }
625
857
                     safer_gnutls_strerror(ret));
626
858
        dhparamsfilename = NULL;
627
859
      }
 
860
      free(params.data);
628
861
    } while(false);
629
862
  }
630
863
  if(dhparamsfilename == NULL){
631
864
    if(mc->dh_bits == 0){
 
865
#if GNUTLS_VERSION_NUMBER < 0x030600
632
866
      /* Find out the optimal number of DH bits */
633
867
      /* Try to read the private key file */
634
868
      gnutls_datum_t buffer = { .data = NULL, .size = 0 };
714
948
          }
715
949
        }
716
950
      }
717
 
      uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
 
951
      unsigned int uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
718
952
      if(uret != 0){
719
953
        mc->dh_bits = uret;
720
954
        if(debug){
732
966
                     safer_gnutls_strerror(ret));
733
967
        goto globalfail;
734
968
      }
735
 
    } else if(debug){
736
 
      fprintf_plus(stderr, "DH bits explicitly set to %u\n",
737
 
                   mc->dh_bits);
738
 
    }
739
 
    ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
740
 
    if(ret != GNUTLS_E_SUCCESS){
741
 
      fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
742
 
                   " bits): %s\n", mc->dh_bits,
743
 
                   safer_gnutls_strerror(ret));
744
 
      goto globalfail;
 
969
#endif
 
970
    } else {                    /* dh_bits != 0 */
 
971
      if(debug){
 
972
        fprintf_plus(stderr, "DH bits explicitly set to %u\n",
 
973
                     mc->dh_bits);
 
974
      }
 
975
      ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
 
976
      if(ret != GNUTLS_E_SUCCESS){
 
977
        fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
 
978
                     " bits): %s\n", mc->dh_bits,
 
979
                     safer_gnutls_strerror(ret));
 
980
        goto globalfail;
 
981
      }
 
982
      gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
745
983
    }
746
984
  }
747
 
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
748
985
  
749
986
  return 0;
750
987
  
761
998
  int ret;
762
999
  /* GnuTLS session creation */
763
1000
  do {
764
 
    ret = gnutls_init(session, GNUTLS_SERVER);
 
1001
    ret = gnutls_init(session, (GNUTLS_SERVER
 
1002
#if GNUTLS_VERSION_NUMBER >= 0x030506
 
1003
                                | GNUTLS_NO_TICKETS
 
1004
#endif
 
1005
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
1006
                                | GNUTLS_ENABLE_RAWPK
 
1007
#endif
 
1008
                                ));
765
1009
    if(quit_now){
766
1010
      return -1;
767
1011
    }
815
1059
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
816
1060
                      __attribute__((unused)) const char *txt){}
817
1061
 
818
 
/* Set effective uid to 0, return errno */
819
 
__attribute__((warn_unused_result))
820
 
int raise_privileges(void){
821
 
  int old_errno = errno;
822
 
  int ret = 0;
823
 
  if(seteuid(0) == -1){
824
 
    ret = errno;
825
 
  }
826
 
  errno = old_errno;
827
 
  return ret;
828
 
}
829
 
 
830
 
/* Set effective and real user ID to 0.  Return errno. */
831
 
__attribute__((warn_unused_result))
832
 
int raise_privileges_permanently(void){
833
 
  int old_errno = errno;
834
 
  int ret = raise_privileges();
835
 
  if(ret != 0){
836
 
    errno = old_errno;
837
 
    return ret;
838
 
  }
839
 
  if(setuid(0) == -1){
840
 
    ret = errno;
841
 
  }
842
 
  errno = old_errno;
843
 
  return ret;
844
 
}
845
 
 
846
 
/* Set effective user ID to unprivileged saved user ID */
847
 
__attribute__((warn_unused_result))
848
 
int lower_privileges(void){
849
 
  int old_errno = errno;
850
 
  int ret = 0;
851
 
  if(seteuid(uid) == -1){
852
 
    ret = errno;
853
 
  }
854
 
  errno = old_errno;
855
 
  return ret;
856
 
}
857
 
 
858
 
/* Lower privileges permanently */
859
 
__attribute__((warn_unused_result))
860
 
int lower_privileges_permanently(void){
861
 
  int old_errno = errno;
862
 
  int ret = 0;
863
 
  if(setuid(uid) == -1){
864
 
    ret = errno;
865
 
  }
866
 
  errno = old_errno;
867
 
  return ret;
868
 
}
869
 
 
870
1062
/* Helper function to add_local_route() and delete_local_route() */
871
1063
__attribute__((nonnull, warn_unused_result))
872
1064
static bool add_delete_local_route(const bool add,
911
1103
      ret = setgid(0);
912
1104
      if(ret == -1){
913
1105
        perror_plus("setgid");
 
1106
        close(devnull);
914
1107
        _exit(EX_NOPERM);
915
1108
      }
916
1109
      /* Reset supplementary groups */
918
1111
      ret = setgroups(0, NULL);
919
1112
      if(ret == -1){
920
1113
        perror_plus("setgroups");
 
1114
        close(devnull);
921
1115
        _exit(EX_NOPERM);
922
1116
      }
923
1117
    }
924
1118
    ret = dup2(devnull, STDIN_FILENO);
925
1119
    if(ret == -1){
926
1120
      perror_plus("dup2(devnull, STDIN_FILENO)");
 
1121
      close(devnull);
927
1122
      _exit(EX_OSERR);
928
1123
    }
929
1124
    ret = close(devnull);
930
1125
    if(ret == -1){
931
1126
      perror_plus("close");
932
 
      _exit(EX_OSERR);
933
1127
    }
934
1128
    ret = dup2(STDERR_FILENO, STDOUT_FILENO);
935
1129
    if(ret == -1){
970
1164
  }
971
1165
  if(pid == -1){
972
1166
    perror_plus("fork");
 
1167
    close(devnull);
973
1168
    return false;
974
1169
  }
 
1170
  ret = close(devnull);
 
1171
  if(ret == -1){
 
1172
    perror_plus("close");
 
1173
  }
975
1174
  int status;
976
1175
  pid_t pret = -1;
977
1176
  errno = 0;
1077
1276
    bool match = false;
1078
1277
    {
1079
1278
      char *interface = NULL;
1080
 
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
1081
 
                                 interface))){
 
1279
      while((interface = argz_next(mc->interfaces,
 
1280
                                   mc->interfaces_size,
 
1281
                                   interface))){
1082
1282
        if(if_nametoindex(interface) == (unsigned int)if_index){
1083
1283
          match = true;
1084
1284
          break;
1237
1437
           with an explicit route added with the server's address.
1238
1438
           
1239
1439
           Avahi bug reference:
1240
 
           http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
 
1440
           https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1241
1441
           https://bugs.debian.org/587961
1242
1442
        */
1243
1443
        if(debug){
1491
1691
  return retval;
1492
1692
}
1493
1693
 
1494
 
__attribute__((nonnull))
1495
1694
static void resolve_callback(AvahiSServiceResolver *r,
1496
1695
                             AvahiIfIndex interface,
1497
1696
                             AvahiProtocol proto,
1652
1851
      perror_plus("ioctl SIOCGIFFLAGS");
1653
1852
      errno = old_errno;
1654
1853
    }
 
1854
    if((close(s) == -1) and debug){
 
1855
      old_errno = errno;
 
1856
      perror_plus("close");
 
1857
      errno = old_errno;
 
1858
    }
1655
1859
    return false;
1656
1860
  }
 
1861
  if((close(s) == -1) and debug){
 
1862
    old_errno = errno;
 
1863
    perror_plus("close");
 
1864
    errno = old_errno;
 
1865
  }
1657
1866
  return true;
1658
1867
}
1659
1868
 
1920
2129
      return;
1921
2130
    }
1922
2131
  }
 
2132
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
 
2133
  if(devnull == -1){
 
2134
    perror_plus("open(\"/dev/null\", O_RDONLY)");
 
2135
    return;
 
2136
  }
1923
2137
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
1924
2138
                           runnable_hook, alphasort);
1925
2139
  if(numhooks == -1){
1926
2140
    perror_plus("scandir");
 
2141
    close(devnull);
1927
2142
    return;
1928
2143
  }
1929
2144
  struct dirent *direntry;
1930
2145
  int ret;
1931
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1932
 
  if(devnull == -1){
1933
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
1934
 
    return;
1935
 
  }
1936
2146
  for(int i = 0; i < numhooks; i++){
1937
2147
    direntry = direntries[i];
1938
2148
    if(debug){
2194
2404
  
2195
2405
  /* Sleep checking until interface is running.
2196
2406
     Check every 0.25s, up to total time of delay */
2197
 
  for(int i=0; i < delay * 4; i++){
 
2407
  for(int i = 0; i < delay * 4; i++){
2198
2408
    if(interface_is_running(interface)){
2199
2409
      break;
2200
2410
    }
2287
2497
 
2288
2498
int main(int argc, char *argv[]){
2289
2499
  mandos_context mc = { .server = NULL, .dh_bits = 0,
 
2500
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2501
                        .priority = "SECURE128:!CTYPE-X.509"
 
2502
                        ":+CTYPE-RAWPK:!RSA:!VERS-ALL:+VERS-TLS1.3"
 
2503
                        ":%PROFILE_ULTRA",
 
2504
#elif GNUTLS_VERSION_NUMBER < 0x030600
2290
2505
                        .priority = "SECURE256:!CTYPE-X.509"
2291
2506
                        ":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
 
2507
#else
 
2508
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
 
2509
#endif
2292
2510
                        .current_server = NULL, .interfaces = NULL,
2293
2511
                        .interfaces_size = 0 };
2294
2512
  AvahiSServiceBrowser *sb = NULL;
2305
2523
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2306
2524
  const char *seckey = PATHDIR "/" SECKEY;
2307
2525
  const char *pubkey = PATHDIR "/" PUBKEY;
 
2526
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2527
  const char *tls_privkey = PATHDIR "/" TLS_PRIVKEY;
 
2528
  const char *tls_pubkey = PATHDIR "/" TLS_PUBKEY;
 
2529
#endif
2308
2530
  const char *dh_params_file = NULL;
2309
2531
  char *interfaces_hooks = NULL;
2310
2532
  
2358
2580
      { .name = "pubkey", .key = 'p',
2359
2581
        .arg = "FILE",
2360
2582
        .doc = "OpenPGP public key file base name",
2361
 
        .group = 2 },
 
2583
        .group = 1 },
 
2584
      { .name = "tls-privkey", .key = 't',
 
2585
        .arg = "FILE",
 
2586
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2587
        .doc = "TLS private key file base name",
 
2588
#else
 
2589
        .doc = "Dummy; ignored (requires GnuTLS 3.6.6)",
 
2590
#endif
 
2591
        .group = 1 },
 
2592
      { .name = "tls-pubkey", .key = 'T',
 
2593
        .arg = "FILE",
 
2594
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2595
        .doc = "TLS public key file base name",
 
2596
#else
 
2597
        .doc = "Dummy; ignored (requires GnuTLS 3.6.6)",
 
2598
#endif
 
2599
        .group = 1 },
2362
2600
      { .name = "dh-bits", .key = 129,
2363
2601
        .arg = "BITS",
2364
2602
        .doc = "Bit length of the prime number used in the"
2420
2658
      case 'p':                 /* --pubkey */
2421
2659
        pubkey = arg;
2422
2660
        break;
 
2661
      case 't':                 /* --tls-privkey */
 
2662
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2663
        tls_privkey = arg;
 
2664
#endif
 
2665
        break;
 
2666
      case 'T':                 /* --tls-pubkey */
 
2667
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2668
        tls_pubkey = arg;
 
2669
#endif
 
2670
        break;
2423
2671
      case 129:                 /* --dh-bits */
2424
2672
        errno = 0;
2425
2673
        tmpmax = strtoimax(arg, &tmp, 10);
2460
2708
        argp_state_help(state, state->out_stream,
2461
2709
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2462
2710
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
2711
        __builtin_unreachable();
2463
2712
      case -3:                  /* --usage */
2464
2713
        argp_state_help(state, state->out_stream,
2465
2714
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
2715
        __builtin_unreachable();
2466
2716
      case 'V':                 /* --version */
2467
2717
        fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2468
2718
        exit(argp_err_exit_status);
2495
2745
  }
2496
2746
  
2497
2747
  {
2498
 
    /* Work around Debian bug #633582:
2499
 
       <http://bugs.debian.org/633582> */
2500
 
    
2501
2748
    /* Re-raise privileges */
2502
2749
    ret = raise_privileges();
2503
2750
    if(ret != 0){
2506
2753
    } else {
2507
2754
      struct stat st;
2508
2755
      
 
2756
      /* Work around Debian bug #633582:
 
2757
         <https://bugs.debian.org/633582> */
 
2758
 
2509
2759
      if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2510
2760
        int seckey_fd = open(seckey, O_RDONLY);
2511
2761
        if(seckey_fd == -1){
2570
2820
        }
2571
2821
      }
2572
2822
      
 
2823
      /* Work around Debian bug #981302
 
2824
         <https://bugs.debian.org/981302> */
 
2825
      if(lstat("/dev/fd", &st) != 0 and errno == ENOENT){
 
2826
        ret = symlink("/proc/self/fd", "/dev/fd");
 
2827
        if(ret == -1){
 
2828
          perror_plus("Failed to create /dev/fd symlink");
 
2829
        }
 
2830
      }
 
2831
 
2573
2832
      /* Lower privileges */
2574
2833
      ret = lower_privileges();
2575
2834
      if(ret != 0){
2779
3038
    goto end;
2780
3039
  }
2781
3040
  
 
3041
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
3042
  ret = init_gnutls_global(tls_pubkey, tls_privkey, dh_params_file, &mc);
 
3043
#elif GNUTLS_VERSION_NUMBER < 0x030600
2782
3044
  ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
 
3045
#else
 
3046
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
 
3047
#endif
2783
3048
  if(ret == -1){
2784
3049
    fprintf_plus(stderr, "init_gnutls_global failed\n");
2785
3050
    exitcode = EX_UNAVAILABLE;
2957
3222
 end:
2958
3223
  
2959
3224
  if(debug){
2960
 
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
 
3225
    if(signal_received){
 
3226
      fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
 
3227
                   argv[0], signal_received,
 
3228
                   strsignal(signal_received));
 
3229
    } else {
 
3230
      fprintf_plus(stderr, "%s exiting\n", argv[0]);
 
3231
    }
2961
3232
  }
2962
3233
  
2963
3234
  /* Cleanup things */
3015
3286
      /* Take down the network interfaces which were brought up */
3016
3287
      {
3017
3288
        char *interface = NULL;
3018
 
        while((interface=argz_next(interfaces_to_take_down,
3019
 
                                   interfaces_to_take_down_size,
3020
 
                                   interface))){
 
3289
        while((interface = argz_next(interfaces_to_take_down,
 
3290
                                     interfaces_to_take_down_size,
 
3291
                                     interface))){
3021
3292
          ret = take_down_interface(interface);
3022
3293
          if(ret != 0){
3023
3294
            errno = ret;
3052
3323
                                                | O_PATH));
3053
3324
    if(dir_fd == -1){
3054
3325
      perror_plus("open");
 
3326
      return;
3055
3327
    }
3056
3328
    int numentries = scandirat(dir_fd, ".", &direntries,
3057
3329
                               notdotentries, alphasort);
3074
3346
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3075
3347
            dret = 0;
3076
3348
          }
3077
 
          if(dret == -1){
 
3349
          if((dret == -1) and (errno != ENOENT)){
3078
3350
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3079
3351
                         direntries[i]->d_name, strerror(errno));
3080
3352
          }
3084
3356
      
3085
3357
      /* need to clean even if 0 because man page doesn't specify */
3086
3358
      free(direntries);
3087
 
      if(numentries == -1){
3088
 
        perror_plus("scandirat");
3089
 
      }
3090
3359
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
3091
3360
      if(dret == -1 and errno != ENOENT){
3092
3361
        perror_plus("rmdir");