/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: 2014-07-25 22:44:20 UTC
  • mto: This revision was merged to the branch mainline in revision 724.
  • Revision ID: teddy@recompile.se-20140725224420-4a5ct2ptt0hsc92z
Require Python 2.7.

This is in preparation for the eventual move to Python 3, which will
happen as soon as all Python modules required by Mandos are available.
The mandos-ctl and mandos-monitor programs are already portable
between Python 2.6 and Python 3 without changes; this change will
bring the requirement up to Python 2.7.

* INSTALL (Prerequisites/Libraries/Mandos Server): Document
                                                   requirement of
                                                   Python 2.7; remove
                                                   Python-argparse
                                                   which is in the
                                                   Python 2.7 standard
                                                   library.
* debian/control (Source: mandos/Build-Depends-Indep): Depend on
                                                       exactly the
                                                       python2.7
                                                       package and all
                                                       the Python 2.7
                                                       versions of the
                                                       python modules.
  (Package: mandos/Depends): - '' - but still depend on python (<=2.7)
                            and the generic versions of the Python
                            modules; this is for mandos-ctl and
                            mandos-monitor, both of which are
                            compatible with Python 3, and use
                            #!/usr/bin/python.
* mandos: Use #!/usr/bin/python2.7 instead of #!/usr/bin/python.

Show diffs side-by-side

added added

removed removed

Lines of Context:
9
9
 * "browse_callback", and parts of "main".
10
10
 * 
11
11
 * Everything else is
12
 
 * Copyright © 2008-2016 Teddy Hogeborn
13
 
 * Copyright © 2008-2016 Björn Påhlsson
 
12
 * Copyright © 2008-2014 Teddy Hogeborn
 
13
 * Copyright © 2008-2014 Björn Påhlsson
14
14
 * 
15
15
 * This program is free software: you can redistribute it and/or
16
16
 * modify it under the terms of the GNU General Public License as
46
46
#include <stdlib.h>             /* free(), EXIT_SUCCESS, srand(),
47
47
                                   strtof(), abort() */
48
48
#include <stdbool.h>            /* bool, false, true */
49
 
#include <string.h>             /* strcmp(), strlen(), strerror(),
50
 
                                   asprintf(), strncpy(), strsignal()
51
 
                                */
 
49
#include <string.h>             /* memset(), strcmp(), strlen(),
 
50
                                   strerror(), asprintf(), strcpy() */
52
51
#include <sys/ioctl.h>          /* ioctl */
53
52
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
54
53
                                   sockaddr_in6, PF_INET6,
58
57
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
59
58
                                   inet_pton(), connect(),
60
59
                                   getnameinfo() */
61
 
#include <fcntl.h>              /* open(), unlinkat(), AT_REMOVEDIR */
 
60
#include <fcntl.h>              /* open(), unlinkat() */
62
61
#include <dirent.h>             /* opendir(), struct dirent, readdir()
63
62
                                 */
64
63
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
65
64
                                   strtoimax() */
66
 
#include <errno.h>              /* perror(), errno, EINTR, EINVAL,
67
 
                                   EAI_SYSTEM, ENETUNREACH,
68
 
                                   EHOSTUNREACH, ECONNREFUSED, EPROTO,
69
 
                                   EIO, ENOENT, ENXIO, ENOMEM, EISDIR,
70
 
                                   ENOTEMPTY,
 
65
#include <errno.h>              /* perror(), errno,
71
66
                                   program_invocation_short_name */
72
67
#include <time.h>               /* nanosleep(), time(), sleep() */
73
68
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
310
305
      return false;
311
306
    }
312
307
    
313
 
    ret = close(fd);
 
308
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
314
309
    if(ret == -1){
315
310
      perror_plus("close");
316
311
    }
498
493
  return plaintext_length;
499
494
}
500
495
 
501
 
__attribute__((warn_unused_result, const))
502
 
static const char *safe_string(const char *str){
503
 
  if(str == NULL)
504
 
    return "(unknown)";
505
 
  return str;
506
 
}
507
 
 
508
496
__attribute__((warn_unused_result))
509
497
static const char *safer_gnutls_strerror(int value){
510
498
  const char *ret = gnutls_strerror(value);
511
 
  return safe_string(ret);
 
499
  if(ret == NULL)
 
500
    ret = "(unknown)";
 
501
  return ret;
512
502
}
513
503
 
514
504
/* GnuTLS log function callback */
518
508
  fprintf_plus(stderr, "GnuTLS: %s", string);
519
509
}
520
510
 
521
 
__attribute__((nonnull(1, 2, 4), warn_unused_result))
 
