/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/mandos-client.c

  • Committer: Teddy Hogeborn
  • Date: 2021-02-01 19:30:45 UTC
  • mto: This revision was merged to the branch mainline in revision 404.
  • Revision ID: teddy@recompile.se-20210201193045-lpg6aprpc4srem6k
Fix issue with french translation

Initial white space was missing in both msgid and msgstr of the french
translation, leading to checking tools reporing an incomplete
translation.  The string is a raw command line command, and therefore
did not need translation, so this was never a user-visible issue.

* debian/po/fr.po: Add missing whitespace to the id and translation
  for msgid " mandos-keygen -F/dev/null|grep ^key_id".

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-2013 Teddy Hogeborn
13
 
 * Copyright © 2008-2013 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-2020 Teddy Hogeborn
 
13
 * Copyright © 2008-2020 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
 */
32
33
/* Needed by GPGME, specifically gpgme_data_seek() */
33
34
#ifndef _LARGEFILE_SOURCE
34
35
#define _LARGEFILE_SOURCE
35
 
#endif
 
36
#endif  /* not _LARGEFILE_SOURCE */
36
37
#ifndef _FILE_OFFSET_BITS
37
38
#define _FILE_OFFSET_BITS 64
38
 
#endif
 
39
#endif  /* not _FILE_OFFSET_BITS */
39
40
 
40
41
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
41
42
 
42
43
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
43
 
                                   stdout, ferror(), remove() */
 
44
                                   stdout, ferror() */
44
45
#include <stdint.h>             /* uint16_t, uint32_t, intptr_t */
45
46
#include <stddef.h>             /* NULL, size_t, ssize_t */
46
47
#include <stdlib.h>             /* free(), EXIT_SUCCESS, srand(),
47
48
                                   strtof(), abort() */
48
49
#include <stdbool.h>            /* bool, false, true */
49
 
#include <string.h>             /* memset(), strcmp(), strlen(),
50
 
                                   strerror(), asprintf(), strcpy() */
 
50
#include <string.h>             /* strcmp(), strlen(), strerror(),
 
51
                                   asprintf(), strncpy(), strsignal()
 
52
                                */
51
53
#include <sys/ioctl.h>          /* ioctl */
52
54
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
53
55
                                   sockaddr_in6, PF_INET6,
57
59
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
58
60
                                   inet_pton(), connect(),
59
61
                                   getnameinfo() */
60
 
#include <fcntl.h>              /* open() */
 
62
#include <fcntl.h>              /* open(), unlinkat(), AT_REMOVEDIR */
61
63
#include <dirent.h>             /* opendir(), struct dirent, readdir()
62
64
                                 */
63
65
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
64
66
                                   strtoimax() */
65
 
#include <errno.h>              /* perror(), errno,
 
67
#include <errno.h>              /* perror(), errno, EINTR, EINVAL,
 
68
                                   EAI_SYSTEM, ENETUNREACH,
 
69
                                   EHOSTUNREACH, ECONNREFUSED, EPROTO,
 
70
                                   EIO, ENOENT, ENXIO, ENOMEM, EISDIR,
 
71
                                   ENOTEMPTY,
66
72
                                   program_invocation_short_name */
67
73
#include <time.h>               /* nanosleep(), time(), sleep() */
68
74
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
73
79
                                */
74
80
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
75
81
                                   getuid(), getgid(), seteuid(),
76
 
                                   setgid(), pause(), _exit() */
 
82
                                   setgid(), pause(), _exit(),
 
83
                                   unlinkat(), lstat(), symlink() */
77
84
#include <arpa/inet.h>          /* inet_pton(), htons() */
78
85
#include <iso646.h>             /* not, or, and */
79
86
#include <argp.h>               /* struct argp_option, error_t, struct
116
123
                                   gnutls_*
117
124
                                   init_gnutls_session(),
118
125
                                   GNUTLS_* */
 
126
#if GNUTLS_VERSION_NUMBER < 0x030600
119
127
#include <gnutls/openpgp.h>
120
128
                         /* gnutls_certificate_set_openpgp_key_file(),
121
129
                            GNUTLS_OPENPGP_FMT_BASE64 */
 
130
#elif GNUTLS_VERSION_NUMBER >= 0x030606
 
131
#include <gnutls/x509.h>        /* gnutls_pkcs_encrypt_flags_t,
 
132
                                 GNUTLS_PKCS_PLAIN,
 
133
                                 GNUTLS_PKCS_NULL_PASSWORD */
 
134
#endif
122
135
 
123
136
/* GPGME */
124
137
#include <gpgme.h>              /* All GPGME types, constants and
132
145
#define PATHDIR "/conf/conf.d/mandos"
133
146
#define SECKEY "seckey.txt"
134
147
#define PUBKEY "pubkey.txt"
 
148
#define TLS_PRIVKEY "tls-privkey.pem"
 
149
#define TLS_PUBKEY "tls-pubkey.pem"
135
150
#define HOOKDIR "/lib/mandos/network-hooks.d"
136
151
 
137
152
bool debug = false;
138
153
static const char mandos_protocol_version[] = "1";
139
 
const char * argp_program_version = "mandos-client " VERSION;
140
 
const char * argp_program_bug_address = "<mandos@recompile.se>";
 
154
const char *argp_program_version = "mandos-client " VERSION;
 
155
const char *argp_program_bug_address = "<mandos@recompile.se>";
141
156
static const char sys_class_net[] = "/sys/class/net";
142
157
char *connect_to = NULL;
143
158
const char *hookdir = HOOKDIR;
 
159
int hookdir_fd = -1;
144
160
uid_t uid = 65534;
145
161
gid_t gid = 65534;
146
162
 
232
248
                          .af = af };
233
249
  if(new_server->ip == NULL){
234
250
    perror_plus("strdup");
 
251
    free(new_server);
235
252
    return false;
236
253
  }
237
254
  ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
238
255
  if(ret == -1){
239
256
    perror_plus("clock_gettime");
 
257
#ifdef __GNUC__
 
258
#pragma GCC diagnostic push
 
259
#pragma GCC diagnostic ignored "-Wcast-qual"
 
260
#endif
 
261
    free((char *)(new_server->ip));
 
262
#ifdef __GNUC__
 
263
#pragma GCC diagnostic pop
 
264
#endif
 
265
    free(new_server);
240
266
    return false;
241
267
  }
242
268
  /* Special case of first server */
254
280
  return true;
255
281
}
256
282
 
 
283
/* Set effective uid to 0, return errno */
 
284
__attribute__((warn_unused_result))
 
285
int raise_privileges(void){
 
286
  int old_errno = errno;
 
287
  int ret = 0;
 
288
  if(seteuid(0) == -1){
 
289
    ret = errno;
 
290
  }
 
291
  errno = old_errno;
 
292
  return ret;
 
293
}
 
294
 
 
295
/* Set effective and real user ID to 0.  Return errno. */
 
296
__attribute__((warn_unused_result))
 
297
int raise_privileges_permanently(void){
 
298
  int old_errno = errno;
 
299
  int ret = raise_privileges();
 
300
  if(ret != 0){
 
301
    errno = old_errno;
 
302
    return ret;
 
303
  }
 
304
  if(setuid(0) == -1){
 
305
    ret = errno;
 
306
  }
 
307
  errno = old_errno;
 
308
  return ret;
 
309
}
 
310
 
 
311
/* Set effective user ID to unprivileged saved user ID */
 
312
__attribute__((warn_unused_result))
 
313
int lower_privileges(void){
 
314
  int old_errno = errno;
 
315
  int ret = 0;
 
316
  if(seteuid(uid) == -1){
 
317
    ret = errno;
 
318
  }
 
319
  errno = old_errno;
 
320
  return ret;
 
321
}
 
322
 
 
323
/* Lower privileges permanently */
 
324
__attribute__((warn_unused_result))
 
325
int lower_privileges_permanently(void){
 
326
  int old_errno = errno;
 
327
  int ret = 0;
 
328
  if(setuid(uid) == -1){
 
329
    ret = errno;
 
330
  }
 
331
  errno = old_errno;
 
332
  return ret;
 
333
}
 
334
 
257
335
/* 
258
336
 * Initialize GPGME.
259
337
 */
279
357
      return false;
280
358
    }
281
359
    
 
360
    /* Workaround for systems without a real-time clock; see also
 
361
       Debian bug #894495: <https://bugs.debian.org/894495> */
 
362
    do {
 
363
      {
 
364
        time_t currtime = time(NULL);
 
365
        if(currtime != (time_t)-1){
 
366
          struct tm tm;
 
367
          if(gmtime_r(&currtime, &tm) == NULL) {
 
368
            perror_plus("gmtime_r");
 
369
            break;
 
370
          }
 
371
          if(tm.tm_year != 70 or tm.tm_mon != 0){
 
372
            break;
 
373
          }
 
374
          if(debug){
 
375
            fprintf_plus(stderr, "System clock is January 1970");
 
376
          }
 
377
        } else {
 
378
          if(debug){
 
379
            fprintf_plus(stderr, "System clock is invalid");
 
380
          }
 
381
        }
 
382
      }
 
383
      struct stat keystat;
 
384
      ret = fstat(fd, &keystat);
 
385
      if(ret != 0){
 
386
        perror_plus("fstat");
 
387
        break;
 
388
      }
 
389
      ret = raise_privileges();
 
390
      if(ret != 0){
 
391
        errno = ret;
 
392
        perror_plus("Failed to raise privileges");
 
393
        break;
 
394
      }
 
395
      if(debug){
 
396
        fprintf_plus(stderr,
 
397
                     "Setting system clock to key file mtime");
 
398
      }
 
399
      if(clock_settime(CLOCK_REALTIME, &keystat.st_mtim) != 0){
 
400
        perror_plus("clock_settime");
 
401
      }
 
402
      ret = lower_privileges();
 
403
      if(ret != 0){
 
404
        errno = ret;
 
405
        perror_plus("Failed to lower privileges");
 
406
      }
 
407
    } while(false);
 
408
 
282
409
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
283
410
    if(rc != GPG_ERR_NO_ERROR){
284
411
      fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
292
419
                   gpgme_strsource(rc), gpgme_strerror(rc));
293
420
      return false;
294
421
    }
 
