/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-01 19:30:45 UTC
  • 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-2014 Teddy Hogeborn
13
 
 * Copyright © 2008-2014 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
 */
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(), unlinkat() */
 
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,
74
80
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
75
81
                                   getuid(), getgid(), seteuid(),
76
82
                                   setgid(), pause(), _exit(),
77
 
                                   unlinkat() */
 
83
                                   unlinkat(), lstat(), symlink() */
78
84
#include <arpa/inet.h>          /* inet_pton(), htons() */
79
85
#include <iso646.h>             /* not, or, and */
80
86
#include <argp.h>               /* struct argp_option, error_t, struct
117
123
                                   gnutls_*
118
124
                                   init_gnutls_session(),
119
125
                                   GNUTLS_* */
 
126
#if GNUTLS_VERSION_NUMBER < 0x030600
120
127
#include <gnutls/openpgp.h>
121
128
                         /* gnutls_certificate_set_openpgp_key_file(),
122
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
123
135
 
124
136
/* GPGME */
125
137
#include <gpgme.h>              /* All GPGME types, constants and
133
145
#define PATHDIR "/conf/conf.d/mandos"
134
146
#define SECKEY "seckey.txt"
135
147
#define PUBKEY "pubkey.txt"
 
148
#define TLS_PRIVKEY "tls-privkey.pem"
 
149
#define TLS_PUBKEY "tls-pubkey.pem"
136
150
#define HOOKDIR "/lib/mandos/network-hooks.d"
137
151
 
138
152
bool debug = false;
240
254
  ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
241
255
  if(ret == -1){
242
256
    perror_plus("clock_gettime");
243
 
    free(new_server->ip);
 
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
244
265
    free(new_server);
245
266
    return false;
246
267
  }
259
280
  return true;
260
281
}
261
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
 
262
335
/* 
263
336
 * Initialize GPGME.
264
337
 */
284
357
      return false;
285
358
    }
286
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
 
287
409
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
288
410
    if(rc != GPG_ERR_NO_ERROR){
289
411
      fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
297
419
                   gpgme_strsource(rc), gpgme_strerror(rc));
298
420
      return false;
299
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
    }
300
497
    
301
 
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
 
498
    ret = close(fd);
302
499
    if(ret == -1){
303
500
      perror_plus("close");
304
501
    }
343
540
  /* Create new GPGME "context" */
344
541
  rc = gpgme_new(&(mc->ctx));
345
542
  if(rc != GPG_ERR_NO_ERROR){
346
 
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
347
 
                 "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
348
 
                 gpgme_strerror(rc));
 
543
    fprintf_plus(stderr, "bad gpgme_new: %s: %s\n",
 
544
                 gpgme_strsource(rc), gpgme_strerror(rc));
349
545
    return false;
350
546
  }
351
547
  
387
583
  /* Create new empty GPGME data buffer for the plaintext */
388
584
  rc = gpgme_data_new(&dh_plain);
389
585
  if(rc != GPG_ERR_NO_ERROR){
390
 
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
391
 
                 "bad gpgme_data_new: %s: %s\n",
 
586
    fprintf_plus(stderr, "bad gpgme_data_new: %s: %s\n",
392
587
                 gpgme_strsource(rc), gpgme_strerror(rc));
393
588
    gpgme_data_release(dh_crypto);
394
589
    return -1;
407
602
      if(result == NULL){
408
603
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
409
604
      } else {
410
 
        fprintf_plus(stderr, "Unsupported algorithm: %s\n",
411
 
                     result->unsupported_algorithm);
412
 
        fprintf_plus(stderr, "Wrong key usage: %u\n",
413
 
                     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");
414
611
        if(result->file_name != NULL){
415
612
          fprintf_plus(stderr, "File name: %s\n", result->file_name);
416
613
        }
417
 
        gpgme_recipient_t recipient;
418
 
        recipient = result->recipients;
419
 
        while(recipient != NULL){
 
614
 
 
615
        for(gpgme_recipient_t r = result->recipients; r != NULL;
 
616
            r = r->next){
420
617
          fprintf_plus(stderr, "Public key algorithm: %s\n",
421
 
                       gpgme_pubkey_algo_name
422
 
                       (recipient->pubkey_algo));
423
 
          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);
424
620
          fprintf_plus(stderr, "Secret key available: %s\n",
425
 
                       recipient->status == GPG_ERR_NO_SECKEY
426
 
                       ? "No" : "Yes");
427
 
          recipient = recipient->next;
 
621
                       r->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
428
622
        }
429
623
      }
430
624
    }
486
680
  return plaintext_length;
487
681
}
488
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
 