511
__attribute__((nonnull, warn_unused_result))
522
512
static int init_gnutls_global(const char *pubkeyfilename,
523
513
                              const char *seckeyfilename,
524
 
                              const char *dhparamsfilename,
525
514
                              mandos_context *mc){
526
515
  int ret;
527
 
  unsigned int uret;
528
516
  
529
517
  if(debug){
530
518
    fprintf_plus(stderr, "Initializing GnuTLS\n");
531
519
  }
532
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
  
533
528
  if(debug){
534
529
    /* "Use a log level over 10 to enable all debugging options."
535
530
     * - GnuTLS manual
543
538
  if(ret != GNUTLS_E_SUCCESS){
544
539
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
545
540
                 safer_gnutls_strerror(ret));
 
541
    gnutls_global_deinit();
546
542
    return -1;
547
543
  }
548
544
  
573
569
                 safer_gnutls_strerror(ret));
574
570
    goto globalfail;
575
571
  }
576
 
  /* If a Diffie-Hellman parameters file was given, try to use it */
577
 
  if(dhparamsfilename != NULL){
578
 
    gnutls_datum_t params = { .data = NULL, .size = 0 };
579
 
    do {
580
 
      int dhpfile = open(dhparamsfilename, O_RDONLY);
581
 
      if(dhpfile == -1){
582
 
        perror_plus("open");
583
 
        dhparamsfilename = NULL;
584
 
        break;
585
 
      }
586
 
      size_t params_capacity = 0;
587
 
      while(true){
588
 
        params_capacity = incbuffer((char **)&params.data,
589
 
                                    (size_t)params.size,
590
 
                                    (size_t)params_capacity);
591
 
        if(params_capacity == 0){
592
 
          perror_plus("incbuffer");
593
 
          free(params.data);
594
 
          params.data = NULL;
595
 
          dhparamsfilename = NULL;
596
 
          break;
597
 
        }
598
 
        ssize_t bytes_read = read(dhpfile,
599
 
                                  params.data + params.size,
600
 
                                  BUFFER_SIZE);
601
 
        /* EOF */
602
 
        if(bytes_read == 0){
603
 
          break;
604
 
        }
605
 
        /* check bytes_read for failure */
606
 
        if(bytes_read < 0){
607
 
          perror_plus("read");
608
 
          free(params.data);
609
 
          params.data = NULL;
610
 
          dhparamsfilename = NULL;
611
 
          break;
612
 
        }
613
 
        params.size += (unsigned int)bytes_read;
614
 
      }
615
 
      if(params.data == NULL){
616
 
        dhparamsfilename = NULL;
617
 
      }
618
 
      if(dhparamsfilename == NULL){
619
 
        break;
620
 
      }
621
 
      ret = gnutls_dh_params_import_pkcs3(mc->dh_params, &params,
622
 
                                          GNUTLS_X509_FMT_PEM);
623
 
      if(ret != GNUTLS_E_SUCCESS){
624
 
        fprintf_plus(stderr, "Failed to parse DH parameters in file"
625
 
                     " \"%s\": %s\n", dhparamsfilename,
626
 
                     safer_gnutls_strerror(ret));
627
 
        dhparamsfilename = NULL;
628
 
      }
629
 
      free(params.data);
630
 
    } while(false);
631
 
  }
632
 
  if(dhparamsfilename == NULL){
633
 
    if(mc->dh_bits == 0){
634
 
      /* Find out the optimal number of DH bits */
635
 
      /* Try to read the private key file */
636
 
      gnutls_datum_t buffer = { .data = NULL, .size = 0 };
637
 
      do {
638
 
        int secfile = open(seckeyfilename, O_RDONLY);
639
 
        if(secfile == -1){
640
 
          perror_plus("open");
641
 
          break;
642
 
        }
643
 
        size_t buffer_capacity = 0;
644
 
        while(true){
645
 
          buffer_capacity = incbuffer((char **)&buffer.data,
646
 
                                      (size_t)buffer.size,
647
 
                                      (size_t)buffer_capacity);
648
 
          if(buffer_capacity == 0){
649
 
            perror_plus("incbuffer");
650
 
            free(buffer.data);
651
 
            buffer.data = NULL;
652
 
            break;
653
 
          }
654
 
          ssize_t bytes_read = read(secfile,
655
 
                                    buffer.data + buffer.size,
656
 
                                    BUFFER_SIZE);
657
 
          /* EOF */
658
 
          if(bytes_read == 0){
659
 
            break;
660
 
          }
661
 
          /* check bytes_read for failure */
662
 
          if(bytes_read < 0){
663
 
            perror_plus("read");
664
 
            free(buffer.data);
665
 
            buffer.data = NULL;
666
 
            break;
667
 
          }
668
 
          buffer.size += (unsigned int)bytes_read;
669
 
        }
670
 
        close(secfile);
671
 
      } while(false);
672
 
      /* If successful, use buffer to parse private key */
673
 
      gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
674
 
      if(buffer.data != NULL){
675
 
        {
676
 
          gnutls_openpgp_privkey_t privkey = NULL;
677
 
          ret = gnutls_openpgp_privkey_init(&privkey);
678
 
          if(ret != GNUTLS_E_SUCCESS){
679
 
            fprintf_plus(stderr, "Error initializing OpenPGP key"
680
 
                         " structure: %s",
681
 
                         safer_gnutls_strerror(ret));
682
 
            free(buffer.data);
683
 
            buffer.data = NULL;
684
 
          } else {
685
 
            ret = gnutls_openpgp_privkey_import
686
 
              (privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
687
 
            if(ret != GNUTLS_E_SUCCESS){
688
 
              fprintf_plus(stderr, "Error importing OpenPGP key : %s",
689
 
                           safer_gnutls_strerror(ret));
690
 
              privkey = NULL;
691
 
            }
692
 
            free(buffer.data);
693
 
            buffer.data = NULL;
694
 
            if(privkey != NULL){
695
 
              /* Use private key to suggest an appropriate
696
 
                 sec_param */
697
 
              sec_param = gnutls_openpgp_privkey_sec_param(privkey);
698
 
              gnutls_openpgp_privkey_deinit(privkey);
699
 
              if(debug){
700
 
                fprintf_plus(stderr, "This OpenPGP key implies using"
701
 
                             " a GnuTLS security parameter \"%s\".\n",
702
 
                             safe_string(gnutls_sec_param_get_name
703
 
                                         (sec_param)));
704
 
              }
705
 
            }
706
 
          }
707
 
        }
708
 
        if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
709
 
          /* Err on the side of caution */
710
 
          sec_param = GNUTLS_SEC_PARAM_ULTRA;
711
 
          if(debug){
712
 
            fprintf_plus(stderr, "Falling back to security parameter"
713
 
                         " \"%s\"\n",
714
 
                         safe_string(gnutls_sec_param_get_name
715
 
                                     (sec_param)));
716
 
          }
717
 
        }
718
 
      }
719
 
      uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
720
 
      if(uret != 0){
721
 
        mc->dh_bits = uret;
722
 
        if(debug){
723
 
          fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
724
 
                       " implies %u DH bits; using that.\n",
725
 
                       safe_string(gnutls_sec_param_get_name
726
 
                                   (sec_param)),
727
 
                       mc->dh_bits);
728
 
        }
729
 
      } else {
730
 
        fprintf_plus(stderr, "Failed to get implied number of DH"
731
 
                     " bits for security parameter \"%s\"): %s\n",
732
 
                     safe_string(gnutls_sec_param_get_name
733
 
                                 (sec_param)),
734
 
                     safer_gnutls_strerror(ret));
735
 
        goto globalfail;
736
 
      }
737
 
    } else if(debug){
738
 
      fprintf_plus(stderr, "DH bits explicitly set to %u\n",
739
 
                   mc->dh_bits);
740
 
    }
741
 
    ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
742
 
    if(ret != GNUTLS_E_SUCCESS){
743
 
      fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
744
 
                   " bits): %s\n", mc->dh_bits,
745
 
                   safer_gnutls_strerror(ret));
746
 
      goto globalfail;
747
 
    }
748
 
  }
 
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
  