422
    {
 
423
      gpgme_import_result_t import_result
 
424
        = gpgme_op_import_result(mc->ctx);
 
425
      if((import_result->imported < 1
 
426
          or import_result->not_imported > 0)
 
427
         and import_result->unchanged == 0){
 
428
        fprintf_plus(stderr, "bad gpgme_op_import_results:\n");
 
429
        fprintf_plus(stderr,
 
430
                     "The total number of considered keys: %d\n",
 
431
                     import_result->considered);
 
432
        fprintf_plus(stderr,
 
433
                     "The number of keys without user ID: %d\n",
 
434
                     import_result->no_user_id);
 
435
        fprintf_plus(stderr,
 
436
                     "The total number of imported keys: %d\n",
 
437
                     import_result->imported);
 
438
        fprintf_plus(stderr, "The number of imported RSA keys: %d\n",
 
439
                     import_result->imported_rsa);
 
440
        fprintf_plus(stderr, "The number of unchanged keys: %d\n",
 
441
                     import_result->unchanged);
 
442
        fprintf_plus(stderr, "The number of new user IDs: %d\n",
 
443
                     import_result->new_user_ids);
 
444
        fprintf_plus(stderr, "The number of new sub keys: %d\n",
 
445
                     import_result->new_sub_keys);
 
446
        fprintf_plus(stderr, "The number of new signatures: %d\n",
 
447
                     import_result->new_signatures);
 
448
        fprintf_plus(stderr, "The number of new revocations: %d\n",
 
449
                     import_result->new_revocations);
 
450
        fprintf_plus(stderr,
 
451
                     "The total number of secret keys read: %d\n",
 
452
                     import_result->secret_read);
 
453
        fprintf_plus(stderr,
 
454
                     "The number of imported secret keys: %d\n",
 
455
                     import_result->secret_imported);
 
456
        fprintf_plus(stderr,
 
457
                     "The number of unchanged secret keys: %d\n",
 
458
                     import_result->secret_unchanged);
 
459
        fprintf_plus(stderr, "The number of keys not imported: %d\n",
 
460
                     import_result->not_imported);
 
461
        for(gpgme_import_status_t import_status
 
462
              = import_result->imports;
 
463
            import_status != NULL;
 
464
            import_status = import_status->next){
 
465
          fprintf_plus(stderr, "Import status for key: %s\n",
 
466
                       import_status->fpr);
 
467
          if(import_status->result != GPG_ERR_NO_ERROR){
 
468
            fprintf_plus(stderr, "Import result: %s: %s\n",
 
469
                         gpgme_strsource(import_status->result),
 
470
                         gpgme_strerror(import_status->result));
 
471
          }
 
472
          fprintf_plus(stderr, "Key status:\n");
 
473
          fprintf_plus(stderr,
 
474
                       import_status->status & GPGME_IMPORT_NEW
 
475
                       ? "The key was new.\n"
 
476
                       : "The key was not new.\n");
 
477
          fprintf_plus(stderr,
 
478
                       import_status->status & GPGME_IMPORT_UID
 
479
                       ? "The key contained new user IDs.\n"
 
480
                       : "The key did not contain new user IDs.\n");
 
481
          fprintf_plus(stderr,
 
482
                       import_status->status & GPGME_IMPORT_SIG
 
483
                       ? "The key contained new signatures.\n"
 
484
                       : "The key did not contain new signatures.\n");
 
485
          fprintf_plus(stderr,
 
486
                       import_status->status & GPGME_IMPORT_SUBKEY
 
487
                       ? "The key contained new sub keys.\n"
 
488
                       : "The key did not contain new sub keys.\n");
 
489
          fprintf_plus(stderr,
 
490
                       import_status->status & GPGME_IMPORT_SECRET
 
491
                       ? "The key contained a secret key.\n"
 
492
                       : "The key did not contain a secret key.\n");
 
493
        }
 
494
        return false;
 
495
      }
 
496
    }
295
497
    
296
 
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
 
498
    ret = close(fd);
297
499
    if(ret == -1){
298
500
      perror_plus("close");
299
501
    }
338
540
  /* Create new GPGME "context" */
339
541
  rc = gpgme_new(&(mc->ctx));
340
542
  if(rc != GPG_ERR_NO_ERROR){
341
 
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
342
 
                 "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
343
 
                 gpgme_strerror(rc));
 
543
    fprintf_plus(stderr, "bad gpgme_new: %s: %s\n",
 
544
                 gpgme_strsource(rc), gpgme_strerror(rc));
344
545
    return false;
345
546
  }
346
547
  
382
583
  /* Create new empty GPGME data buffer for the plaintext */
383
584
  rc = gpgme_data_new(&dh_plain);
384
585
  if(rc != GPG_ERR_NO_ERROR){
385
 
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
386
 
                 "bad gpgme_data_new: %s: %s\n",
 
586
    fprintf_plus(stderr, "bad gpgme_data_new: %s: %s\n",
387
587
                 gpgme_strsource(rc), gpgme_strerror(rc));
388
588
    gpgme_data_release(dh_crypto);
389
589
    return -1;
402
602
      if(result == NULL){
403
603
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
404
604
      } else {
405
 
        fprintf_plus(stderr, "Unsupported algorithm: %s\n",
406
 
                     result->unsupported_algorithm);
407
 
        fprintf_plus(stderr, "Wrong key usage: %u\n",
408
 
                     result->wrong_key_usage);
 
605
        if(result->unsupported_algorithm != NULL) {
 
606
          fprintf_plus(stderr, "Unsupported algorithm: %s\n",
 
607
                       result->unsupported_algorithm);
 
608
        }
 
609
        fprintf_plus(stderr, "Wrong key usage: %s\n",
 
610
                     result->wrong_key_usage ? "Yes" : "No");
409
611
        if(result->file_name != NULL){
410
612
          fprintf_plus(stderr, "File name: %s\n", result->file_name);
411
613
        }
412
 
        gpgme_recipient_t recipient;
413
 
        recipient = result->recipients;
414
 
        while(recipient != NULL){
 
614
 
 
615
        for(gpgme_recipient_t r = result->recipients; r != NULL;
 
616
            r = r->next){
415
617
          fprintf_plus(stderr, "Public key algorithm: %s\n",
416
 
                       gpgme_pubkey_algo_name
417
 
                       (recipient->pubkey_algo));
418
 
          fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
 
618
                       gpgme_pubkey_algo_name(r->pubkey_algo));
 
619
          fprintf_plus(stderr, "Key ID: %s\n", r->keyid);
419
620
          fprintf_plus(stderr, "Secret key available: %s\n",
420
 
                       recipient->status == GPG_ERR_NO_SECKEY
421
 
                       ? "No" : "Yes");
422
 
          recipient = recipient->next;
 
621
                       r->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
423
622
        }
424
623
      }
425
624
    }
481
680
  return plaintext_length;
482
681
}
483
682
 
 
683
__attribute__((warn_unused_result, const))
 
684
static const char *safe_string(const char *str){
 
685
  if(str == NULL)
 
686
    return "(unknown)";
 
687
  return str;
 
688
}
 
689
 
484
690
__attribute__((warn_unused_result))
485
691
static const char *safer_gnutls_strerror(int value){
486
692
  const char *ret = gnutls_strerror(value);
487
 
  if(ret == NULL)
488
 
    ret = "(unknown)";
489
 
  return ret;
 
693
  return safe_string(ret);
490
694
}
491
695
 
492
696
/* GnuTLS log function callback */
496
700
  fprintf_plus(stderr, "GnuTLS: %s", string);
497
701
}
498
702
 
499
 
__attribute__((nonnull, warn_unused_result))
 
703
__attribute__((nonnull(1, 2, 4), warn_unused_result))
500
704
static int init_gnutls_global(const char *pubkeyfilename,
501
705
                              const char *seckeyfilename,
 
706
                              const char *dhparamsfilename,
502
707
                              mandos_context *mc){
503
708
  int ret;
504
709
  
506
711
    fprintf_plus(stderr, "Initializing GnuTLS\n");
507
712
  }
508
713
  
509
 
  ret = gnutls_global_init();
510
 
  if(ret != GNUTLS_E_SUCCESS){
511
 
    fprintf_plus(stderr, "GnuTLS global_init: %s\n",
512
 
                 safer_gnutls_strerror(ret));
513
 
    return -1;
514
 
  }
515
 
  
516
714
  if(debug){
517
715
    /* "Use a log level over 10 to enable all debugging options."
518
716
     * - GnuTLS manual
526
724
  if(ret != GNUTLS_E_SUCCESS){
527
725
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
528
726
                 safer_gnutls_strerror(ret));
529
 
    gnutls_global_deinit();
530
727
    return -1;
531
728
  }
532
729
  
533
730
  if(debug){
534
 
    fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
535
 
                 " secret key %s as GnuTLS credentials\n",
 
731
    fprintf_plus(stderr, "Attempting to use public key %s and"
 
732
                 " private key %s as GnuTLS credentials\n",
536
733
                 pubkeyfilename,
537
734
                 seckeyfilename);
538
735
  }
539
736
  
 
737
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
738
  ret = gnutls_certificate_set_rawpk_key_file
 
739
    (mc->cred, pubkeyfilename, seckeyfilename,
 
740
     GNUTLS_X509_FMT_PEM,       /* format */
 
741
     NULL,                      /* pass */
 
742
     /* key_usage */
 
743
     GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
 
744
     NULL,                      /* names */
 
745
     0,                         /* names_length */
 
746
     /* privkey_flags */
 
747
     GNUTLS_PKCS_PLAIN | GNUTLS_PKCS_NULL_PASSWORD,
 
748
     0);                        /* pkcs11_flags */
 
749
#elif GNUTLS_VERSION_NUMBER < 0x030600
540
750
  ret = gnutls_certificate_set_openpgp_key_file
541
751
    (mc->cred, pubkeyfilename, seckeyfilename,
542
752
     GNUTLS_OPENPGP_FMT_BASE64);
 
753
#else
 
754
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
 
755
#endif
543
756
  if(ret != GNUTLS_E_SUCCESS){
544
757
    fprintf_plus(stderr,
545
 
                 "Error[%d] while reading the OpenPGP key pair ('%s',"
 
758
                 "Error[%d] while reading the key pair ('%s',"
546
759
                 " '%s')\n", ret, pubkeyfilename, seckeyfilename);
547
760
    fprintf_plus(stderr, "The GnuTLS error is: %s\n",
548
761
                 safer_gnutls_strerror(ret));
557
770
                 safer_gnutls_strerror(ret));
