/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-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
 
      close(dhpfile);
617
 
      if(params.data == NULL){
618
 
        dhparamsfilename = NULL;
619
 
      }
620
 
      if(dhparamsfilename == NULL){
621
 
        break;
622
 
      }
623
 
      ret = gnutls_dh_params_import_pkcs3(mc->dh_params, &params,
624
 
                                          GNUTLS_X509_FMT_PEM);
625
 
      if(ret != GNUTLS_E_SUCCESS){
626
 
        fprintf_plus(stderr, "Failed to parse DH parameters in file"
627
 
                     " \"%s\": %s\n", dhparamsfilename,
628
 
                     safer_gnutls_strerror(ret));
629
 
        dhparamsfilename = NULL;
630
 
      }
631
 
      free(params.data);
632
 
    } while(false);
633
 
  }
634
 
  if(dhparamsfilename == NULL){
635
 
    if(mc->dh_bits == 0){
636
 
      /* Find out the optimal number of DH bits */
637
 
      /* Try to read the private key file */
638
 
      gnutls_datum_t buffer = { .data = NULL, .size = 0 };
639
 
      do {
640
 
        int secfile = open(seckeyfilename, O_RDONLY);
641
 
        if(secfile == -1){
642
 
          perror_plus("open");
643
 
          break;
644
 
        }
645
 
        size_t buffer_capacity = 0;
646
 
        while(true){
647
 
          buffer_capacity = incbuffer((char **)&buffer.data,
648
 
                                      (size_t)buffer.size,
649
 
                                      (size_t)buffer_capacity);
650
 
          if(buffer_capacity == 0){
651
 
            perror_plus("incbuffer");
652
 
            free(buffer.data);
653
 
            buffer.data = NULL;
654
 
            break;
655
 
          }
656
 
          ssize_t bytes_read = read(secfile,
657
 
                                    buffer.data + buffer.size,
658
 
                                    BUFFER_SIZE);
659
 
          /* EOF */
660
 
          if(bytes_read == 0){
661
 
            break;
662
 
          }
663
 
          /* check bytes_read for failure */
664
 
          if(bytes_read < 0){
665
 
            perror_plus("read");
666
 
            free(buffer.data);
667
 
            buffer.data = NULL;
668
 
            break;
669
 
          }
670
 
          buffer.size += (unsigned int)bytes_read;
671
 
        }
672
 
        close(secfile);
673
 
      } while(false);
674
 
      /* If successful, use buffer to parse private key */
675
 
      gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
676
 
      if(buffer.data != NULL){
677
 
        {
678
 
          gnutls_openpgp_privkey_t privkey = NULL;
679
 
          ret = gnutls_openpgp_privkey_init(&privkey);
680
 
          if(ret != GNUTLS_E_SUCCESS){
681
 
            fprintf_plus(stderr, "Error initializing OpenPGP key"
682
 
                         " structure: %s",
683
 
                         safer_gnutls_strerror(ret));
684
 
            free(buffer.data);
685
 
            buffer.data = NULL;
686
 
          } else {
687
 
            ret = gnutls_openpgp_privkey_import
688
 
              (privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
689
 
            if(ret != GNUTLS_E_SUCCESS){
690
 
              fprintf_plus(stderr, "Error importing OpenPGP key : %s",
691
 
                           safer_gnutls_strerror(ret));
692
 
              privkey = NULL;
693
 
            }
694
 
            free(buffer.data);
695
 
            buffer.data = NULL;
696
 
            if(privkey != NULL){
697
 
              /* Use private key to suggest an appropriate
698
 
                 sec_param */
699
 
              sec_param = gnutls_openpgp_privkey_sec_param(privkey);
700
 
              gnutls_openpgp_privkey_deinit(privkey);
701
 
              if(debug){
702
 
                fprintf_plus(stderr, "This OpenPGP key implies using"
703
 
                             " a GnuTLS security parameter \"%s\".\n",
704
 
                             safe_string(gnutls_sec_param_get_name
705
 
                                         (sec_param)));
706
 
              }
707
 
            }
708
 
          }
709
 
        }
710
 
        if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
711
 
          /* Err on the side of caution */
712
 
          sec_param = GNUTLS_SEC_PARAM_ULTRA;
713
 
          if(debug){
714
 
            fprintf_plus(stderr, "Falling back to security parameter"
715
 
                         " \"%s\"\n",
716
 
                         safe_string(gnutls_sec_param_get_name
717
 
                                     (sec_param)));
718
 
          }
719
 
        }
720
 
      }
721
 
      uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
722
 
      if(uret != 0){
723
 
        mc->dh_bits = uret;
724
 
        if(debug){
725
 
          fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
726
 
                       " implies %u DH bits; using that.\n",
727
 
                       safe_string(gnutls_sec_param_get_name
728
 
                                   (sec_param)),
729
 
                       mc->dh_bits);
730
 
        }
731
 
      } else {
732
 
        fprintf_plus(stderr, "Failed to get implied number of DH"
733
 
                     " bits for security parameter \"%s\"): %s\n",
734
 
                     safe_string(gnutls_sec_param_get_name
735
 
                                 (sec_param)),
736
 
                     safer_gnutls_strerror(ret));
737
 
        goto globalfail;
738
 
      }
739
 
    } else if(debug){
740
 
      fprintf_plus(stderr, "DH bits explicitly set to %u\n",
741
 
                   mc->dh_bits);
742
 
    }