749
579
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
750
580
  
751
581
  return 0;
753
583
 globalfail:
754
584
  
755
585
  gnutls_certificate_free_credentials(mc->cred);
 
586
  gnutls_global_deinit();
756
587
  gnutls_dh_params_deinit(mc->dh_params);
757
588
  return -1;
758
589
}
810
641
  /* ignore client certificate if any. */
811
642
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
812
643
  
 
644
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
 
645
  
813
646
  return 0;
814
647
}
815
648
 
817
650
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
818
651
                      __attribute__((unused)) const char *txt){}
819
652
 
820
 
/* Set effective uid to 0, return errno */
821
 
__attribute__((warn_unused_result))
822
 
int raise_privileges(void){
823
 
  int old_errno = errno;
824
 
  int ret = 0;
825
 
  if(seteuid(0) == -1){
826
 
    ret = errno;
827
 
  }
828
 
  errno = old_errno;
829
 
  return ret;
830
 
}
831
 
 
832
 
/* Set effective and real user ID to 0.  Return errno. */
833
 
__attribute__((warn_unused_result))
834
 
int raise_privileges_permanently(void){
835
 
  int old_errno = errno;
836
 
  int ret = raise_privileges();
837
 
  if(ret != 0){
838
 
    errno = old_errno;
839
 
    return ret;
840
 
  }
841
 
  if(setuid(0) == -1){
842
 
    ret = errno;
843
 
  }
844
 
  errno = old_errno;
845
 
  return ret;
846
 
}
847
 
 
848
 
/* Set effective user ID to unprivileged saved user ID */
849
 
__attribute__((warn_unused_result))
850
 
int lower_privileges(void){
851
 
  int old_errno = errno;
852
 
  int ret = 0;
853
 
  if(seteuid(uid) == -1){
854
 
    ret = errno;
855
 
  }
856
 
  errno = old_errno;
857
 
  return ret;
858
 
}
859
 
 
860
 
/* Lower privileges permanently */
861
 
__attribute__((warn_unused_result))
862
 
int lower_privileges_permanently(void){
863
 
  int old_errno = errno;
864
 
  int ret = 0;
865
 
  if(setuid(uid) == -1){
866
 
    ret = errno;
867
 
  }
868
 
  errno = old_errno;
869
 
  return ret;
870
 
}
871
 
 
872
 
/* Helper function to add_local_route() and delete_local_route() */
873
 
__attribute__((nonnull, warn_unused_result))
874
 
static bool add_delete_local_route(const bool add,
875
 
                                   const char *address,
876
 
                                   AvahiIfIndex if_index){
877
 
  int ret;
878
 
  char helper[] = "mandos-client-iprouteadddel";
879
 
  char add_arg[] = "add";
880
 
  char delete_arg[] = "delete";
881
 
  char debug_flag[] = "--debug";
882
 
  char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
883
 
  if(pluginhelperdir == NULL){
884
 
    if(debug){
885
 
      fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
886
 
                   " variable not set; cannot run helper\n");
887
 
    }
888
 
    return false;
889
 
  }
890
 
  
891
 
  char interface[IF_NAMESIZE];
892
 
  if(if_indextoname((unsigned int)if_index, interface) == NULL){
893
 
    perror_plus("if_indextoname");
894
 
    return false;
895
 
  }
896
 
  
897
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
898
 
  if(devnull == -1){
899
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
900
 
    return false;
901
 
  }
902
 
  pid_t pid = fork();
903
 
  if(pid == 0){
904
 
    /* Child */
905
 
    /* Raise privileges */
906
 
    errno = raise_privileges_permanently();
907
 
    if(errno != 0){
908
 
      perror_plus("Failed to raise privileges");
909
 
      /* _exit(EX_NOPERM); */
910
 
    } else {
911
 
      /* Set group */
912
 
      errno = 0;
913
 
      ret = setgid(0);
914
 
      if(ret == -1){
915
 
        perror_plus("setgid");
916
 
        _exit(EX_NOPERM);
917
 
      }
918
 
      /* Reset supplementary groups */
919
 
      errno = 0;
920
 
      ret = setgroups(0, NULL);
921
 
      if(ret == -1){
922
 
        perror_plus("setgroups");
923
 
        _exit(EX_NOPERM);
924
 
      }
925
 
    }
926
 
    ret = dup2(devnull, STDIN_FILENO);
927
 
    if(ret == -1){
928
 
      perror_plus("dup2(devnull, STDIN_FILENO)");
929
 
      _exit(EX_OSERR);
930
 
    }
931
 
    ret = close(devnull);
932
 
    if(ret == -1){
933
 
      perror_plus("close");
934
 
      _exit(EX_OSERR);
935
 
    }
936
 
    ret = dup2(STDERR_FILENO, STDOUT_FILENO);
937
 
    if(ret == -1){
938
 
      perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
939
 
      _exit(EX_OSERR);
940
 
    }
941
 
    int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
942
 
                                                    O_RDONLY
943
 
                                                    | O_DIRECTORY
944
 
                                                    | O_PATH
945
 
                                                    | O_CLOEXEC));
946
 
    if(helperdir_fd == -1){
947
 
      perror_plus("open");
948
 
      _exit(EX_UNAVAILABLE);
949
 
    }
950
 
    int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
951
 
                                                   helper, O_RDONLY));