558
771
    goto globalfail;
559
772
  }
560
 
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
561
 
  if(ret != GNUTLS_E_SUCCESS){
562
 
    fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
563
 
                 safer_gnutls_strerror(ret));
564
 
    goto globalfail;
565
 
  }
566
 
  
567
 
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
 
773
  /* If a Diffie-Hellman parameters file was given, try to use it */
 
774
  if(dhparamsfilename != NULL){
 
775
    gnutls_datum_t params = { .data = NULL, .size = 0 };
 
776
    do {
 
777
      int dhpfile = open(dhparamsfilename, O_RDONLY);
 
778
      if(dhpfile == -1){
 
779
        perror_plus("open");
 
780
        dhparamsfilename = NULL;
 
781
        break;
 
782
      }
 
783
      size_t params_capacity = 0;
 
784
      while(true){
 
785
        params_capacity = incbuffer((char **)&params.data,
 
786
                                    (size_t)params.size,
 
787
                                    (size_t)params_capacity);
 
788
        if(params_capacity == 0){
 
789
          perror_plus("incbuffer");
 
790
          free(params.data);
 
791
          params.data = NULL;
 
792
          dhparamsfilename = NULL;
 
793
          break;
 
794
        }
 
795
        ssize_t bytes_read = read(dhpfile,
 
796
                                  params.data + params.size,
 
797
                                  BUFFER_SIZE);
 
798
        /* EOF */
 
799
        if(bytes_read == 0){
 
800
          break;
 
801
        }
 
802
        /* check bytes_read for failure */
 
803
        if(bytes_read < 0){
 
804
          perror_plus("read");
 
805
          free(params.data);
 
806
          params.data = NULL;
 
807
          dhparamsfilename = NULL;
 
808
          break;
 
809
        }
 
810
        params.size += (unsigned int)bytes_read;
 
811
      }
 
812
      ret = close(dhpfile);
 
813
      if(ret == -1){
 
814
        perror_plus("close");
 
815
      }
 
816
      if(params.data == NULL){
 
817
        dhparamsfilename = NULL;
 
818
      }
 
819
      if(dhparamsfilename == NULL){
 
820
        break;
 
821
      }
 
822
      ret = gnutls_dh_params_import_pkcs3(mc->dh_params, &params,
 
823
                                          GNUTLS_X509_FMT_PEM);
 
824
      if(ret != GNUTLS_E_SUCCESS){
 
825
        fprintf_plus(stderr, "Failed to parse DH parameters in file"
 
826
                     " \"%s\": %s\n", dhparamsfilename,
 
827
                     safer_gnutls_strerror(ret));
 
828
        dhparamsfilename = NULL;
 
829
      }
 
830
      free(params.data);
 
831
    } while(false);
 
832
  }
 
833
  if(dhparamsfilename == NULL){
 
834
    if(mc->dh_bits == 0){
 
835
#if GNUTLS_VERSION_NUMBER < 0x030600
 
836
      /* Find out the optimal number of DH bits */
 
837
      /* Try to read the private key file */
 
838
      gnutls_datum_t buffer = { .data = NULL, .size = 0 };
 
839
      do {
 
840
        int secfile = open(seckeyfilename, O_RDONLY);
 
841
        if(secfile == -1){
 
842
          perror_plus("open");
 
843
          break;
 
844
        }
 
845
        size_t buffer_capacity = 0;
 
846
        while(true){
 
847
          buffer_capacity = incbuffer((char **)&buffer.data,
 
848
                                      (size_t)buffer.size,
 
849
                                      (size_t)buffer_capacity);
 
850
          if(buffer_capacity == 0){
 
851
            perror_plus("incbuffer");
 
852
            free(buffer.data);
 
853
            buffer.data = NULL;
 
854
            break;
 
855
          }
 
856
          ssize_t bytes_read = read(secfile,
 
857
                                    buffer.data + buffer.size,
 
858
                                    BUFFER_SIZE);
 
859
          /* EOF */
 
860
          if(bytes_read == 0){
 
861
            break;
 
862
          }
 
863
          /* check bytes_read for failure */
 
864
          if(bytes_read < 0){
 
865
            perror_plus("read");
 
866
            free(buffer.data);
 
867
            buffer.data = NULL;
 
868
            break;
 
869
          }
 
870
          buffer.size += (unsigned int)bytes_read;
 
871
        }
 
872
        close(secfile);
 
873
      } while(false);
 
874
      /* If successful, use buffer to parse private key */
 
875
      gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
876
      if(buffer.data != NULL){
 
877
        {
 
878
          gnutls_openpgp_privkey_t privkey = NULL;
 
879
          ret = gnutls_openpgp_privkey_init(&privkey);
 
880
          if(ret != GNUTLS_E_SUCCESS){
 
881
            fprintf_plus(stderr, "Error initializing OpenPGP key"
 
882
                         " structure: %s",
 
883
                         safer_gnutls_strerror(ret));
 
884
            free(buffer.data);
 
885
            buffer.data = NULL;
 
886
          } else {
 
887
            ret = gnutls_openpgp_privkey_import
 
888
              (privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
 
889
            if(ret != GNUTLS_E_SUCCESS){
 
890
              fprintf_plus(stderr, "Error importing OpenPGP key : %s",
 
891
                           safer_gnutls_strerror(ret));
 
892
              privkey = NULL;
 
893
            }
 
894
            free(buffer.data);
 
895
            buffer.data = NULL;
 
896
            if(privkey != NULL){
 
897
              /* Use private key to suggest an appropriate
 
898
                 sec_param */
 
899
              sec_param = gnutls_openpgp_privkey_sec_param(privkey);
 
900
              gnutls_openpgp_privkey_deinit(privkey);
 
901
              if(debug){
 
902
                fprintf_plus(stderr, "This OpenPGP key implies using"
 
903
                             " a GnuTLS security parameter \"%s\".\n",
 
904
                             safe_string(gnutls_sec_param_get_name
 
905
                                         (sec_param)));
 
906
              }
 
907
            }
 
908
          }
 
909
        }
 
910
        if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
 
911
          /* Err on the side of caution */
 
912
          sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
913
          if(debug){
 
914
            fprintf_plus(stderr, "Falling back to security parameter"
 
915
                         " \"%s\"\n",
 
916
                         safe_string(gnutls_sec_param_get_name
 
917
                                     (sec_param)));
 
918
          }
 
919
        }
 
920
      }
 
921
      unsigned int uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
 
922
      if(uret != 0){
 
923
        mc->dh_bits = uret;
 
924
        if(debug){
 
925
          fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
 
926
                       " implies %u DH bits; using that.\n",
 
927
                       safe_string(gnutls_sec_param_get_name
 
928
                                   (sec_param)),
 
929
                       mc->dh_bits);
 
930
        }
 
931
      } else {
 
932
        fprintf_plus(stderr, "Failed to get implied number of DH"
 
933
                     " bits for security parameter \"%s\"): %s\n",
 
934
                     safe_string(gnutls_sec_param_get_name
 
935
                                 (sec_param)),
 
936
                     safer_gnutls_strerror(ret));
 
937
        goto globalfail;
 
938
      }
 
939
#endif
 
940
    } else {                    /* dh_bits != 0 */
 
941
      if(debug){
 
942
        fprintf_plus(stderr, "DH bits explicitly set to %u\n",
 
943
                     mc->dh_bits);
 
944
      }
 
945
      ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
 
946
      if(ret != GNUTLS_E_SUCCESS){
 
947
        fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
 
948
                     " bits): %s\n", mc->dh_bits,
 
949
                     safer_gnutls_strerror(ret));
 
950
        goto globalfail;
 
951
      }
 
952
      gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
 
953
    }
 
954
  }
568
955
  
569
956
  return 0;
570
957
  
571
958
 globalfail:
572
959
  
573
960
  gnutls_certificate_free_credentials(mc->cred);
574
 
  gnutls_global_deinit();
575
961
  gnutls_dh_params_deinit(mc->dh_params);
576
962
  return -1;
577
963
}
582
968
  int ret;
583
969
  /* GnuTLS session creation */
584
970
  do {
585
 
    ret = gnutls_init(session, GNUTLS_SERVER);
 
971
    ret = gnutls_init(session, (GNUTLS_SERVER
 
972
#if GNUTLS_VERSION_NUMBER >= 0x030506
 
973
                                | GNUTLS_NO_TICKETS
 
974
#endif
 
975
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
976
                                | GNUTLS_ENABLE_RAWPK
 
977
#endif
 
978
                                ));
586
979
    if(quit_now){
587
980
      return -1;
588
981
    }
629
1022
  /* ignore client certificate if any. */
630
1023
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
631
1024
  
632
 
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
633
 
  
634
1025
  return 0;
635
1026
}
636
1027
 
638
1029
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
639
1030
                      __attribute__((unused)) const char *txt){}
640
1031
 
 
1032
/* Helper function to add_local_route() and delete_local_route() */
 
1033
__attribute__((nonnull, warn_unused_result))
 
1034
static bool add_delete_local_route(const bool add,
 
1035
                                   const char *address,
 
1036
                                   AvahiIfIndex if_index){
 
1037
  int ret;
 
1038
  char helper[] = "mandos-client-iprouteadddel";
 
1039
  char add_arg[] = "add";
 
1040
  char delete_arg[] = "delete";
 
1041
  char debug_flag[] = "--debug";
 
1042
  char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
 
1043
  if(pluginhelperdir == NULL){
 
1044
    if(debug){
 
1045
      fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
 
1046
                   " variable not set; cannot run helper\n");
 
1047
    }
 
1048
    return false;
 
1049
  }
 
1050
  
 
1051
  char interface[IF_NAMESIZE];
 
1052
  if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
1053
    perror_plus("if_indextoname");
 
1054
    return false;
 
1055
  }
 
1056
  
 
1057
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
 
1058
  if(devnull == -1){
 
1059
    perror_plus("open(\"/dev/null\", O_RDONLY)");
 
1060
    return false;
 
1061
  }
 
1062
  pid_t pid = fork();
 
