/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to plugins.d/mandos-client.c

  • Committer: Teddy Hogeborn
  • Date: 2015-03-10 18:03:38 UTC
  • mto: (237.7.304 trunk)
  • mto: This revision was merged to the branch mainline in revision 325.
  • Revision ID: teddy@recompile.se-20150310180338-pcxw6r2qmw9k6br9
Add ":!RSA" to GnuTLS priority string, to disallow non-DHE kx.

If Mandos was somehow made to use a non-ephemeral Diffie-Hellman key
exchange algorithm in the TLS handshake, any saved network traffic
could then be decrypted later if the Mandos client key was obtained.
By default, Mandos uses ephemeral DH key exchanges which does not have
this problem, but a non-ephemeral key exchange algorithm was still
enabled by default.  The simplest solution is to simply turn that off,
which ensures that Mandos will always use ephemeral DH key exchanges.

There is a "PFS" priority string specifier, but we can't use it because:

1. Security-wise, it is a mix between "NORMAL" and "SECURE128" - it
   enables a lot more algorithms than "SECURE256".

2. It is only available since GnuTLS 3.2.4.

Thanks to Andreas Fischer <af@bantuX.org> for reporting this issue.

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-2018 Teddy Hogeborn
13
 
 * Copyright © 2008-2018 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
 
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
23
21
 * WITHOUT ANY WARRANTY; without even the implied warranty of
24
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25
23
 * General Public License for more details.
26
24
 * 
27
25
 * You should have received a copy of the GNU General Public License
28
 
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
 
26
 * along with this program.  If not, see
 
27
 * <http://www.gnu.org/licenses/>.
29
28
 * 
30
29
 * Contact the authors at <mandos@recompile.se>.
31
30
 */
47
46
#include <stdlib.h>             /* free(), EXIT_SUCCESS, srand(),
48
47
                                   strtof(), abort() */
49
48
#include <stdbool.h>            /* bool, false, true */
50
 
#include <string.h>             /* strcmp(), strlen(), strerror(),
51
 
                                   asprintf(), strncpy(), strsignal()
52
 
                                */
 
49
#include <string.h>             /* memset(), strcmp(), strlen(),
 
50
                                   strerror(), asprintf(), strcpy() */
53
51
#include <sys/ioctl.h>          /* ioctl */
54
52
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
55
53
                                   sockaddr_in6, PF_INET6,
59
57
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
60
58
                                   inet_pton(), connect(),
61
59
                                   getnameinfo() */
62
 
#include <fcntl.h>              /* open(), unlinkat(), AT_REMOVEDIR */
 
60
#include <fcntl.h>              /* open(), unlinkat() */
63
61
#include <dirent.h>             /* opendir(), struct dirent, readdir()
64
62
                                 */
65
63
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
66
64
                                   strtoimax() */
67
 
#include <errno.h>              /* perror(), errno, EINTR, EINVAL,
68
 
                                   EAI_SYSTEM, ENETUNREACH,
69
 
                                   EHOSTUNREACH, ECONNREFUSED, EPROTO,
70
 
                                   EIO, ENOENT, ENXIO, ENOMEM, EISDIR,
71
 
                                   ENOTEMPTY,
 
65
#include <errno.h>              /* perror(), errno,
72
66
                                   program_invocation_short_name */
73
67
#include <time.h>               /* nanosleep(), time(), sleep() */
74
68
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
311
305
      return false;
312
306
    }
313
307
    
314
 
    ret = close(fd);
 
308
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
315
309
    if(ret == -1){
316
310
      perror_plus("close");
317
311
    }
499
493
  return plaintext_length;
500
494
}
501
495
 
502
 
__attribute__((warn_unused_result, const))
503
 
static const char *safe_string(const char *str){
504
 
  if(str == NULL)
505
 
    return "(unknown)";
506
 
  return str;
507
 
}
508
 
 
509
496
__attribute__((warn_unused_result))
510
497
static const char *safer_gnutls_strerror(int value){
511
498
  const char *ret = gnutls_strerror(value);
512
 
  return safe_string(ret);
 
499
  if(ret == NULL)
 
500
    ret = "(unknown)";
 
501
  return ret;
513
502
}
514
503
 
515
504
/* GnuTLS log function callback */
519
508
  fprintf_plus(stderr, "GnuTLS: %s", string);
520
509
}
521
510
 
522
 
__attribute__((nonnull(1, 2, 4), warn_unused_result))
 
