/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: 2019-07-14 22:39:15 UTC
  • Revision ID: teddy@recompile.se-20190714223915-aqjkms3t3taa6tye
Only use sanitizing options when debugging

The C compiler's sanitizing options introduce code in the output
binary which is fragile and not very security conscious.  It has
become clear that sanitizing is only really meant for use while
debugging.

As a side effect, this makes compilation faster, as the Makefile, for
production builds, no longer runs the compiler repeatedly to find all
its currently supported sanitizing options.

* Makefile (DEBUG): Add "$(SANITIZE)".
  (SANITIZE): Comment out.
  (CFLAGS): Remove "$(SANITIZE)".
  (plugins.d/mandos-client): Revert back to use plain $(LINK.c), since
                             we no longer need to remove the leak
                             sanitizer by overriding CFLAGS.

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-2015 Teddy Hogeborn
13
 
 * Copyright © 2008-2015 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-2019 Teddy Hogeborn
 
13
 * Copyright © 2008-2019 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,
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;
266
280
  return true;
267
281
}
268
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
 
269
335
/* 
270
336
 * Initialize GPGME.
271
337
 */
291
357
      return false;
292
358
    }
293
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
      time_t keytime = keystat.st_mtim.tv_sec;
 
400
      if(stime(&keytime) != 0){
 
401
        perror_plus("stime");
 
402
      }
 
403
      ret = lower_privileges();
 
404
      if(ret != 0){
 
405
        errno = ret;
 
406
        perror_plus("Failed to lower privileges");
 
407
      }
 
408
    } while(false);
 
409
 
294
410
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
295
411
    if(rc != GPG_ERR_NO_ERROR){
296
412
      fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
304
420
                   gpgme_strsource(rc), gpgme_strerror(rc));
305
421
      return false;
306
422
    }
 
423
    {
 
424
      gpgme_import_result_t import_result
 
425
        = gpgme_op_import_result(mc->ctx);
 
426
      if((import_result->imported < 1
 
427
          or import_result->not_imported > 0)
 
428
         and import_result->unchanged == 0){
 
429
        fprintf_plus(stderr, "bad gpgme_op_import_results:\n");
 
430
        fprintf_plus(stderr,
 
431
                     "The total number of considered keys: %d\n",
 
432
                     import_result->considered);
 
433
        fprintf_plus(stderr,
 
434
                     "The number of keys without user ID: %d\n",
 
435
                     import_result->no_user_id);
 
436
        fprintf_plus(stderr,
 
437
                     "The total number of imported keys: %d\n",
 
438
                     import_result->imported);
 
439
        fprintf_plus(stderr, "The number of imported RSA keys: %d\n",
 
440
                     import_result->imported_rsa);
 
441
        fprintf_plus(stderr, "The number of unchanged keys: %d\n",
 
442
                     import_result->unchanged);
 
443
        fprintf_plus(stderr, "The number of new user IDs: %d\n",
 
444
                     import_result->new_user_ids);
 
445
        fprintf_plus(stderr, "The number of new sub keys: %d\n",
 
446
                     import_result->new_sub_keys);
 
447
        fprintf_plus(stderr, "The number of new signatures: %d\n",
 
448
                     import_result->new_signatures);
 
449
        fprintf_plus(stderr, "The number of new revocations: %d\n",
 
450
                     import_result->new_revocations);
 
451
        fprintf_plus(stderr,
 
452
                     "The total number of secret keys read: %d\n",
 
453
                     import_result->secret_read);
 
454
        fprintf_plus(stderr,
 
455
                     "The number of imported secret keys: %d\n",
 
456
                     import_result->secret_imported);
 
457
        fprintf_plus(stderr,
 
458
                     "The number of unchanged secret keys: %d\n",
 
459
                     import_result->secret_unchanged);
 
460
        fprintf_plus(stderr, "The number of keys not imported: %d\n",
 
461
                     import_result->not_imported);
 
462
        for(gpgme_import_status_t import_status
 
463
              = import_result->imports;
 
464
            import_status != NULL;
 
465
            import_status = import_status->next){
 
466
          fprintf_plus(stderr, "Import status for key: %s\n",
 
467
                       import_status->fpr);
 
468
          if(import_status->result != GPG_ERR_NO_ERROR){
 
469
            fprintf_plus(stderr, "Import result: %s: %s\n",
 
470
                         gpgme_strsource(import_status->result),
 
471
                         gpgme_strerror(import_status->result));
 
472
          }
 
473
          fprintf_plus(stderr, "Key status:\n");
 
474
          fprintf_plus(stderr,
 
475
                       import_status->status & GPGME_IMPORT_NEW
 
476
                       ? "The key was new.\n"
 
477
                       : "The key was not new.\n");
 
478
          fprintf_plus(stderr,
 
479
                       import_status->status & GPGME_IMPORT_UID
 
480
                       ? "The key contained new user IDs.\n"
 
481
                       : "The key did not contain new user IDs.\n");
 
482
          fprintf_plus(stderr,
 
483
                       import_status->status & GPGME_IMPORT_SIG
 
484
                       ? "The key contained new signatures.\n"
 
485
                       : "The key did not contain new signatures.\n");
 
486
          fprintf_plus(stderr,
 
487
                       import_status->status & GPGME_IMPORT_SUBKEY
 
488
                       ? "The key contained new sub keys.\n"
 
489
                       : "The key did not contain new sub keys.\n");
 
490
          fprintf_plus(stderr,
 
491
                       import_status->status & GPGME_IMPORT_SECRET
 
492
                       ? "The key contained a secret key.\n"
 
493
                       : "The key did not contain a secret key.\n");
 
494
        }
 
495
        return false;
 
496
      }
 
497
    }
307
498
    
308
 
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
 
499
    ret = close(fd);
309
500
    if(ret == -1){
310
501
      perror_plus("close");
311
502
    }
350
541
  /* Create new GPGME "context" */
351
542
  rc = gpgme_new(&(mc->ctx));
352
543
  if(rc != GPG_ERR_NO_ERROR){
353
 
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
354
 
                 "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
355
 
                 gpgme_strerror(rc));
 
544
    fprintf_plus(stderr, "bad gpgme_new: %s: %s\n",
 
545
                 gpgme_strsource(rc), gpgme_strerror(rc));
356
546
    return false;
357
547
  }
358
548
  