1063
  if(pid == 0){
 
1064
    /* Child */
 
1065
    /* Raise privileges */
 
1066
    errno = raise_privileges_permanently();
 
1067
    if(errno != 0){
 
1068
      perror_plus("Failed to raise privileges");
 
1069
      /* _exit(EX_NOPERM); */
 
1070
    } else {
 
1071
      /* Set group */
 
1072
      errno = 0;
 
1073
      ret = setgid(0);
 
1074
      if(ret == -1){
 
1075
        perror_plus("setgid");
 
1076
        close(devnull);
 
1077
        _exit(EX_NOPERM);
 
1078
      }
 
1079
      /* Reset supplementary groups */
 
1080
      errno = 0;
 
1081
      ret = setgroups(0, NULL);
 
1082
      if(ret == -1){
 
1083
        perror_plus("setgroups");
 
1084
        close(devnull);
 
1085
        _exit(EX_NOPERM);
 
1086
      }
 
1087
    }
 
1088
    ret = dup2(devnull, STDIN_FILENO);
 
1089
    if(ret == -1){
 
1090
      perror_plus("dup2(devnull, STDIN_FILENO)");
 
1091
      close(devnull);
 
1092
      _exit(EX_OSERR);
 
1093
    }
 
1094
    ret = close(devnull);
 
1095
    if(ret == -1){
 
1096
      perror_plus("close");
 
1097
    }
 
1098
    ret = dup2(STDERR_FILENO, STDOUT_FILENO);
 
1099
    if(ret == -1){
 
1100
      perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
 
1101
      _exit(EX_OSERR);
 
1102
    }
 
1103
    int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
 
1104
                                                    O_RDONLY
 
1105
                                                    | O_DIRECTORY
 
1106
                                                    | O_PATH
 
1107
                                                    | O_CLOEXEC));
 
1108
    if(helperdir_fd == -1){
 
1109
      perror_plus("open");
 
1110
      _exit(EX_UNAVAILABLE);
 
1111
    }
 
1112
    int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
 
1113
                                                   helper, O_RDONLY));
 
1114
    if(helper_fd == -1){
 
1115
      perror_plus("openat");
 
1116
      close(helperdir_fd);
 
1117
      _exit(EX_UNAVAILABLE);
 
1118
    }
 
1119
    close(helperdir_fd);
 
1120
#ifdef __GNUC__
 
1121
#pragma GCC diagnostic push
 
1122
#pragma GCC diagnostic ignored "-Wcast-qual"
 
1123
#endif
 
1124
    if(fexecve(helper_fd, (char *const [])
 
1125
               { helper, add ? add_arg : delete_arg, (char *)address,
 
1126
                   interface, debug ? debug_flag : NULL, NULL },
 
1127
               environ) == -1){
 
1128
#ifdef __GNUC__
 
1129
#pragma GCC diagnostic pop
 
1130
#endif
 
1131
      perror_plus("fexecve");
 
1132
      _exit(EXIT_FAILURE);
 
1133
    }
 
1134
  }
 
1135
  if(pid == -1){
 
1136
    perror_plus("fork");
 
1137
    close(devnull);
 
1138
    return false;
 
1139
  }
 
1140
  ret = close(devnull);
 
1141
  if(ret == -1){
 
1142
    perror_plus("close");
 
1143
  }
 
1144
  int status;
 
1145
  pid_t pret = -1;
 
1146
  errno = 0;
 
1147
  do {
 
1148
    pret = waitpid(pid, &status, 0);
 
1149
    if(pret == -1 and errno == EINTR and quit_now){
 
1150
      int errno_raising = 0;
 
1151
      if((errno = raise_privileges()) != 0){
 
1152
        errno_raising = errno;
 
1153
        perror_plus("Failed to raise privileges in order to"
 
1154
                    " kill helper program");
 
1155
      }
 
1156
      if(kill(pid, SIGTERM) == -1){
 
1157
        perror_plus("kill");
 
1158
      }
 
1159
      if((errno_raising == 0) and (errno = lower_privileges()) != 0){
 
1160
        perror_plus("Failed to lower privileges after killing"
 
1161
                    " helper program");
 
1162
      }
 
1163
      return false;
 
1164
    }
 
1165
  } while(pret == -1 and errno == EINTR);
 
1166
  if(pret == -1){
 
1167
    perror_plus("waitpid");
 
1168
    return false;
 
1169
  }
 
1170
  if(WIFEXITED(status)){
 
1171
    if(WEXITSTATUS(status) != 0){
 
1172
      fprintf_plus(stderr, "Error: iprouteadddel exited"
 
1173
                   " with status %d\n", WEXITSTATUS(status));
 
1174
      return false;
 
1175
    }
 
1176
    return true;
 
1177
  }
 
1178
  if(WIFSIGNALED(status)){
 
1179
    fprintf_plus(stderr, "Error: iprouteadddel died by"
 
1180
                 " signal %d\n", WTERMSIG(status));
 
1181
    return false;
 
1182
  }
 
1183
  fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
 
1184
  return false;
 
1185
}
 
1186
 
 
1187
__attribute__((nonnull, warn_unused_result))
 
1188
static bool add_local_route(const char *address,
 
1189
                            AvahiIfIndex if_index){
 
1190
  if(debug){
 
1191
    fprintf_plus(stderr, "Adding route to %s\n", address);
 
1192
  }
 
1193
  return add_delete_local_route(true, address, if_index);
 
1194
}
 
1195
 
 
1196
__attribute__((nonnull, warn_unused_result))
 
1197
static bool delete_local_route(const char *address,
 
1198
                               AvahiIfIndex if_index){
 
1199
  if(debug){
 
1200
    fprintf_plus(stderr, "Removing route to %s\n", address);
 
1201
  }
 
1202
  return add_delete_local_route(false, address, if_index);
 
1203
}
 
1204
 
641
1205
/* Called when a Mandos server is found */
642
1206
__attribute__((nonnull, warn_unused_result))
643
1207
static int start_mandos_communication(const char *ip, in_port_t port,
654
1218
  int retval = -1;
655
1219
  gnutls_session_t session;
656
1220
  int pf;                       /* Protocol family */
 
1221
  bool route_added = false;
657
1222
  
658
1223
  errno = 0;
659
1224
  
681
1246
    bool match = false;
682
1247
    {
683
1248
      char *interface = NULL;
684
 
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
685
 
                                 interface))){
 
1249
      while((interface = argz_next(mc->interfaces,
 
1250
                                   mc->interfaces_size,
 
1251
                                   interface))){
686
1252
        if(if_nametoindex(interface) == (unsigned int)if_index){
687
1253
          match = true;
688
1254
          break;
717
1283
                 PRIuMAX "\n", ip, (uintmax_t)port);
718
1284
  }
719
1285
  
720
 
  tcp_sd = socket(pf, SOCK_STREAM, 0);
 
1286
  tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
721
1287
  if(tcp_sd < 0){
722
1288
    int e = errno;
723
1289
    perror_plus("socket");
730
1296
    goto mandos_end;
731
1297
  }
732
1298
  
733
 
  memset(&to, 0, sizeof(to));
734
1299
  if(af == AF_INET6){
735
 
    ((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
736
 
    ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
 
1300
    struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
 
1301
    *to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
 
1302
    ret = inet_pton(af, ip, &to6->sin6_addr);
737
1303
  } else {                      /* IPv4 */
738
 
    ((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
739
 
    ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
 
1304
    struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
 
1305
    *to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
 
1306
    ret = inet_pton(af, ip, &to4->sin_addr);
740
1307
  }
741
1308
  if(ret < 0 ){
742
1309
    int e = errno;
812
1379
    goto mandos_end;
813
1380
  }
814
1381
  
815
 
  if(af == AF_INET6){
816
 
    ret = connect(tcp_sd, (struct sockaddr *)&to,
817
 
                  sizeof(struct sockaddr_in6));
818
 
  } else {
819
 
    ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
820
 
                  sizeof(struct sockaddr_in));
821
 
  }
822
 
  if(ret < 0){
823
 
    if((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
824
 
      int e = errno;
825
 
      perror_plus("connect");
826
 
      errno = e;
827
 
    }
828
 
    goto mandos_end;
829
 
  }
830
 
  
831
 
  if(quit_now){
832
 
    errno = EINTR;
833
 
    goto mandos_end;
 
1382
  while(true){
 
1383
    if(af == AF_INET6){
 
1384
      ret = connect(tcp_sd, (struct sockaddr *)&to,
 
1385
                    sizeof(struct sockaddr_in6));
 
1386
    } else {
 
1387
      ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
 
1388
                    sizeof(struct sockaddr_in));
 
1389
    }
 
1390
    if(ret < 0){
 
1391
      if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
 
1392
         and if_index != AVAHI_IF_UNSPEC
 
1393
         and connect_to == NULL
 
1394
         and not route_added and
 
1395
         ((af == AF_INET6 and not
 
1396
           IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
 
1397
                                    &to)->sin6_addr)))
 
1398
          or (af == AF_INET and
 
1399
              /* Not a a IPv4LL address */
 
1400
              (ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
 
1401
               & 0xFFFF0000L) != 0xA9FE0000L))){
 
1402
        /* Work around Avahi bug - Avahi does not announce link-local
 
1403
           addresses if it has a global address, so local hosts with
 
1404
           *only* a link-local address (e.g. Mandos clients) cannot
 
1405
           connect to a Mandos server announced by Avahi on a server
 
1406
           host with a global address.  Work around this by retrying
 
1407
           with an explicit route added with the server's address.
 
1408
           
 
1409
           Avahi bug reference:
 
1410
           https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
 
1411
           https://bugs.debian.org/587961
 
1412
        */
 
1413
        if(debug){
 
1414
          fprintf_plus(stderr, "Mandos server unreachable, trying"
 
1415
                       " direct route\n");
 
1416
        }
 
1417
        int e = errno;
 
1418
        route_added = add_local_route(ip, if_index);
 
1419
        if(route_added){
 
1420
          continue;
 
1421
        }
 
1422
        errno = e;
 
1423
      }
 
1424
      if(errno != ECONNREFUSED or debug){
 
1425
        int e = errno;
 
1426
        perror_plus("connect");
 
1427
        errno = e;
 
1428
      }
 
1429
      goto mandos_end;
 
1430
    }
 
1431
    
 
1432
    if(quit_now){
 
1433
      errno = EINTR;
 
1434
      goto mandos_end;
 
1435
    }
 
1436
    break;
834
1437
  }
835
1438
  
836
1439
  const char *out = mandos_protocol_version;
990
1593
                                               &decrypted_buffer, mc);