511
__attribute__((nonnull, warn_unused_result))
523
512
static int init_gnutls_global(const char *pubkeyfilename,
524
513
                              const char *seckeyfilename,
525
 
                              const char *dhparamsfilename,
526
514
                              mandos_context *mc){
527
515
  int ret;
528
 
  unsigned int uret;
529
516
  
530
517
  if(debug){
531
518
    fprintf_plus(stderr, "Initializing GnuTLS\n");
532
519
  }
533
520
  
 
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
  
534
528
  if(debug){
535
529
    /* "Use a log level over 10 to enable all debugging options."
536
530
     * - GnuTLS manual
544
538
  if(ret != GNUTLS_E_SUCCESS){
545
539
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
546
540
                 safer_gnutls_strerror(ret));
 
541
    gnutls_global_deinit();
547
542
    return -1;
548
543
  }
549
544
  
574
569
                 safer_gnutls_strerror(ret));
575
570
    goto globalfail;
576
571
  }
577
 
  /* If a Diffie-Hellman parameters file was given, try to use it */
578
 
  if(dhparamsfilename != NULL){
579
 
    gnutls_datum_t params = { .data = NULL, .size = 0 };
580
 
    do {
581
 
      int dhpfile = open(dhparamsfilename, O_RDONLY);
582
 
      if(dhpfile == -1){
583
 
        perror_plus("open");
584
 
        dhparamsfilename = NULL;
585
 
        break;
586
 
      }
587
 
      size_t params_capacity = 0;
588
 
      while(true){
589
 
        params_capacity = incbuffer((char **)&params.data,
590
 
                                    (size_t)params.size,
591
 
                                    (size_t)params_capacity);
592
 
        if(params_capacity == 0){
593
 
          perror_plus("incbuffer");
594
 
          free(params.data);
595
 
          params.data = NULL;
596
 
          dhparamsfilename = NULL;
597
 
          break;
598
 
        }
599
 
        ssize_t bytes_read = read(dhpfile,
600
 
                                  params.data + params.size,
601
 
                                  BUFFER_SIZE);
602
 
        /* EOF */
603
 
        if(bytes_read == 0){
604
 
          break;
605
 
        }
606
 
        /* check bytes_read for failure */
607
 
        if(bytes_read < 0){
608
 
          perror_plus("read");
609
 
          free(params.data);
610
 
          params.data = NULL;
611
 
          dhparamsfilename = NULL;
612
 
          break;
613
 
        }
614
 
        params.size += (unsigned int)bytes_read;
615
 
      }
616
 
      ret = close(dhpfile);
617
 
      if(ret == -1){
618
 
        perror_plus("close");
619
 
      }
620
 
      if(params.data == NULL){
621
 
        dhparamsfilename = NULL;
622
 
      }
623
 
      if(dhparamsfilename == NULL){
624
 
        break;
625
 
      }
626
 
      ret = gnutls_dh_params_import_pkcs3(mc->dh_params, &params,
627
 
                                          GNUTLS_X509_FMT_PEM);
628
 
      if(ret != GNUTLS_E_SUCCESS){
629
 
        fprintf_plus(stderr, "Failed to parse DH parameters in file"
630
 
                     " \"%s\": %s\n", dhparamsfilename,
631
 
                     safer_gnutls_strerror(ret));
632
 
        dhparamsfilename = NULL;
633
 
      }
634
 
      free(params.data);
635
 
    } while(false);
636
 
  }
637
 
  if(dhparamsfilename == NULL){
638
 
    if(mc->dh_bits == 0){
639
 
      /* Find out the optimal number of DH bits */
640
 
      /* Try to read the private key file */
641
 
      gnutls_datum_t buffer = { .data = NULL, .size = 0 };
642
 
      do {
643
 
        int secfile = open(seckeyfilename, O_RDONLY);
644
 
        if(secfile == -1){
645
 
          perror_plus("open");
646
 
          break;
647
 
        }
648
 
        size_t buffer_capacity = 0;
649
 
        while(true){
650
 
          buffer_capacity = incbuffer((char **)&buffer.data,
651
 
                                      (size_t)buffer.size,
652
 
                                      (size_t)buffer_capacity);
653
 
          if(buffer_capacity == 0){
654
 
            perror_plus("incbuffer");
655
 
            free(buffer.data);
656
 
            buffer.data = NULL;
657
 
            break;
658
 
          }
659
 
          ssize_t bytes_read = read(secfile,
660
 
                                    buffer.data + buffer.size,
661
 
                                    BUFFER_SIZE);
662
 
          /* EOF */
663
 
          if(bytes_read == 0){
664
 
            break;
665
 
          }
666
 
          /* check bytes_read for failure */
667
 
          if(bytes_read < 0){
668
 
            perror_plus("read");
669
 
            free(buffer.data);
670
 
            buffer.data = NULL;
671
 
            break;
672
 
          }
673
 
          buffer.size += (unsigned int)bytes_read;
674
 
        }
675
 
        close(secfile);
676
 
      } while(false);
677
 
      /* If successful, use buffer to parse private key */
678
 
      gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
679
 
      if(buffer.data != NULL){
680
 
        {
681
 
          gnutls_openpgp_privkey_t privkey = NULL;
682
 
          ret = gnutls_openpgp_privkey_init(&privkey);
683
 
          if(ret != GNUTLS_E_SUCCESS){
684
 
            fprintf_plus(stderr, "Error initializing OpenPGP key"
685
 
                         " structure: %s",
686
 
                         safer_gnutls_strerror(ret));
687
 
            free(buffer.data);
688
 
            buffer.data = NULL;
689
 
          } else {
690
 
            ret = gnutls_openpgp_privkey_import
691
 
              (privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
692
 
            if(ret != GNUTLS_E_SUCCESS){
693
 
              fprintf_plus(stderr, "Error importing OpenPGP key : %s",
694
 
                           safer_gnutls_strerror(ret));
695
 
              privkey = NULL;
696
 
            }
697
 
            free(buffer.data);
698
 
            buffer.data = NULL;
699
 
            if(privkey != NULL){
700
 
              /* Use private key to suggest an appropriate
701
 
                 sec_param */
702
 
              sec_param = gnutls_openpgp_privkey_sec_param(privkey);
703
 
              gnutls_openpgp_privkey_deinit(privkey);
704
 
              if(debug){
705
 
                fprintf_plus(stderr, "This OpenPGP key implies using"
706
 
                             " a GnuTLS security parameter \"%s\".\n",
707
 
                             safe_string(gnutls_sec_param_get_name
708
 
                                         (sec_param)));
709
 
              }
710
 
            }
711
 
          }
712
 
        }
713
 
        if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
714
 
          /* Err on the side of caution */
715
 
          sec_param = GNUTLS_SEC_PARAM_ULTRA;
716
 
          if(debug){
717
 
            fprintf_plus(stderr, "Falling back to security parameter"
718
 
                         " \"%s\"\n",
719
 
                         safe_string(gnutls_sec_param_get_name
720
 
                                     (sec_param)));
721
 
          }
722
 
        }
723
 
      }
724
 
      uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
725
 
      if(uret != 0){
726
 
        mc->dh_bits = uret;
727
 
        if(debug){
728
 
          fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
729
 
                       " implies %u DH bits; using that.\n",
730
 
                       safe_string(gnutls_sec_param_get_name
731
 
                                   (sec_param)),
732
 
                       mc->dh_bits);
733
 
        }
734
 
      } else {
735
 
        fprintf_plus(stderr, "Failed to get implied number of DH"
736
 
                     " bits for security parameter \"%s\"): %s\n",
737
 
                     safe_string(gnutls_sec_param_get_name
738
 
                                 (sec_param)),
739
 
                     safer_gnutls_strerror(ret));
740
 
        goto globalfail;
741
 
      }
742
 
    } else if(debug){
743
 
      fprintf_plus(stderr, "DH bits explicitly set to %u\n",
744
 
                   mc->dh_bits);
745
 
    }
746
 
    ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
747
 
    if(ret != GNUTLS_E_SUCCESS){
748
 
      fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
749
 
                   " bits): %s\n", mc->dh_bits,
750
 
                   safer_gnutls_strerror(ret));
751
 
      goto globalfail;
752
 
    }
753
 
  }
 
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
  
754
579
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
755
580
  
756
581
  return 0;
758
583
 globalfail:
759
584
  
760
585
  gnutls_certificate_free_credentials(mc->cred);
 
586
  gnutls_global_deinit();
761
587
  gnutls_dh_params_deinit(mc->dh_params);
762
588
  return -1;
763
589
}
815
641
  /* ignore client certificate if any. */
816
642
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
817
643
  
 
644
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
 