952
 
    if(helper_fd == -1){
953
 
      perror_plus("openat");
954
 
      close(helperdir_fd);
955
 
      _exit(EX_UNAVAILABLE);
956
 
    }
957
 
    close(helperdir_fd);
958
 
#ifdef __GNUC__
959
 
#pragma GCC diagnostic push
960
 
#pragma GCC diagnostic ignored "-Wcast-qual"
961
 
#endif
962
 
    if(fexecve(helper_fd, (char *const [])
963
 
               { helper, add ? add_arg : delete_arg, (char *)address,
964
 
                   interface, debug ? debug_flag : NULL, NULL },
965
 
               environ) == -1){
966
 
#ifdef __GNUC__
967
 
#pragma GCC diagnostic pop
968
 
#endif
969
 
      perror_plus("fexecve");
970
 
      _exit(EXIT_FAILURE);
971
 
    }
972
 
  }
973
 
  if(pid == -1){
974
 
    perror_plus("fork");
975
 
    return false;
976
 
  }
977
 
  int status;
978
 
  pid_t pret = -1;
979
 
  errno = 0;
980
 
  do {
981
 
    pret = waitpid(pid, &status, 0);
982
 
    if(pret == -1 and errno == EINTR and quit_now){
983
 
      int errno_raising = 0;
984
 
      if((errno = raise_privileges()) != 0){
985
 
        errno_raising = errno;
986
 
        perror_plus("Failed to raise privileges in order to"
987
 
                    " kill helper program");
988
 
      }
989
 
      if(kill(pid, SIGTERM) == -1){
990
 
        perror_plus("kill");
991
 
      }
992
 
      if((errno_raising == 0) and (errno = lower_privileges()) != 0){
993
 
        perror_plus("Failed to lower privileges after killing"
994
 
                    " helper program");
995
 
      }
996
 
      return false;
997
 
    }
998
 
  } while(pret == -1 and errno == EINTR);
999
 
  if(pret == -1){
1000
 
    perror_plus("waitpid");
1001
 
    return false;
1002
 
  }
1003
 
  if(WIFEXITED(status)){
1004
 
    if(WEXITSTATUS(status) != 0){
1005
 
      fprintf_plus(stderr, "Error: iprouteadddel exited"
1006
 
                   " with status %d\n", WEXITSTATUS(status));
1007
 
      return false;
1008
 
    }
1009
 
    return true;
1010
 
  }
1011
 
  if(WIFSIGNALED(status)){
1012
 
    fprintf_plus(stderr, "Error: iprouteadddel died by"
1013
 
                 " signal %d\n", WTERMSIG(status));
1014
 
    return false;
1015
 
  }
1016
 
  fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1017
 
  return false;
1018
 
}
1019
 
 
1020
 
__attribute__((nonnull, warn_unused_result))
1021
 
static bool add_local_route(const char *address,
1022
 
                            AvahiIfIndex if_index){
1023
 
  if(debug){
1024
 
    fprintf_plus(stderr, "Adding route to %s\n", address);
1025
 
  }
1026
 
  return add_delete_local_route(true, address, if_index);
1027
 
}
1028
 
 
1029
 
__attribute__((nonnull, warn_unused_result))
1030
 
static bool delete_local_route(const char *address,
1031
 
                               AvahiIfIndex if_index){
1032
 
  if(debug){
1033
 
    fprintf_plus(stderr, "Removing route to %s\n", address);
1034
 
  }
1035
 
  return add_delete_local_route(false, address, if_index);
1036
 
}
1037
 
 
1038
653
/* Called when a Mandos server is found */
1039
654
__attribute__((nonnull, warn_unused_result))
1040
655
static int start_mandos_communication(const char *ip, in_port_t port,
1051
666
  int retval = -1;
1052
667
  gnutls_session_t session;
1053
668
  int pf;                       /* Protocol family */
1054
 
  bool route_added = false;
1055
669
  
1056
670
  errno = 0;
1057
671
  
1079
693
    bool match = false;
1080
694
    {
1081
695
      char *interface = NULL;
1082
 
      while((interface = argz_next(mc->interfaces,
1083
 
                                   mc->interfaces_size,
1084
 
                                   interface))){
 
696
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
 
697
                                 interface))){
1085
698
        if(if_nametoindex(interface) == (unsigned int)if_index){
1086
699
          match = true;
1087
700
          break;
1116
729
                 PRIuMAX "\n", ip, (uintmax_t)port);
1117
730
  }
1118
731
  
1119
 
  tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
 
732
  tcp_sd = socket(pf, SOCK_STREAM, 0);
1120
733
  if(tcp_sd < 0){
1121
734
    int e = errno;
1122
735
    perror_plus("socket");
1129
742
    goto mandos_end;
1130
743
  }
1131
744
  
 
745
  memset(&to, 0, sizeof(to));
1132
746
  if(af == AF_INET6){
1133
 
    struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1134
 
    *to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1135
 
    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);
1136
749
  } else {                      /* IPv4 */
1137
 
    struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1138
 
    *to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1139
 
    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);
1140
752
  }
1141
753
  if(ret < 0 ){
1142
754
    int e = errno;
1212
824
    goto mandos_end;
1213
825
  }
1214
826
  
1215
 
  while(true){
1216
 
    if(af == AF_INET6){
1217
 
      ret = connect(tcp_sd, (struct sockaddr *)&to,
1218
 
                    sizeof(struct sockaddr_in6));
1219
 
    } else {
1220
 
      ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1221
 
                    sizeof(struct sockaddr_in));
1222
 
    }
1223
 
    if(ret < 0){
1224
 
      if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1225
 
         and if_index != AVAHI_IF_UNSPEC
1226
 
         and connect_to == NULL
1227
 
         and not route_added and
1228
 
         ((af == AF_INET6 and not
1229
 
           IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1230
 
                                    &to)->sin6_addr)))