394
584
  /* Create new empty GPGME data buffer for the plaintext */
395
585
  rc = gpgme_data_new(&dh_plain);
396
586
  if(rc != GPG_ERR_NO_ERROR){
397
 
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
398
 
                 "bad gpgme_data_new: %s: %s\n",
 
587
    fprintf_plus(stderr, "bad gpgme_data_new: %s: %s\n",
399
588
                 gpgme_strsource(rc), gpgme_strerror(rc));
400
589
    gpgme_data_release(dh_crypto);
401
590
    return -1;
414
603
      if(result == NULL){
415
604
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
416
605
      } else {
417
 
        fprintf_plus(stderr, "Unsupported algorithm: %s\n",
418
 
                     result->unsupported_algorithm);
419
 
        fprintf_plus(stderr, "Wrong key usage: %u\n",
420
 
                     result->wrong_key_usage);
 
606
        if(result->unsupported_algorithm != NULL) {
 
607
          fprintf_plus(stderr, "Unsupported algorithm: %s\n",
 
608
                       result->unsupported_algorithm);
 
609
        }
 
610
        fprintf_plus(stderr, "Wrong key usage: %s\n",
 
611
                     result->wrong_key_usage ? "Yes" : "No");
421
612
        if(result->file_name != NULL){
422
613
          fprintf_plus(stderr, "File name: %s\n", result->file_name);
423
614
        }
424
 
        gpgme_recipient_t recipient;
425
 
        recipient = result->recipients;
426
 
        while(recipient != NULL){
 
615
 
 
616
        for(gpgme_recipient_t r = result->recipients; r != NULL;
 
617
            r = r->next){
427
618
          fprintf_plus(stderr, "Public key algorithm: %s\n",
428
 
                       gpgme_pubkey_algo_name
429
 
                       (recipient->pubkey_algo));
430
 
          fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
 
619
                       gpgme_pubkey_algo_name(r->pubkey_algo));
 
620
          fprintf_plus(stderr, "Key ID: %s\n", r->keyid);
431
621
          fprintf_plus(stderr, "Secret key available: %s\n",
432
 
                       recipient->status == GPG_ERR_NO_SECKEY
433
 
                       ? "No" : "Yes");
434
 
          recipient = recipient->next;
 
622
                       r->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
435
623
        }
436
624
      }
437
625
    }
493
681
  return plaintext_length;
494
682
}
495
683
 
 
684
__attribute__((warn_unused_result, const))
 
685
static const char *safe_string(const char *str){
 
686
  if(str == NULL)
 
687
    return "(unknown)";
 
688
  return str;
 
689
}
 
690
 
496
691
__attribute__((warn_unused_result))
497
692
static const char *safer_gnutls_strerror(int value){
498
693
  const char *ret = gnutls_strerror(value);
499
 
  if(ret == NULL)
500
 
    ret = "(unknown)";
501
 
  return ret;
 
694
  return safe_string(ret);
502
695
}
503
696
 
504
697
/* GnuTLS log function callback */
508
701
  fprintf_plus(stderr, "GnuTLS: %s", string);
509
702
}
510
703
 
511
 
__attribute__((nonnull, warn_unused_result))
 
704
__attribute__((nonnull(1, 2, 4), warn_unused_result))
512
705
static int init_gnutls_global(const char *pubkeyfilename,
513
706
                              const char *seckeyfilename,
 
707
                              const char *dhparamsfilename,
514
708
                              mandos_context *mc){
515
709
  int ret;
516
710
  
518
712
    fprintf_plus(stderr, "Initializing GnuTLS\n");
519
713
  }
520
714
  
521
 
  ret = gnutls_global_init();
522
 
  if(ret != GNUTLS_E_SUCCESS){
523
 
    fprintf_plus(stderr, "GnuTLS global_init: %s\n",
524
 
                 safer_gnutls_strerror(ret));
525
 
    return -1;
526
 
  }
527
 
  
528
715
  if(debug){
529
716
    /* "Use a log level over 10 to enable all debugging options."
530
717
     * - GnuTLS manual
538
725
  if(ret != GNUTLS_E_SUCCESS){
539
726
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
540
727
                 safer_gnutls_strerror(ret));
541
 
    gnutls_global_deinit();
542
728
    return -1;
543
729
  }
544
730
  
545
731
  if(debug){
546
 
    fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
547
 
                 " secret key %s as GnuTLS credentials\n",
 
732
    fprintf_plus(stderr, "Attempting to use public key %s and"
 
733
                 " private key %s as GnuTLS credentials\n",
548
734
                 pubkeyfilename,
549
735
                 seckeyfilename);
550
736
  }
551
737
  
 
738
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
739
  ret = gnutls_certificate_set_rawpk_key_file
 
740
    (mc->cred, pubkeyfilename, seckeyfilename,
 
741
     GNUTLS_X509_FMT_PEM,       /* format */
 
742
     NULL,                      /* pass */
 
743
     /* key_usage */
 
744
     GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
 
745
     NULL,                      /* names */
 
746
     0,                         /* names_length */
 
747
     /* privkey_flags */
 
748
     GNUTLS_PKCS_PLAIN | GNUTLS_PKCS_NULL_PASSWORD,
 
749
     0);                        /* pkcs11_flags */
 
750
#elif GNUTLS_VERSION_NUMBER < 0x030600
552
751
  ret = gnutls_certificate_set_openpgp_key_file
553
752
    (mc->cred, pubkeyfilename, seckeyfilename,
554
753
     GNUTLS_OPENPGP_FMT_BASE64);
 
754
#else
 
755
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
 
756
#endif
555
757
  if(ret != GNUTLS_E_SUCCESS){
556
758
    fprintf_plus(stderr,
557
 
                 "Error[%d] while reading the OpenPGP key pair ('%s',"
 
759
                 "Error[%d] while reading the key pair ('%s',"
558
760
                 " '%s')\n", ret, pubkeyfilename, seckeyfilename);
559
761
    fprintf_plus(stderr, "The GnuTLS error is: %s\n",
560
762
                 safer_gnutls_strerror(ret));
569
771
                 safer_gnutls_strerror(ret));
570
772
    goto globalfail;
571
773
  }
572
 
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
573
 
  if(ret != GNUTLS_E_SUCCESS){
574
 
    fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
575
 
                 safer_gnutls_strerror(ret));
576
 
    goto globalfail;