645
  
818
646
  return 0;
819
647
}
820
648
 
822
650
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
823
651
                      __attribute__((unused)) const char *txt){}
824
652
 
825
 
/* Set effective uid to 0, return errno */
826
 
__attribute__((warn_unused_result))
827
 
int raise_privileges(void){
828
 
  int old_errno = errno;
829
 
  int ret = 0;
830
 
  if(seteuid(0) == -1){
831
 
    ret = errno;
832
 
  }
833
 
  errno = old_errno;
834
 
  return ret;
835
 
}
836
 
 
837
 
/* Set effective and real user ID to 0.  Return errno. */
838
 
__attribute__((warn_unused_result))
839
 
int raise_privileges_permanently(void){
840
 
  int old_errno = errno;
841
 
  int ret = raise_privileges();
842
 
  if(ret != 0){
843
 
    errno = old_errno;
844
 
    return ret;
845
 
  }
846
 
  if(setuid(0) == -1){
847
 
    ret = errno;
848
 
  }
849
 
  errno = old_errno;
850
 
  return ret;
851
 
}
852
 
 
853
 
/* Set effective user ID to unprivileged saved user ID */
854
 
__attribute__((warn_unused_result))
855
 
int lower_privileges(void){
856
 
  int old_errno = errno;
857
 
  int ret = 0;
858
 
  if(seteuid(uid) == -1){
859
 
    ret = errno;
860
 
  }
861
 
  errno = old_errno;
862
 
  return ret;
863
 
}
864
 
 
865
 
/* Lower privileges permanently */
866
 
__attribute__((warn_unused_result))
867
 
int lower_privileges_permanently(void){
868
 
  int old_errno = errno;
869
 
  int ret = 0;
870
 
  if(setuid(uid) == -1){
871
 
    ret = errno;
872
 
  }
873
 
  errno = old_errno;
874
 
  return ret;
875
 
}
876
 
 
877
 
/* Helper function to add_local_route() and delete_local_route() */
878
 
__attribute__((nonnull, warn_unused_result))
879
 
static bool add_delete_local_route(const bool add,
880
 
                                   const char *address,
881
 
                                   AvahiIfIndex if_index){
882
 
  int ret;
883
 
  char helper[] = "mandos-client-iprouteadddel";
884
 
  char add_arg[] = "add";
885
 
  char delete_arg[] = "delete";
886
 
  char debug_flag[] = "--debug";
887
 
  char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
888
 
  if(pluginhelperdir == NULL){
889
 
    if(debug){
890
 
      fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
891
 
                   " variable not set; cannot run helper\n");
892
 
    }
893
 
    return false;
894
 
  }
895
 
  
896
 
  char interface[IF_NAMESIZE];
897
 
  if(if_indextoname((unsigned int)if_index, interface) == NULL){
898
 
    perror_plus("if_indextoname");
899
 
    return false;
900
 
  }
901
 
  
902
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
903
 
  if(devnull == -1){
904
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
905
 
    return false;
906
 
  }
907
 
  pid_t pid = fork();
908
 
  if(pid == 0){
909
 
    /* Child */
910
 
    /* Raise privileges */
911
 
    errno = raise_privileges_permanently();
912
 
    if(errno != 0){
913
 
      perror_plus("Failed to raise privileges");
914
 
      /* _exit(EX_NOPERM); */
915
 
    } else {
916
 
      /* Set group */
917
 
      errno = 0;
918
 
      ret = setgid(0);
919
 
      if(ret == -1){
920
 
        perror_plus("setgid");
921
 
        _exit(EX_NOPERM);
922
 
      }
923
 
      /* Reset supplementary groups */
924
 
      errno = 0;
925
 
      ret = setgroups(0, NULL);
926
 
      if(ret == -1){
927
 
        perror_plus("setgroups");
928
 
        _exit(EX_NOPERM);
929
 
      }
930
 
    }
931
 
    ret = dup2(devnull, STDIN_FILENO);
932
 
    if(ret == -1){
933
 
      perror_plus("dup2(devnull, STDIN_FILENO)");
934
 
      _exit(EX_OSERR);
935
 
    }
936
 
    ret = close(devnull);
937
 
    if(ret == -1){
938
 
      perror_plus("close");
939
 
      _exit(EX_OSERR);
940
 
    }
941
 
    ret = dup2(STDERR_FILENO, STDOUT_FILENO);
942
 
    if(ret == -1){
943
 
      perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
944
 
      _exit(EX_OSERR);
945
 
    }
946
 
    int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
947
 
                                                    O_RDONLY
948
 
                                                    | O_DIRECTORY
949
 
                                                    | O_PATH
950
 
                                                    | O_CLOEXEC));
951
 
    if(helperdir_fd == -1){
952
 
      perror_plus("open");
953
 
      _exit(EX_UNAVAILABLE);
954
 
    }
955
 
    int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
956
 
                                                   helper, O_RDONLY));
957
 
    if(helper_fd == -1){
958
 
      perror_plus("openat");
959
 
      close(helperdir_fd);
960
 
      _exit(EX_UNAVAILABLE);
961
 
    }
962
 
    close(helperdir_fd);
963
 
#ifdef __GNUC__
964
 
#pragma GCC diagnostic push
965
 
#pragma GCC diagnostic ignored "-Wcast-qual"
966
 
#endif
967
 
    if(fexecve(helper_fd, (char *const [])
968
 
               { helper, add ? add_arg : delete_arg, (char *)address,
969
 
                   interface, debug ? debug_flag : NULL, NULL },
970
 
               environ) == -1){
971
 
#ifdef __GNUC__
972
 
#pragma GCC diagnostic pop
973
 
#endif
974
 
      perror_plus("fexecve");
975
 
      _exit(EXIT_FAILURE);
976
 
    }
977
 
  }
978
 
  if(pid == -1){
979
 
    perror_plus("fork");
980
 
    return false;
981
 
  }
982
 
  int status;
983
 
  pid_t pret = -1;
984
 
  errno = 0;
985
 
  do {
986
 
    pret = waitpid(pid, &status, 0);
987
 
    if(pret == -1 and errno == EINTR and quit_now){
988
 
      int errno_raising = 0;
989
 
      if((errno = raise_privileges()) != 0){
990
 
        errno_raising = errno;
991
 
        perror_plus("Failed to raise privileges in order to"
992
 
                    " kill helper program");
993
 
      }
994
 
      if(kill(pid, SIGTERM) == -1){
995
 
        perror_plus("kill");
996
 
      }
997
 
      if((errno_raising == 0) and (errno = lower_privileges()) != 0){
998
 
        perror_plus("Failed to lower privileges after killing"
999
 
                    " helper program");
1000
 
      }
1001
 
      return false;
1002
 
    }
1003
 
  } while(pret == -1 and errno == EINTR);