1231
 
          or (af == AF_INET and
1232
 
              /* Not a a IPv4LL address */
1233
 
              (ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1234
 
               & 0xFFFF0000L) != 0xA9FE0000L))){
1235
 
        /* Work around Avahi bug - Avahi does not announce link-local
1236
 
           addresses if it has a global address, so local hosts with
1237
 
           *only* a link-local address (e.g. Mandos clients) cannot
1238
 
           connect to a Mandos server announced by Avahi on a server
1239
 
           host with a global address.  Work around this by retrying
1240
 
           with an explicit route added with the server's address.
1241
 
           
1242
 
           Avahi bug reference:
1243
 
           https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1244
 
           https://bugs.debian.org/587961
1245
 
        */
1246
 
        if(debug){
1247
 
          fprintf_plus(stderr, "Mandos server unreachable, trying"
1248
 
                       " direct route\n");
1249
 
        }
1250
 
        int e = errno;
1251
 
        route_added = add_local_route(ip, if_index);
1252
 
        if(route_added){
1253
 
          continue;
1254
 
        }
1255
 
        errno = e;
1256
 
      }
1257
 
      if(errno != ECONNREFUSED or debug){
1258
 
        int e = errno;
1259
 
        perror_plus("connect");
1260
 
        errno = e;
1261
 
      }
1262
 
      goto mandos_end;
1263
 
    }
1264
 
    
1265
 
    if(quit_now){
1266
 
      errno = EINTR;
1267
 
      goto mandos_end;
1268
 
    }
1269
 
    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;
1270
846
  }
1271
847
  
1272
848
  const char *out = mandos_protocol_version;
1426
1002
                                               &decrypted_buffer, mc);
1427
1003
    if(decrypted_buffer_size >= 0){
1428
1004
      
1429
 
      clearerr(stdout);
1430
1005
      written = 0;
1431
1006
      while(written < (size_t) decrypted_buffer_size){
1432
1007
        if(quit_now){
1448
1023
        }
1449
1024
        written += (size_t)ret;
1450
1025
      }
1451
 
      ret = fflush(stdout);
1452
 
      if(ret != 0){
1453
 
        int e = errno;
1454
 
        if(debug){
1455
 
          fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1456
 
                       strerror(errno));
1457
 
        }
1458
 
        errno = e;
1459
 
        goto mandos_end;
1460
 
      }
1461
1026
      retval = 0;
1462
1027
    }
1463
1028
  }
1466
1031
  
1467
1032
 mandos_end:
1468
1033
  {
1469
 
    if(route_added){
1470
 
      if(not delete_local_route(ip, if_index)){
1471
 
        fprintf_plus(stderr, "Failed to delete local route to %s on"
1472
 
                     " interface %d", ip, if_index);
1473
 
      }
1474
 
    }
1475
1034
    int e = errno;
1476
1035
    free(decrypted_buffer);
1477
1036
    free(buffer);
1478
1037
    if(tcp_sd >= 0){
1479
 
      ret = close(tcp_sd);
 
1038
      ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
1480
1039
    }
1481
1040
    if(ret == -1){
1482
1041
      if(e == 0){
1637
1196
__attribute__((nonnull, warn_unused_result))
1638
1197
bool get_flags(const char *ifname, struct ifreq *ifr){
1639
1198
  int ret;
1640
 
  int old_errno;
 
1199
  error_t ret_errno;
1641
1200
  
1642
1201
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1643
1202
  if(s < 0){
1644
 
    old_errno = errno;
 
1203
    ret_errno = errno;
1645
1204
    perror_plus("socket");
1646
 
    errno = old_errno;
 
1205
    errno = ret_errno;
1647
1206
    return false;
1648
1207
  }
1649
 
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1650
 
  ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
 
1208
  strcpy(ifr->ifr_name, ifname);
1651
1209
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1652
1210
  if(ret == -1){
1653
1211
    if(debug){
1654
 
      old_errno = errno;
 
1212
      ret_errno = errno;
1655
1213
      perror_plus("ioctl SIOCGIFFLAGS");
1656
 
      errno = old_errno;
 
1214
      errno = ret_errno;
1657
1215
    }
1658
1216
    return false;
1659
1217
  }
1904
1462
  }
1905
1463
}
1906
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
 
1907
1517
__attribute__((nonnull))
1908
1518
void run_network_hooks(const char *mode, const char *interface,
1909
1519
                       const float delay){
1910
1520
  struct dirent **direntries = NULL;
1911
1521
  if(hookdir_fd == -1){
1912
 
    hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
1913
 
                      | O_CLOEXEC);
 
1522
    hookdir_fd = open(hookdir, O_RDONLY);
1914
1523
    if(hookdir_fd == -1){
1915
1524
      if(errno == ENOENT){
1916
1525
        if(debug){
1923
1532
      return;
1924
1533
    }
1925
1534
  }
 
1535
#ifdef __GLIBC__
 
1536
#if __GLIBC_PREREQ(2, 15)
1926
1537
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
1927
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__ */
1928
1547
  if(numhooks == -1){
1929
1548
    perror_plus("scandir");
1930
1549
    return;
1931
1550
  }
1932
1551
  struct dirent *direntry;
1933
1552
  int ret;
1934
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1935
 
  if(devnull == -1){
1936
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
1937
 
    return;
1938
 
  }
 
1553
  int devnull = open("/dev/null", O_RDONLY);
1939
1554
  for(int i = 0; i < numhooks; i++){
1940
1555
    direntry = direntries[i];
1941
1556
    if(debug){
1965
1580
        perror_plus("setgroups");
1966
1581
        _exit(EX_NOPERM);
1967
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
      }
1968
1598
      ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1969
1599
      if(ret == -1){
1970
1600
        perror_plus("setenv");
2005
1635
          _exit(EX_OSERR);
2006
1636
        }
2007
1637
      }
2008
 
      int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
2009
 
                                                   direntry->d_name,
2010
 
                                                   O_RDONLY));
 
1638
      int hook_fd = openat(hookdir_fd, direntry->d_name, O_RDONLY);
2011
1639
      if(hook_fd == -1){
2012
1640
        perror_plus("openat");
2013
1641
        _exit(EXIT_FAILURE);
2014
1642
      }
2015
 
      if(close(hookdir_fd) == -1){
 
1643
      if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
2016
1644
        perror_plus("close");
2017
1645
        _exit(EXIT_FAILURE);
2018
1646
      }
2019
 
      ret = dup2(devnull, STDIN_FILENO);
2020
 
      if(ret == -1){
2021
 
        perror_plus("dup2(devnull, STDIN_FILENO)");
2022
 
        _exit(EX_OSERR);
2023
 
      }
2024
 
      ret = close(devnull);
2025
 
      if(ret == -1){
2026
 
        perror_plus("close");
2027
 
        _exit(EX_OSERR);
2028
 
      }
2029
 
      ret = dup2(STDERR_FILENO, STDOUT_FILENO);
2030
 
      if(ret == -1){
2031
 
        perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
2032
 
        _exit(EX_OSERR);
2033
 
      }
2034
1647
      if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
2035
1648
                 environ) == -1){
2036
1649
        perror_plus("fexecve");
2037
1650
        _exit(EXIT_FAILURE);
2038
1651
      }