743
 
    ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
744
 
    if(ret != GNUTLS_E_SUCCESS){
745
 
      fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
746
 
                   " bits): %s\n", mc->dh_bits,
747
 
                   safer_gnutls_strerror(ret));
748
 
      goto globalfail;
749
 
    }
750
 
  }
 
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
  
751
579
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
752
580
  
753
581
  return 0;
755
583
 globalfail:
756
584
  
757
585
  gnutls_certificate_free_credentials(mc->cred);
 
586
  gnutls_global_deinit();
758
587
  gnutls_dh_params_deinit(mc->dh_params);
759
588
  return -1;
760
589
}
812
641
  /* ignore client certificate if any. */
813
642
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
814
643
  
 
644
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
 
645
  
815
646
  return 0;
816
647
}
817
648
 
819
650
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
820
651
                      __attribute__((unused)) const char *txt){}
821
652
 
822
 
/* Set effective uid to 0, return errno */
823
 
__attribute__((warn_unused_result))
824
 
int raise_privileges(void){
825
 
  int old_errno = errno;
826
 
  int ret = 0;
827
 
  if(seteuid(0) == -1){
828
 
    ret = errno;
829
 
  }
830
 
  errno = old_errno;
831
 
  return ret;
832
 
}
833
 
 
834
 
/* Set effective and real user ID to 0.  Return errno. */
835
 
__attribute__((warn_unused_result))
836
 
int raise_privileges_permanently(void){
837
 
  int old_errno = errno;
838
 
  int ret = raise_privileges();
839
 
  if(ret != 0){
840
 
    errno = old_errno;
841
 
    return ret;
842
 
  }
843
 
  if(setuid(0) == -1){
844
 
    ret = errno;
845
 
  }
846
 
  errno = old_errno;
847
 
  return ret;
848
 
}
849
 
 
850
 
/* Set effective user ID to unprivileged saved user ID */
851
 
__attribute__((warn_unused_result))
852
 
int lower_privileges(void){
853
 
  int old_errno = errno;
854
 
  int ret = 0;
855
 
  if(seteuid(uid) == -1){
856
 
    ret = errno;
857
 
  }
858
 
  errno = old_errno;
859
 
  return ret;
860
 
}
861
 
 
862
 
/* Lower privileges permanently */
863
 
__attribute__((warn_unused_result))
864
 
int lower_privileges_permanently(void){
865
 
  int old_errno = errno;
866
 
  int ret = 0;
867
 
  if(setuid(uid) == -1){
868
 
    ret = errno;
869
 
  }
870
 
  errno = old_errno;
871
 
  return ret;
872
 
}
873
 
 
874
 
/* Helper function to add_local_route() and delete_local_route() */
875
 
__attribute__((nonnull, warn_unused_result))
876
 
static bool add_delete_local_route(const bool add,
877
 
                                   const char *address,
878
 
                                   AvahiIfIndex if_index){
879
 
  int ret;
880
 
  char helper[] = "mandos-client-iprouteadddel";
881
 
  char add_arg[] = "add";
882
 
  char delete_arg[] = "delete";
883
 
  char debug_flag[] = "--debug";
884
 
  char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
885
 
  if(pluginhelperdir == NULL){
886
 
    if(debug){
887
 
      fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
888
 
                   " variable not set; cannot run helper\n");
889
 
    }
890
 
    return false;
891
 
  }
892
 
  
893
 
  char interface[IF_NAMESIZE];
894
 
  if(if_indextoname((unsigned int)if_index, interface) == NULL){
895
 
    perror_plus("if_indextoname");
896
 
    return false;
897
 
  }
898
 
  
899
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
900
 
  if(devnull == -1){
901
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
902
 
    return false;
903
 
  }
904
 
  pid_t pid = fork();