489
690
__attribute__((warn_unused_result))
490
691
static const char *safer_gnutls_strerror(int value){
491
692
  const char *ret = gnutls_strerror(value);
492
 
  if(ret == NULL)
493
 
    ret = "(unknown)";
494
 
  return ret;
 
693
  return safe_string(ret);
495
694
}
496
695
 
497
696
/* GnuTLS log function callback */
501
700
  fprintf_plus(stderr, "GnuTLS: %s", string);
502
701
}
503
702
 
504
 
__attribute__((nonnull, warn_unused_result))
 
703
__attribute__((nonnull(1, 2, 4), warn_unused_result))
505
704
static int init_gnutls_global(const char *pubkeyfilename,
506
705
                              const char *seckeyfilename,
 
706
                              const char *dhparamsfilename,
507
707
                              mandos_context *mc){
508
708
  int ret;
509
709
  
511
711
    fprintf_plus(stderr, "Initializing GnuTLS\n");
512
712
  }
513
713
  
514
 
  ret = gnutls_global_init();
515
 
  if(ret != GNUTLS_E_SUCCESS){
516
 
    fprintf_plus(stderr, "GnuTLS global_init: %s\n",
517
 
                 safer_gnutls_strerror(ret));
518
 
    return -1;
519
 
  }
520
 
  
521
714
  if(debug){
522
715
    /* "Use a log level over 10 to enable all debugging options."
523
716
     * - GnuTLS manual
531
724
  if(ret != GNUTLS_E_SUCCESS){
532
725
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
533
726
                 safer_gnutls_strerror(ret));
534
 
    gnutls_global_deinit();
535
727
    return -1;
536
728
  }
537
729
  
538
730
  if(debug){
539
 
    fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
540
 
                 " 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",
541
733
                 pubkeyfilename,
542
734
                 seckeyfilename);
543
735
  }
544
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
545
750
  ret = gnutls_certificate_set_openpgp_key_file
546
751
    (mc->cred, pubkeyfilename, seckeyfilename,
547
752
     GNUTLS_OPENPGP_FMT_BASE64);
 
753
#else
 
754
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
 
755
#endif
548
756
  if(ret != GNUTLS_E_SUCCESS){
549
757
    fprintf_plus(stderr,
550
 
                 "Error[%d] while reading the OpenPGP key pair ('%s',"
 
758
                 "Error[%d] while reading the key pair ('%s',"
551
759
                 " '%s')\n", ret, pubkeyfilename, seckeyfilename);
552
760
    fprintf_plus(stderr, "The GnuTLS error is: %s\n",
553
761
                 safer_gnutls_strerror(ret));
562
770
                 safer_gnutls_strerror(ret));
563
771
    goto globalfail;
564
772
  }
565
 
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
566
 
  if(ret != GNUTLS_E_SUCCESS){
567
 
    fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
568
 
                 safer_gnutls_strerror(ret));
569
 
    goto globalfail;
570
 
  }
571
 
  
572
 
  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
  }
573
955
  
574
956
  return 0;
575
957
  
576
958
 globalfail:
577
959
  
578
960
  gnutls_certificate_free_credentials(mc->cred);
579
 
  gnutls_global_deinit();
580
961
  gnutls_dh_params_deinit(mc->dh_params);
581
962
  return -1;
582
963
}
587
968
  int ret;
588
969
  /* GnuTLS session creation */
589
970
  do {
590
 
    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
                                ));
591
979
    if(quit_now){
592
980
      return -1;
593
981
    }
634
1022
  /* ignore client certificate if any. */
635
1023
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
636
1024
  
637
 
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
638
 
  
639
1025
  return 0;
640
1026
}
641
1027
 
643
1029
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
644
1030
                      __attribute__((unused)) const char *txt){}
645
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
 