2039
1652
    } else {
2040
 
      if(hook_pid == -1){
2041
 
        perror_plus("fork");
2042
 
        free(direntry);
2043
 
        continue;
2044
 
      }
2045
1653
      int status;
2046
1654
      if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
2047
1655
        perror_plus("waitpid");
2076
1684
    free(direntry);
2077
1685
  }
2078
1686
  free(direntries);
2079
 
  if(close(hookdir_fd) == -1){
 
1687
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
2080
1688
    perror_plus("close");
2081
1689
  } else {
2082
1690
    hookdir_fd = -1;
2085
1693
}
2086
1694
 
2087
1695
__attribute__((nonnull, warn_unused_result))
2088
 
int bring_up_interface(const char *const interface,
2089
 
                       const float delay){
2090
 
  int old_errno = errno;
 
1696
error_t bring_up_interface(const char *const interface,
 
1697
                           const float delay){
 
1698
  error_t old_errno = errno;
2091
1699
  int ret;
2092
1700
  struct ifreq network;
2093
1701
  unsigned int if_index = if_nametoindex(interface);
2103
1711
  }
2104
1712
  
2105
1713
  if(not interface_is_up(interface)){
2106
 
    int ret_errno = 0;
2107
 
    int ioctl_errno = 0;
 
1714
    error_t ret_errno = 0, ioctl_errno = 0;
2108
1715
    if(not get_flags(interface, &network)){
2109
1716
      ret_errno = errno;
2110
1717
      fprintf_plus(stderr, "Failed to get flags for interface "
2123
1730
    }
2124
1731
    
2125
1732
    if(quit_now){
2126
 
      ret = close(sd);
 
1733
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
2127
1734
      if(ret == -1){
2128
1735
        perror_plus("close");
2129
1736
      }
2179
1786
    }
2180
1787
    
2181
1788
    /* Close the socket */
2182
 
    ret = close(sd);
 
1789
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
2183
1790
    if(ret == -1){
2184
1791
      perror_plus("close");
2185
1792
    }
2197
1804
  
2198
1805
  /* Sleep checking until interface is running.
2199
1806
     Check every 0.25s, up to total time of delay */
2200
 
  for(int i = 0; i < delay * 4; i++){
 
1807
  for(int i=0; i < delay * 4; i++){
2201
1808
    if(interface_is_running(interface)){
2202
1809
      break;
2203
1810
    }
2213
1820
}
2214
1821
 
2215
1822
__attribute__((nonnull, warn_unused_result))
2216
 
int take_down_interface(const char *const interface){
2217
 
  int old_errno = errno;
 
1823
error_t take_down_interface(const char *const interface){
 
1824
  error_t old_errno = errno;
2218
1825
  struct ifreq network;
2219
1826
  unsigned int if_index = if_nametoindex(interface);
2220
1827
  if(if_index == 0){
2223
1830
    return ENXIO;
2224
1831
  }
2225
1832
  if(interface_is_up(interface)){
2226
 
    int ret_errno = 0;
2227
 
    int ioctl_errno = 0;
 
1833
    error_t ret_errno = 0, ioctl_errno = 0;
2228
1834
    if(not get_flags(interface, &network) and debug){
2229
1835
      ret_errno = errno;
2230
1836
      fprintf_plus(stderr, "Failed to get flags for interface "
2268
1874
    }
2269
1875
    
2270
1876
    /* Close the socket */
2271
 
    int ret = close(sd);
 
1877
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
2272
1878
    if(ret == -1){
2273
1879
      perror_plus("close");
2274
1880
    }
2289
1895
}
2290
1896
 
2291
1897
int main(int argc, char *argv[]){
2292
 
  mandos_context mc = { .server = NULL, .dh_bits = 0,
2293
 
                        .priority = "SECURE256:!CTYPE-X.509"
2294
 
                        ":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
2295
 
                        .current_server = NULL, .interfaces = NULL,
2296
 
                        .interfaces_size = 0 };
 
1898
  mandos_context mc = { .server = NULL, .dh_bits = 1024,
 
1899
                        .priority = "SECURE256:!CTYPE-X.509:"
 
1900
                        "+CTYPE-OPENPGP", .current_server = NULL,
 
1901
                        .interfaces = NULL, .interfaces_size = 0 };
2297
1902
  AvahiSServiceBrowser *sb = NULL;
2298
1903
  error_t ret_errno;
2299
1904
  int ret;
2308
1913
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2309
1914
  const char *seckey = PATHDIR "/" SECKEY;
2310
1915
  const char *pubkey = PATHDIR "/" PUBKEY;
2311
 
  const char *dh_params_file = NULL;
2312
1916
  char *interfaces_hooks = NULL;
2313
1917
  
2314
1918
  bool gnutls_initialized = false;
2367
1971
        .doc = "Bit length of the prime number used in the"
2368
1972
        " Diffie-Hellman key exchange",
2369
1973
        .group = 2 },
2370
 
      { .name = "dh-params", .key = 134,
2371
 
        .arg = "FILE",
2372
 
        .doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
2373
 
        " for the Diffie-Hellman key exchange",
2374
 
        .group = 2 },
2375
1974
      { .name = "priority", .key = 130,
2376
1975
        .arg = "STRING",
2377
1976
        .doc = "GnuTLS priority string for the TLS handshake",
2432
2031
        }
2433
2032
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2434
2033
        break;
2435
 
      case 134:                 /* --dh-params */
2436
 
        dh_params_file = arg;
2437
 
        break;
2438
2034
      case 130:                 /* --priority */
2439
2035
        mc.priority = arg;
2440
2036
        break;
2480
2076
                         .args_doc = "",
2481
2077
                         .doc = "Mandos client -- Get and decrypt"
2482
2078
                         " passwords from a Mandos server" };
2483
 
    ret_errno = argp_parse(&argp, argc, argv,
2484
 
                           ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2485
 
    switch(ret_errno){
 
2079
    ret = argp_parse(&argp, argc, argv,
 
2080
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2081
    switch(ret){
2486
2082
    case 0:
2487
2083
      break;
2488
2084
    case ENOMEM:
2489
2085
    default:
2490
 
      errno = ret_errno;
 
2086
      errno = ret;
2491
2087
      perror_plus("argp_parse");
2492
2088
      exitcode = EX_OSERR;
2493
2089
      goto end;
2496
2092
      goto end;
2497
2093
    }
2498
2094
  }
2499
 
  
 
2095
    
2500
2096
  {
2501
2097
    /* Work around Debian bug #633582:
2502
 
       <https://bugs.debian.org/633582> */
 
2098
       <http://bugs.debian.org/633582> */
2503
2099
    
2504
2100
    /* Re-raise privileges */
2505
 
    ret = raise_privileges();
2506
 
    if(ret != 0){
2507
 
      errno = ret;
 
2101
    ret_errno = raise_privileges();
 
2102
    if(ret_errno != 0){
 
2103
      errno = ret_errno;
2508
2104
      perror_plus("Failed to raise privileges");
2509
2105
    } else {
2510
2106
      struct stat st;
2526
2122
              }
2527
2123
            }
2528
2124
          }
2529
 
          close(seckey_fd);
 
2125
          TEMP_FAILURE_RETRY(close(seckey_fd));
2530
2126
        }
2531
2127
      }
2532
 
      
 
2128
    
2533
2129
      if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2534
2130
        int pubkey_fd = open(pubkey, O_RDONLY);
2535
2131
        if(pubkey_fd == -1){
2547
2143
              }
2548
2144
            }
2549
2145
          }
2550
 
          close(pubkey_fd);
2551
 
        }
2552
 
      }
2553
 
      
2554
 
      if(dh_params_file != NULL
2555
 
         and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2556
 
        int dhparams_fd = open(dh_params_file, O_RDONLY);
2557
 
        if(dhparams_fd == -1){
2558
 
          perror_plus("open");
2559
 
        } else {
2560
 
          ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
2561
 
          if(ret == -1){
2562
 
            perror_plus("fstat");
2563
 
          } else {
2564
 
            if(S_ISREG(st.st_mode)
2565
 
               and st.st_uid == 0 and st.st_gid == 0){
2566
 
              ret = fchown(dhparams_fd, uid, gid);
2567
 
              if(ret == -1){
2568
 
                perror_plus("fchown");
2569
 
              }
2570
 
            }
2571
 
          }
2572
 
          close(dhparams_fd);
2573
 
        }
2574
 
      }
2575
 
      
 
2146
          TEMP_FAILURE_RETRY(close(pubkey_fd));
 
2147
        }
 
2148
      }
 