905
 
  if(pid == 0){
906
 
    /* Child */
907
 
    /* Raise privileges */
908
 
    errno = raise_privileges_permanently();
909
 
    if(errno != 0){
910
 
      perror_plus("Failed to raise privileges");
911
 
      /* _exit(EX_NOPERM); */
912
 
    } else {
913
 
      /* Set group */
914
 
      errno = 0;
915
 
      ret = setgid(0);
916
 
      if(ret == -1){
917
 
        perror_plus("setgid");
918
 
        _exit(EX_NOPERM);
919
 
      }
920
 
      /* Reset supplementary groups */
921
 
      errno = 0;
922
 
      ret = setgroups(0, NULL);
923
 
      if(ret == -1){
924
 
        perror_plus("setgroups");
925
 
        _exit(EX_NOPERM);
926
 
      }
927
 
    }
928
 
    ret = dup2(devnull, STDIN_FILENO);
929
 
    if(ret == -1){
930
 
      perror_plus("dup2(devnull, STDIN_FILENO)");
931
 
      _exit(EX_OSERR);
932
 
    }
933
 
    ret = close(devnull);
934
 
    if(ret == -1){
935
 
      perror_plus("close");
936
 
      _exit(EX_OSERR);
937
 
    }
938
 
    ret = dup2(STDERR_FILENO, STDOUT_FILENO);
939
 
    if(ret == -1){
940
 
      perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
941
 
      _exit(EX_OSERR);
942
 
    }
943
 
    int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
944
 
                                                    O_RDONLY
945
 
                                                    | O_DIRECTORY
946
 
                                                    | O_PATH
947
 
                                                    | O_CLOEXEC));
948
 
    if(helperdir_fd == -1){
949
 
      perror_plus("open");
950
 
      _exit(EX_UNAVAILABLE);
951
 
    }
952
 
    int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
953
 
                                                   helper, O_RDONLY));
954
 
    if(helper_fd == -1){
955
 
      perror_plus("openat");
956
 
      close(helperdir_fd);
957
 
      _exit(EX_UNAVAILABLE);
958
 
    }
959
 
    close(helperdir_fd);
960
 
#ifdef __GNUC__
961
 
#pragma GCC diagnostic push
962
 
#pragma GCC diagnostic ignored "-Wcast-qual"
963
 
#endif
964
 
    if(fexecve(helper_fd, (char *const [])
965
 
               { helper, add ? add_arg : delete_arg, (char *)address,
966
 
                   interface, debug ? debug_flag : NULL, NULL },
967
 
               environ) == -1){
968
 
#ifdef __GNUC__
969
 
#pragma GCC diagnostic pop
970
 
#endif
971
 
      perror_plus("fexecve");
972
 
      _exit(EXIT_FAILURE);
973
 
    }
974
 
  }
975
 
  if(pid == -1){
976
 
    perror_plus("fork");
977
 
    return false;
978
 
  }
979
 
  int status;
980
 
  pid_t pret = -1;
981
 
  errno = 0;
982
 
  do {
983
 
    pret = waitpid(pid, &status, 0);
984
 
    if(pret == -1 and errno == EINTR and quit_now){
985
 
      int errno_raising = 0;
986
 
      if((errno = raise_privileges()) != 0){
987
 
        errno_raising = errno;
988
 
        perror_plus("Failed to raise privileges in order to"
989
 
                    " kill helper program");
990
 
      }
991
 
      if(kill(pid, SIGTERM) == -1){
992
 
        perror_plus("kill");
993
 
      }
994
 
      if((errno_raising == 0) and (errno = lower_privileges()) != 0){
995
 
        perror_plus("Failed to lower privileges after killing"
996
 
                    " helper program");
997
 
      }
998
 
      return false;
999
 
    }
1000
 
  } while(pret == -1 and errno == EINTR);
1001
 
  if(pret == -1){
1002
 
    perror_plus("waitpid");
1003
 
    return false;
1004
 
  }
1005
 
  if(WIFEXITED(status)){
1006
 
    if(WEXITSTATUS(status) != 0){
1007
 
      fprintf_plus(stderr, "Error: iprouteadddel exited"
1008
 
                   " with status %d\n", WEXITSTATUS(status));
1009
 
      return false;
1010
 
    }
1011
 
    return true;
1012
 
  }
1013
 
  if(WIFSIGNALED(status)){
1014
 
    fprintf_plus(stderr, "Error: iprouteadddel died by"
1015
 
                 " signal %d\n", WTERMSIG(status));
1016
 
    return false;
1017
 
  }
1018
 
  fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1019
 
  return false;
1020
 
}
1021
 
 
1022
 
__attribute__((nonnull, warn_unused_result))
1023
 
static bool add_local_route(const char *address,
1024
 
                            AvahiIfIndex if_index){
1025
 
  if(debug){
1026
 
    fprintf_plus(stderr, "Adding route to %s\n", address);
1027
 
  }
1028
 
  return add_delete_local_route(true, address, if_index);
1029
 
}
1030
 
 
1031
 
__attribute__((nonnull, warn_unused_result))
1032
 