646
1205
/* Called when a Mandos server is found */
647
1206
__attribute__((nonnull, warn_unused_result))
648
1207
static int start_mandos_communication(const char *ip, in_port_t port,
659
1218
  int retval = -1;
660
1219
  gnutls_session_t session;
661
1220
  int pf;                       /* Protocol family */
 
1221
  bool route_added = false;
662
1222
  
663
1223
  errno = 0;
664
1224
  
686
1246
    bool match = false;
687
1247
    {
688
1248
      char *interface = NULL;
689
 
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
690
 
                                 interface))){
 
1249
      while((interface = argz_next(mc->interfaces,
 
1250
                                   mc->interfaces_size,
 
1251
                                   interface))){
691
1252
        if(if_nametoindex(interface) == (unsigned int)if_index){
692
1253
          match = true;
693
1254
          break;
722
1283
                 PRIuMAX "\n", ip, (uintmax_t)port);
723
1284
  }
724
1285
  
725
 
  tcp_sd = socket(pf, SOCK_STREAM, 0);
 
1286
  tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
726
1287
  if(tcp_sd < 0){
727
1288
    int e = errno;
728
1289
    perror_plus("socket");
735
1296
    goto mandos_end;
736
1297
  }
737
1298
  
738
 
  memset(&to, 0, sizeof(to));
739
1299
  if(af == AF_INET6){
740
 
    ((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
741
 
    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);
742
1303
  } else {                      /* IPv4 */
743
 
    ((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
744
 
    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);
745
1307
  }
746
1308
  if(ret < 0 ){
747
1309
    int e = errno;
817
1379
    goto mandos_end;
818
1380
  }
819
1381
  
820
 
  if(af == AF_INET6){
821
 
    ret = connect(tcp_sd, (struct sockaddr *)&to,
822
 
                  sizeof(struct sockaddr_in6));
823
 
  } else {
824
 
    ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
825
 
                  sizeof(struct sockaddr_in));
826
 
  }
827
 
  if(ret < 0){
828
 
    if((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
829
 
      int e = errno;
830
 
      perror_plus("connect");
831
 
      errno = e;
832
 
    }
833
 
    goto mandos_end;
834
 
  }
835
 
  
836
 
  if(quit_now){
837
 
    errno = EINTR;
838
 
    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;
839
1437
  }
840
1438
  
841
1439
  const char *out = mandos_protocol_version;
995
1593
                                               &decrypted_buffer, mc);
996
1594
    if(decrypted_buffer_size >= 0){
997
1595
      
 
1596
      clearerr(stdout);
998
1597
      written = 0;
999
1598
      while(written < (size_t) decrypted_buffer_size){
1000
1599
        if(quit_now){
1016
1615
        }
1017
1616
        written += (size_t)ret;
1018
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
      }
1019
1628
      retval = 0;
1020
1629
    }
1021
1630
  }
1024
1633
  
1025
1634
 mandos_end:
1026
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
    }
1027
1642
    int e = errno;
1028
1643
    free(decrypted_buffer);
1029
1644
    free(buffer);
1030
1645
    if(tcp_sd >= 0){
1031
 
      ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
 
1646
      ret = close(tcp_sd);
1032
1647
    }
1033
1648
    if(ret == -1){
1034
1649
      if(e == 0){
1046
1661
  return retval;
1047
1662
}
1048
1663
 
1049
 
__attribute__((nonnull))
1050
1664
static void resolve_callback(AvahiSServiceResolver *r,
1051
1665
                             AvahiIfIndex interface,
1052
1666
                             AvahiProtocol proto,
1069
1683
     timed out */
1070
1684
  
1071
1685
  if(quit_now){
 
1686
    avahi_s_service_resolver_free(r);
1072
1687
    return;
1073
1688
  }
1074
1689
  
1188
1803
__attribute__((nonnull, warn_unused_result))
1189
1804
bool get_flags(const char *ifname, struct ifreq *ifr){
1190
1805
  int ret;
1191
 
  error_t ret_errno;
 
1806
  int old_errno;
1192
1807
  
1193
1808
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1194
1809
  if(s < 0){
1195
 
    ret_errno = errno;
 
1810
    old_errno = errno;
1196
1811
    perror_plus("socket");
1197
 
    errno = ret_errno;
 
1812
    errno = old_errno;
1198
1813
    return false;
1199
1814
  }
1200
 
  strcpy(ifr->ifr_name, ifname);
 
1815
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
 
1816
  ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1201
1817
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1202
1818
  if(ret == -1){
1203
1819
    if(debug){
1204
 
      ret_errno = errno;
 
1820
      old_errno = errno;
1205
1821
      perror_plus("ioctl SIOCGIFFLAGS");
1206
 
      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;
1207
1828
    }
1208
1829
    return false;
1209
1830
  }
 
1831
  if((close(s) == -1) and debug){
 
1832
    old_errno = errno;
 
1833
    perror_plus("close");
 
1834
    errno = old_errno;
 
1835
  }
1210
1836
  return true;
1211
1837
}
1212
1838
 
1454
2080
  }
1455
2081
}
1456
2082
 