2149
    
2576
2150
      /* Lower privileges */
2577
 
      ret = lower_privileges();
2578
 
      if(ret != 0){
2579
 
        errno = ret;
 
2151
      ret_errno = lower_privileges();
 
2152
      if(ret_errno != 0){
 
2153
        errno = ret_errno;
2580
2154
        perror_plus("Failed to lower privileges");
2581
2155
      }
2582
2156
    }
2752
2326
      errno = bring_up_interface(interface, delay);
2753
2327
      if(not interface_was_up){
2754
2328
        if(errno != 0){
2755
 
          fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
2756
 
                       " %s\n", interface, strerror(errno));
 
2329
          perror_plus("Failed to bring up interface");
2757
2330
        } else {
2758
2331
          errno = argz_add(&interfaces_to_take_down,
2759
2332
                           &interfaces_to_take_down_size,
2782
2355
    goto end;
2783
2356
  }
2784
2357
  
2785
 
  ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
 
2358
  ret = init_gnutls_global(pubkey, seckey, &mc);
2786
2359
  if(ret == -1){
2787
2360
    fprintf_plus(stderr, "init_gnutls_global failed\n");
2788
2361
    exitcode = EX_UNAVAILABLE;
2910
2483
    
2911
2484
    /* Allocate a new server */
2912
2485
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2913
 
                                 &config, NULL, NULL, &ret);
 
2486
                                 &config, NULL, NULL, &ret_errno);
2914
2487
    
2915
2488
    /* Free the Avahi configuration data */
2916
2489
    avahi_server_config_free(&config);
2919
2492
  /* Check if creating the Avahi server object succeeded */
2920
2493
  if(mc.server == NULL){
2921
2494
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2922
 
                 avahi_strerror(ret));
 
2495
                 avahi_strerror(ret_errno));
2923
2496
    exitcode = EX_UNAVAILABLE;
2924
2497
    goto end;
2925
2498
  }
2960
2533
 end:
2961
2534
  
2962
2535
  if(debug){
2963
 
    if(signal_received){
2964
 
      fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
2965
 
                   argv[0], signal_received,
2966
 
                   strsignal(signal_received));
2967
 
    } else {
2968
 
      fprintf_plus(stderr, "%s exiting\n", argv[0]);
2969
 
    }
 
2536
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
2970
2537
  }
2971
2538
  
2972
2539
  /* Cleanup things */
2983
2550
  