static bool delete_local_route(const char *address,
1033
 
                               AvahiIfIndex if_index){
1034
 
  if(debug){
1035
 
    fprintf_plus(stderr, "Removing route to %s\n", address);
1036
 
  }
1037
 
  return add_delete_local_route(false, address, if_index);
1038
 
}
1039
 
 
1040
653
/* Called when a Mandos server is found */
1041
654
__attribute__((nonnull, warn_unused_result))
1042
655
static int start_mandos_communication(const char *ip, in_port_t port,
1053
666
  int retval = -1;
1054
667
  gnutls_session_t session;
1055
668
  int pf;                       /* Protocol family */
1056
 
  bool route_added = false;
1057
669
  
1058
670
  errno = 0;
1059
671
  
1081
693
    bool match = false;
1082
694
    {
1083
695
      char *interface = NULL;
1084
 
      while((interface = argz_next(mc->interfaces,
1085
 
                                   mc->interfaces_size,
1086
 
                                   interface))){
 
696
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
 
697
                                 interface))){
1087
698
        if(if_nametoindex(interface) == (unsigned int)if_index){
1088
699
          match = true;
1089
700
          break;
1118
729
                 PRIuMAX "\n", ip, (uintmax_t)port);
1119
730
  }
1120
731
  
1121
 
  tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
 
732
  tcp_sd = socket(pf, SOCK_STREAM, 0);
1122
733
  if(tcp_sd < 0){
1123
734
    int e = errno;
1124
735
    perror_plus("socket");
1131
742
    goto mandos_end;
1132
743
  }
1133
744
  
 
745
  memset(&to, 0, sizeof(to));
1134
746
  if(af == AF_INET6){
1135
 
    struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1136
 
    *to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1137
 
    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);
1138
749
  } else {                      /* IPv4 */
1139
 
    struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1140
 
    *to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1141
 
    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);
1142
752
  }
1143
753
  if(ret < 0 ){
1144
754
    int e = errno;
1214
824
    goto mandos_end;
1215
825
  }
1216
826
  
1217
 
  while(true){
1218
 
    if(af == AF_INET6){
1219
 
      ret = connect(tcp_sd, (struct sockaddr *)&to,
1220
 
                    sizeof(struct sockaddr_in6));
1221
 
    } else {
1222
 
      ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1223
 
                    sizeof(struct sockaddr_in));
1224
 
    }
1225
 
    if(ret < 0){
1226
 
      if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1227
 
         and if_index != AVAHI_IF_UNSPEC
1228
 
         and connect_to == NULL
1229
 
         and not route_added and
1230
 
         ((af == AF_INET6 and not
1231
 
           IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1232
 
                                    &to)->sin6_addr)))
1233
 
          or (af == AF_INET and
1234
 
              /* Not a a IPv4LL address */
1235
 
              (ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1236
 
               & 0xFFFF0000L) != 0xA9FE0000L))){
1237
 
        /* Work around Avahi bug - Avahi does not announce link-local
1238
 
           addresses if it has a global address, so local hosts with
1239
 
           *only* a link-local address (e.g. Mandos clients) cannot
1240
 
           connect to a Mandos server announced by Avahi on a server
1241
 
           host with a global address.  Work around this by retrying
1242
 
           with an explicit route added with the server's address.
1243
 
           
1244
 
           Avahi bug reference:
1245
 
           https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1246
 
           https://bugs.debian.org/587961
1247
 
        */
1248
 
        if(debug){
1249
 
          fprintf_plus(stderr, "Mandos server unreachable, trying"
1250
 
                       " direct route\n");
1251
 
        }
1252
 
        int e = errno;
1253
 
        route_added = add_local_route(ip, if_index);
1254
 
        if(route_added){
1255
 
          continue;
1256
 
        }
1257
 
        errno = e;
1258
 
      }
1259
 
      if(errno != ECONNREFUSED or debug){
1260
 
        int e = errno;
1261
 
        perror_plus("connect");
1262
 
        errno = e;
1263
 
      }
1264
 
      goto mandos_end;
1265
 
    }
1266
 
    
1267
 
    if(quit_now){
1268
 
      errno = EINTR;
1269
 
      goto mandos_end;
1270
 
    }
1271
 
    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;
1272
846
  }
1273
847
  
1274
848
  const char *out = mandos_protocol_version;
1428
1002
                                               &decrypted_buffer, mc);
1429
1003
    if(decrypted_buffer_size >= 0){
1430
1004
      
1431
 
      clearerr(stdout);
1432
1005
      written = 0;
1433
1006
      while(written < (size_t) decrypted_buffer_size){
1434
1007
        if(quit_now){
1450
1023
        }
1451
1024
        written += (size_t)ret;
1452
1025
      }
1453
 
      ret = fflush(stdout);
1454
 
      if(ret != 0){
1455
 
        int e = errno;
1456
 
        if(debug){
1457
 
          fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1458
 
                       strerror(errno));
1459
 
        }
1460
 
        errno = e;
1461
 
        goto mandos_end;
1462
 
      }
1463
1026
      retval = 0;
1464
1027
    }
1465
1028
  }
1468
1031
  
1469
1032
 mandos_end:
1470
1033
  {
1471
 
    if(route_added){
1472
 
      if(not delete_local_route(ip, if_index)){
1473
 
        fprintf_plus(stderr, "Failed to delete local route to %s on"
1474
 
                     " interface %d", ip, if_index);
1475
 
      }
1476
 
    }
1477
1034
    int e = errno;
1478
1035
    free(decrypted_buffer);
1479
1036
    free(buffer);
1480
1037
    if(tcp_sd >= 0){
1481
 
      ret = close(tcp_sd);
 
1038
      ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
1482
1039
    }
1483
1040
    if(ret == -1){
1484
1041
      if(e == 0){
1496
1053
  return retval;
1497
1054
}
1498
1055
 
 
1056
__attribute__((nonnull))
1499
1057
static void resolve_callback(AvahiSServiceResolver *r,
1500
1058
                             AvahiIfIndex interface,
1501
1059
                             AvahiProtocol proto,
1638
1196
__attribute__((nonnull, warn_unused_result))
1639
1197
bool get_flags(const char *ifname, struct ifreq *ifr){
1640
1198
  int ret;
1641
 
  int old_errno;
 
1199
  error_t ret_errno;
1642
1200
  
1643
1201
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1644
1202
  if(s < 0){
1645
 
    old_errno = errno;
 
1203
    ret_errno = errno;
1646
1204
    perror_plus("socket");
1647
 
    errno = old_errno;
 
1205
    errno = ret_errno;
1648
1206
    return false;
1649
1207
  }
1650
 
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1651
 
  ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
 
1208
  strcpy(ifr->ifr_name, ifname);
1652
1209
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1653
1210
  if(ret == -1){
1654
1211
    if(debug){
1655
 
      old_errno = errno;
 
1212
      ret_errno = errno;
1656
1213
      perror_plus("ioctl SIOCGIFFLAGS");
1657
 
      errno = old_errno;
 
1214
      errno = ret_errno;
1658
1215
    }
1659
 
    close(s);
1660
1216
    return false;
1661
1217
  }
1662
 
  close(s);
1663
1218
  return true;
1664
1219
}
1665
1220
 
1907
1462
  }
1908
1463
}
1909
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
 
1910
1517
__attribute__((nonnull))
1911
1518
void run_network_hooks(const char *mode, const char *interface,
1912
1519
                       const float delay){
1913
1520
  struct dirent **direntries = NULL;
1914
1521
  if(hookdir_fd == -1){
1915
 
    hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
1916
 
                      | O_CLOEXEC);
 
1522
    hookdir_fd = open(hookdir, O_RDONLY);
1917
1523
    if(hookdir_fd == -1){
1918
1524
      if(errno == ENOENT){
1919
1525
        if(debug){
1926
1532
      return;
1927
1533
    }
1928
1534
  }
1929
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1930
 
  if(devnull == -1){
1931
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
1932
 
    return;
1933
 
  }
 
1535
#ifdef __GLIBC__
 
1536
#if __GLIBC_PREREQ(2, 15)
1934
1537
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
1935
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__ */
1936
1547
  if(numhooks == -1){
1937
1548
    perror_plus("scandir");
1938
 
    close(devnull);
1939
1549
    return;
1940
1550
  }
1941
1551
  struct dirent *direntry;
1942
1552
  int ret;
 
1553
  int devnull = open("/dev/null", O_RDONLY);
1943
1554
  for(int i = 0; i < numhooks; i++){
1944
1555
    direntry = direntries[i];
1945
1556
    if(debug){
1969
1580
        perror_plus("setgroups");
1970
1581
        _exit(EX_NOPERM);
1971
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
      }
1972
1598
      ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1973
1599
      if(ret == -1){
1974
1600
        perror_plus("setenv");
2009
1635
          _exit(EX_OSERR);
2010
1636
        }
2011
1637
      }
2012
 
      int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
2013
 
                                                   direntry->d_name,
2014
 
                                                   O_RDONLY));
 
1638
      int hook_fd = openat(hookdir_fd, direntry->d_name, O_RDONLY);
2015
1639
      if(hook_fd == -1){
2016
1640
        perror_plus("openat");
2017
1641
        _exit(EXIT_FAILURE);
2018
1642
      }
2019
 
      if(close(hookdir_fd) == -1){
 
1643
      if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
2020
1644
        perror_plus("close");
2021
1645
        _exit(EXIT_FAILURE);
2022
1646
      }
2023
 
      ret = dup2(devnull, STDIN_FILENO);
2024
 
      if(ret == -1){
2025
 
        perror_plus("dup2(devnull, STDIN_FILENO)");
2026
 
        _exit(EX_OSERR);
2027
 
      }
2028
 
      ret = close(devnull);
2029
 
      if(ret == -1){
2030
 
        perror_plus("close");
2031
 
        _exit(EX_OSERR);
2032
 
      }
2033
 
      ret = dup2(STDERR_FILENO, STDOUT_FILENO);
2034
 
      if(ret == -1){
2035
 
        perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
2036
 
        _exit(EX_OSERR);
2037
 
      }
2038
1647
      if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
2039
1648
                 environ) == -1){
2040
1649
        perror_plus("fexecve");
2041
1650
        _exit(EXIT_FAILURE);
2042
1651
      }