991
1594
    if(decrypted_buffer_size >= 0){
992
1595
      
 
1596
      clearerr(stdout);
993
1597
      written = 0;
994
1598
      while(written < (size_t) decrypted_buffer_size){
995
1599
        if(quit_now){
1011
1615
        }
1012
1616
        written += (size_t)ret;
1013
1617
      }
 
1618
      ret = fflush(stdout);
 
1619
      if(ret != 0){
 
1620
        int e = errno;
 
1621
        if(debug){
 
1622
          fprintf_plus(stderr, "Error writing encrypted data: %s\n",
 
1623
                       strerror(errno));
 
1624
        }
 
1625
        errno = e;
 
1626
        goto mandos_end;
 
1627
      }
1014
1628
      retval = 0;
1015
1629
    }
1016
1630
  }
1019
1633
  
1020
1634
 mandos_end:
1021
1635
  {
 
1636
    if(route_added){
 
1637
      if(not delete_local_route(ip, if_index)){
 
1638
        fprintf_plus(stderr, "Failed to delete local route to %s on"
 
1639
                     " interface %d", ip, if_index);
 
1640
      }
 
1641
    }
1022
1642
    int e = errno;
1023
1643
    free(decrypted_buffer);
1024
1644
    free(buffer);
1025
1645
    if(tcp_sd >= 0){
1026
 
      ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
 
1646
      ret = close(tcp_sd);
1027
1647
    }
1028
1648
    if(ret == -1){
1029
1649
      if(e == 0){
1041
1661
  return retval;
1042
1662
}
1043
1663
 
1044
 
__attribute__((nonnull))
1045
1664
static void resolve_callback(AvahiSServiceResolver *r,
1046
1665
                             AvahiIfIndex interface,
1047
1666
                             AvahiProtocol proto,
1064
1683
     timed out */
1065
1684
  
1066
1685
  if(quit_now){
 
1686
    avahi_s_service_resolver_free(r);
1067
1687
    return;
1068
1688
  }
1069
1689
  
1183
1803
__attribute__((nonnull, warn_unused_result))
1184
1804
bool get_flags(const char *ifname, struct ifreq *ifr){
1185
1805
  int ret;
1186
 
  error_t ret_errno;
 
1806
  int old_errno;
1187
1807
  
1188
1808
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1189
1809
  if(s < 0){
1190
 
    ret_errno = errno;
 
1810
    old_errno = errno;
1191
1811
    perror_plus("socket");
1192
 
    errno = ret_errno;
 
1812
    errno = old_errno;
1193
1813
    return false;
1194
1814
  }
1195
 
  strcpy(ifr->ifr_name, ifname);
 
1815
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
 
1816
  ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1196
1817
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1197
1818
  if(ret == -1){
1198
1819
    if(debug){
1199
 
      ret_errno = errno;
 
1820
      old_errno = errno;
1200
1821
      perror_plus("ioctl SIOCGIFFLAGS");
1201
 
      errno = ret_errno;
 
1822
      errno = old_errno;
 
1823
    }
 
1824
    if((close(s) == -1) and debug){
 
1825
      old_errno = errno;
 
1826
      perror_plus("close");
 
1827
      errno = old_errno;
1202
1828
    }
1203
1829
    return false;
1204
1830
  }
 
1831
  if((close(s) == -1) and debug){
 
1832
    old_errno = errno;
 
1833
    perror_plus("close");
 
1834
    errno = old_errno;
 
1835
  }
1205
1836
  return true;
1206
1837
}
1207
1838
 
1335
1966
  sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1336
1967
                "abcdefghijklmnopqrstuvwxyz"
1337
1968
                "0123456789"
1338
 
                "_-");
 
1969
                "_.-");
1339
1970
  if((direntry->d_name)[sret] != '\0'){
1340
1971
    /* Contains non-allowed characters */
1341
1972
    if(debug){
1345
1976
    return 0;
1346
1977
  }
1347
1978
  
1348
 
  char *fullname = NULL;
1349
 
  ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1350
 
  if(ret < 0){
1351
 
    perror_plus("asprintf");
1352
 
    return 0;
1353
 
  }
1354
 
  
1355
 
  ret = stat(fullname, &st);
 
1979
  ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1356
1980
  if(ret == -1){
1357
1981
    if(debug){
1358
1982
      perror_plus("Could not stat hook");
1456
2080
  }
1457
2081
}
1458
2082
 
1459
 
/* Set effective uid to 0, return errno */
1460
 
__attribute__((warn_unused_result))
1461
 
error_t raise_privileges(void){
1462
 
  error_t old_errno = errno;
1463
 
  error_t ret_errno = 0;
1464
 
  if(seteuid(0) == -1){
1465
 
    ret_errno = errno;
1466
 
    perror_plus("seteuid");
1467
 
  }
1468
 
  errno = old_errno;
1469
 
  return ret_errno;
1470
 
}
1471
 
 
1472
 
/* Set effective and real user ID to 0.  Return errno. */
1473
 
__attribute__((warn_unused_result))
1474
 
error_t raise_privileges_permanently(void){
1475
 
  error_t old_errno = errno;
1476
 
  error_t ret_errno = raise_privileges();
1477
 
  if(ret_errno != 0){
1478
 
    errno = old_errno;
1479
 
    return ret_errno;
1480
 
  }
1481
 
  if(setuid(0) == -1){
1482
 
    ret_errno = errno;
1483
 
    perror_plus("seteuid");
1484
 
  }
1485
 
  errno = old_errno;
1486
 
  return ret_errno;
1487
 
}
1488
 
 
1489
 
/* Set effective user ID to unprivileged saved user ID */
1490
 
__attribute__((warn_unused_result))
1491
 
error_t lower_privileges(void){
1492
 
  error_t old_errno = errno;
1493
 
  error_t ret_errno = 0;
1494
 
  if(seteuid(uid) == -1){
1495
 
    ret_errno = errno;
1496
 
    perror_plus("seteuid");
1497
 
  }
1498
 
  errno = old_errno;
1499
 
  return ret_errno;
1500
 
}
1501
 
 
1502
 
/* Lower privileges permanently */
1503
 
__attribute__((warn_unused_result))
1504
 
error_t lower_privileges_permanently(void){
1505
 
  error_t old_errno = errno;
1506
 
  error_t ret_errno = 0;
1507
 
  if(setuid(uid) == -1){
1508
 
    ret_errno = errno;
1509
 
    perror_plus("setuid");
1510
 
  }
1511
 
  errno = old_errno;
1512
 
  return ret_errno;
1513
 
}
1514
 
 
1515
2083
__attribute__((nonnull))
1516
2084
void run_network_hooks(const char *mode, const char *interface,
1517
2085
                       const float delay){
1518
 
  struct dirent **direntries;
1519
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1520
 
                         alphasort);
 
2086
  struct dirent **direntries = NULL;
 
2087
  if(hookdir_fd == -1){
 
2088
    hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
 
2089
                      | O_CLOEXEC);
 
2090
    if(hookdir_fd == -1){
 
2091
      if(errno == ENOENT){
 
2092
        if(debug){
 
2093
          fprintf_plus(stderr, "Network hook directory \"%s\" not"
 
2094
                       " found\n", hookdir);
 
2095
        }
 
2096
      } else {
 
2097
        perror_plus("open");
 
2098
      }
 
2099
      return;
 
2100
    }
 
2101
  }
 
2102
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
 
2103
  if(devnull == -1){
 
2104
    perror_plus("open(\"/dev/null\", O_RDONLY)");
 
2105
    return;
 
2106
  }
 
2107
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
 
2108
                           runnable_hook, alphasort);