1004
 
  if(pret == -1){
1005
 
    perror_plus("waitpid");
1006
 
    return false;
1007
 
  }
1008
 
  if(WIFEXITED(status)){
1009
 
    if(WEXITSTATUS(status) != 0){
1010
 
      fprintf_plus(stderr, "Error: iprouteadddel exited"
1011
 
                   " with status %d\n", WEXITSTATUS(status));
1012
 
      return false;
1013
 
    }
1014
 
    return true;
1015
 
  }
1016
 
  if(WIFSIGNALED(status)){
1017
 
    fprintf_plus(stderr, "Error: iprouteadddel died by"
1018
 
                 " signal %d\n", WTERMSIG(status));
1019
 
    return false;
1020
 
  }
1021
 
  fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1022
 
  return false;
1023
 
}
1024
 
 
1025
 
__attribute__((nonnull, warn_unused_result))
1026
 
static bool add_local_route(const char *address,
1027
 
                            AvahiIfIndex if_index){
1028
 
  if(debug){
1029
 
    fprintf_plus(stderr, "Adding route to %s\n", address);
1030
 
  }
1031
 
  return add_delete_local_route(true, address, if_index);
1032
 
}
1033
 
 
1034
 
__attribute__((nonnull, warn_unused_result))
1035
 
static bool delete_local_route(const char *address,
1036
 
                               AvahiIfIndex if_index){
1037
 
  if(debug){
1038
 
    fprintf_plus(stderr, "Removing route to %s\n", address);
1039
 
  }
1040
 
  return add_delete_local_route(false, address, if_index);
1041
 
}
1042
 
 
1043
653
/* Called when a Mandos server is found */
1044
654
__attribute__((nonnull, warn_unused_result))
1045
655
static int start_mandos_communication(const char *ip, in_port_t port,
1056
666
  int retval = -1;
1057
667
  gnutls_session_t session;
1058
668
  int pf;                       /* Protocol family */
1059
 
  bool route_added = false;
1060
669
  
1061
670
  errno = 0;
1062
671
  
1084
693
    bool match = false;
1085
694
    {
1086
695
      char *interface = NULL;
1087
 
      while((interface = argz_next(mc->interfaces,
1088
 
                                   mc->interfaces_size,
1089
 
                                   interface))){
 
696
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
 
697
                                 interface))){
1090
698
        if(if_nametoindex(interface) == (unsigned int)if_index){
1091
699
          match = true;
1092
700
          break;
1121
729
                 PRIuMAX "\n", ip, (uintmax_t)port);
1122
730
  }
1123
731
  
1124
 
  tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
 
732
  tcp_sd = socket(pf, SOCK_STREAM, 0);
1125
733
  if(tcp_sd < 0){
1126
734
    int e = errno;
1127
735
    perror_plus("socket");
1134
742
    goto mandos_end;
1135
743
  }
1136
744
  
 
745
  memset(&to, 0, sizeof(to));
1137
746
  if(af == AF_INET6){
1138
 
    struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1139
 
    *to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1140
 
    ret = inet_pton(af, ip, &to6->sin6_addr);
 
747
    ((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
 
748
    ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
1141
749
  } else {                      /* IPv4 */
1142
 
    struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1143
 
    *to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1144
 
    ret = inet_pton(af, ip, &to4->sin_addr);
 
750
    ((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
 
751
    ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
1145
752
  }
1146
753
  if(ret < 0 ){
1147
754
    int e = errno;
1217
824
    goto mandos_end;
1218
825
  }
1219
826
  
1220
 
  while(true){
1221
 
    if(af == AF_INET6){
1222
 
      ret = connect(tcp_sd, (struct sockaddr *)&to,
1223
 
                    sizeof(struct sockaddr_in6));
1224
 
    } else {
1225
 
      ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1226
 
                    sizeof(struct sockaddr_in));
1227
 
    }
1228
 
    if(ret < 0){
1229
 
      if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1230
 
         and if_index != AVAHI_IF_UNSPEC
1231
 
         and connect_to == NULL
1232
 
         and not route_added and
1233
 
         ((af == AF_INET6 and not
1234
 
           IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1235
 
                                    &to)->sin6_addr)))
1236
 
          or (af == AF_INET and
1237
 
              /* Not a a IPv4LL address */
1238
 
              (ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1239
 
               & 0xFFFF0000L) != 0xA9FE0000L))){
1240
 
        /* Work around Avahi bug - Avahi does not announce link-local
1241
 
           addresses if it has a global address, so local hosts with
1242
 
           *only* a link-local address (e.g. Mandos clients) cannot
1243
 
           connect to a Mandos server announced by Avahi on a server
1244
 
           host with a global address.  Work around this by retrying
1245
 
           with an explicit route added with the server's address.
1246
 
           
1247
 
           Avahi bug reference:
1248
 
           https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1249
 
           https://bugs.debian.org/587961
1250
 
        */
1251
 
        if(debug){
1252
 
          fprintf_plus(stderr, "Mandos server unreachable, trying"
1253
 
                       " direct route\n");
1254
 
        }
1255
 
        int e = errno;
1256
 
        route_added = add_local_route(ip, if_index);
1257
 
        if(route_added){
1258
 
          continue;
1259
 
        }
1260
 
        errno = e;
1261
 
      }
1262
 
      if(errno != ECONNREFUSED or debug){
1263
 
        int e = errno;
1264
 
        perror_plus("connect");
1265
 
        errno = e;
1266
 
      }
1267
 
      goto mandos_end;
1268
 
    }
1269
 
    
1270
 
    if(quit_now){
1271
 
      errno = EINTR;
1272
 
      goto mandos_end;
1273
 
    }
1274
 
    break;
 
827
  if(af == AF_INET6){
 
828
    ret = connect(tcp_sd, (struct sockaddr *)&to,
 
829
                  sizeof(struct sockaddr_in6));
 
830
  } else {
 
831
    ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
 
832
                  sizeof(struct sockaddr_in));
 
833
  }
 
834
  if(ret < 0){
 
835
    if((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
 
836
      int e = errno;
 
837
      perror_plus("connect");
 
838
      errno = e;
 
839
    }
 
840
    goto mandos_end;
 
841
  }
 
842
  
 
843
  if(quit_now){
 
844
    errno = EINTR;
 
845
    goto mandos_end;
1275
846
  }
1276
847
  
1277
848
  const char *out = mandos_protocol_version;
1431
1002
                                               &decrypted_buffer, mc);