577
 
  }
578
 
  
579
 
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
 
774
  /* If a Diffie-Hellman parameters file was given, try to use it */
 
775
  if(dhparamsfilename != NULL){
 
776
    gnutls_datum_t params = { .data = NULL, .size = 0 };
 
777
    do {
 
778
      int dhpfile = open(dhparamsfilename, O_RDONLY);
 
779
      if(dhpfile == -1){
 
780
        perror_plus("open");
 
781
        dhparamsfilename = NULL;
 
782
        break;
 
783
      }
 
784
      size_t params_capacity = 0;
 
785
      while(true){
 
786
        params_capacity = incbuffer((char **)&params.data,
 
787
                                    (size_t)params.size,
 
788
                                    (size_t)params_capacity);
 
789
        if(params_capacity == 0){
 
790
          perror_plus("incbuffer");
 
791
          free(params.data);
 
792
          params.data = NULL;
 
793
          dhparamsfilename = NULL;
 
794
          break;
 
795
        }
 
796
        ssize_t bytes_read = read(dhpfile,
 
797
                                  params.data + params.size,
 
798
                                  BUFFER_SIZE);
 
799
        /* EOF */
 
800
        if(bytes_read == 0){
 
801
          break;
 
802
        }
 
803
        /* check bytes_read for failure */
 
804
        if(bytes_read < 0){
 
805
          perror_plus("read");
 
806
          free(params.data);
 
807
          params.data = NULL;
 
808
          dhparamsfilename = NULL;
 
809
          break;
 
810
        }
 
811
        params.size += (unsigned int)bytes_read;
 
812
      }
 
813
      ret = close(dhpfile);
 
814
      if(ret == -1){
 
815
        perror_plus("close");
 
816
      }
 
817
      if(params.data == NULL){
 
818
        dhparamsfilename = NULL;
 
819
      }
 
820
      if(dhparamsfilename == NULL){
 
821
        break;
 
822
      }
 
823
      ret = gnutls_dh_params_import_pkcs3(mc->dh_params, &params,
 
824
                                          GNUTLS_X509_FMT_PEM);
 
825
      if(ret != GNUTLS_E_SUCCESS){
 
826
        fprintf_plus(stderr, "Failed to parse DH parameters in file"
 
827
                     " \"%s\": %s\n", dhparamsfilename,
 
828
                     safer_gnutls_strerror(ret));
 
829
        dhparamsfilename = NULL;
 
830
      }
 
831
      free(params.data);
 
832
    } while(false);
 
833
  }
 
834
  if(dhparamsfilename == NULL){
 
835
    if(mc->dh_bits == 0){
 
836
#if GNUTLS_VERSION_NUMBER < 0x030600
 
837
      /* Find out the optimal number of DH bits */
 
838
      /* Try to read the private key file */
 
839
      gnutls_datum_t buffer = { .data = NULL, .size = 0 };
 
840
      do {
 
841
        int secfile = open(seckeyfilename, O_RDONLY);
 
842
        if(secfile == -1){
 
843
          perror_plus("open");
 
844
          break;
 
845
        }
 
846
        size_t buffer_capacity = 0;
 
847
        while(true){
 
848
          buffer_capacity = incbuffer((char **)&buffer.data,
 
849
                                      (size_t)buffer.size,
 
850
                                      (size_t)buffer_capacity);
 
851
          if(buffer_capacity == 0){
 
852
            perror_plus("incbuffer");
 
853
            free(buffer.data);
 
854
            buffer.data = NULL;
 
855
            break;
 
856
          }
 
857
          ssize_t bytes_read = read(secfile,
 
858
                                    buffer.data + buffer.size,
 
859
                                    BUFFER_SIZE);
 
860
          /* EOF */
 
861
          if(bytes_read == 0){
 
862
            break;
 
863
          }
 
864
          /* check bytes_read for failure */
 
865
          if(bytes_read < 0){
 
866
            perror_plus("read");
 
867
            free(buffer.data);
 
868
            buffer.data = NULL;
 
869
            break;
 
870
          }
 
871
          buffer.size += (unsigned int)bytes_read;
 
872
        }
 
873
        close(secfile);
 
874
      } while(false);
 
875
      /* If successful, use buffer to parse private key */
 
876
      gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
877
      if(buffer.data != NULL){
 
878
        {
 
879
          gnutls_openpgp_privkey_t privkey = NULL;
 
880
          ret = gnutls_openpgp_privkey_init(&privkey);
 
881
          if(ret != GNUTLS_E_SUCCESS){
 
882
            fprintf_plus(stderr, "Error initializing OpenPGP key"
 
883
                         " structure: %s",
 
884
                         safer_gnutls_strerror(ret));
 
885
            free(buffer.data);
 
886
            buffer.data = NULL;
 
887
          } else {
 
888
            ret = gnutls_openpgp_privkey_import
 
889
              (privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
 
890
            if(ret != GNUTLS_E_SUCCESS){
 
891
              fprintf_plus(stderr, "Error importing OpenPGP key : %s",
 
892
                           safer_gnutls_strerror(ret));
 
893
              privkey = NULL;
 
894
            }
 
895
            free(buffer.data);
 
896
            buffer.data = NULL;
 
897
            if(privkey != NULL){
 
898
              /* Use private key to suggest an appropriate
 
899
                 sec_param */
 
900
              sec_param = gnutls_openpgp_privkey_sec_param(privkey);
 
901
              gnutls_openpgp_privkey_deinit(privkey);
 
902
              if(debug){
 
903
                fprintf_plus(stderr, "This OpenPGP key implies using"
 
904
                             " a GnuTLS security parameter \"%s\".\n",
 
905
                             safe_string(gnutls_sec_param_get_name
 
906
                                         (sec_param)));
 
907
              }
 
908
            }
 
909
          }
 
910
        }
 
911
        if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
 
912
          /* Err on the side of caution */
 
913
          sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
914
          if(debug){
 
915
            fprintf_plus(stderr, "Falling back to security parameter"
 
916
                         " \"%s\"\n",
 
917
                         safe_string(gnutls_sec_param_get_name
 
918
                                     (sec_param)));
 
919
          }
 
920
        }
 
921
      }
 
922
      unsigned int uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
 
923
      if(uret != 0){
 
924
        mc->dh_bits = uret;
 
925
        if(debug){
 
926
          fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
 
927
                       " implies %u DH bits; using that.\n",
 
928
                       safe_string(gnutls_sec_param_get_name
 
929
                                   (sec_param)),
 
930
                       mc->dh_bits);
 
931
        }
 
932
      } else {
 
933
        fprintf_plus(stderr, "Failed to get implied number of DH"
 
934
                     " bits for security parameter \"%s\"): %s\n",
 
935
                     safe_string(gnutls_sec_param_get_name
 
936
                                 (sec_param)),
 
937
                     safer_gnutls_strerror(ret));
 
938
        goto globalfail;
 
939
      }
 
940
#endif
 
941
    } else {                    /* dh_bits != 0 */
 
942
      if(debug){
 
943
        fprintf_plus(stderr, "DH bits explicitly set to %u\n",
 
944
                     mc->dh_bits);
 
945
      }
 
946
      ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
 
947
      if(ret != GNUTLS_E_SUCCESS){
 
948
        fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
 
949
                     " bits): %s\n", mc->dh_bits,
 
950
                     safer_gnutls_strerror(ret));
 
951
        goto globalfail;
 
952
      }
 