1521
2109
  if(numhooks == -1){
1522
 
    if(errno == ENOENT){
1523
 
      if(debug){
1524
 
        fprintf_plus(stderr, "Network hook directory \"%s\" not"
1525
 
                     " found\n", hookdir);
1526
 
      }
1527
 
    } else {
1528
 
      perror_plus("scandir");
 
2110
    perror_plus("scandir");
 
2111
    close(devnull);
 
2112
    return;
 
2113
  }
 
2114
  struct dirent *direntry;
 
2115
  int ret;
 
2116
  for(int i = 0; i < numhooks; i++){
 
2117
    direntry = direntries[i];
 
2118
    if(debug){
 
2119
      fprintf_plus(stderr, "Running network hook \"%s\"\n",
 
2120
                   direntry->d_name);
1529
2121
    }
1530
 
  } else {
1531
 
    struct dirent *direntry;
1532
 
    int ret;
1533
 
    int devnull = open("/dev/null", O_RDONLY);
1534
 
    for(int i = 0; i < numhooks; i++){
1535
 
      direntry = direntries[i];
1536
 
      char *fullname = NULL;
1537
 
      ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1538
 
      if(ret < 0){
 
2122
    pid_t hook_pid = fork();
 
2123
    if(hook_pid == 0){
 
2124
      /* Child */
 
2125
      /* Raise privileges */
 
2126
      errno = raise_privileges_permanently();
 
2127
      if(errno != 0){
 
2128
        perror_plus("Failed to raise privileges");
 
2129
        _exit(EX_NOPERM);
 
2130
      }
 
2131
      /* Set group */
 
2132
      errno = 0;
 
2133
      ret = setgid(0);
 
2134
      if(ret == -1){
 
2135
        perror_plus("setgid");
 
2136
        _exit(EX_NOPERM);
 
2137
      }
 
2138
      /* Reset supplementary groups */
 
2139
      errno = 0;
 
2140
      ret = setgroups(0, NULL);
 
2141
      if(ret == -1){
 
2142
        perror_plus("setgroups");
 
2143
        _exit(EX_NOPERM);
 
2144
      }
 
2145
      ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
 
2146
      if(ret == -1){
 
2147
        perror_plus("setenv");
 
2148
        _exit(EX_OSERR);
 
2149
      }
 
2150
      ret = setenv("DEVICE", interface, 1);
 
2151
      if(ret == -1){
 
2152
        perror_plus("setenv");
 
2153
        _exit(EX_OSERR);
 
2154
      }
 
2155
      ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
 
2156
      if(ret == -1){
 
2157
        perror_plus("setenv");
 
2158
        _exit(EX_OSERR);
 
2159
      }
 
2160
      ret = setenv("MODE", mode, 1);
 
2161
      if(ret == -1){
 
2162
        perror_plus("setenv");
 
2163
        _exit(EX_OSERR);
 
2164
      }
 
2165
      char *delaystring;
 
2166
      ret = asprintf(&delaystring, "%f", (double)delay);
 
2167
      if(ret == -1){
1539
2168
        perror_plus("asprintf");
1540
 
        continue;
1541
 
      }
1542
 
      if(debug){
1543
 
        fprintf_plus(stderr, "Running network hook \"%s\"\n",
1544
 
                     direntry->d_name);
1545
 
      }
1546
 
      pid_t hook_pid = fork();
1547
 
      if(hook_pid == 0){
1548
 
        /* Child */
1549
 
        /* Raise privileges */
1550
 
        if(raise_privileges_permanently() != 0){
1551
 
          perror_plus("Failed to raise privileges");
1552
 
          _exit(EX_NOPERM);
1553
 
        }
1554
 
        /* Set group */
1555
 
        errno = 0;
1556
 
        ret = setgid(0);
1557
 
        if(ret == -1){
1558
 
          perror_plus("setgid");
1559
 
          _exit(EX_NOPERM);
1560
 
        }
1561
 
        /* Reset supplementary groups */
1562
 
        errno = 0;
1563
 
        ret = setgroups(0, NULL);
1564
 
        if(ret == -1){
1565
 
          perror_plus("setgroups");
1566
 
          _exit(EX_NOPERM);
1567
 
        }
1568
 
        ret = dup2(devnull, STDIN_FILENO);
1569
 
        if(ret == -1){
1570
 
          perror_plus("dup2(devnull, STDIN_FILENO)");
1571
 
          _exit(EX_OSERR);
1572
 
        }
1573
 
        ret = close(devnull);
1574
 
        if(ret == -1){
1575
 
          perror_plus("close");
1576
 
          _exit(EX_OSERR);
1577
 
        }
1578
 
        ret = dup2(STDERR_FILENO, STDOUT_FILENO);
1579
 
        if(ret == -1){
1580
 
          perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
1581
 
          _exit(EX_OSERR);
1582
 
        }
1583
 
        ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1584
 
        if(ret == -1){
1585
 
          perror_plus("setenv");
1586
 
          _exit(EX_OSERR);
1587
 
        }
1588
 
        ret = setenv("DEVICE", interface, 1);
1589
 
        if(ret == -1){
1590
 
          perror_plus("setenv");
1591
 
          _exit(EX_OSERR);
1592
 
        }
1593
 
        ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1594
 
        if(ret == -1){
1595
 
          perror_plus("setenv");
1596
 
          _exit(EX_OSERR);
1597
 
        }
1598
 
        ret = setenv("MODE", mode, 1);
1599
 
        if(ret == -1){
1600
 
          perror_plus("setenv");
1601
 
          _exit(EX_OSERR);
1602
 
        }
1603
 
        char *delaystring;
1604
 
        ret = asprintf(&delaystring, "%f", (double)delay);
1605
 
        if(ret == -1){
1606
 
          perror_plus("asprintf");
1607
 
          _exit(EX_OSERR);
1608
 
        }
1609
 
        ret = setenv("DELAY", delaystring, 1);
1610
 
        if(ret == -1){
1611
 
          free(delaystring);
1612
 
          perror_plus("setenv");
1613
 
          _exit(EX_OSERR);
1614
 
        }
 
2169
        _exit(EX_OSERR);
 
2170
      }
 
2171
      ret = setenv("DELAY", delaystring, 1);
 
2172
      if(ret == -1){
1615
2173
        free(delaystring);
1616
 
        if(connect_to != NULL){
1617
 
          ret = setenv("CONNECT", connect_to, 1);
1618
 
          if(ret == -1){
1619
 
            perror_plus("setenv");
1620
 
            _exit(EX_OSERR);
1621
 
          }
1622
 
        }
1623
 
        if(execl(fullname, direntry->d_name, mode, NULL) == -1){
1624
 
          perror_plus("execl");
1625
 
          _exit(EXIT_FAILURE);
1626
 
        }
 
2174
        perror_plus("setenv");
 
2175
        _exit(EX_OSERR);
 
2176
      }
 
2177
      free(delaystring);
 
2178
      if(connect_to != NULL){
 
2179
        ret = setenv("CONNECT", connect_to, 1);
 
2180
        if(ret == -1){
 
2181
          perror_plus("setenv");
 
2182
          _exit(EX_OSERR);
 
2183
        }
 
2184
      }
 
2185
      int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
 
2186
                                                   direntry->d_name,
 
2187
                                                   O_RDONLY));
 
2188
      if(hook_fd == -1){
 
2189
        perror_plus("openat");
 
2190
        _exit(EXIT_FAILURE);
 
2191
      }
 
2192
      if(close(hookdir_fd) == -1){
 
2193
        perror_plus("close");
 
2194
        _exit(EXIT_FAILURE);
 
2195
      }
 
2196
      ret = dup2(devnull, STDIN_FILENO);
 
2197
      if(ret == -1){
 
2198
        perror_plus("dup2(devnull, STDIN_FILENO)");
 
2199
        _exit(EX_OSERR);
 
2200
      }
 
2201
      ret = close(devnull);
 
2202
      if(ret == -1){
 
2203
        perror_plus("close");
 
2204
        _exit(EX_OSERR);
 
2205
      }
 
2206
      ret = dup2(STDERR_FILENO, STDOUT_FILENO);
 
2207
      if(ret == -1){
 
2208
        perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
 
2209
        _exit(EX_OSERR);
 
2210
      }
 
2211
      if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
 
2212
                 environ) == -1){
 
2213
        perror_plus("fexecve");
 
2214
        _exit(EXIT_FAILURE);
 
2215
      }
 
2216
    } else {
 
2217
      if(hook_pid == -1){
 
2218
        perror_plus("fork");
 
2219
        free(direntry);
 
2220
        continue;
 
2221
      }
 
2222
      int status;
 
2223
      if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
 
2224
        perror_plus("waitpid");
 
2225
        free(direntry);
 
2226
        continue;
 
2227
      }
 
2228
      if(WIFEXITED(status)){
 
2229
        if(WEXITSTATUS(status) != 0){
 
2230
          fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
 
2231
                       " with status %d\n", direntry->d_name,
 
2232
                       WEXITSTATUS(status));
 
2233
          free(direntry);
 
2234
          continue;
 
2235
        }
 
2236
      } else if(WIFSIGNALED(status)){
 
2237
        fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
 
2238
                     " signal %d\n", direntry->d_name,
 
2239
                     WTERMSIG(status));
 
2240
        free(direntry);
 
2241
        continue;
1627
2242
      } else {
1628
 
        int status;
1629
 
        if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1630
 
          perror_plus("waitpid");
1631
 
          free(fullname);
1632
 
          continue;
1633
 
        }
1634
 
        if(WIFEXITED(status)){
1635
 
          if(WEXITSTATUS(status) != 0){
1636
 
            fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1637
 
                         " with status %d\n", direntry->d_name,
1638
 
                         WEXITSTATUS(status));
1639
 
            free(fullname);
1640
 
            continue;
1641
 
          }
1642
 
        } else if(WIFSIGNALED(status)){
1643
 
          fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1644
 
                       " signal %d\n", direntry->d_name,
1645
 
                       WTERMSIG(status));
1646
 
          free(fullname);
1647
 
          continue;
1648
 
        } else {
1649
 
          fprintf_plus(stderr, "Warning: network hook \"%s\""
1650
 
                       " crashed\n", direntry->d_name);
1651
 
          free(fullname);
1652
 
          continue;
1653
 
        }
1654
 
      }
1655
 
      free(fullname);
1656
 
      if(debug){
1657
 
        fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1658
 
                     direntry->d_name);
1659
 
      }
1660
 
    }
1661
 
    close(devnull);
1662
 
  }
 
2243
        fprintf_plus(stderr, "Warning: network hook \"%s\""
 
2244
                     " crashed\n", direntry->d_name);
 
2245
        free(direntry);
 
2246
        continue;
 
2247
      }
 
2248
    }
 
2249
    if(debug){
 
2250
      fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
 
2251
                   direntry->d_name);
 
2252
    }
 
2253
    free(direntry);
 
2254
  }
 
2255
  free(direntries);
 
2256
  if(close(hookdir_fd) == -1){
 
2257
    perror_plus("close");
 
2258
  } else {
 
2259
    hookdir_fd = -1;
 
2260
  }
 
2261
  close(devnull);
1663
2262
}
1664
2263
 
1665
2264
__attribute__((nonnull, warn_unused_result))
1666
 