1457
 
/* Set effective uid to 0, return errno */
1458
 
__attribute__((warn_unused_result))
1459
 
error_t raise_privileges(void){
1460
 
  error_t old_errno = errno;
1461
 
  error_t ret_errno = 0;
1462
 
  if(seteuid(0) == -1){
1463
 
    ret_errno = errno;
1464
 
  }
1465
 
  errno = old_errno;
1466
 
  return ret_errno;
1467
 
}
1468
 
 
1469
 
/* Set effective and real user ID to 0.  Return errno. */
1470
 
__attribute__((warn_unused_result))
1471
 
error_t raise_privileges_permanently(void){
1472
 
  error_t old_errno = errno;
1473
 
  error_t ret_errno = raise_privileges();
1474
 
  if(ret_errno != 0){
1475
 
    errno = old_errno;
1476
 
    return ret_errno;
1477
 
  }
1478
 
  if(setuid(0) == -1){
1479
 
    ret_errno = errno;
1480
 
  }
1481
 
  errno = old_errno;
1482
 
  return ret_errno;
1483
 
}
1484
 
 
1485
 
/* Set effective user ID to unprivileged saved user ID */
1486
 
__attribute__((warn_unused_result))
1487
 
error_t lower_privileges(void){
1488
 
  error_t old_errno = errno;
1489
 
  error_t ret_errno = 0;
1490
 
  if(seteuid(uid) == -1){
1491
 
    ret_errno = errno;
1492
 
  }
1493
 
  errno = old_errno;
1494
 
  return ret_errno;
1495
 
}
1496
 
 
1497
 
/* Lower privileges permanently */
1498
 
__attribute__((warn_unused_result))
1499
 
error_t lower_privileges_permanently(void){
1500
 
  error_t old_errno = errno;
1501
 
  error_t ret_errno = 0;
1502
 
  if(setuid(uid) == -1){
1503
 
    ret_errno = errno;
1504
 
  }
1505
 
  errno = old_errno;
1506
 
  return ret_errno;
1507
 
}
1508
 
 
1509
2083
__attribute__((nonnull))
1510
2084
void run_network_hooks(const char *mode, const char *interface,
1511
2085
                       const float delay){
1512
2086
  struct dirent **direntries = NULL;
1513
2087
  if(hookdir_fd == -1){
1514
 
    hookdir_fd = open(hookdir, O_RDONLY);
 
2088
    hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
 
2089
                      | O_CLOEXEC);
1515
2090
    if(hookdir_fd == -1){
1516
2091
      if(errno == ENOENT){
1517
2092
        if(debug){
1524
2099
      return;
1525
2100
    }
1526
2101
  }
1527
 
#ifdef __GLIBC__
1528
 
#if __GLIBC_PREREQ(2, 15)
 
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
  }
1529
2107
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
1530
2108
                           runnable_hook, alphasort);
1531
 
#else  /* not __GLIBC_PREREQ(2, 15) */
1532
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1533
 
                         alphasort);
1534
 
#endif  /* not __GLIBC_PREREQ(2, 15) */
1535
 
#else   /* not __GLIBC__ */
1536
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1537
 
                         alphasort);
1538
 
#endif  /* not __GLIBC__ */
1539
2109
  if(numhooks == -1){
1540
2110
    perror_plus("scandir");
 
2111
    close(devnull);
1541
2112
    return;
1542
2113
  }
1543
2114
  struct dirent *direntry;
1544
2115
  int ret;
1545
 
  int devnull = open("/dev/null", O_RDONLY);