1432
1003
    if(decrypted_buffer_size >= 0){
1433
1004
      
1434
 
      clearerr(stdout);
1435
1005
      written = 0;
1436
1006
      while(written < (size_t) decrypted_buffer_size){
1437
1007
        if(quit_now){
1453
1023
        }
1454
1024
        written += (size_t)ret;
1455
1025
      }
1456
 
      ret = fflush(stdout);
1457
 
      if(ret != 0){
1458
 
        int e = errno;
1459
 
        if(debug){
1460
 
          fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1461
 
                       strerror(errno));
1462
 
        }
1463
 
        errno = e;
1464
 
        goto mandos_end;
1465
 
      }
1466
1026
      retval = 0;
1467
1027
    }
1468
1028
  }
1471
1031
  
1472
1032
 mandos_end:
1473
1033
  {
1474
 
    if(route_added){
1475
 
      if(not delete_local_route(ip, if_index)){
1476
 
        fprintf_plus(stderr, "Failed to delete local route to %s on"
1477
 
                     " interface %d", ip, if_index);
1478
 
      }
1479
 
    }
1480
1034
    int e = errno;
1481
1035
    free(decrypted_buffer);
1482
1036
    free(buffer);
1483
1037
    if(tcp_sd >= 0){
1484
 
      ret = close(tcp_sd);
 
1038
      ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
1485
1039
    }
1486
1040
    if(ret == -1){
1487
1041
      if(e == 0){
1499
1053
  return retval;
1500
1054
}
1501
1055
 
 
1056
__attribute__((nonnull))
1502
1057
static void resolve_callback(AvahiSServiceResolver *r,
1503
1058
                             AvahiIfIndex interface,
1504
1059
                             AvahiProtocol proto,
1641
1196
__attribute__((nonnull, warn_unused_result))
1642
1197
bool get_flags(const char *ifname, struct ifreq *ifr){
1643
1198
  int ret;
1644
 
  int old_errno;
 
1199
  error_t ret_errno;
1645
1200
  
1646
1201
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1647
1202
  if(s < 0){
1648
 
    old_errno = errno;
 
1203
    ret_errno = errno;
1649
1204
    perror_plus("socket");
1650
 
    errno = old_errno;
 
1205
    errno = ret_errno;
1651
1206
    return false;
1652
1207
  }
1653
 
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1654
 
  ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
 
1208
  strcpy(ifr->ifr_name, ifname);
1655
1209
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1656
1210
  if(ret == -1){
1657
1211
    if(debug){
1658
 
      old_errno = errno;
 
1212
      ret_errno = errno;
1659
1213
      perror_plus("ioctl SIOCGIFFLAGS");
1660
 
      errno = old_errno;
1661
 
    }
1662
 
    if((close(s) == -1) and debug){
1663
 
      old_errno = errno;
1664
 
      perror_plus("close");
1665
 
      errno = old_errno;
 
1214
      errno = ret_errno;
1666
1215
    }
1667
1216
    return false;
1668
1217
  }
1669
 
  if((close(s) == -1) and debug){
1670
 
    old_errno = errno;
1671
 
    perror_plus("close");
1672
 
    errno = old_errno;
1673
 
  }
1674
1218
  return true;
1675
1219
}
1676
1220
 
1918
1462
  }
1919
1463
}
1920
1464
 
 
1465
/* Set effective uid to 0, return errno */
 
1466
__attribute__((warn_unused_result))
 
1467
error_t raise_privileges(void){
 
1468
  error_t old_errno = errno;
 
1469
  error_t ret_errno = 0;
 
1470
  if(seteuid(0) == -1){
 
1471
    ret_errno = errno;
 
1472
  }
 
1473
  errno = old_errno;
 
1474
  return ret_errno;
 
1475
}
 
1476
 
 
1477
/* Set effective and real user ID to 0.  Return errno. */
 
1478
__attribute__((warn_unused_result))
 
1479
error_t raise_privileges_permanently(void){
 
1480
  error_t old_errno = errno;
 
1481
  error_t ret_errno = raise_privileges();
 
1482
  if(ret_errno != 0){
 
1483
    errno = old_errno;
 
1484
    return ret_errno;
 
1485
  }
 
1486
  if(setuid(0) == -1){
 
1487
    ret_errno = errno;
 
1488
  }
 
1489
  errno = old_errno;
 
1490
  return ret_errno;
 
1491
}
 
1492
 
 
1493
/* Set effective user ID to unprivileged saved user ID */
 
1494
__attribute__((warn_unused_result))
 
1495
error_t lower_privileges(void){
 
1496
  error_t old_errno = errno;
 
1497
  error_t ret_errno = 0;
 
1498
  if(seteuid(uid) == -1){
 
1499
    ret_errno = errno;
 
1500
  }
 
1501
  errno = old_errno;
 
1502
  return ret_errno;
 
1503
}
 
1504
 
 
1505
/* Lower privileges permanently */
 
1506
__attribute__((warn_unused_result))
 
1507
error_t lower_privileges_permanently(void){
 
1508
  error_t old_errno = errno;
 
1509
  error_t ret_errno = 0;
 
1510
  if(setuid(uid) == -1){
 
1511
    ret_errno = errno;
 
1512
  }
 
1513
  errno = old_errno;
 
1514
  return ret_errno;
 
1515
}
 
1516
 
1921
1517
__attribute__((nonnull))
1922
1518
void run_network_hooks(const char *mode, const char *interface,
1923
1519
                       const float delay){
1924
1520
  struct dirent **direntries = NULL;
1925
1521
  if(hookdir_fd == -1){
1926
 
    hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
1927
 
                      | O_CLOEXEC);
 
1522
    hookdir_fd = open(hookdir, O_RDONLY);
1928
1523
    if(hookdir_fd == -1){
1929
1524
      if(errno == ENOENT){
1930
1525
        if(debug){
1937
1532
      return;
1938
1533
    }
1939
1534
  }
1940
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1941
 
  if(devnull == -1){
1942
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
1943
 
    return;
1944
 
  }
 
1535
#ifdef __GLIBC__
 
1536
#if __GLIBC_PREREQ(2, 15)
1945
1537
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
1946
1538
                           runnable_hook, alphasort);
 
1539
#else  /* not __GLIBC_PREREQ(2, 15) */
 
1540
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
1541
                         alphasort);
 
1542
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
1543
#else   /* not __GLIBC__ */
 
1544
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
1545
                         alphasort);
 