2984
2551
  if(gnutls_initialized){
2985
2552
    gnutls_certificate_free_credentials(mc.cred);
 
2553
    gnutls_global_deinit();
2986
2554
    gnutls_dh_params_deinit(mc.dh_params);
2987
2555
  }
2988
2556
  
3011
2579
  
3012
2580
  /* Re-raise privileges */
3013
2581
  {
3014
 
    ret = raise_privileges();
3015
 
    if(ret != 0){
3016
 
      errno = ret;
 
2582
    ret_errno = raise_privileges();
 
2583
    if(ret_errno != 0){
 
2584
      errno = ret_errno;
3017
2585
      perror_plus("Failed to raise privileges");
3018
2586
    } else {
3019
2587
      
3024
2592
      /* Take down the network interfaces which were brought up */
3025
2593
      {
3026
2594
        char *interface = NULL;
3027
 
        while((interface = argz_next(interfaces_to_take_down,
3028
 
                                     interfaces_to_take_down_size,
3029
 
                                     interface))){
3030
 
          ret = take_down_interface(interface);
3031
 
          if(ret != 0){
3032
 
            errno = ret;
 
2595
        while((interface=argz_next(interfaces_to_take_down,
 
2596
                                   interfaces_to_take_down_size,
 
2597
                                   interface))){
 
2598
          ret_errno = take_down_interface(interface);
 
2599
          if(ret_errno != 0){
 
2600
            errno = ret_errno;
3033
2601
            perror_plus("Failed to take down interface");
3034
2602
          }
3035
2603
        }
3040
2608
      }
3041
2609
    }
3042
2610
    
3043
 
    ret = lower_privileges_permanently();
3044
 
    if(ret != 0){
3045
 
      errno = ret;
 
2611
    ret_errno = lower_privileges_permanently();
 
2612
    if(ret_errno != 0){
 
2613
      errno = ret_errno;
3046
2614
      perror_plus("Failed to lower privileges permanently");
3047
2615
    }
3048
2616
  }
3050
2618
  free(interfaces_to_take_down);
3051
2619
  free(interfaces_hooks);
3052
2620
  
3053
 
  void clean_dir_at(int base, const char * const dirname,
3054
 
                    uintmax_t level){
3055
 
    struct dirent **direntries = NULL;
3056
 
    int dret;
3057
 
    int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3058
 
                                                O_RDONLY
3059
 
                                                | O_NOFOLLOW
3060
 
                                                | O_DIRECTORY
3061
 
                                                | O_PATH));
3062
 
    if(dir_fd == -1){
3063
 
      perror_plus("open");
3064
 
    }
3065
 
    int numentries = scandirat(dir_fd, ".", &direntries,
3066
 
                               notdotentries, alphasort);
3067
 
    if(numentries >= 0){
3068
 
      for(int i = 0; i < numentries; i++){
3069
 
        if(debug){
3070
 
          fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3071
 
                       dirname, direntries[i]->d_name);
3072
 
        }
3073
 
        dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3074
 
        if(dret == -1){
3075
 
          if(errno == EISDIR){
3076
 
              dret = unlinkat(dir_fd, direntries[i]->d_name,
3077
 
                              AT_REMOVEDIR);
3078
 
          }         
3079
 
          if((dret == -1) and (errno == ENOTEMPTY)
3080
 
             and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3081
 
                  == 0) and (level == 0)){
3082
 
            /* Recurse only in this special case */
3083
 
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3084
 
            dret = 0;
3085
 
          }
3086
 
          if(dret == -1){
3087
 
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3088
 
                         direntries[i]->d_name, strerror(errno));
3089
 
          }
3090
 
        }
3091
 
        free(direntries[i]);
3092
 
      }
3093
 
      
3094
 
      /* need to clean even if 0 because man page doesn't specify */
3095
 
      free(direntries);
3096
 
      if(numentries == -1){
3097
 
        perror_plus("scandirat");
3098
 
      }
3099
 
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
3100
 
      if(dret == -1 and errno != ENOENT){
3101
 
        perror_plus("rmdir");
3102
 
      }
3103
 
    } else {
3104
 
      perror_plus("scandirat");
3105
 
    }
3106
 
    close(dir_fd);
3107
 
  }
3108
 
  
3109
2621
  /* Removes the GPGME temp directory and all files inside */
3110
2622
  if(tempdir != NULL){
3111
 
    clean_dir_at(-1, tempdir, 0);
 
2623
    struct dirent **direntries = NULL;
 
2624
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY |
 
2625
                                                  O_NOFOLLOW));
 
2626
    if(tempdir_fd == -1){
 
2627
      perror_plus("open");
 
2628
    } else {
 
2629
#ifdef __GLIBC__
 
2630
#if __GLIBC_PREREQ(2, 15)
 
2631
      int numentries = scandirat(tempdir_fd, ".", &direntries,
 
2632
                                 notdotentries, alphasort);
 
2633
#else  /* not __GLIBC_PREREQ(2, 15) */
 
2634
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2635
                               alphasort);
 
2636
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
2637
#else   /* not __GLIBC__ */
 
2638
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2639
                               alphasort);
 
2640
#endif  /* not __GLIBC__ */
 
2641
      if(numentries >= 0){
 
2642
        for(int i = 0; i < numentries; i++){
 
2643
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
 
2644
          if(ret == -1){
 
2645
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
 
2646
                         " \"%s\", 0): %s\n", tempdir,
 
2647
                         direntries[i]->d_name, strerror(errno));
 
2648
          }
 
2649
          free(direntries[i]);
 
2650
        }
 
2651
        
 
2652
        /* need to clean even if 0 because man page doesn't specify */
 
2653
        free(direntries);
 
2654
        if(numentries == -1){
 
2655
          perror_plus("scandir");
 
2656
        }
 
2657
        ret = rmdir(tempdir);
 
2658
        if(ret == -1 and errno != ENOENT){
 
2659
          perror_plus("rmdir");
 
2660
        }
 
2661
      }
 
2662
      TEMP_FAILURE_RETRY(close(tempdir_fd));
 
2663
    }
3112
2664
  }
3113
2665
  
3114
2666
  if(quit_now){