1546
2116
  for(int i = 0; i < numhooks; i++){
1547
2117
    direntry = direntries[i];
1548
2118
    if(debug){
1572
2142
        perror_plus("setgroups");
1573
2143
        _exit(EX_NOPERM);
1574
2144
      }
1575
 
      ret = dup2(devnull, STDIN_FILENO);
1576
 
      if(ret == -1){
1577
 
        perror_plus("dup2(devnull, STDIN_FILENO)");
1578
 
        _exit(EX_OSERR);
1579
 
      }
1580
 
      ret = close(devnull);
1581
 
      if(ret == -1){
1582
 
        perror_plus("close");
1583
 
        _exit(EX_OSERR);
1584
 
      }
1585
 
      ret = dup2(STDERR_FILENO, STDOUT_FILENO);
1586
 
      if(ret == -1){
1587
 
        perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
1588
 
        _exit(EX_OSERR);
1589
 
      }
1590
2145
      ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1591
2146
      if(ret == -1){
1592
2147
        perror_plus("setenv");
1627
2182
          _exit(EX_OSERR);
1628
2183
        }
1629
2184
      }
1630
 
      int hook_fd = openat(hookdir_fd, direntry->d_name, O_RDONLY);
 
2185
      int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
 
2186
                                                   direntry->d_name,
 
2187
                                                   O_RDONLY));
1631
2188
      if(hook_fd == -1){
1632
2189
        perror_plus("openat");
1633
2190
        _exit(EXIT_FAILURE);
1634
2191
      }
1635
 
      if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
2192
      if(close(hookdir_fd) == -1){
1636
2193
        perror_plus("close");
1637
2194
        _exit(EXIT_FAILURE);
1638
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
      }
1639
2211
      if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
1640
2212
                 environ) == -1){
1641
2213
        perror_plus("fexecve");
1642
2214
        _exit(EXIT_FAILURE);
1643
2215
      }
1644
2216
    } else {
 
2217
      if(hook_pid == -1){
 
2218
        perror_plus("fork");
 
2219
        free(direntry);
 
2220
        continue;
 
2221
      }
1645
2222
      int status;
1646
2223
      if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1647
2224
        perror_plus("waitpid");
 
2225
        free(direntry);
1648
2226
        continue;
1649
2227
      }
1650
2228
      if(WIFEXITED(status)){
1652
2230
          fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1653
2231
                       " with status %d\n", direntry->d_name,
1654
2232
                       WEXITSTATUS(status));
 
2233
          free(direntry);
1655
2234
          continue;
1656
2235
        }
1657
2236
      } else if(WIFSIGNALED(status)){
1658
2237
        fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1659
2238
                     " signal %d\n", direntry->d_name,
1660
2239
                     WTERMSIG(status));
 
2240
        free(direntry);
1661
2241
        continue;
1662
2242
      } else {
1663
2243
        fprintf_plus(stderr, "Warning: network hook \"%s\""
1664
2244
                     " crashed\n", direntry->d_name);
 
2245
        free(direntry);
1665
2246
        continue;
1666
2247
      }
1667
2248
    }
1669
2250
      fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1670
2251
                   direntry->d_name);
1671
2252
    }
 
2253
    free(direntry);
1672
2254
  }
1673
2255
  free(direntries);
1674
 
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
2256
  if(close(hookdir_fd) == -1){
1675
2257
    perror_plus("close");
1676
2258
  } else {
1677
2259
    hookdir_fd = -1;
1680
2262
}
1681
2263
 
1682
2264
__attribute__((nonnull, warn_unused_result))
1683
 