1546
#endif  /* not __GLIBC__ */
1947
1547
  if(numhooks == -1){
1948
1548
    perror_plus("scandir");
1949
 
    close(devnull);
1950
1549
    return;
1951
1550
  }
1952
1551
  struct dirent *direntry;
1953
1552
  int ret;
 
1553
  int devnull = open("/dev/null", O_RDONLY);
1954
1554
  for(int i = 0; i < numhooks; i++){
1955
1555
    direntry = direntries[i];
1956
1556
    if(debug){
1980
1580
        perror_plus("setgroups");
1981
1581
        _exit(EX_NOPERM);
1982
1582
      }
 
1583
      ret = dup2(devnull, STDIN_FILENO);
 
1584
      if(ret == -1){
 
1585
        perror_plus("dup2(devnull, STDIN_FILENO)");
 
1586
        _exit(EX_OSERR);
 
1587
      }
 
1588
      ret = close(devnull);
 
1589
      if(ret == -1){
 
1590
        perror_plus("close");
 
1591
        _exit(EX_OSERR);
 
1592
      }
 
1593
      ret = dup2(STDERR_FILENO, STDOUT_FILENO);
 
1594
      if(ret == -1){
 
1595
        perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
 
1596
        _exit(EX_OSERR);
 
1597
      }
1983
1598
      ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1984
1599
      if(ret == -1){
1985
1600
        perror_plus("setenv");
2020
1635
          _exit(EX_OSERR);
2021
1636
        }
2022
1637
      }
2023
 
      int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
2024
 
                                                   direntry->d_name,
2025
 
                                                   O_RDONLY));
 
1638
      int hook_fd = openat(hookdir_fd, direntry->d_name, O_RDONLY);
2026
1639
      if(hook_fd == -1){
2027
1640
        perror_plus("openat");
2028
1641
        _exit(EXIT_FAILURE);
2029
1642
      }
2030
 
      if(close(hookdir_fd) == -1){
 
1643
      if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
2031
1644
        perror_plus("close");
2032
1645
        _exit(EXIT_FAILURE);
2033
1646
      }
2034
 
      ret = dup2(devnull, STDIN_FILENO);
2035
 
      if(ret == -1){
2036
 
        perror_plus("dup2(devnull, STDIN_FILENO)");
2037
 
        _exit(EX_OSERR);
2038
 
      }
2039
 
      ret = close(devnull);
2040
 
      if(ret == -1){
2041
 
        perror_plus("close");
2042
 
        _exit(EX_OSERR);
2043
 
      }
2044
 
      ret = dup2(STDERR_FILENO, STDOUT_FILENO);
2045
 
      if(ret == -1){
2046
 
        perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
2047
 
        _exit(EX_OSERR);
2048
 
      }
2049
1647
      if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
2050
1648
                 environ) == -1){
2051
1649
        perror_plus("fexecve");
2091
1689
    free(direntry);
2092
1690
  }
2093
1691
  free(direntries);
2094
 
  if(close(hookdir_fd) == -1){
 
1692
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
2095
1693
    perror_plus("close");
2096
1694
  } else {
2097
1695
    hookdir_fd = -1;
2100
1698
}
2101
1699
 
2102
1700
__attribute__((nonnull, warn_unused_result))
2103
 