953
      gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
 
954
    }
 
955
  }
580
956
  
581
957
  return 0;
582
958
  
583
959
 globalfail:
584
960
  
585
961
  gnutls_certificate_free_credentials(mc->cred);
586
 
  gnutls_global_deinit();
587
962
  gnutls_dh_params_deinit(mc->dh_params);
588
963
  return -1;
589
964
}
594
969
  int ret;
595
970
  /* GnuTLS session creation */
596
971
  do {
597
 
    ret = gnutls_init(session, GNUTLS_SERVER);
 
972
    ret = gnutls_init(session, (GNUTLS_SERVER
 
973
#if GNUTLS_VERSION_NUMBER >= 0x030506
 
974
                                | GNUTLS_NO_TICKETS
 
975
#endif
 
976
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
977
                                | GNUTLS_ENABLE_RAWPK
 
978
#endif
 
979
                                ));
598
980
    if(quit_now){
599
981
      return -1;
600
982
    }
641
1023
  /* ignore client certificate if any. */
642
1024
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
643
1025
  
644
 
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
645
 
  
646
1026
  return 0;
647
1027
}
648
1028
 
650
1030
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
651
1031
                      __attribute__((unused)) const char *txt){}
652
1032
 
653
 
/* Set effective uid to 0, return errno */
654
 
__attribute__((warn_unused_result))
655
 
error_t raise_privileges(void){
656
 
  error_t old_errno = errno;
657
 
  error_t ret_errno = 0;
658
 
  if(seteuid(0) == -1){
659
 
    ret_errno = errno;
660
 
  }
661
 
  errno = old_errno;
662
 
  return ret_errno;
663
 
}
664
 
 
665
 
/* Set effective and real user ID to 0.  Return errno. */
666
 
__attribute__((warn_unused_result))
667
 
error_t raise_privileges_permanently(void){
668
 
  error_t old_errno = errno;
669
 
  error_t ret_errno = raise_privileges();
670
 
  if(ret_errno != 0){
671
 
    errno = old_errno;
672
 
    return ret_errno;
673
 
  }
674
 
  if(setuid(0) == -1){
675
 
    ret_errno = errno;
676
 
  }
677
 
  errno = old_errno;
678
 
  return ret_errno;
679
 
}
680
 
 
681
 
/* Set effective user ID to unprivileged saved user ID */
682
 
__attribute__((warn_unused_result))
683
 
error_t lower_privileges(void){
684
 
  error_t old_errno = errno;
685
 
  error_t ret_errno = 0;
686
 
  if(seteuid(uid) == -1){
687
 
    ret_errno = errno;
688
 
  }
689
 
  errno = old_errno;
690
 
  return ret_errno;
691
 
}
692
 
 
693
 
/* Lower privileges permanently */
694
 
__attribute__((warn_unused_result))
695
 
error_t lower_privileges_permanently(void){
696
 
  error_t old_errno = errno;
697
 
  error_t ret_errno = 0;
698
 
  if(setuid(uid) == -1){
699
 
    ret_errno = errno;
700
 
  }
701
 
  errno = old_errno;
702
 
  return ret_errno;
703
 
}
704
 
 
705
1033
/* Helper function to add_local_route() and delete_local_route() */
706
1034
__attribute__((nonnull, warn_unused_result))
707
1035
static bool add_delete_local_route(const bool add,
761
1089
      perror_plus("dup2(devnull, STDIN_FILENO)");
762
1090
      _exit(EX_OSERR);
763
1091
    }
764
 
    ret = (int)TEMP_FAILURE_RETRY(close(devnull));
 
1092
    ret = close(devnull);
765
1093
    if(ret == -1){
766
1094
      perror_plus("close");
767
1095
      _exit(EX_OSERR);
784
1112
                                                   helper, O_RDONLY));
785
1113
    if(helper_fd == -1){
786
1114
      perror_plus("openat");
 
1115
      close(helperdir_fd);
787
1116
      _exit(EX_UNAVAILABLE);
788
1117
    }
789
 
    TEMP_FAILURE_RETRY(close(helperdir_fd));
 
1118
    close(helperdir_fd);
790
1119
#ifdef __GNUC__
791
1120
#pragma GCC diagnostic push
792
1121
#pragma GCC diagnostic ignored "-Wcast-qual"
911
1240
    bool match = false;