2043
1652
    } else {
2044
 
      if(hook_pid == -1){
2045
 
        perror_plus("fork");
2046
 
        free(direntry);
2047
 
        continue;
2048
 
      }
2049
1653
      int status;
2050
1654
      if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
2051
1655
        perror_plus("waitpid");
2080
1684
    free(direntry);
2081
1685
  }
2082
1686
  free(direntries);
2083
 
  if(close(hookdir_fd) == -1){
 
1687
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
2084
1688
    perror_plus("close");
2085
1689
  } else {
2086
1690
    hookdir_fd = -1;
2089
1693
}
2090
1694
 
2091
1695
__attribute__((nonnull, warn_unused_result))
2092
 
int bring_up_interface(const char *const interface,
2093
 
                       const float delay){
2094
 
  int old_errno = errno;
 
1696
error_t bring_up_interface(const char *const interface,
 
1697
                           const float delay){
 
1698
  error_t old_errno = errno;
2095
1699
  int ret;
2096
1700
  struct ifreq network;
2097
1701
  unsigned int if_index = if_nametoindex(interface);
2107
1711
  }
2108
1712
  
2109
1713
  if(not interface_is_up(interface)){
2110
 
    int ret_errno = 0;
2111
 
    int ioctl_errno = 0;
 
1714
    error_t ret_errno = 0, ioctl_errno = 0;
2112
1715
    if(not get_flags(interface, &network)){
2113
1716
      ret_errno = errno;
2114
1717
      fprintf_plus(stderr, "Failed to get flags for interface "
2127
1730
    }
2128
1731
    
2129
1732
    if(quit_now){
2130
 
      ret = close(sd);
 
1733
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
2131
1734
      if(ret == -1){
2132
1735
        perror_plus("close");
2133
1736
      }
2183
1786
    }
2184
1787
    
2185
1788
    /* Close the socket */
2186
 
    ret = close(sd);
 
1789
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
2187
1790
    if(ret == -1){
2188
1791
      perror_plus("close");
2189
1792
    }
2201
1804
  
2202
1805
  /* Sleep checking until interface is running.
2203
1806
     Check every 0.25s, up to total time of delay */
2204
 
  for(int i = 0; i < delay * 4; i++){
 
1807
  for(int i=0; i < delay * 4; i++){
2205
1808
    if(interface_is_running(interface)){
2206
1809
      break;
2207
1810
    }
2217
1820
}
2218
1821
 
2219
1822
__attribute__((nonnull, warn_unused_result))
2220
 
int take_down_interface(const char *const interface){
2221
 
  int old_errno = errno;
 
1823
error_t take_down_interface(const char *const interface){
 
1824
  error_t old_errno = errno;
2222
1825
  struct ifreq network;
2223
1826
  unsigned int if_index = if_nametoindex(interface);
2224
1827
  if(if_index == 0){
2227
1830
    return ENXIO;
2228
1831
  }
2229
1832
  if(interface_is_up(interface)){
2230
 
    int ret_errno = 0;
2231
 
    int ioctl_errno = 0;
 
1833
    error_t ret_errno = 0, ioctl_errno = 0;
2232
1834
    if(not get_flags(interface, &network) and debug){
2233
1835
      ret_errno = errno;
2234
1836
      fprintf_plus(stderr, "Failed to get flags for interface "
2272
1874
    }
2273
1875
    
2274
1876
    /* Close the socket */
2275
 
    int ret = close(sd);
 
1877
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
2276
1878
    if(ret == -1){
2277
1879
      perror_plus("close");
2278
1880
    }
2293
1895
}
2294
1896
 
2295
1897
int main(int argc, char *argv[]){
2296
 
  mandos_context mc = { .server = NULL, .dh_bits = 0,
2297
 
                        .priority = "SECURE256:!CTYPE-X.509"
2298
 
                        ":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
2299
 
                        .current_server = NULL, .interfaces = NULL,
2300
 
                        .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 };
2301
1902
  AvahiSServiceBrowser *sb = NULL;
2302
1903
  error_t ret_errno;
2303
1904
  int ret;
2312
1913
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2313
1914
  const char *seckey = PATHDIR "/" SECKEY;
2314
1915
  const char *pubkey = PATHDIR "/" PUBKEY;
2315
 
  const char *dh_params_file = NULL;
2316
1916
  char *interfaces_hooks = NULL;
2317
1917
  
2318
1918
  bool gnutls_initialized = false;
2371
1971
        .doc = "Bit length of the prime number used in the"
2372
1972
        " Diffie-Hellman key exchange",
2373
1973
        .group = 2 },
2374
 
      { .name = "dh-params", .key = 134,
2375
 
        .arg = "FILE",
2376
 
        .doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
2377
 
        " for the Diffie-Hellman key exchange",
2378
 
        .group = 2 },
2379
1974
      { .name = "priority", .key = 130,
2380
1975
        .arg = "STRING",
2381
1976
        .doc = "GnuTLS priority string for the TLS handshake",
2436
2031
        }
2437
2032
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2438
2033
        break;
2439
 
      case 134:                 /* --dh-params */
2440
 
        dh_params_file = arg;
2441
 
        break;
2442
2034
      case 130:                 /* --priority */
2443
2035
        mc.priority = arg;
2444
2036
        break;
2484
2076
                         .args_doc = "",
2485
2077
                         .doc = "Mandos client -- Get and decrypt"
2486
2078
                         " passwords from a Mandos server" };
2487
 
    ret_errno = argp_parse(&argp, argc, argv,
2488
 
                           ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2489
 
    switch(ret_errno){
 
2079
    ret = argp_parse(&argp, argc, argv,
 
2080
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2081
    switch(ret){
2490
2082
    case 0:
2491
2083
      break;
2492
2084
    case ENOMEM:
2493
2085
    default:
2494
 
      errno = ret_errno;
 
2086
      errno = ret;
2495
2087
      perror_plus("argp_parse");
2496
2088
      exitcode = EX_OSERR;
2497
2089
      goto end;
2500
2092
      goto end;
2501
2093
    }
2502
2094
  }
2503
 
  
 
2095
    
2504
2096
  {
2505
2097
    /* Work around Debian bug #633582:
2506
 
       <https://bugs.debian.org/633582> */
 
2098
       <http://bugs.debian.org/633582> */
2507
2099
    
2508
2100
    /* Re-raise privileges */
2509
 
    ret = raise_privileges();
2510
 
    if(ret != 0){
2511
 
      errno = ret;
 
2101
    ret_errno = raise_privileges();
 
2102
    if(ret_errno != 0){
 
2103
      errno = ret_errno;
2512
2104
      perror_plus("Failed to raise privileges");
2513
2105
    } else {
2514
2106
      struct stat st;
2530
2122
              }
2531
2123
            }
2532
2124
          }
2533
 
          close(seckey_fd);
 
2125
          TEMP_FAILURE_RETRY(close(seckey_fd));
2534
2126
        }
2535
2127
      }
2536
 
      
 
2128
    
2537
2129
      if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2538
2130
        int pubkey_fd = open(pubkey, O_RDONLY);
2539
2131
        if(pubkey_fd == -1){
2551
2143
              }
2552
2144
            }
2553
2145
          }
2554
 
          close(pubkey_fd);
2555
 
        }
2556
 
      }
2557
 
      
2558
 
      if(dh_params_file != NULL
2559
 
         and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2560
 
        int dhparams_fd = open(dh_params_file, O_RDONLY);
2561
 
        if(dhparams_fd == -1){
2562
 
          perror_plus("open");
2563
 
        } else {
2564
 
          ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
2565
 
          if(ret == -1){
2566
 
            perror_plus("fstat");
2567
 
          } else {
2568
 
            if(S_ISREG(st.st_mode)
2569
 
               and st.st_uid == 0 and st.st_gid == 0){
2570
 
              ret = fchown(dhparams_fd, uid, gid);
2571
 
              if(ret == -1){
2572
 
                perror_plus("fchown");
2573
 
              }
2574
 
            }
2575
 
          }
2576
 
          close(dhparams_fd);
2577
 
        }
2578
 
      }
2579
 
      
 
2146
          TEMP_FAILURE_RETRY(close(pubkey_fd));
 
2147
        }
 
2148
      }
 