error_t bring_up_interface(const char *const interface,
1684
 
                           const float delay){
1685
 
  error_t old_errno = errno;
 
2265
int bring_up_interface(const char *const interface,
 
2266
                       const float delay){
 
2267
  int old_errno = errno;
1686
2268
  int ret;
1687
2269
  struct ifreq network;
1688
2270
  unsigned int if_index = if_nametoindex(interface);
1698
2280
  }
1699
2281
  
1700
2282
  if(not interface_is_up(interface)){
1701
 
    error_t ret_errno = 0, ioctl_errno = 0;
 
2283
    int ret_errno = 0;
 
2284
    int ioctl_errno = 0;
1702
2285
    if(not get_flags(interface, &network)){
1703
2286
      ret_errno = errno;
1704
2287
      fprintf_plus(stderr, "Failed to get flags for interface "
1717
2300
    }
1718
2301
    
1719
2302
    if(quit_now){
1720
 
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2303
      ret = close(sd);
1721
2304
      if(ret == -1){
1722
2305
        perror_plus("close");
1723
2306
      }
1773
2356
    }
1774
2357
    
1775
2358
    /* Close the socket */
1776
 
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2359
    ret = close(sd);
1777
2360
    if(ret == -1){
1778
2361
      perror_plus("close");
1779
2362
    }
1791
2374
  
1792
2375
  /* Sleep checking until interface is running.
1793
2376
     Check every 0.25s, up to total time of delay */
1794
 
  for(int i=0; i < delay * 4; i++){
 
2377
  for(int i = 0; i < delay * 4; i++){
1795
2378
    if(interface_is_running(interface)){
1796
2379
      break;
1797
2380
    }
1807
2390
}
1808
2391
 
1809
2392
__attribute__((nonnull, warn_unused_result))
1810
 
error_t take_down_interface(const char *const interface){
1811
 
  error_t old_errno = errno;
 
2393
int take_down_interface(const char *const interface){
 
2394
  int old_errno = errno;
1812
2395
  struct ifreq network;
1813
2396
  unsigned int if_index = if_nametoindex(interface);
1814
2397
  if(if_index == 0){
1817
2400
    return ENXIO;
1818
2401
  }
1819
2402
  if(interface_is_up(interface)){
1820
 
    error_t ret_errno = 0, ioctl_errno = 0;
 
2403
    int ret_errno = 0;
 
2404
    int ioctl_errno = 0;
1821
2405
    if(not get_flags(interface, &network) and debug){
1822
2406
      ret_errno = errno;
1823
2407
      fprintf_plus(stderr, "Failed to get flags for interface "
1861
2445
    }
1862
2446
    
1863
2447
    /* Close the socket */
1864
 
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2448
    int ret = close(sd);
1865
2449
    if(ret == -1){
1866
2450
      perror_plus("close");
1867
2451
    }
1882
2466
}
1883
2467
 
1884
2468
int main(int argc, char *argv[]){
1885
 
  mandos_context mc = { .server = NULL, .dh_bits = 1024,
1886
 
                        .priority = "SECURE256:!CTYPE-X.509:"
1887
 
                        "+CTYPE-OPENPGP", .current_server = NULL,
1888
 
                        .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 };
1889
2482
  AvahiSServiceBrowser *sb = NULL;
1890
2483
  error_t ret_errno;
1891
2484
  int ret;
1900
2493
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1901
2494
  const char *seckey = PATHDIR "/" SECKEY;
1902
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;
1903
2501
  char *interfaces_hooks = NULL;
1904
2502
  
1905
2503
  bool gnutls_initialized = false;
1952
2550
      { .name = "pubkey", .key = 'p',
1953
2551
        .arg = "FILE",
1954
2552
        .doc = "OpenPGP public key file base name",
1955
 
        .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 },
1956
2570
      { .name = "dh-bits", .key = 129,
1957
2571
        .arg = "BITS",
1958
2572
        .doc = "Bit length of the prime number used in the"
1959
2573
        " Diffie-Hellman key exchange",
1960
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 },
1961
2580
      { .name = "priority", .key = 130,
1962
2581
        .arg = "STRING",
1963
2582
        .doc = "GnuTLS priority string for the TLS handshake",
2009
2628
      case 'p':                 /* --pubkey */
2010
2629
        pubkey = arg;
2011
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;
2012
2641
      case 129:                 /* --dh-bits */
2013
2642
        errno = 0;
2014
2643
        tmpmax = strtoimax(arg, &tmp, 10);
2018
2647
        }
2019
2648
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2020
2649
        break;
 
2650
      case 134:                 /* --dh-params */
 
2651
        dh_params_file = arg;
 
2652
        break;
2021
2653
      case 130:                 /* --priority */
2022
2654
        mc.priority = arg;
2023
2655
        break;
2046
2678
        argp_state_help(state, state->out_stream,
2047
2679
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2048
2680
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
2681
        __builtin_unreachable();
2049
2682
      case -3:                  /* --usage */
2050
2683
        argp_state_help(state, state->out_stream,
2051
2684
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
2685
        __builtin_unreachable();
2052
2686
      case 'V':                 /* --version */
2053
2687
        fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2054
2688
        exit(argp_err_exit_status);
2063
2697
                         .args_doc = "",
2064
2698
                         .doc = "Mandos client -- Get and decrypt"
2065
2699
                         " passwords from a Mandos server" };
2066
 
    ret = argp_parse(&argp, argc, argv,
2067
 
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2068
 
    switch(ret){
 
2700
    ret_errno = argp_parse(&argp, argc, argv,
 
2701
                           ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2702
    switch(ret_errno){
2069
2703
    case 0:
2070
2704
      break;
2071
2705
    case ENOMEM:
2072
2706
    default:
2073
 
      errno = ret;
 
2707
      errno = ret_errno;
2074
2708
      perror_plus("argp_parse");
2075
2709
      exitcode = EX_OSERR;
2076
2710
      goto end;
2079
2713
      goto end;
2080
2714
    }
2081
2715
  }
2082
 
    
 
2716
  
2083
2717
  {
2084
 
    /* Work around Debian bug #633582:
2085
 
       <http://bugs.debian.org/633582> */
2086
 
    
2087
2718
    /* Re-raise privileges */
2088
 
    ret_errno = raise_privileges();
2089
 
    if(ret_errno != 0){
2090
 
      errno = ret_errno;
 
2719
    ret = raise_privileges();
 
2720
    if(ret != 0){
 
2721
      errno = ret;
2091
2722
      perror_plus("Failed to raise privileges");
2092
2723
    } else {
2093
2724
      struct stat st;
2094
2725
      
 
2726
      /* Work around Debian bug #633582:
 
2727
         <https://bugs.debian.org/633582> */
 
2728
 
2095
2729
      if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2096
2730
        int seckey_fd = open(seckey, O_RDONLY);
2097
2731
        if(seckey_fd == -1){
2109
2743
              }
2110
2744
            }
2111
2745
          }
2112
 
          TEMP_FAILURE_RETRY(close(seckey_fd));
 
2746
          close(seckey_fd);
2113
2747
        }
2114
2748
      }
2115
 
    
 
2749
      
2116
2750
      if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2117
2751
        int pubkey_fd = open(pubkey, O_RDONLY);
2118
2752
        if(pubkey_fd == -1){
2130
2764
              }
2131
2765
            }
2132
2766
          }
2133
 
          TEMP_FAILURE_RETRY(close(pubkey_fd));
2134
 
        }
2135
 
      }
2136
 
    
 
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
 
2137
2802
      /* Lower privileges */
2138
 
      ret_errno = lower_privileges();
2139
 
      if(ret_errno != 0){
2140
 
        errno = ret_errno;
 
2803
      ret = lower_privileges();
 
2804
      if(ret != 0){
 
2805
        errno = ret;
2141
2806
        perror_plus("Failed to lower privileges");
2142
2807
      }
2143
2808
    }
2268
2933
        if(ret_errno != 0){
2269
2934
          errno = ret_errno;
2270
2935
          perror_plus("argz_add");
 
2936
          free(direntries[i]);
2271
2937
          continue;
2272
2938
        }
2273
2939
        if(debug){
2274
2940
          fprintf_plus(stderr, "Will use interface \"%s\"\n",
2275
2941
                       direntries[i]->d_name);
2276
2942
        }
 
2943
        free(direntries[i]);
2277
2944
      }
2278
2945
      free(direntries);
2279
2946
    } else {
2311
2978
      errno = bring_up_interface(interface, delay);
2312
2979
      if(not interface_was_up){
2313
2980
        if(errno != 0){
2314
 
          perror_plus("Failed to bring up interface");
 
2981
          fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
 
2982
                       " %s\n", interface, strerror(errno));
2315
2983
        } else {
2316
2984
          errno = argz_add(&interfaces_to_take_down,
2317
2985
                           &interfaces_to_take_down_size,
2340
3008
    goto end;
2341
3009
  }
2342
3010
  
2343
 
  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
2344
3018
  if(ret == -1){
2345
3019
    fprintf_plus(stderr, "init_gnutls_global failed\n");
2346
3020
    exitcode = EX_UNAVAILABLE;
2468
3142
    
2469
3143
    /* Allocate a new server */
2470
3144
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2471
 
                                 &config, NULL, NULL, &ret_errno);
 
3145
                                 &config, NULL, NULL, &ret);
2472
3146
    
2473
3147
    /* Free the Avahi configuration data */
2474
3148
    avahi_server_config_free(&config);
2477
3151
  /* Check if creating the Avahi server object succeeded */
2478
3152
  if(mc.server == NULL){
2479
3153
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2480
 
                 avahi_strerror(ret_errno));
 
3154
                 avahi_strerror(ret));
2481
3155
    exitcode = EX_UNAVAILABLE;
2482
3156
    goto end;
2483
3157
  }
2518
3192
 end:
2519
3193
  
2520
3194
  if(debug){
2521
 
    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
    }
2522
3202
  }
2523
3203
  
2524
3204
  /* Cleanup things */
2535
3215
  
2536
3216
  if(gnutls_initialized){
2537
3217
    gnutls_certificate_free_credentials(mc.cred);
2538
 
    gnutls_global_deinit();
2539
3218
    gnutls_dh_params_deinit(mc.dh_params);
2540
3219
  }
2541
3220
  
2549
3228
    mc.current_server->prev->next = NULL;
2550
3229
    while(mc.current_server != NULL){
2551
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
2552
3239
      free(mc.current_server);
2553
3240
      mc.current_server = next;
2554
3241
    }
2556
3243
  
2557
3244
  /* Re-raise privileges */
2558
3245
  {
2559
 
    ret_errno = raise_privileges();
2560
 
    if(ret_errno != 0){
2561
 
      errno = ret_errno;
 
3246
    ret = raise_privileges();
 
3247
    if(ret != 0){
 
3248
      errno = ret;
2562
3249
      perror_plus("Failed to raise privileges");
2563
3250
    } else {
2564
3251
      
2569
3256
      /* Take down the network interfaces which were brought up */
2570
3257
      {
2571
3258
        char *interface = NULL;
2572
 
        while((interface=argz_next(interfaces_to_take_down,
2573
 
                                   interfaces_to_take_down_size,
2574
 
                                   interface))){
2575
 
          ret_errno = take_down_interface(interface);
2576
 
          if(ret_errno != 0){
2577
 
            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;
2578
3265
            perror_plus("Failed to take down interface");
2579
3266
          }
2580
3267
        }
2585
3272
      }
2586
3273
    }
2587
3274
    
2588
 
    ret_errno = lower_privileges_permanently();
2589
 
    if(ret_errno != 0){
2590
 
      errno = ret_errno;
 
3275
    ret = lower_privileges_permanently();
 
3276
    if(ret != 0){
 
3277
      errno = ret;
2591
3278
      perror_plus("Failed to lower privileges permanently");
2592
3279
    }
2593
3280
  }
2595
3282
  free(interfaces_to_take_down);
2596
3283
  free(interfaces_hooks);
2597
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
  
2598
3339
  /* Removes the GPGME temp directory and all files inside */
2599
3340
  if(tempdir != NULL){
2600
 
    struct dirent **direntries = NULL;
2601
 
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY |
2602
 
                                                  O_NOFOLLOW));
2603
 
    if(tempdir_fd == -1){
2604
 
      perror_plus("open");
2605
 
    } else {
2606
 
#ifdef __GLIBC__
2607
 
#if __GLIBC_PREREQ(2, 15)
2608
 
      int numentries = scandirat(tempdir_fd, ".", &direntries,
2609
 
                                 notdotentries, alphasort);
2610
 
#else  /* not __GLIBC_PREREQ(2, 15) */
2611
 
      int numentries = scandir(tempdir, &direntries, notdotentries,
2612
 
                               alphasort);
2613
 
#endif  /* not __GLIBC_PREREQ(2, 15) */
2614
 
#else   /* not __GLIBC__ */
2615
 
      int numentries = scandir(tempdir, &direntries, notdotentries,
2616
 
                               alphasort);
2617
 
#endif  /* not __GLIBC__ */
2618
 
      if(numentries >= 0){
2619
 
        for(int i = 0; i < numentries; i++){
2620
 
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
2621
 
          if(ret == -1){
2622
 
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
2623
 
                         " \"%s\", 0): %s\n", tempdir,
2624
 
                         direntries[i]->d_name, strerror(errno));
2625
 
          }
2626
 
        }
2627
 
        
2628
 
        /* need to clean even if 0 because man page doesn't specify */
2629
 
        free(direntries);
2630
 
        if(numentries == -1){
2631
 
          perror_plus("scandir");
2632
 
        }
2633
 
        ret = rmdir(tempdir);
2634
 
        if(ret == -1 and errno != ENOENT){
2635
 
          perror_plus("rmdir");
2636
 
        }
2637
 
      }
2638
 
      TEMP_FAILURE_RETRY(close(tempdir_fd));
2639
 
    }
 
3341
    clean_dir_at(-1, tempdir, 0);
2640
3342
  }
2641
3343
  
2642
3344
  if(quit_now){