error_t bring_up_interface(const char *const interface,
1667
 
                           const float delay){
1668
 
  error_t old_errno = errno;
 
2265
int bring_up_interface(const char *const interface,
 
2266
                       const float delay){
 
2267
  int old_errno = errno;
1669
2268
  int ret;
1670
2269
  struct ifreq network;
1671
2270
  unsigned int if_index = if_nametoindex(interface);
1681
2280
  }
1682
2281
  
1683
2282
  if(not interface_is_up(interface)){
1684
 
    error_t ret_errno = 0, ioctl_errno = 0;
 
2283
    int ret_errno = 0;
 
2284
    int ioctl_errno = 0;
1685
2285
    if(not get_flags(interface, &network)){
1686
2286
      ret_errno = errno;
1687
2287
      fprintf_plus(stderr, "Failed to get flags for interface "
1700
2300
    }
1701
2301
    
1702
2302
    if(quit_now){
1703
 
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2303
      ret = close(sd);
1704
2304
      if(ret == -1){
1705
2305
        perror_plus("close");
1706
2306
      }
1715
2315
    
1716
2316
    /* Raise privileges */
1717
2317
    ret_errno = raise_privileges();
 
2318
    if(ret_errno != 0){
 
2319
      errno = ret_errno;
 
2320
      perror_plus("Failed to raise privileges");
 
2321
    }
 
2322
    
 
2323
#ifdef __linux__
 
2324
    int ret_linux;
1718
2325
    bool restore_loglevel = false;
1719
 
    if(ret_errno != 0){
1720
 
      perror_plus("Failed to raise privileges");
1721
 
    }
1722
 
#ifdef __linux__
1723
 
    int ret_linux;
1724
2326
    if(ret_errno == 0){
1725
2327
      /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1726
2328
         messages about the network interface to mess up the prompt */
1754
2356
    }
1755
2357
    
1756
2358
    /* Close the socket */
1757
 
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2359
    ret = close(sd);
1758
2360
    if(ret == -1){
1759
2361
      perror_plus("close");
1760
2362
    }
1772
2374
  
1773
2375
  /* Sleep checking until interface is running.
1774
2376
     Check every 0.25s, up to total time of delay */
1775
 
  for(int i=0; i < delay * 4; i++){
 
2377
  for(int i = 0; i < delay * 4; i++){
1776
2378
    if(interface_is_running(interface)){
1777
2379
      break;
1778
2380
    }
1788
2390
}
1789
2391
 
1790
2392
__attribute__((nonnull, warn_unused_result))
1791
 
error_t take_down_interface(const char *const interface){
1792
 
  error_t old_errno = errno;
 
2393
int take_down_interface(const char *const interface){
 
2394
  int old_errno = errno;
1793
2395
  struct ifreq network;
1794
2396
  unsigned int if_index = if_nametoindex(interface);
1795
2397
  if(if_index == 0){
1798
2400
    return ENXIO;
1799
2401
  }
1800
2402
  if(interface_is_up(interface)){
1801
 
    error_t ret_errno = 0, ioctl_errno = 0;
 
2403
    int ret_errno = 0;
 
2404
    int ioctl_errno = 0;
1802
2405
    if(not get_flags(interface, &network) and debug){
1803
2406
      ret_errno = errno;
1804
2407
      fprintf_plus(stderr, "Failed to get flags for interface "
1824
2427
    /* Raise privileges */
1825
2428
    ret_errno = raise_privileges();
1826
2429
    if(ret_errno != 0){
 
2430
      errno = ret_errno;
1827
2431
      perror_plus("Failed to raise privileges");
1828
2432
    }
 
2433
    
1829
2434
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1830
2435
    ioctl_errno = errno;
1831
2436
    
1840
2445
    }
1841
2446
    
1842
2447
    /* Close the socket */
1843
 
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2448
    int ret = close(sd);
1844
2449
    if(ret == -1){
1845
2450
      perror_plus("close");
1846
2451
    }
1861
2466
}
1862
2467
 
1863
2468
int main(int argc, char *argv[]){
1864
 
  mandos_context mc = { .server = NULL, .dh_bits = 1024,
1865
 
                        .priority = "SECURE256:!CTYPE-X.509:"
1866
 
                        "+CTYPE-OPENPGP", .current_server = NULL,
1867
 
                        .interfaces = NULL, .interfaces_size = 0 };
 
2469
  mandos_context mc = { .server = NULL, .dh_bits = 0,
 
2470
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2471
                        .priority = "SECURE128:!CTYPE-X.509"
 
2472
                        ":+CTYPE-RAWPK:!RSA:!VERS-ALL:+VERS-TLS1.3"
 
2473
                        ":%PROFILE_ULTRA",
 
2474
#elif GNUTLS_VERSION_NUMBER < 0x030600
 
2475
                        .priority = "SECURE256:!CTYPE-X.509"
 
2476
                        ":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
 
2477
#else
 
2478
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
 
2479
#endif
 
2480
                        .current_server = NULL, .interfaces = NULL,
 
2481
                        .interfaces_size = 0 };
1868
2482
  AvahiSServiceBrowser *sb = NULL;
1869
2483
  error_t ret_errno;
1870
2484
  int ret;
1879
2493
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1880
2494
  const char *seckey = PATHDIR "/" SECKEY;
1881
2495
  const char *pubkey = PATHDIR "/" PUBKEY;
 
2496
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2497
  const char *tls_privkey = PATHDIR "/" TLS_PRIVKEY;
 
2498
  const char *tls_pubkey = PATHDIR "/" TLS_PUBKEY;
 
2499
#endif
 
2500
  const char *dh_params_file = NULL;
1882
2501
  char *interfaces_hooks = NULL;
1883
2502
  
1884
2503
  bool gnutls_initialized = false;
1931
2550
      { .name = "pubkey", .key = 'p',
1932
2551
        .arg = "FILE",
1933
2552
        .doc = "OpenPGP public key file base name",
1934
 
        .group = 2 },
 
2553
        .group = 1 },
 
2554
      { .name = "tls-privkey", .key = 't',
 
2555
        .arg = "FILE",
 
2556
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2557
        .doc = "TLS private key file base name",
 
2558
#else
 
2559
        .doc = "Dummy; ignored (requires GnuTLS 3.6.6)",
 
2560
#endif
 
2561
        .group = 1 },
 
2562
      { .name = "tls-pubkey", .key = 'T',
 
2563
        .arg = "FILE",
 
2564
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2565
        .doc = "TLS public key file base name",
 
2566
#else
 
2567
        .doc = "Dummy; ignored (requires GnuTLS 3.6.6)",
 
2568
#endif
 
2569
        .group = 1 },
1935
2570
      { .name = "dh-bits", .key = 129,
1936
2571
        .arg = "BITS",
1937
2572
        .doc = "Bit length of the prime number used in the"
1938
2573
        " Diffie-Hellman key exchange",
1939
2574
        .group = 2 },
 
2575
      { .name = "dh-params", .key = 134,
 
2576
        .arg = "FILE",
 
2577
        .doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
 
2578
        " for the Diffie-Hellman key exchange",
 
2579
        .group = 2 },
1940
2580
      { .name = "priority", .key = 130,
1941
2581
        .arg = "STRING",
1942
2582
        .doc = "GnuTLS priority string for the TLS handshake",
1988
2628
      case 'p':                 /* --pubkey */
1989
2629
        pubkey = arg;
1990
2630
        break;
 
2631
      case 't':                 /* --tls-privkey */
 
2632
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2633
        tls_privkey = arg;
 
2634
#endif
 
2635
        break;
 
2636
      case 'T':                 /* --tls-pubkey */
 
2637
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2638
        tls_pubkey = arg;
 
2639
#endif
 
2640
        break;
1991
2641
      case 129:                 /* --dh-bits */
1992
2642
        errno = 0;
1993
2643
        tmpmax = strtoimax(arg, &tmp, 10);
1997
2647
        }
1998
2648
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
1999
2649
        break;
 
2650
      case 134:                 /* --dh-params */
 
2651
        dh_params_file = arg;
 
2652
        break;
2000
2653
      case 130:                 /* --priority */
2001
2654
        mc.priority = arg;
2002
2655
        break;
2025
2678
        argp_state_help(state, state->out_stream,
2026
2679
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2027
2680
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
2681
        __builtin_unreachable();
2028
2682
      case -3:                  /* --usage */
2029
2683
        argp_state_help(state, state->out_stream,
2030
2684
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
2685
        __builtin_unreachable();
2031
2686
      case 'V':                 /* --version */
2032
2687
        fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2033
2688
        exit(argp_err_exit_status);
2042
2697
                         .args_doc = "",
2043
2698
                         .doc = "Mandos client -- Get and decrypt"
2044
2699
                         " passwords from a Mandos server" };
2045
 
    ret = argp_parse(&argp, argc, argv,
2046
 
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2047
 
    switch(ret){
 
2700
    ret_errno = argp_parse(&argp, argc, argv,
 
2701
                           ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2702
    switch(ret_errno){
2048
2703
    case 0:
2049
2704
      break;
2050
2705
    case ENOMEM:
2051
2706
    default:
2052
 
      errno = ret;
 
2707
      errno = ret_errno;
2053
2708
      perror_plus("argp_parse");
2054
2709
      exitcode = EX_OSERR;
2055
2710
      goto end;
2058
2713
      goto end;
2059
2714
    }
2060
2715
  }
2061
 
    
 
2716
  
2062
2717
  {
2063
 
    /* Work around Debian bug #633582:
2064
 
       <http://bugs.debian.org/633582> */
2065
 
    
2066
2718
    /* Re-raise privileges */
2067
 
    ret_errno = raise_privileges();
2068
 
    if(ret_errno != 0){
2069
 
      errno = ret_errno;
 
2719
    ret = raise_privileges();
 
2720
    if(ret != 0){
 
2721
      errno = ret;
2070
2722
      perror_plus("Failed to raise privileges");
2071
2723
    } else {
2072
2724
      struct stat st;
2073
2725
      
 
2726
      /* Work around Debian bug #633582:
 
2727
         <https://bugs.debian.org/633582> */
 
2728
 
2074
2729
      if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2075
2730
        int seckey_fd = open(seckey, O_RDONLY);
2076
2731
        if(seckey_fd == -1){
2088
2743
              }
2089
2744
            }
2090
2745
          }
2091
 
          TEMP_FAILURE_RETRY(close(seckey_fd));
 
2746
          close(seckey_fd);
2092
2747
        }
2093
2748
      }
2094
 
    
 
2749
      
2095
2750
      if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2096
2751
        int pubkey_fd = open(pubkey, O_RDONLY);
2097
2752
        if(pubkey_fd == -1){
2109
2764
              }
2110
2765
            }
2111
2766
          }
2112
 
          TEMP_FAILURE_RETRY(close(pubkey_fd));
2113
 
        }
2114
 
      }
2115
 
    
 
2767
          close(pubkey_fd);
 
2768
        }
 
2769
      }
 
2770
      
 
2771
      if(dh_params_file != NULL
 
2772
         and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
 
2773
        int dhparams_fd = open(dh_params_file, O_RDONLY);
 
2774
        if(dhparams_fd == -1){
 
2775
          perror_plus("open");
 
2776
        } else {
 
2777
          ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
 
2778
          if(ret == -1){
 
2779
            perror_plus("fstat");
 
2780
          } else {
 
2781
            if(S_ISREG(st.st_mode)
 
2782
               and st.st_uid == 0 and st.st_gid == 0){
 
2783
              ret = fchown(dhparams_fd, uid, gid);
 
2784
              if(ret == -1){
 
2785
                perror_plus("fchown");
 
2786
              }
 
2787
            }
 
2788
          }
 
2789
          close(dhparams_fd);
 
2790
        }
 
2791
      }
 
2792
      
 
2793
      /* Work around Debian bug #981302
 
2794
         <https://bugs.debian.org/981302> */
 
2795
      if(lstat("/dev/fd", &st) != 0 and errno == ENOENT){
 
2796
        ret = symlink("/proc/self/fd", "/dev/fd");
 
2797
        if(ret == -1){
 
2798
          perror_plus("Failed to create /dev/fd symlink");
 
2799
        }
 
2800
      }
 
2801
 
2116
2802
      /* Lower privileges */
2117
 
      ret_errno = lower_privileges();
2118
 
      if(ret_errno != 0){
2119
 
        errno = ret_errno;
 
2803
      ret = lower_privileges();
 
2804
      if(ret != 0){
 
2805
        errno = ret;
2120
2806
        perror_plus("Failed to lower privileges");
2121
2807
      }
2122
2808
    }
2235
2921
  
2236
2922
  /* If no interfaces were specified, make a list */
2237
2923
  if(mc.interfaces == NULL){
2238
 
    struct dirent **direntries;
 
2924
    struct dirent **direntries = NULL;
2239
2925
    /* Look for any good interfaces */
2240
2926
    ret = scandir(sys_class_net, &direntries, good_interface,
2241
2927
                  alphasort);
2247
2933
        if(ret_errno != 0){
2248
2934
          errno = ret_errno;
2249
2935
          perror_plus("argz_add");
 
2936
          free(direntries[i]);
2250
2937
          continue;
2251
2938
        }
2252
2939
        if(debug){
2253
2940
          fprintf_plus(stderr, "Will use interface \"%s\"\n",
2254
2941
                       direntries[i]->d_name);
2255
2942
        }
 
2943
        free(direntries[i]);
2256
2944
      }
2257
2945
      free(direntries);
2258
2946
    } else {
2259
 
      free(direntries);
 
2947
      if(ret == 0){
 
2948
        free(direntries);
 
2949
      }
2260
2950
      fprintf_plus(stderr, "Could not find a network interface\n");
2261
2951
      exitcode = EXIT_FAILURE;
2262
2952
      goto end;
2288
2978
      errno = bring_up_interface(interface, delay);
2289
2979
      if(not interface_was_up){
2290
2980
        if(errno != 0){
2291
 
          perror_plus("Failed to bring up interface");
 
2981
          fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
 
2982
                       " %s\n", interface, strerror(errno));
2292
2983
        } else {
2293
2984
          errno = argz_add(&interfaces_to_take_down,
2294
2985
                           &interfaces_to_take_down_size,
2317
3008
    goto end;
2318
3009
  }
2319
3010
  
2320
 
  ret = init_gnutls_global(pubkey, seckey, &mc);
 
3011
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
3012
  ret = init_gnutls_global(tls_pubkey, tls_privkey, dh_params_file, &mc);
 
3013
#elif GNUTLS_VERSION_NUMBER < 0x030600
 
3014
  ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
 
3015
#else
 
3016
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
 
3017
#endif
2321
3018
  if(ret == -1){
2322
3019
    fprintf_plus(stderr, "init_gnutls_global failed\n");
2323
3020
    exitcode = EX_UNAVAILABLE;
2445
3142
    
2446
3143
    /* Allocate a new server */
2447
3144
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2448
 
                                 &config, NULL, NULL, &ret_errno);
 
3145
                                 &config, NULL, NULL, &ret);
2449
3146
    
2450
3147
    /* Free the Avahi configuration data */
2451
3148
    avahi_server_config_free(&config);
2454
3151
  /* Check if creating the Avahi server object succeeded */
2455
3152
  if(mc.server == NULL){
2456
3153
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2457
 
                 avahi_strerror(ret_errno));
 
3154
                 avahi_strerror(ret));
2458
3155
    exitcode = EX_UNAVAILABLE;
2459
3156
    goto end;
2460
3157
  }
2484
3181
  if(debug){
2485
3182
    fprintf_plus(stderr, "Starting Avahi loop search\n");
2486
3183
  }
2487
 
 
 
3184
  
2488
3185
  ret = avahi_loop_with_timeout(simple_poll,
2489
3186
                                (int)(retry_interval * 1000), &mc);
2490
3187
  if(debug){
2495
3192
 end:
2496
3193
  
2497
3194
  if(debug){
2498
 
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
 
3195
    if(signal_received){
 
3196
      fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
 
3197
                   argv[0], signal_received,
 
3198
                   strsignal(signal_received));
 
3199
    } else {
 
3200
      fprintf_plus(stderr, "%s exiting\n", argv[0]);
 
3201
    }
2499
3202
  }
2500
3203
  
2501
3204
  /* Cleanup things */
2512
3215
  
2513
3216
  if(gnutls_initialized){
2514
3217
    gnutls_certificate_free_credentials(mc.cred);
2515
 
    gnutls_global_deinit();
2516
3218
    gnutls_dh_params_deinit(mc.dh_params);
2517
3219
  }
2518
3220
  
2526
3228
    mc.current_server->prev->next = NULL;
2527
3229
    while(mc.current_server != NULL){
2528
3230
      server *next = mc.current_server->next;
 
3231
#ifdef __GNUC__
 
3232
#pragma GCC diagnostic push
 
3233
#pragma GCC diagnostic ignored "-Wcast-qual"
 
3234
#endif
 
3235
      free((char *)(mc.current_server->ip));
 
3236
#ifdef __GNUC__
 
3237
#pragma GCC diagnostic pop
 
3238
#endif
2529
3239
      free(mc.current_server);
2530
3240
      mc.current_server = next;
2531
3241
    }
2533
3243
  
2534
3244
  /* Re-raise privileges */
2535
3245
  {
2536
 
    ret_errno = raise_privileges();
2537
 
    if(ret_errno != 0){
 
3246
    ret = raise_privileges();
 
3247
    if(ret != 0){
 
3248
      errno = ret;
2538
3249
      perror_plus("Failed to raise privileges");
2539
3250
    } else {
2540
3251
      
2545
3256
      /* Take down the network interfaces which were brought up */
2546
3257
      {
2547
3258
        char *interface = NULL;
2548
 
        while((interface=argz_next(interfaces_to_take_down,
2549
 
                                   interfaces_to_take_down_size,
2550
 
                                   interface))){
2551
 
          ret_errno = take_down_interface(interface);
2552
 
          if(ret_errno != 0){
2553
 
            errno = ret_errno;
 
3259
        while((interface = argz_next(interfaces_to_take_down,
 
3260
                                     interfaces_to_take_down_size,
 
3261
                                     interface))){
 
3262
          ret = take_down_interface(interface);
 
3263
          if(ret != 0){
 
3264
            errno = ret;
2554
3265
            perror_plus("Failed to take down interface");
2555
3266
          }
2556
3267
        }
2560
3271
        }
2561
3272
      }
2562
3273
    }
2563
 
    ret_errno = lower_privileges_permanently();
2564
 
    if(ret_errno != 0){
 
3274
    
 
3275
    ret = lower_privileges_permanently();
 
3276
    if(ret != 0){
 
3277
      errno = ret;
2565
3278
      perror_plus("Failed to lower privileges permanently");
2566
3279
    }
2567
3280
  }
2569
3282
  free(interfaces_to_take_down);
2570
3283
  free(interfaces_hooks);
2571
3284
  
 
3285
  void clean_dir_at(int base, const char * const dirname,
 
3286
                    uintmax_t level){
 
3287
    struct dirent **direntries = NULL;
 
3288
    int dret;
 
3289
    int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
 
3290
                                                O_RDONLY
 
3291
                                                | O_NOFOLLOW
 
3292
                                                | O_DIRECTORY
 
3293
                                                | O_PATH));
 
3294
    if(dir_fd == -1){
 
3295
      perror_plus("open");
 
3296
      return;
 
3297
    }
 
3298
    int numentries = scandirat(dir_fd, ".", &direntries,
 
3299
                               notdotentries, alphasort);
 
3300
    if(numentries >= 0){
 
3301
      for(int i = 0; i < numentries; i++){
 
3302
        if(debug){
 
3303
          fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
 
3304
                       dirname, direntries[i]->d_name);
 
3305
        }
 
3306
        dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
 
3307
        if(dret == -1){
 
3308
          if(errno == EISDIR){
 
3309
              dret = unlinkat(dir_fd, direntries[i]->d_name,
 
3310
                              AT_REMOVEDIR);
 
3311
          }         
 
3312
          if((dret == -1) and (errno == ENOTEMPTY)
 
3313
             and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
 
3314
                  == 0) and (level == 0)){
 
3315
            /* Recurse only in this special case */
 
3316
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
 
3317
            dret = 0;
 
3318
          }
 
3319
          if((dret == -1) and (errno != ENOENT)){
 
3320
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
 
3321
                         direntries[i]->d_name, strerror(errno));
 
3322
          }
 
3323
        }
 
3324
        free(direntries[i]);
 
3325
      }
 
3326
      
 
3327
      /* need to clean even if 0 because man page doesn't specify */
 
3328
      free(direntries);
 
3329
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
 
3330
      if(dret == -1 and errno != ENOENT){
 
3331
        perror_plus("rmdir");
 
3332
      }
 
3333
    } else {
 
3334
      perror_plus("scandirat");
 
3335
    }
 
3336
    close(dir_fd);
 
3337
  }
 