2149
    
2580
2150
      /* Lower privileges */
2581
 
      ret = lower_privileges();
2582
 
      if(ret != 0){
2583
 
        errno = ret;
 
2151
      ret_errno = lower_privileges();
 
2152
      if(ret_errno != 0){
 
2153
        errno = ret_errno;
2584
2154
        perror_plus("Failed to lower privileges");
2585
2155
      }
2586
2156
    }
2756
2326
      errno = bring_up_interface(interface, delay);
2757
2327
      if(not interface_was_up){
2758
2328
        if(errno != 0){
2759
 
          fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
2760
 
                       " %s\n", interface, strerror(errno));
 
2329
          perror_plus("Failed to bring up interface");
2761
2330
        } else {
2762
2331
          errno = argz_add(&interfaces_to_take_down,
2763
2332
                           &interfaces_to_take_down_size,
2786
2355
    goto end;
2787
2356
  }
2788
2357
  
2789
 
  ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
 
2358
  ret = init_gnutls_global(pubkey, seckey, &mc);
2790
2359
  if(ret == -1){
2791
2360
    fprintf_plus(stderr, "init_gnutls_global failed\n");
2792
2361
    exitcode = EX_UNAVAILABLE;
2914
2483
    
2915
2484
    /* Allocate a new server */
2916
2485
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2917
 
                                 &config, NULL, NULL, &ret);
 
2486
                                 &config, NULL, NULL, &ret_errno);
2918
2487
    
2919
2488
    /* Free the Avahi configuration data */
2920
2489
    avahi_server_config_free(&config);