912
1241
    {
913
1242
      char *interface = NULL;
914
 
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
915
 
                                 interface))){
 
1243
      while((interface = argz_next(mc->interfaces,
 
1244
                                   mc->interfaces_size,
 
1245
                                   interface))){
916
1246
        if(if_nametoindex(interface) == (unsigned int)if_index){
917
1247
          match = true;
918
1248
          break;
960
1290
    goto mandos_end;
961
1291
  }
962
1292
  
963
 
  memset(&to, 0, sizeof(to));
964
1293
  if(af == AF_INET6){
965
 
    ((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
966
 
    ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
 
1294
    struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
 
1295
    *to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
 
1296
    ret = inet_pton(af, ip, &to6->sin6_addr);
967
1297
  } else {                      /* IPv4 */
968
 
    ((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
969
 
    ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
 
1298
    struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
 
1299
    *to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
 
1300
    ret = inet_pton(af, ip, &to4->sin_addr);
970
1301
  }
971
1302
  if(ret < 0 ){
972
1303
    int e = errno;
1051
1382
                    sizeof(struct sockaddr_in));
1052
1383
    }
1053
1384
    if(ret < 0){
1054
 
      if(errno == ENETUNREACH
 
1385
      if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1055
1386
         and if_index != AVAHI_IF_UNSPEC
1056
1387
         and connect_to == NULL
1057
1388
         and not route_added and
1070
1401
           with an explicit route added with the server's address.
1071
1402
           
1072
1403
           Avahi bug reference:
1073
 
           http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
 
1404
           https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1074
1405
           https://bugs.debian.org/587961
1075
1406
        */
1076
1407
        if(debug){
1256
1587
                                               &decrypted_buffer, mc);
1257
1588
    if(decrypted_buffer_size >= 0){
1258
1589
      
 
1590
      clearerr(stdout);
1259
1591
      written = 0;
1260
1592
      while(written < (size_t) decrypted_buffer_size){
1261
1593
        if(quit_now){
1277
1609
        }
1278
1610
        written += (size_t)ret;
1279
1611
      }
 
1612
      ret = fflush(stdout);
 
1613
      if(ret != 0){
 
1614
        int e = errno;
 
1615
        if(debug){
 
1616
          fprintf_plus(stderr, "Error writing encrypted data: %s\n",
 
1617
                       strerror(errno));
 
1618
        }
 
1619
        errno = e;
 
1620
        goto mandos_end;
 
1621
      }
1280
1622
      retval = 0;
1281
1623
    }
1282
1624
  }
1295
1637
    free(decrypted_buffer);
1296
1638
    free(buffer);
1297
1639
    if(tcp_sd >= 0){
1298
 
      ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
 
1640
      ret = close(tcp_sd);
1299
1641
    }
1300
1642
    if(ret == -1){
1301
1643
      if(e == 0){
1313
1655
  return retval;
1314
1656
}
1315
1657
 
1316
 
__attribute__((nonnull))
1317
1658
static void resolve_callback(AvahiSServiceResolver *r,
1318
1659
                             AvahiIfIndex interface,
1319
1660
                             AvahiProtocol proto,
1456
1797
__attribute__((nonnull, warn_unused_result))
1457
1798
bool get_flags(const char *ifname, struct ifreq *ifr){
1458
1799
  int ret;
1459
 
  error_t ret_errno;
 
1800
  int old_errno;
1460
1801
  
1461
1802
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1462
1803
  if(s < 0){
1463
 
    ret_errno = errno;
 
1804
    old_errno = errno;
1464
1805
    perror_plus("socket");
1465
 
    errno = ret_errno;
 
1806
    errno = old_errno;
1466
1807
    return false;
1467
1808
  }
1468
 
  strcpy(ifr->ifr_name, ifname);
 
1809
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
 
1810
  ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1469
1811
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1470
1812
  if(ret == -1){
1471
1813
    if(debug){
1472
 
      ret_errno = errno;
 
1814
      old_errno = errno;
1473
1815
      perror_plus("ioctl SIOCGIFFLAGS");
1474
 
      errno = ret_errno;
 
1816
      errno = old_errno;
 
1817
    }
 
1818
    if((close(s) == -1) and debug){
 
1819
      old_errno = errno;
 
1820
      perror_plus("close");
 
1821
      errno = old_errno;
1475
1822
    }
1476
1823
    return false;
1477
1824
  }
 
1825
  if((close(s) == -1) and debug){
 
1826
    old_errno = errno;
 
1827
    perror_plus("close");
 
1828
    errno = old_errno;
 
1829
  }
1478
1830
  return true;
1479
1831
}
1480
1832
 
1741
2093
      return;
1742
2094
    }
1743
2095
  }
1744
 
#ifdef __GLIBC__
1745
 
#if __GLIBC_PREREQ(2, 15)
 
2096
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
 
2097
  if(devnull == -1){
 
2098
    perror_plus("open(\"/dev/null\", O_RDONLY)");
 
2099
    return;
 
2100
  }
1746
2101
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
1747
2102
                           runnable_hook, alphasort);
1748
 
#else  /* not __GLIBC_PREREQ(2, 15) */
1749
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1750
 
                         alphasort);
1751
 
#endif  /* not __GLIBC_PREREQ(2, 15) */
1752
 
#else   /* not __GLIBC__ */
1753
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1754
 
                         alphasort);
1755
 
#endif  /* not __GLIBC__ */
1756
2103
  if(numhooks == -1){
1757
2104
    perror_plus("scandir");
 
2105
    close(devnull);
1758
2106
    return;
1759
2107
  }
1760
2108
  struct dirent *direntry;
1761
2109
  int ret;
1762
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1763
 
  if(devnull == -1){
1764
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
1765
 
    return;
1766
 
  }
1767
2110
  for(int i = 0; i < numhooks; i++){
1768
2111
    direntry = direntries[i];
1769
2112
    if(debug){
1840
2183
        perror_plus("openat");
1841
2184
        _exit(EXIT_FAILURE);
1842
2185
      }
1843
 
      if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
2186
      if(close(hookdir_fd) == -1){
1844
2187
        perror_plus("close");
1845
2188
        _exit(EXIT_FAILURE);
1846
2189
      }
1849
2192
        perror_plus("dup2(devnull, STDIN_FILENO)");
1850
2193
        _exit(EX_OSERR);
1851
2194
      }
1852
 
      ret = (int)TEMP_FAILURE_RETRY(close(devnull));
 
2195
      ret = close(devnull);
1853
2196
      if(ret == -1){
1854
2197
        perror_plus("close");
1855
2198
        _exit(EX_OSERR);
1904
2247
    free(direntry);
1905
2248
  }
1906
2249
  free(direntries);
1907
 
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
2250
  if(close(hookdir_fd) == -1){
1908
2251
    perror_plus("close");
1909
2252
  } else {
1910
2253
    hookdir_fd = -1;
1913
2256
}
1914
2257
 
1915
2258
__attribute__((nonnull, warn_unused_result))
1916
 
error_t bring_up_interface(const char *const interface,
1917
 
                           const float delay){
1918
 
  error_t old_errno = errno;
 
2259
int bring_up_interface(const char *const interface,
 
2260
                       const float delay){
 
2261
  int old_errno = errno;
1919
2262
  int ret;
1920
2263
  struct ifreq network;
1921
2264
  unsigned int if_index = if_nametoindex(interface);
1931
2274
  }
1932
2275
  
1933
2276
  if(not interface_is_up(interface)){
1934
 
    error_t ret_errno = 0, ioctl_errno = 0;
 
2277
    int ret_errno = 0;
 
2278
    int ioctl_errno = 0;
1935
2279
    if(not get_flags(interface, &network)){
1936
2280
      ret_errno = errno;
1937
2281
      fprintf_plus(stderr, "Failed to get flags for interface "
1950
2294
    }
1951
2295
    
1952
2296
    if(quit_now){
1953
 
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2297
      ret = close(sd);
1954
2298
      if(ret == -1){
1955
2299
        perror_plus("close");
1956
2300
      }
2006
2350
    }
2007
2351
    
2008
2352
    /* Close the socket */
2009
 
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2353
    ret = close(sd);
2010
2354
    if(ret == -1){
2011
2355
      perror_plus("close");
2012
2356
    }
2024
2368
  
2025
2369
  /* Sleep checking until interface is running.
2026
2370
     Check every 0.25s, up to total time of delay */
2027
 
  for(int i=0; i < delay * 4; i++){
 
2371
  for(int i = 0; i < delay * 4; i++){
2028
2372
    if(interface_is_running(interface)){
2029
2373
      break;
2030
2374
    }
2040
2384
}
2041
2385
 
2042
2386
__attribute__((nonnull, warn_unused_result))
2043
 
error_t take_down_interface(const char *const interface){
2044
 
  error_t old_errno = errno;
 
2387
int take_down_interface(const char *const interface){
 
2388
  int old_errno = errno;
2045
2389
  struct ifreq network;
2046
2390
  unsigned int if_index = if_nametoindex(interface);
2047
2391
  if(if_index == 0){
2050
2394
    return ENXIO;
2051
2395
  }
2052
2396
  if(interface_is_up(interface)){
2053
 
    error_t ret_errno = 0, ioctl_errno = 0;
 
2397
    int ret_errno = 0;
 
2398
    int ioctl_errno = 0;
2054
2399
    if(not get_flags(interface, &network) and debug){
2055
2400
      ret_errno = errno;
2056
2401
      fprintf_plus(stderr, "Failed to get flags for interface "
2094
2439
    }
2095
2440
    
2096
2441
    /* Close the socket */
2097
 
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2442
    int ret = close(sd);
2098
2443
    if(ret == -1){
2099
2444
      perror_plus("close");
2100
2445
    }
2115
2460
}
2116
2461
 
2117
2462
int main(int argc, char *argv[]){
2118
 
  mandos_context mc = { .server = NULL, .dh_bits = 1024,
2119
 
                        .priority = "SECURE256:!CTYPE-X.509:"
2120
 
                        "+CTYPE-OPENPGP", .current_server = NULL,
2121
 
                        .interfaces = NULL, .interfaces_size = 0 };
 
2463
  mandos_context mc = { .server = NULL, .dh_bits = 0,
 
2464
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2465
                        .priority = "SECURE128:!CTYPE-X.509"
 
2466
                        ":+CTYPE-RAWPK:!RSA:!VERS-ALL:+VERS-TLS1.3"
 
2467
                        ":%PROFILE_ULTRA",
 
2468
#elif GNUTLS_VERSION_NUMBER < 0x030600
 
2469
                        .priority = "SECURE256:!CTYPE-X.509"
 
2470
                        ":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
 
2471
#else
 
2472
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
 
2473
#endif
 
2474
                        .current_server = NULL, .interfaces = NULL,
 
2475
                        .interfaces_size = 0 };
2122
2476
  AvahiSServiceBrowser *sb = NULL;
2123
2477
  error_t ret_errno;
2124
2478
  int ret;
2133
2487
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2134
2488
  const char *seckey = PATHDIR "/" SECKEY;
2135
2489
  const char *pubkey = PATHDIR "/" PUBKEY;
 
2490
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2491
  const char *tls_privkey = PATHDIR "/" TLS_PRIVKEY;
 
2492
  const char *tls_pubkey = PATHDIR "/" TLS_PUBKEY;
 
2493
#endif
 
2494
  const char *dh_params_file = NULL;
2136
2495
  char *interfaces_hooks = NULL;
2137
2496
  
2138
2497
  bool gnutls_initialized = false;
2185
2544
      { .name = "pubkey", .key = 'p',
2186
2545
        .arg = "FILE",
2187
2546
        .doc = "OpenPGP public key file base name",
2188
 
        .group = 2 },
 
2547
        .group = 1 },
 
2548
      { .name = "tls-privkey", .key = 't',
 
2549
        .arg = "FILE",
 
2550
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2551
        .doc = "TLS private key file base name",
 
2552
#else
 
2553
        .doc = "Dummy; ignored (requires GnuTLS 3.6.6)",
 
2554
#endif
 
2555
        .group = 1 },
 
2556
      { .name = "tls-pubkey", .key = 'T',
 
2557
        .arg = "FILE",
 
2558
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2559
        .doc = "TLS public key file base name",
 
2560
#else
 
2561
        .doc = "Dummy; ignored (requires GnuTLS 3.6.6)",
 
2562
#endif
 
2563
        .group = 1 },
2189
2564
      { .name = "dh-bits", .key = 129,
2190
2565
        .arg = "BITS",
2191
2566
        .doc = "Bit length of the prime number used in the"
2192
2567
        " Diffie-Hellman key exchange",
2193
2568
        .group = 2 },
 
2569
      { .name = "dh-params", .key = 134,
 
2570
        .arg = "FILE",
 
2571
        .doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
 
2572
        " for the Diffie-Hellman key exchange",
 
2573
        .group = 2 },
2194
2574
      { .name = "priority", .key = 130,
2195
2575
        .arg = "STRING",
2196
2576
        .doc = "GnuTLS priority string for the TLS handshake",
2242
2622
      case 'p':                 /* --pubkey */
2243
2623
        pubkey = arg;
2244
2624
        break;
 
2625
      case 't':                 /* --tls-privkey */
 
2626
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2627
        tls_privkey = arg;
 
2628
#endif
 
2629
        break;
 
2630
      case 'T':                 /* --tls-pubkey */
 
2631
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2632
        tls_pubkey = arg;
 
2633
#endif
 
2634
        break;
2245
2635
      case 129:                 /* --dh-bits */
2246
2636
        errno = 0;
2247
2637
        tmpmax = strtoimax(arg, &tmp, 10);
2251
2641
        }
2252
2642
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2253
2643
        break;
 
2644
      case 134:                 /* --dh-params */
 
2645
        dh_params_file = arg;
 
2646
        break;
2254
2647
      case 130:                 /* --priority */
2255
2648
        mc.priority = arg;
2256
2649
        break;
2279
2672
        argp_state_help(state, state->out_stream,
2280
2673
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2281
2674
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
2675
        __builtin_unreachable();
2282
2676
      case -3:                  /* --usage */
2283
2677
        argp_state_help(state, state->out_stream,
2284
2678
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
2679
        __builtin_unreachable();
2285
2680
      case 'V':                 /* --version */
2286
2681
        fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2287
2682
        exit(argp_err_exit_status);
2296
2691
                         .args_doc = "",
2297
2692
                         .doc = "Mandos client -- Get and decrypt"
2298
2693
                         " passwords from a Mandos server" };
2299
 
    ret = argp_parse(&argp, argc, argv,
2300
 
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2301
 
    switch(ret){
 
2694
    ret_errno = argp_parse(&argp, argc, argv,
 
2695
                           ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2696
    switch(ret_errno){
2302
2697
    case 0:
2303
2698
      break;
2304
2699
    case ENOMEM:
2305
2700
    default:
2306
 
      errno = ret;
 
2701
      errno = ret_errno;
2307
2702
      perror_plus("argp_parse");
2308
2703
      exitcode = EX_OSERR;
2309
2704
      goto end;
2315
2710
  
2316
2711
  {
2317
2712
    /* Work around Debian bug #633582:
2318
 
       <http://bugs.debian.org/633582> */
 
2713
       <https://bugs.debian.org/633582> */
2319
2714
    
2320
2715
    /* Re-raise privileges */
2321
 
    ret_errno = raise_privileges();
2322
 
    if(ret_errno != 0){
2323
 
      errno = ret_errno;
 
2716
    ret = raise_privileges();
 
2717
    if(ret != 0){
 
2718
      errno = ret;
2324
2719
      perror_plus("Failed to raise privileges");
2325
2720
    } else {
2326
2721
      struct stat st;
2342
2737
              }
2343
2738
            }
2344
2739
          }
2345
 
          TEMP_FAILURE_RETRY(close(seckey_fd));
 
2740
          close(seckey_fd);
2346
2741
        }
2347
2742
      }
2348
2743
      
2363
2758
              }
2364
2759
            }
2365
2760
          }
2366
 
          TEMP_FAILURE_RETRY(close(pubkey_fd));
 
2761
          close(pubkey_fd);
 
2762
        }
 
2763
      }
 
2764
      
 
2765
      if(dh_params_file != NULL
 
2766
         and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
 
2767
        int dhparams_fd = open(dh_params_file, O_RDONLY);
 
2768
        if(dhparams_fd == -1){
 
2769
          perror_plus("open");
 
2770
        } else {
 
2771
          ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
 
2772
          if(ret == -1){
 
2773
            perror_plus("fstat");
 
2774
          } else {
 
2775
            if(S_ISREG(st.st_mode)
 
2776
               and st.st_uid == 0 and st.st_gid == 0){
 
2777
              ret = fchown(dhparams_fd, uid, gid);
 
2778
              if(ret == -1){
 
2779
                perror_plus("fchown");
 
2780
              }
 
2781
            }
 
2782
          }
 
2783
          close(dhparams_fd);
2367
2784
        }
2368
2785
      }
2369
2786
      
2370
2787
      /* Lower privileges */
2371
 
      ret_errno = lower_privileges();
2372
 
      if(ret_errno != 0){
2373
 
        errno = ret_errno;
 
2788
      ret = lower_privileges();
 
2789
      if(ret != 0){
 
2790
        errno = ret;
2374
2791
        perror_plus("Failed to lower privileges");
2375
2792
      }
2376
2793
    }
2546
2963
      errno = bring_up_interface(interface, delay);
2547
2964
      if(not interface_was_up){
2548
2965
        if(errno != 0){
2549
 
          perror_plus("Failed to bring up interface");
 
2966
          fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
 
2967
                       " %s\n", interface, strerror(errno));
2550
2968
        } else {
2551
2969
          errno = argz_add(&interfaces_to_take_down,
2552
2970
                           &interfaces_to_take_down_size,
2575
2993
    goto end;
2576
2994
  }
2577
2995
  
2578
 
  ret = init_gnutls_global(pubkey, seckey, &mc);
 
2996
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2997
  ret = init_gnutls_global(tls_pubkey, tls_privkey, dh_params_file, &mc);
 
2998
#elif GNUTLS_VERSION_NUMBER < 0x030600
 
2999
  ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
 
3000
#else
 
3001
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
 
3002
#endif
2579
3003
  if(ret == -1){
2580
3004
    fprintf_plus(stderr, "init_gnutls_global failed\n");
2581
3005
    exitcode = EX_UNAVAILABLE;
2703
3127
    
2704
3128
    /* Allocate a new server */
2705
3129
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2706
 
                                 &config, NULL, NULL, &ret_errno);
 
3130
                                 &config, NULL, NULL, &ret);
2707
3131
    
2708
3132
    /* Free the Avahi configuration data */
2709
3133
    avahi_server_config_free(&config);
2712
3136
  /* Check if creating the Avahi server object succeeded */
2713
3137
  if(mc.server == NULL){
2714
3138
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2715
 
                 avahi_strerror(ret_errno));
 
3139
                 avahi_strerror(ret));
2716
3140
    exitcode = EX_UNAVAILABLE;
2717
3141
    goto end;
2718
3142
  }
2753
3177
 end:
2754
3178
  
2755
3179
  if(debug){
2756
 
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
 
3180
    if(signal_received){
 
3181
      fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
 
3182
                   argv[0], signal_received,
 
3183
                   strsignal(signal_received));
 
3184
    } else {
 
3185
      fprintf_plus(stderr, "%s exiting\n", argv[0]);
 
3186
    }
2757
3187
  }
2758
3188
  
2759
3189
  /* Cleanup things */
2770
3200
  
2771
3201
  if(gnutls_initialized){
2772
3202
    gnutls_certificate_free_credentials(mc.cred);
2773
 
    gnutls_global_deinit();
2774
3203
    gnutls_dh_params_deinit(mc.dh_params);
2775
3204
  }
2776
3205
  
2799
3228
  
2800
3229
  /* Re-raise privileges */
2801
3230
  {
2802
 
    ret_errno = raise_privileges();
2803
 
    if(ret_errno != 0){
2804
 
      errno = ret_errno;
 
3231
    ret = raise_privileges();
 
3232
    if(ret != 0){
 
3233
      errno = ret;
2805
3234
      perror_plus("Failed to raise privileges");
2806
3235
    } else {
2807
3236
      
2812
3241
      /* Take down the network interfaces which were brought up */
2813
3242
      {
2814
3243
        char *interface = NULL;
2815
 
        while((interface=argz_next(interfaces_to_take_down,
2816
 
                                   interfaces_to_take_down_size,
2817
 
                                   interface))){
2818
 
          ret_errno = take_down_interface(interface);
2819
 
          if(ret_errno != 0){
2820
 
            errno = ret_errno;
 
3244
        while((interface = argz_next(interfaces_to_take_down,
 
3245
                                     interfaces_to_take_down_size,
 
3246
                                     interface))){
 
3247
          ret = take_down_interface(interface);
 
3248
          if(ret != 0){
 
3249
            errno = ret;
2821
3250
            perror_plus("Failed to take down interface");
2822
3251
          }
2823
3252
        }
2828
3257
      }
2829
3258
    }
2830
3259
    
2831
 
    ret_errno = lower_privileges_permanently();
2832
 
    if(ret_errno != 0){
2833
 
      errno = ret_errno;
 
3260
    ret = lower_privileges_permanently();
 
3261
    if(ret != 0){
 
3262
      errno = ret;
2834
3263
      perror_plus("Failed to lower privileges permanently");
2835
3264
    }
2836
3265
  }
2838
3267
  free(interfaces_to_take_down);
2839
3268
  free(interfaces_hooks);
2840
3269
  
 
3270
  void clean_dir_at(int base, const char * const dirname,
 
3271
                    uintmax_t level){
 
3272
    struct dirent **direntries = NULL;
 
3273
    int dret;
 
3274
    int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
 
3275
                                                O_RDONLY
 
3276
                                                | O_NOFOLLOW
 
3277
                                                | O_DIRECTORY
 
3278
                                                | O_PATH));
 
3279
    if(dir_fd == -1){
 
3280
      perror_plus("open");
 
3281
      return;
 
3282
    }
 
3283
    int numentries = scandirat(dir_fd, ".", &direntries,
 
3284
                               notdotentries, alphasort);
 
3285
    if(numentries >= 0){
 
3286
      for(int i = 0; i < numentries; i++){
 
3287
        if(debug){
 
3288
          fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
 
3289
                       dirname, direntries[i]->d_name);
 
3290
        }
 
3291
        dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
 
3292
        if(dret == -1){
 
3293
          if(errno == EISDIR){
 
3294
              dret = unlinkat(dir_fd, direntries[i]->d_name,
 
3295
                              AT_REMOVEDIR);
 
3296
          }         
 
3297
          if((dret == -1) and (errno == ENOTEMPTY)
 
3298
             and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
 
3299
                  == 0) and (level == 0)){
 
3300
            /* Recurse only in this special case */
 
3301
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
 
3302
            dret = 0;
 
3303
          }
 
3304
          if((dret == -1) and (errno != ENOENT)){
 
3305
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
 
3306
                         direntries[i]->d_name, strerror(errno));
 
3307
          }
 
3308
        }
 
3309
        free(direntries[i]);
 
3310
      }
 
3311
      
 
3312
      /* need to clean even if 0 because man page doesn't specify */
 
3313
      free(direntries);
 
3314
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
 
3315
      if(dret == -1 and errno != ENOENT){
 
3316
        perror_plus("rmdir");
 
3317
      }
 
3318
    } else {
 
3319
      perror_plus("scandirat");
 
3320
    }
 
3321
    close(dir_fd);
 
3322
  }
 
3323
  
2841
3324
  /* Removes the GPGME temp directory and all files inside */
2842
3325
  if(tempdir != NULL){
2843
 
    struct dirent **direntries = NULL;
2844
 
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
2845
 
                                                  | O_NOFOLLOW
2846
 
                                                  | O_DIRECTORY
2847
 
                                                  | O_PATH));
2848
 
    if(tempdir_fd == -1){
2849
 
      perror_plus("open");
2850
 
    } else {
2851
 
#ifdef __GLIBC__
2852
 
#if __GLIBC_PREREQ(2, 15)
2853
 
      int numentries = scandirat(tempdir_fd, ".", &direntries,
2854
 
                                 notdotentries, alphasort);
2855
 
#else  /* not __GLIBC_PREREQ(2, 15) */
2856
 
      int numentries = scandir(tempdir, &direntries, notdotentries,
2857
 
                               alphasort);
2858
 
#endif  /* not __GLIBC_PREREQ(2, 15) */
2859
 
#else   /* not __GLIBC__ */
2860
 
      int numentries = scandir(tempdir, &direntries, notdotentries,
2861
 
                               alphasort);
2862
 
#endif  /* not __GLIBC__ */
2863
 
      if(numentries >= 0){
2864
 
        for(int i = 0; i < numentries; i++){
2865
 
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
2866
 
          if(ret == -1){
2867
 
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
2868
 
                         " \"%s\", 0): %s\n", tempdir,
2869
 
                         direntries[i]->d_name, strerror(errno));
2870
 
          }
2871
 
          free(direntries[i]);
2872
 
        }
2873
 
        
2874
 
        /* need to clean even if 0 because man page doesn't specify */
2875
 
        free(direntries);
2876
 
        if(numentries == -1){
2877
 
          perror_plus("scandir");
2878
 
        }
2879
 
        ret = rmdir(tempdir);
2880
 
        if(ret == -1 and errno != ENOENT){
2881
 
          perror_plus("rmdir");
2882
 
        }
2883
 
      }
2884
 
      TEMP_FAILURE_RETRY(close(tempdir_fd));
2885
 
    }
 
3326
    clean_dir_at(-1, tempdir, 0);
2886
3327
  }
2887
3328
  
2888
3329
  if(quit_now){