3338
  
2572
3339
  /* Removes the GPGME temp directory and all files inside */
2573
3340
  if(tempdir != NULL){
2574
 
    struct dirent **direntries = NULL;
2575
 
    struct dirent *direntry = NULL;
2576
 
    int numentries = scandir(tempdir, &direntries, notdotentries,
2577
 
                             alphasort);
2578
 
    if(numentries > 0){
2579
 
      for(int i = 0; i < numentries; i++){
2580
 
        direntry = direntries[i];
2581
 
        char *fullname = NULL;
2582
 
        ret = asprintf(&fullname, "%s/%s", tempdir,
2583
 
                       direntry->d_name);
2584
 
        if(ret < 0){
2585
 
          perror_plus("asprintf");
2586
 
          continue;
2587
 
        }
2588
 
        ret = remove(fullname);
2589
 
        if(ret == -1){
2590
 
          fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname,
2591
 
                       strerror(errno));
2592
 
        }
2593
 
        free(fullname);
2594
 
      }
2595
 
    }
2596
 
 
2597
 
    /* need to clean even if 0 because man page doesn't specify */
2598
 
    free(direntries);
2599
 
    if(numentries == -1){
2600
 
      perror_plus("scandir");
2601
 
    }
2602
 
    ret = rmdir(tempdir);
2603
 
    if(ret == -1 and errno != ENOENT){
2604
 
      perror_plus("rmdir");
2605
 
    }
 
3341
    clean_dir_at(-1, tempdir, 0);
2606
3342
  }
2607
3343
  
2608
3344
  if(quit_now){