int bring_up_interface(const char *const interface,
2104
 
                       const float delay){
2105
 
  int old_errno = errno;
 
1701
error_t bring_up_interface(const char *const interface,
 
1702
                           const float delay){
 
1703
  error_t old_errno = errno;
2106
1704
  int ret;
2107
1705
  struct ifreq network;
2108
1706
  unsigned int if_index = if_nametoindex(interface);
2118
1716
  }
2119
1717
  
2120
1718
  if(not interface_is_up(interface)){
2121
 
    int ret_errno = 0;
2122
 
    int ioctl_errno = 0;
 
1719
    error_t ret_errno = 0, ioctl_errno = 0;
2123
1720
    if(not get_flags(interface, &network)){
2124
1721
      ret_errno = errno;
2125
1722
      fprintf_plus(stderr, "Failed to get flags for interface "
2138
1735
    }
2139
1736
    
2140
1737
    if(quit_now){
2141
 
      ret = close(sd);
 
1738
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
2142
1739
      if(ret == -1){
2143
1740
        perror_plus("close");
2144
1741
      }
2194
1791
    }
2195
1792
    
2196
1793
    /* Close the socket */
2197
 
    ret = close(sd);
 
1794
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
2198
1795
    if(ret == -1){
2199
1796
      perror_plus("close");
2200
1797
    }
2212
1809
  
2213
1810
  /* Sleep checking until interface is running.
2214
1811
     Check every 0.25s, up to total time of delay */
2215
 
  for(int i = 0; i < delay * 4; i++){
 
1812
  for(int i=0; i < delay * 4; i++){
2216
1813
    if(interface_is_running(interface)){
2217
1814
      break;
2218
1815
    }
2228
1825
}
2229
1826
 
2230
1827
__attribute__((nonnull, warn_unused_result))
2231
 
int take_down_interface(const char *const interface){
2232
 
  int old_errno = errno;
 
1828
error_t take_down_interface(const char *const interface){
 
1829
  error_t old_errno = errno;
2233
1830
  struct ifreq network;
2234
1831
  unsigned int if_index = if_nametoindex(interface);
2235
1832
  if(if_index == 0){
2238
1835
    return ENXIO;
2239
1836
  }
2240
1837
  if(interface_is_up(interface)){
2241
 
    int ret_errno = 0;
2242
 
    int ioctl_errno = 0;
 
1838
    error_t ret_errno = 0, ioctl_errno = 0;
2243
1839
    if(not get_flags(interface, &network) and debug){
2244
1840
      ret_errno = errno;
2245
1841
      fprintf_plus(stderr, "Failed to get flags for interface "
2283
1879
    }
2284
1880
    
2285
1881
    /* Close the socket */
2286
 
    int ret = close(sd);
 
1882
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
2287
1883
    if(ret == -1){
2288
1884
      perror_plus("close");
2289
1885
    }
2304
1900
}
2305
1901
 
2306
1902
int main(int argc, char *argv[]){
2307
 
  mandos_context mc = { .server = NULL, .dh_bits = 0,
2308
 
                        .priority = "SECURE256:!CTYPE-X.509"
2309
 
                        ":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
2310
 
                        .current_server = NULL, .interfaces = NULL,
2311
 
                        .interfaces_size = 0 };
 
1903
  mandos_context mc = { .server = NULL, .dh_bits = 1024,
 
1904
                        .priority = "SECURE256:!CTYPE-X.509:"
 
1905
                        "+CTYPE-OPENPGP:!RSA", .current_server = NULL,
 
1906
                        .interfaces = NULL, .interfaces_size = 0 };
2312
1907
  AvahiSServiceBrowser *sb = NULL;
2313
1908
  error_t ret_errno;
2314
1909
  int ret;
2323
1918
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2324
1919
  const char *seckey = PATHDIR "/" SECKEY;
2325
1920
  const char *pubkey = PATHDIR "/" PUBKEY;
2326
 
  const char *dh_params_file = NULL;
2327
1921
  char *interfaces_hooks = NULL;
2328
1922
  
2329
1923
  bool gnutls_initialized = false;
2382
1976
        .doc = "Bit length of the prime number used in the"
2383
1977
        " Diffie-Hellman key exchange",
2384
1978
        .group = 2 },
2385
 
      { .name = "dh-params", .key = 134,
2386
 
        .arg = "FILE",
2387
 
        .doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
2388
 
        " for the Diffie-Hellman key exchange",
2389
 
        .group = 2 },
2390
1979
      { .name = "priority", .key = 130,
2391
1980
        .arg = "STRING",
2392
1981
        .doc = "GnuTLS priority string for the TLS handshake",
2447
2036
        }
2448
2037
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2449
2038
        break;
2450
 
      case 134:                 /* --dh-params */
2451
 
        dh_params_file = arg;
2452
 
        break;
2453
2039
      case 130:                 /* --priority */
2454
2040
        mc.priority = arg;
2455
2041
        break;
2495
2081
                         .args_doc = "",
2496
2082
                         .doc = "Mandos client -- Get and decrypt"
2497
2083
                         " passwords from a Mandos server" };
2498
 
    ret_errno = argp_parse(&argp, argc, argv,
2499
 
                           ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2500
 
    switch(ret_errno){
 
2084
    ret = argp_parse(&argp, argc, argv,
 
2085
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2086
    switch(ret){
2501
2087
    case 0:
2502
2088
      break;
2503
2089
    case ENOMEM:
2504
2090
    default:
2505
 
      errno = ret_errno;
 
2091
      errno = ret;
2506
2092
      perror_plus("argp_parse");
2507
2093
      exitcode = EX_OSERR;
2508
2094
      goto end;
2511
2097
      goto end;
2512
2098
    }
2513
2099
  }
2514
 
  
 
2100
    
2515
2101
  {
2516
2102
    /* Work around Debian bug #633582:
2517
 
       <https://bugs.debian.org/633582> */
 
2103
       <http://bugs.debian.org/633582> */
2518
2104
    
2519
2105
    /* Re-raise privileges */
2520
 
    ret = raise_privileges();
2521
 
    if(ret != 0){
2522
 
      errno = ret;
 
2106
    ret_errno = raise_privileges();
 
2107
    if(ret_errno != 0){
 
2108
      errno = ret_errno;
2523
2109
      perror_plus("Failed to raise privileges");
2524
2110
    } else {
2525
2111
      struct stat st;
2541
2127
              }
2542
2128
            }
2543
2129
          }
2544
 
          close(seckey_fd);
 
2130
          TEMP_FAILURE_RETRY(close(seckey_fd));
2545
2131
        }
2546
2132
      }
2547
 
      
 
2133
    
2548
2134
      if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2549
2135
        int pubkey_fd = open(pubkey, O_RDONLY);
2550
2136
        if(pubkey_fd == -1){
2562
2148
              }
2563
2149
            }
2564
2150
          }
2565
 
          close(pubkey_fd);
2566
 
        }
2567
 
      }
2568
 
      
2569
 
      if(dh_params_file != NULL
2570
 
         and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2571
 
        int dhparams_fd = open(dh_params_file, O_RDONLY);
2572
 
        if(dhparams_fd == -1){
2573
 
          perror_plus("open");
2574
 
        } else {
2575
 
          ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
2576
 
          if(ret == -1){
2577
 
            perror_plus("fstat");
2578
 
          } else {
2579
 
            if(S_ISREG(st.st_mode)
2580
 
               and st.st_uid == 0 and st.st_gid == 0){
2581
 
              ret = fchown(dhparams_fd, uid, gid);
2582
 
              if(ret == -1){
2583
 
                perror_plus("fchown");
2584
 
              }
2585
 
            }
2586
 
          }
2587
 
          close(dhparams_fd);
2588
 
        }
2589
 
      }
2590
 
      
 
2151
          TEMP_FAILURE_RETRY(close(pubkey_fd));
 
2152
        }
 
2153
      }
 
2154
    
2591
2155
      /* Lower privileges */
2592
 
      ret = lower_privileges();
2593
 
      if(ret != 0){
2594
 
        errno = ret;
 
2156
      ret_errno = lower_privileges();
 
2157
      if(ret_errno != 0){
 
2158
        errno = ret_errno;
2595
2159
        perror_plus("Failed to lower privileges");
2596
2160
      }
2597
2161
    }
2767
2331
      errno = bring_up_interface(interface, delay);
2768
2332
      if(not interface_was_up){
2769
2333
        if(errno != 0){
2770
 
          fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
2771
 
                       " %s\n", interface, strerror(errno));
 
2334
          perror_plus("Failed to bring up interface");
2772
2335
        } else {
2773
2336
          errno = argz_add(&interfaces_to_take_down,
2774
2337
                           &interfaces_to_take_down_size,
2797
2360
    goto end;
2798
2361
  }
2799
2362
  
2800
 
  ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
 
2363
  ret = init_gnutls_global(pubkey, seckey, &mc);
2801
2364
  if(ret == -1){
2802
2365
    fprintf_plus(stderr, "init_gnutls_global failed\n");
2803
2366
    exitcode = EX_UNAVAILABLE;
2925
2488
    
2926
2489
    /* Allocate a new server */
2927
2490
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2928
 
                                 &config, NULL, NULL, &ret);
 
2491
                                 &config, NULL, NULL, &ret_errno);
2929
2492
    
2930
2493
    /* Free the Avahi configuration data */
2931
2494
    avahi_server_config_free(&config);
2934
2497
  /* Check if creating the Avahi server object succeeded */
2935
2498
  if(mc.server == NULL){
2936
2499
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2937
 
                 avahi_strerror(ret));
 
2500
                 avahi_strerror(ret_errno));
2938
2501
    exitcode = EX_UNAVAILABLE;
2939
2502
    goto end;
2940
2503
  }
2975
2538
 end:
2976
2539
  
2977
2540
  if(debug){
2978
 
    if(signal_received){
2979
 
      fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
2980
 
                   argv[0], signal_received,
2981
 
                   strsignal(signal_received));
2982
 
    } else {
2983
 
      fprintf_plus(stderr, "%s exiting\n", argv[0]);
2984
 
    }
 
2541
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
2985
2542
  }
2986
2543
  
2987
2544
  /* Cleanup things */
2998
2555
  
2999
2556
  if(gnutls_initialized){
3000
2557
    gnutls_certificate_free_credentials(mc.cred);
 
2558
    gnutls_global_deinit();
3001
2559
    gnutls_dh_params_deinit(mc.dh_params);
3002
2560
  }
3003
2561
  
3026
2584
  
3027
2585
  /* Re-raise privileges */
3028
2586
  {
3029
 
    ret = raise_privileges();
3030
 
    if(ret != 0){
3031
 
      errno = ret;
 
2587
    ret_errno = raise_privileges();
 
2588
    if(ret_errno != 0){
 
2589
      errno = ret_errno;
3032
2590
      perror_plus("Failed to raise privileges");
3033
2591
    } else {
3034
2592
      
3039
2597
      /* Take down the network interfaces which were brought up */
3040
2598
      {
3041
2599
        char *interface = NULL;
3042
 
        while((interface = argz_next(interfaces_to_take_down,
3043
 
                                     interfaces_to_take_down_size,
3044
 
                                     interface))){
3045
 
          ret = take_down_interface(interface);
3046
 
          if(ret != 0){
3047
 
            errno = ret;
 
2600
        while((interface=argz_next(interfaces_to_take_down,
 
2601
                                   interfaces_to_take_down_size,
 
2602
                                   interface))){
 
2603
          ret_errno = take_down_interface(interface);
 
2604
          if(ret_errno != 0){
 
2605
            errno = ret_errno;
3048
2606
            perror_plus("Failed to take down interface");
3049
2607
          }
3050
2608
        }
3055
2613
      }
3056
2614
    }
3057
2615
    
3058
 
    ret = lower_privileges_permanently();
3059
 
    if(ret != 0){
3060
 
      errno = ret;
 
2616
    ret_errno = lower_privileges_permanently();
 
2617
    if(ret_errno != 0){
 
2618
      errno = ret_errno;
3061
2619
      perror_plus("Failed to lower privileges permanently");
3062
2620
    }
3063
2621
  }
3065
2623
  free(interfaces_to_take_down);
3066
2624
  free(interfaces_hooks);
3067
2625
  
3068
 
  void clean_dir_at(int base, const char * const dirname,
3069
 
                    uintmax_t level){
3070
 
    struct dirent **direntries = NULL;
3071
 
    int dret;
3072
 
    int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3073
 
                                                O_RDONLY
3074
 
                                                | O_NOFOLLOW
3075
 
                                                | O_DIRECTORY
3076
 
                                                | O_PATH));
3077
 
    if(dir_fd == -1){
3078
 
      perror_plus("open");
3079
 
      return;
3080
 
    }
3081
 
    int numentries = scandirat(dir_fd, ".", &direntries,
3082
 
                               notdotentries, alphasort);
3083
 
    if(numentries >= 0){
3084
 
      for(int i = 0; i < numentries; i++){
3085
 
        if(debug){
3086
 
          fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3087
 
                       dirname, direntries[i]->d_name);
3088
 
        }
3089
 
        dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3090
 
        if(dret == -1){
3091
 
          if(errno == EISDIR){
3092
 
              dret = unlinkat(dir_fd, direntries[i]->d_name,
3093
 
                              AT_REMOVEDIR);
3094
 
          }         
3095
 
          if((dret == -1) and (errno == ENOTEMPTY)
3096
 
             and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3097
 
                  == 0) and (level == 0)){
3098
 
            /* Recurse only in this special case */
3099
 
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3100
 
            dret = 0;
3101
 
          }
3102
 
          if((dret == -1) and (errno != ENOENT)){
3103
 
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3104
 
                         direntries[i]->d_name, strerror(errno));
3105
 
          }
3106
 
        }
3107
 
        free(direntries[i]);
3108
 
      }
3109
 
      
3110
 
      /* need to clean even if 0 because man page doesn't specify */
3111
 
      free(direntries);
3112
 
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
3113
 
      if(dret == -1 and errno != ENOENT){
3114
 
        perror_plus("rmdir");
3115
 
      }
3116
 
    } else {
3117
 
      perror_plus("scandirat");
3118
 
    }
3119
 
    close(dir_fd);
3120
 
  }
3121
 
  
3122
2626
  /* Removes the GPGME temp directory and all files inside */
3123
2627
  if(tempdir != NULL){
3124
 
    clean_dir_at(-1, tempdir, 0);
 
2628
    struct dirent **direntries = NULL;
 
2629
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY |
 
2630
                                                  O_NOFOLLOW));
 
2631
    if(tempdir_fd == -1){
 
2632
      perror_plus("open");
 
2633
    } else {
 
2634
#ifdef __GLIBC__
 
2635
#if __GLIBC_PREREQ(2, 15)
 
2636
      int numentries = scandirat(tempdir_fd, ".", &direntries,
 
2637
                                 notdotentries, alphasort);
 
2638
#else  /* not __GLIBC_PREREQ(2, 15) */
 
2639
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2640
                               alphasort);
 
2641
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
2642
#else   /* not __GLIBC__ */
 
2643
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2644
                               alphasort);
 
2645
#endif  /* not __GLIBC__ */
 
2646
      if(numentries >= 0){
 
2647
        for(int i = 0; i < numentries; i++){
 
2648
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
 
2649
          if(ret == -1){
 
2650
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
 
2651
                         " \"%s\", 0): %s\n", tempdir,
 
2652
                         direntries[i]->d_name, strerror(errno));
 
2653
          }
 
2654
          free(direntries[i]);
 
2655
        }
 
2656
        
 
2657
        /* need to clean even if 0 because man page doesn't specify */
 
2658
        free(direntries);
 
2659
        if(numentries == -1){
 
2660
          perror_plus("scandir");
 
2661
        }
 
2662
        ret = rmdir(tempdir);
 
2663
        if(ret == -1 and errno != ENOENT){
 
2664
          perror_plus("rmdir");
 
2665
        }
 
2666
      }
 
2667
      TEMP_FAILURE_RETRY(close(tempdir_fd));
 
2668
    }
3125
2669
  }
3126
2670
  
3127
2671
  if(quit_now){