2923
2492
  /* Check if creating the Avahi server object succeeded */
2924
2493
  if(mc.server == NULL){
2925
2494
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2926
 
                 avahi_strerror(ret));
 
2495
                 avahi_strerror(ret_errno));
2927
2496
    exitcode = EX_UNAVAILABLE;
2928
2497
    goto end;
2929
2498
  }
2964
2533
 end:
2965
2534
  
2966
2535
  if(debug){
2967
 
    if(signal_received){
2968
 
      fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
2969
 
                   argv[0], signal_received,
2970
 
                   strsignal(signal_received));
2971
 
    } else {
2972
 
      fprintf_plus(stderr, "%s exiting\n", argv[0]);
2973
 
    }
 
2536
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
2974
2537
  }
2975
2538
  
2976
2539
  /* Cleanup things */
2987
2550
  
2988
2551
  if(gnutls_initialized){
2989
2552
    gnutls_certificate_free_credentials(mc.cred);
 
2553
    gnutls_global_deinit();
2990
2554
    gnutls_dh_params_deinit(mc.dh_params);
2991
2555
  }
2992
2556
  
3015
2579
  
3016
2580
  /* Re-raise privileges */
3017
2581
  {
3018
 
    ret = raise_privileges();
3019
 
    if(ret != 0){
3020
 
      errno = ret;
 
2582
    ret_errno = raise_privileges();
 
2583
    if(ret_errno != 0){
 
2584
      errno = ret_errno;
3021
2585
      perror_plus("Failed to raise privileges");
3022
2586
    } else {
3023
2587
      
3028
2592
      /* Take down the network interfaces which were brought up */
3029
2593
      {
3030
2594
        char *interface = NULL;
3031
 
        while((interface = argz_next(interfaces_to_take_down,
3032
 
                                     interfaces_to_take_down_size,
3033
 
                                     interface))){
3034
 
          ret = take_down_interface(interface);
3035
 
          if(ret != 0){
3036
 
            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;
3037
2601
            perror_plus("Failed to take down interface");
3038
2602
          }
3039
2603
        }
3044
2608
      }
3045
2609
    }
3046
2610
    
3047
 
    ret = lower_privileges_permanently();
3048
 
    if(ret != 0){
3049
 
      errno = ret;
 
2611
    ret_errno = lower_privileges_permanently();
 
2612
    if(ret_errno != 0){
 
2613
      errno = ret_errno;
3050
2614
      perror_plus("Failed to lower privileges permanently");
3051
2615
    }
3052
2616
  }
3054
2618
  free(interfaces_to_take_down);
3055
2619
  free(interfaces_hooks);
3056
2620
  
3057
 
  void clean_dir_at(int base, const char * const dirname,
3058
 
                    uintmax_t level){
3059
 
    struct dirent **direntries = NULL;
3060
 
    int dret;
3061
 
    int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3062
 
                                                O_RDONLY
3063
 
                                                | O_NOFOLLOW
3064
 
                                                | O_DIRECTORY
3065
 
                                                | O_PATH));
3066
 
    if(dir_fd == -1){
3067
 
      perror_plus("open");
3068
 
      return;
3069
 
    }
3070
 
    int numentries = scandirat(dir_fd, ".", &direntries,
3071
 
                               notdotentries, alphasort);
3072
 
    if(numentries >= 0){
3073
 
      for(int i = 0; i < numentries; i++){
3074
 
        if(debug){
3075
 
          fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3076
 
                       dirname, direntries[i]->d_name);
3077
 
        }
3078
 
        dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3079
 
        if(dret == -1){
3080
 
          if(errno == EISDIR){
3081
 
              dret = unlinkat(dir_fd, direntries[i]->d_name,
3082
 
                              AT_REMOVEDIR);
3083
 
          }         
3084
 
          if((dret == -1) and (errno == ENOTEMPTY)
3085
 
             and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3086
 
                  == 0) and (level == 0)){
3087
 
            /* Recurse only in this special case */
3088
 
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3089
 
            dret = 0;
3090
 
          }
3091
 
          if((dret == -1) and (errno != ENOENT)){
3092
 
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3093
 
                         direntries[i]->d_name, strerror(errno));
3094
 
          }
3095
 
        }
3096
 
        free(direntries[i]);
3097
 
      }
3098
 
      
3099
 
      /* need to clean even if 0 because man page doesn't specify */
3100
 
      free(direntries);
3101
 
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
3102
 
      if(dret == -1 and errno != ENOENT){
3103
 
        perror_plus("rmdir");
3104
 
      }
3105
 
    } else {
3106
 
      perror_plus("scandirat");
3107
 
    }
3108
 
    close(dir_fd);
3109
 
  }
3110
 
  
3111
2621
  /* Removes the GPGME temp directory and all files inside */
3112
2622
  if(tempdir != NULL){
3113
 
    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
    }
3114
2664
  }
3115
2665
  
3116
2666
  if(quit_now){