/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: 2015-05-23 20:18:34 UTC
  • mto: This revision was merged to the branch mainline in revision 756.
  • Revision ID: teddy@recompile.se-20150523201834-e89ex4ito93yni8x
mandos: Use multiprocessing module to run checkers.

For a long time, the Mandos server has occasionally logged the message
"ERROR: Child process vanished".  This was never a fatal error, but it
has been annoying and slightly worrying, since a definite cause was
not found.  One potential cause could be the "multiprocessing" and
"subprocess" modules conflicting w.r.t. SIGCHLD.  To avoid this,
change the running of checkers from using subprocess.Popen
asynchronously to instead first create a multiprocessing.Process()
(which is asynchronous) calling a function, and have that function
then call subprocess.call() (which is synchronous).  In this way, the
only thing using any asynchronous subprocesses is the multiprocessing
module.

This makes it necessary to change one small thing in the D-Bus API,
since the subprocesses.call() function does not expose the raw wait(2)
status value.

DBUS-API (CheckerCompleted): Change the second value provided by this
                             D-Bus signal from the raw wait(2) status
                             to the actual terminating signal number.
mandos (subprocess_call_pipe): New function to be called by
                               multiprocessing.Process (starting a
                               separate process).
(Client.last_checker signal): New attribute for signal which
                              terminated last checker.  Like
                              last_checker_status, only not accessible
                              via D-Bus.
(Client.checker_callback): Take new "connection" argument and use it
                           to get returncode; set last_checker_signal.
                           Return False so gobject does not call this
                           callback again.
(Client.start_checker): Start checker using a multiprocessing.Process
                        instead of a subprocess.Popen.
(ClientDBus.checker_callback): Take new "connection" argument.        Call
                               Client.checker_callback early to have
                               it set last_checker_status and
                               last_checker_signal; use those.  Change
                               second value provided to D-Bus signal
                               CheckerCompleted to use
                               last_checker_signal if checker was
                               terminated by signal.
mandos-monitor: Update to reflect DBus API change.
(MandosClientWidget.checker_completed): Take "signal" instead of
                                        "condition" argument.  Use it
                                        accordingly.  Remove dead code
                                        (os.WCOREDUMP case).

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() */
 
49
#include <string.h>             /* memset(), strcmp(), strlen(),
 
50
                                   strerror(), asprintf(), strcpy() */
51
51
#include <sys/ioctl.h>          /* ioctl */
52
52
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
53
53
                                   sockaddr_in6, PF_INET6,
57
57
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
58
58
                                   inet_pton(), connect(),
59
59
                                   getnameinfo() */
60
 
#include <fcntl.h>              /* open(), unlinkat(), AT_REMOVEDIR */
 
60
#include <fcntl.h>              /* open(), unlinkat() */
61
61
#include <dirent.h>             /* opendir(), struct dirent, readdir()
62
62
                                 */
63
63
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
64
64
                                   strtoimax() */
65
 
#include <errno.h>              /* perror(), errno, EINTR, EINVAL,
66
 
                                   EAI_SYSTEM, ENETUNREACH,
67
 
                                   EHOSTUNREACH, ECONNREFUSED, EPROTO,
68
 
                                   EIO, ENOENT, ENXIO, ENOMEM, EISDIR,
69
 
                                   ENOTEMPTY,
 
65
#include <errno.h>              /* perror(), errno,
70
66
                                   program_invocation_short_name */
71
67
#include <time.h>               /* nanosleep(), time(), sleep() */
72
68
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
309
305
      return false;
310
306
    }
311
307
    
312
 
    ret = close(fd);
 
308
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
313
309
    if(ret == -1){
314
310
      perror_plus("close");
315
311
    }
517
513
  fprintf_plus(stderr, "GnuTLS: %s", string);
518
514
}
519
515
 
520
 
__attribute__((nonnull(1, 2, 4), warn_unused_result))
 
516
__attribute__((nonnull, warn_unused_result))
521
517
static int init_gnutls_global(const char *pubkeyfilename,
522
518
                              const char *seckeyfilename,
523
 
                              const char *dhparamsfilename,
524
519
                              mandos_context *mc){
525
520
  int ret;
526
521
  unsigned int uret;
529
524
    fprintf_plus(stderr, "Initializing GnuTLS\n");
530
525
  }
531
526
  
 
527
  ret = gnutls_global_init();
 
528
  if(ret != GNUTLS_E_SUCCESS){
 
529
    fprintf_plus(stderr, "GnuTLS global_init: %s\n",
 
530
                 safer_gnutls_strerror(ret));
 
531
    return -1;
 
532
  }
 
533
  
532
534
  if(debug){
533
535
    /* "Use a log level over 10 to enable all debugging options."
534
536
     * - GnuTLS manual
542
544
  if(ret != GNUTLS_E_SUCCESS){
543
545
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
544
546
                 safer_gnutls_strerror(ret));
 
547
    gnutls_global_deinit();
545
548
    return -1;
546
549
  }
547
550
  
572
575
                 safer_gnutls_strerror(ret));
573
576
    goto globalfail;
574
577
  }
575
 
  /* If a Diffie-Hellman parameters file was given, try to use it */
576
 
  if(dhparamsfilename != NULL){
577
 
    gnutls_datum_t params = { .data = NULL, .size = 0 };
578
 
    do {
579
 
      int dhpfile = open(dhparamsfilename, O_RDONLY);
580
 
      if(dhpfile == -1){
581
 
        perror_plus("open");
582
 
        dhparamsfilename = NULL;
583
 
        break;
584
 
      }
585
 
      size_t params_capacity = 0;
 
578
  if(mc->dh_bits == 0){
 
579
    /* Find out the optimal number of DH bits */
 
580
    /* Try to read the private key file */
 
581
    gnutls_datum_t buffer = { .data = NULL, .size = 0 };
 
582
    {
 
583
      int secfile = open(seckeyfilename, O_RDONLY);
 
584
      size_t buffer_capacity = 0;
586
585
      while(true){
587
 
        params_capacity = incbuffer((char **)&params.data,
588
 
                                    (size_t)params.size,
589
 
                                    (size_t)params_capacity);
590
 
        if(params_capacity == 0){
 
586
        buffer_capacity = incbuffer((char **)&buffer.data,
 
587
                                    (size_t)buffer.size,
 
588
                                    (size_t)buffer_capacity);
 
589
        if(buffer_capacity == 0){
591
590
          perror_plus("incbuffer");
592
 
          free(params.data);
593
 
          params.data = NULL;
594
 
          dhparamsfilename = NULL;
 
591
          free(buffer.data);
 
592
          buffer.data = NULL;
595
593
          break;
596
594
        }
597
 
        ssize_t bytes_read = read(dhpfile,
598
 
                                  params.data + params.size,
 
595
        ssize_t bytes_read = read(secfile, buffer.data + buffer.size,
599
596
                                  BUFFER_SIZE);
600
597
        /* EOF */
601
598
        if(bytes_read == 0){
604
601
        /* check bytes_read for failure */
605
602
        if(bytes_read < 0){
606
603
          perror_plus("read");
607
 
          free(params.data);
608
 
          params.data = NULL;
609
 
          dhparamsfilename = NULL;
610
 
          break;
611
 
        }
612
 
        params.size += (unsigned int)bytes_read;
613
 
      }
614
 
      if(params.data == NULL){
615
 
        dhparamsfilename = NULL;
616
 
      }
617
 
      if(dhparamsfilename == NULL){
618
 
        break;
619
 
      }
620
 
      ret = gnutls_dh_params_import_pkcs3(mc->dh_params, &params,
621
 
                                          GNUTLS_X509_FMT_PEM);
622
 
      if(ret != GNUTLS_E_SUCCESS){
623
 
        fprintf_plus(stderr, "Failed to parse DH parameters in file"
624
 
                     " \"%s\": %s\n", dhparamsfilename,
625
 
                     safer_gnutls_strerror(ret));
626
 
        dhparamsfilename = NULL;
627
 
      }
628
 
    } while(false);
629
 
  }
630
 
  if(dhparamsfilename == NULL){
631
 
    if(mc->dh_bits == 0){
632
 
      /* Find out the optimal number of DH bits */
633
 
      /* Try to read the private key file */
634
 
      gnutls_datum_t buffer = { .data = NULL, .size = 0 };
635
 
      do {
636
 
        int secfile = open(seckeyfilename, O_RDONLY);
637
 
        if(secfile == -1){
638
 
          perror_plus("open");
639
 
          break;
640
 
        }
641
 
        size_t buffer_capacity = 0;
642
 
        while(true){
643
 
          buffer_capacity = incbuffer((char **)&buffer.data,
644
 
                                      (size_t)buffer.size,
645
 
                                      (size_t)buffer_capacity);
646
 
          if(buffer_capacity == 0){
647
 
            perror_plus("incbuffer");
648
 
            free(buffer.data);
649
 
            buffer.data = NULL;
650
 
            break;
651
 
          }
652
 
          ssize_t bytes_read = read(secfile,
653
 
                                    buffer.data + buffer.size,
654
 
                                    BUFFER_SIZE);
655
 
          /* EOF */
656
 
          if(bytes_read == 0){
657
 
            break;
658
 
          }
659
 
          /* check bytes_read for failure */
660
 
          if(bytes_read < 0){
661
 
            perror_plus("read");
662
 
            free(buffer.data);
663
 
            buffer.data = NULL;
664
 
            break;
665
 
          }
666
 
          buffer.size += (unsigned int)bytes_read;
667
 
        }
668
 
        close(secfile);
669
 
      } while(false);
670
 
      /* If successful, use buffer to parse private key */
671
 
      gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
672
 
      if(buffer.data != NULL){
673
 
        {
674
 
          gnutls_openpgp_privkey_t privkey = NULL;
675
 
          ret = gnutls_openpgp_privkey_init(&privkey);
 
604
          free(buffer.data);
 
605
          buffer.data = NULL;
 
606
          break;
 
607
        }
 
608
        buffer.size += (unsigned int)bytes_read;
 
609
      }
 
610
      close(secfile);
 
611
    }
 
612
    /* If successful, use buffer to parse private key */
 
613
    gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
614
    if(buffer.data != NULL){
 
615
      {
 
616
        gnutls_openpgp_privkey_t privkey = NULL;
 
617
        ret = gnutls_openpgp_privkey_init(&privkey);
 
618
        if(ret != GNUTLS_E_SUCCESS){
 
619
          fprintf_plus(stderr, "Error initializing OpenPGP key"
 
620
                       " structure: %s", safer_gnutls_strerror(ret));
 
621
          free(buffer.data);
 
622
          buffer.data = NULL;
 
623
        } else {
 
624
          ret = gnutls_openpgp_privkey_import(privkey, &buffer,
 
625
                                            GNUTLS_OPENPGP_FMT_BASE64,
 
626
                                              "", 0);
676
627
          if(ret != GNUTLS_E_SUCCESS){
677
 
            fprintf_plus(stderr, "Error initializing OpenPGP key"
678
 
                         " structure: %s",
 
628
            fprintf_plus(stderr, "Error importing OpenPGP key : %s",
679
629
                         safer_gnutls_strerror(ret));
680
 
            free(buffer.data);
681
 
            buffer.data = NULL;
682
 
          } else {
683
 
            ret = gnutls_openpgp_privkey_import
684
 
              (privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
685
 
            if(ret != GNUTLS_E_SUCCESS){
686
 
              fprintf_plus(stderr, "Error importing OpenPGP key : %s",
687
 
                           safer_gnutls_strerror(ret));
688
 
              privkey = NULL;
689
 
            }
690
 
            free(buffer.data);
691
 
            buffer.data = NULL;
692
 
            if(privkey != NULL){
693
 
              /* Use private key to suggest an appropriate
694
 
                 sec_param */
695
 
              sec_param = gnutls_openpgp_privkey_sec_param(privkey);
696
 
              gnutls_openpgp_privkey_deinit(privkey);
697
 
              if(debug){
698
 
                fprintf_plus(stderr, "This OpenPGP key implies using"
699
 
                             " a GnuTLS security parameter \"%s\".\n",
700
 
                             safe_string(gnutls_sec_param_get_name
701
 
                                         (sec_param)));
702
 
              }
703
 
            }
 
630
            privkey = NULL;
704
631
          }
705
 
        }
706
 
        if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
707
 
          /* Err on the side of caution */
708
 
          sec_param = GNUTLS_SEC_PARAM_ULTRA;
709
 
          if(debug){
710
 
            fprintf_plus(stderr, "Falling back to security parameter"
711
 
                         " \"%s\"\n",
712
 
                         safe_string(gnutls_sec_param_get_name
713
 
                                     (sec_param)));
 
632
          free(buffer.data);
 
633
          buffer.data = NULL;
 
634
          if(privkey != NULL){
 
635
            /* Use private key to suggest an appropriate sec_param */
 
636
            sec_param = gnutls_openpgp_privkey_sec_param(privkey);
 
637
            gnutls_openpgp_privkey_deinit(privkey);
 
638
            if(debug){
 
639
              fprintf_plus(stderr, "This OpenPGP key implies using a"
 
640
                           " GnuTLS security parameter \"%s\".\n",
 
641
                           safe_string(gnutls_sec_param_get_name
 
642
                                       (sec_param)));
 
643
            }
714
644
          }
715
645
        }
716
646
      }
717
 
      uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
718
 
      if(uret != 0){
719
 
        mc->dh_bits = uret;
 
647
      if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
 
648
        /* Err on the side of caution */
 
649
        sec_param = GNUTLS_SEC_PARAM_ULTRA;
720
650
        if(debug){
721
 
          fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
722
 
                       " implies %u DH bits; using that.\n",
 
651
          fprintf_plus(stderr, "Falling back to security parameter"
 
652
                       " \"%s\"\n",
723
653
                       safe_string(gnutls_sec_param_get_name
724
 
                                   (sec_param)),
725
 
                       mc->dh_bits);
 
654
                                   (sec_param)));
726
655
        }
727
 
      } else {
728
 
        fprintf_plus(stderr, "Failed to get implied number of DH"
729
 
                     " bits for security parameter \"%s\"): %s\n",
 
656
      }
 
657
    }
 
658
    uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
 
659
    if(uret != 0){
 
660
      mc->dh_bits = uret;
 
661
      if(debug){
 
662
        fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
 
663
                     " implies %u DH bits; using that.\n",
730
664
                     safe_string(gnutls_sec_param_get_name
731
665
                                 (sec_param)),
732
 
                     safer_gnutls_strerror(ret));
733
 
        goto globalfail;
 
666
                     mc->dh_bits);
734
667
      }
735
 
    } else if(debug){
736
 
      fprintf_plus(stderr, "DH bits explicitly set to %u\n",
737
 
                   mc->dh_bits);
738
 
    }
739
 
    ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
740
 
    if(ret != GNUTLS_E_SUCCESS){
741
 
      fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
742
 
                   " bits): %s\n", mc->dh_bits,
 
668
    } else {
 
669
      fprintf_plus(stderr, "Failed to get implied number of DH"
 
670
                   " bits for security parameter \"%s\"): %s\n",
 
671
                   safe_string(gnutls_sec_param_get_name(sec_param)),
743
672
                   safer_gnutls_strerror(ret));
744
673
      goto globalfail;
745
674
    }
746
 
  }
 
675
  } else if(debug){
 
676
    fprintf_plus(stderr, "DH bits explicitly set to %u\n",
 
677
                 mc->dh_bits);
 
678
  }
 
679
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
 
680
  if(ret != GNUTLS_E_SUCCESS){
 
681
    fprintf_plus(stderr, "Error in GnuTLS prime generation (%u bits):"
 
682
                 " %s\n", mc->dh_bits, safer_gnutls_strerror(ret));
 
683
    goto globalfail;
 
684
  }
 
685
  
747
686
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
748
687
  
749
688
  return 0;
751
690
 globalfail:
752
691
  
753
692
  gnutls_certificate_free_credentials(mc->cred);
 
693
  gnutls_global_deinit();
754
694
  gnutls_dh_params_deinit(mc->dh_params);
755
695
  return -1;
756
696
}
808
748
  /* ignore client certificate if any. */
809
749
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
810
750
  
 
751
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
 
752
  
811
753
  return 0;
812
754
}
813
755
 
815
757
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
816
758
                      __attribute__((unused)) const char *txt){}
817
759
 
818
 
/* Set effective uid to 0, return errno */
819
 
__attribute__((warn_unused_result))
820
 
error_t raise_privileges(void){
821
 
  error_t old_errno = errno;
822
 
  error_t ret_errno = 0;
823
 
  if(seteuid(0) == -1){
824
 
    ret_errno = errno;
825
 
  }
826
 
  errno = old_errno;
827
 
  return ret_errno;
828
 
}
829
 
 
830
 
/* Set effective and real user ID to 0.  Return errno. */
831
 
__attribute__((warn_unused_result))
832
 
error_t raise_privileges_permanently(void){
833
 
  error_t old_errno = errno;
834
 
  error_t ret_errno = raise_privileges();
835
 
  if(ret_errno != 0){
836
 
    errno = old_errno;
837
 
    return ret_errno;
838
 
  }
839
 
  if(setuid(0) == -1){
840
 
    ret_errno = errno;
841
 
  }
842
 
  errno = old_errno;
843
 
  return ret_errno;
844
 
}
845
 
 
846
 
/* Set effective user ID to unprivileged saved user ID */
847
 
__attribute__((warn_unused_result))
848
 
error_t lower_privileges(void){
849
 
  error_t old_errno = errno;
850
 
  error_t ret_errno = 0;
851
 
  if(seteuid(uid) == -1){
852
 
    ret_errno = errno;
853
 
  }
854
 
  errno = old_errno;
855
 
  return ret_errno;
856
 
}
857
 
 
858
 
/* Lower privileges permanently */
859
 
__attribute__((warn_unused_result))
860
 
error_t lower_privileges_permanently(void){
861
 
  error_t old_errno = errno;
862
 
  error_t ret_errno = 0;
863
 
  if(setuid(uid) == -1){
864
 
    ret_errno = errno;
865
 
  }
866
 
  errno = old_errno;
867
 
  return ret_errno;
868
 
}
869
 
 
870
 
/* Helper function to add_local_route() and delete_local_route() */
871
 
__attribute__((nonnull, warn_unused_result))
872
 
static bool add_delete_local_route(const bool add,
873
 
                                   const char *address,
874
 
                                   AvahiIfIndex if_index){
875
 
  int ret;
876
 
  char helper[] = "mandos-client-iprouteadddel";
877
 
  char add_arg[] = "add";
878
 
  char delete_arg[] = "delete";
879
 
  char debug_flag[] = "--debug";
880
 
  char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
881
 
  if(pluginhelperdir == NULL){
882
 
    if(debug){
883
 
      fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
884
 
                   " variable not set; cannot run helper\n");
885
 
    }
886
 
    return false;
887
 
  }
888
 
  
889
 
  char interface[IF_NAMESIZE];
890
 
  if(if_indextoname((unsigned int)if_index, interface) == NULL){
891
 
    perror_plus("if_indextoname");
892
 
    return false;
893
 
  }
894
 
  
895
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
896
 
  if(devnull == -1){
897
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
898
 
    return false;
899
 
  }
900
 
  pid_t pid = fork();
901
 
  if(pid == 0){
902
 
    /* Child */
903
 
    /* Raise privileges */
904
 
    errno = raise_privileges_permanently();
905
 
    if(errno != 0){
906
 
      perror_plus("Failed to raise privileges");
907
 
      /* _exit(EX_NOPERM); */
908
 
    } else {
909
 
      /* Set group */
910
 
      errno = 0;
911
 
      ret = setgid(0);
912
 
      if(ret == -1){
913
 
        perror_plus("setgid");
914
 
        _exit(EX_NOPERM);
915
 
      }
916
 
      /* Reset supplementary groups */
917
 
      errno = 0;
918
 
      ret = setgroups(0, NULL);
919
 
      if(ret == -1){
920
 
        perror_plus("setgroups");
921
 
        _exit(EX_NOPERM);
922
 
      }
923
 
    }
924
 
    ret = dup2(devnull, STDIN_FILENO);
925
 
    if(ret == -1){
926
 
      perror_plus("dup2(devnull, STDIN_FILENO)");
927
 
      _exit(EX_OSERR);
928
 
    }
929
 
    ret = close(devnull);
930
 
    if(ret == -1){
931
 
      perror_plus("close");
932
 
      _exit(EX_OSERR);
933
 
    }
934
 
    ret = dup2(STDERR_FILENO, STDOUT_FILENO);
935
 
    if(ret == -1){
936
 
      perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
937
 
      _exit(EX_OSERR);
938
 
    }
939
 
    int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
940
 
                                                    O_RDONLY
941
 
                                                    | O_DIRECTORY
942
 
                                                    | O_PATH
943
 
                                                    | O_CLOEXEC));
944
 
    if(helperdir_fd == -1){
945
 
      perror_plus("open");
946
 
      _exit(EX_UNAVAILABLE);
947
 
    }
948
 
    int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
949
 
                                                   helper, O_RDONLY));
950
 
    if(helper_fd == -1){
951
 
      perror_plus("openat");
952
 
      close(helperdir_fd);
953
 
      _exit(EX_UNAVAILABLE);
954
 
    }
955
 
    close(helperdir_fd);
956
 
#ifdef __GNUC__
957
 
#pragma GCC diagnostic push
958
 
#pragma GCC diagnostic ignored "-Wcast-qual"
959
 
#endif
960
 
    if(fexecve(helper_fd, (char *const [])
961
 
               { helper, add ? add_arg : delete_arg, (char *)address,
962
 
                   interface, debug ? debug_flag : NULL, NULL },
963
 
               environ) == -1){
964
 
#ifdef __GNUC__
965
 
#pragma GCC diagnostic pop
966
 
#endif
967
 
      perror_plus("fexecve");
968
 
      _exit(EXIT_FAILURE);
969
 
    }
970
 
  }
971
 
  if(pid == -1){
972
 
    perror_plus("fork");
973
 
    return false;
974
 
  }
975
 
  int status;
976
 
  pid_t pret = -1;
977
 
  errno = 0;
978
 
  do {
979
 
    pret = waitpid(pid, &status, 0);
980
 
    if(pret == -1 and errno == EINTR and quit_now){
981
 
      int errno_raising = 0;
982
 
      if((errno = raise_privileges()) != 0){
983
 
        errno_raising = errno;
984
 
        perror_plus("Failed to raise privileges in order to"
985
 
                    " kill helper program");
986
 
      }
987
 
      if(kill(pid, SIGTERM) == -1){
988
 
        perror_plus("kill");
989
 
      }
990
 
      if((errno_raising == 0) and (errno = lower_privileges()) != 0){
991
 
        perror_plus("Failed to lower privileges after killing"
992
 
                    " helper program");
993
 
      }
994
 
      return false;
995
 
    }
996
 
  } while(pret == -1 and errno == EINTR);
997
 
  if(pret == -1){
998
 
    perror_plus("waitpid");
999
 
    return false;
1000
 
  }
1001
 
  if(WIFEXITED(status)){
1002
 
    if(WEXITSTATUS(status) != 0){
1003
 
      fprintf_plus(stderr, "Error: iprouteadddel exited"
1004
 
                   " with status %d\n", WEXITSTATUS(status));
1005
 
      return false;
1006
 
    }
1007
 
    return true;
1008
 
  }
1009
 
  if(WIFSIGNALED(status)){
1010
 
    fprintf_plus(stderr, "Error: iprouteadddel died by"
1011
 
                 " signal %d\n", WTERMSIG(status));
1012
 
    return false;
1013
 
  }
1014
 
  fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1015
 
  return false;
1016
 
}
1017
 
 
1018
 
__attribute__((nonnull, warn_unused_result))
1019
 
static bool add_local_route(const char *address,
1020
 
                            AvahiIfIndex if_index){
1021
 
  if(debug){
1022
 
    fprintf_plus(stderr, "Adding route to %s\n", address);
1023
 
  }
1024
 
  return add_delete_local_route(true, address, if_index);
1025
 
}
1026
 
 
1027
 
__attribute__((nonnull, warn_unused_result))
1028
 
static bool delete_local_route(const char *address,
1029
 
                               AvahiIfIndex if_index){
1030
 
  if(debug){
1031
 
    fprintf_plus(stderr, "Removing route to %s\n", address);
1032
 
  }
1033
 
  return add_delete_local_route(false, address, if_index);
1034
 
}
1035
 
 
1036
760
/* Called when a Mandos server is found */
1037
761
__attribute__((nonnull, warn_unused_result))
1038
762
static int start_mandos_communication(const char *ip, in_port_t port,
1049
773
  int retval = -1;
1050
774
  gnutls_session_t session;
1051
775
  int pf;                       /* Protocol family */
1052
 
  bool route_added = false;
1053
776
  
1054
777
  errno = 0;
1055
778
  
1113
836
                 PRIuMAX "\n", ip, (uintmax_t)port);
1114
837
  }
1115
838
  
1116
 
  tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
 
839
  tcp_sd = socket(pf, SOCK_STREAM, 0);
1117
840
  if(tcp_sd < 0){
1118
841
    int e = errno;
1119
842
    perror_plus("socket");
1126
849
    goto mandos_end;
1127
850
  }
1128
851
  
 
852
  memset(&to, 0, sizeof(to));
1129
853
  if(af == AF_INET6){
1130
 
    struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1131
 
    *to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1132
 
    ret = inet_pton(af, ip, &to6->sin6_addr);
 
854
    ((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
 
855
    ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
1133
856
  } else {                      /* IPv4 */
1134
 
    struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1135
 
    *to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1136
 
    ret = inet_pton(af, ip, &to4->sin_addr);
 
857
    ((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
 
858
    ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
1137
859
  }
1138
860
  if(ret < 0 ){
1139
861
    int e = errno;
1209
931
    goto mandos_end;
1210
932
  }
1211
933
  
1212
 
  while(true){
1213
 
    if(af == AF_INET6){
1214
 
      ret = connect(tcp_sd, (struct sockaddr *)&to,
1215
 
                    sizeof(struct sockaddr_in6));
1216
 
    } else {
1217
 
      ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1218
 
                    sizeof(struct sockaddr_in));
1219
 
    }
1220
 
    if(ret < 0){
1221
 
      if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1222
 
         and if_index != AVAHI_IF_UNSPEC
1223
 
         and connect_to == NULL
1224
 
         and not route_added and
1225
 
         ((af == AF_INET6 and not
1226
 
           IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1227
 
                                    &to)->sin6_addr)))
1228
 
          or (af == AF_INET and
1229
 
              /* Not a a IPv4LL address */
1230
 
              (ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1231
 
               & 0xFFFF0000L) != 0xA9FE0000L))){
1232
 
        /* Work around Avahi bug - Avahi does not announce link-local
1233
 
           addresses if it has a global address, so local hosts with
1234
 
           *only* a link-local address (e.g. Mandos clients) cannot
1235
 
           connect to a Mandos server announced by Avahi on a server
1236
 
           host with a global address.  Work around this by retrying
1237
 
           with an explicit route added with the server's address.
1238
 
           
1239
 
           Avahi bug reference:
1240
 
           http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1241
 
           https://bugs.debian.org/587961
1242
 
        */
1243
 
        if(debug){
1244
 
          fprintf_plus(stderr, "Mandos server unreachable, trying"
1245
 
                       " direct route\n");
1246
 
        }
1247
 
        int e = errno;
1248
 
        route_added = add_local_route(ip, if_index);
1249
 
        if(route_added){
1250
 
          continue;
1251
 
        }
1252
 
        errno = e;
1253
 
      }
1254
 
      if(errno != ECONNREFUSED or debug){
1255
 
        int e = errno;
1256
 
        perror_plus("connect");
1257
 
        errno = e;
1258
 
      }
1259
 
      goto mandos_end;
1260
 
    }
1261
 
    
1262
 
    if(quit_now){
1263
 
      errno = EINTR;
1264
 
      goto mandos_end;
1265
 
    }
1266
 
    break;
 
934
  if(af == AF_INET6){
 
935
    ret = connect(tcp_sd, (struct sockaddr *)&to,
 
936
                  sizeof(struct sockaddr_in6));
 
937
  } else {
 
938
    ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
 
939
                  sizeof(struct sockaddr_in));
 
940
  }
 
941
  if(ret < 0){
 
942
    if((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
 
943
      int e = errno;
 
944
      perror_plus("connect");
 
945
      errno = e;
 
946
    }
 
947
    goto mandos_end;
 
948
  }
 
949
  
 
950
  if(quit_now){
 
951
    errno = EINTR;
 
952
    goto mandos_end;
1267
953
  }
1268
954
  
1269
955
  const char *out = mandos_protocol_version;
1452
1138
  
1453
1139
 mandos_end:
1454
1140
  {
1455
 
    if(route_added){
1456
 
      if(not delete_local_route(ip, if_index)){
1457
 
        fprintf_plus(stderr, "Failed to delete local route to %s on"
1458
 
                     " interface %d", ip, if_index);
1459
 
      }
1460
 
    }
1461
1141
    int e = errno;
1462
1142
    free(decrypted_buffer);
1463
1143
    free(buffer);
1464
1144
    if(tcp_sd >= 0){
1465
 
      ret = close(tcp_sd);
 
1145
      ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
1466
1146
    }
1467
1147
    if(ret == -1){
1468
1148
      if(e == 0){
1632
1312
    errno = ret_errno;
1633
1313
    return false;
1634
1314
  }
1635
 
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1636
 
  ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
 
1315
  strcpy(ifr->ifr_name, ifname);
1637
1316
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1638
1317
  if(ret == -1){
1639
1318
    if(debug){
1890
1569
  }
1891
1570
}
1892
1571
 
 
1572
/* Set effective uid to 0, return errno */
 
1573
__attribute__((warn_unused_result))
 
1574
error_t raise_privileges(void){
 
1575
  error_t old_errno = errno;
 
1576
  error_t ret_errno = 0;
 
1577
  if(seteuid(0) == -1){
 
1578
    ret_errno = errno;
 
1579
  }
 
1580
  errno = old_errno;
 
1581
  return ret_errno;
 
1582
}
 
1583
 
 
1584
/* Set effective and real user ID to 0.  Return errno. */
 
1585
__attribute__((warn_unused_result))
 
1586
error_t raise_privileges_permanently(void){
 
1587
  error_t old_errno = errno;
 
1588
  error_t ret_errno = raise_privileges();
 
1589
  if(ret_errno != 0){
 
1590
    errno = old_errno;
 
1591
    return ret_errno;
 
1592
  }
 
1593
  if(setuid(0) == -1){
 
1594
    ret_errno = errno;
 
1595
  }
 
1596
  errno = old_errno;
 
1597
  return ret_errno;
 
1598
}
 
1599
 
 
1600
/* Set effective user ID to unprivileged saved user ID */
 
1601
__attribute__((warn_unused_result))
 
1602
error_t lower_privileges(void){
 
1603
  error_t old_errno = errno;
 
1604
  error_t ret_errno = 0;
 
1605
  if(seteuid(uid) == -1){
 
1606
    ret_errno = errno;
 
1607
  }
 
1608
  errno = old_errno;
 
1609
  return ret_errno;
 
1610
}
 
1611
 
 
1612
/* Lower privileges permanently */
 
1613
__attribute__((warn_unused_result))
 
1614
error_t lower_privileges_permanently(void){
 
1615
  error_t old_errno = errno;
 
1616
  error_t ret_errno = 0;
 
1617
  if(setuid(uid) == -1){
 
1618
    ret_errno = errno;
 
1619
  }
 
1620
  errno = old_errno;
 
1621
  return ret_errno;
 
1622
}
 
1623
 
1893
1624
__attribute__((nonnull))
1894
1625
void run_network_hooks(const char *mode, const char *interface,
1895
1626
                       const float delay){
1896
1627
  struct dirent **direntries = NULL;
1897
1628
  if(hookdir_fd == -1){
1898
 
    hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
1899
 
                      | O_CLOEXEC);
 
1629
    hookdir_fd = open(hookdir, O_RDONLY);
1900
1630
    if(hookdir_fd == -1){
1901
1631
      if(errno == ENOENT){
1902
1632
        if(debug){
1909
1639
      return;
1910
1640
    }
1911
1641
  }
 
1642
#ifdef __GLIBC__
 
1643
#if __GLIBC_PREREQ(2, 15)
1912
1644
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
1913
1645
                           runnable_hook, alphasort);
 
1646
#else  /* not __GLIBC_PREREQ(2, 15) */
 
1647
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
1648
                         alphasort);
 
1649
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
1650
#else   /* not __GLIBC__ */
 
1651
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
1652
                         alphasort);
 
1653
#endif  /* not __GLIBC__ */
1914
1654
  if(numhooks == -1){
1915
1655
    perror_plus("scandir");
1916
1656
    return;
1917
1657
  }
1918
1658
  struct dirent *direntry;
1919
1659
  int ret;
1920
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1921
 
  if(devnull == -1){
1922
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
1923
 
    return;
1924
 
  }
 
1660
  int devnull = open("/dev/null", O_RDONLY);
1925
1661
  for(int i = 0; i < numhooks; i++){
1926
1662
    direntry = direntries[i];
1927
1663
    if(debug){
1951
1687
        perror_plus("setgroups");
1952
1688
        _exit(EX_NOPERM);
1953
1689
      }
 
1690
      ret = dup2(devnull, STDIN_FILENO);
 
1691
      if(ret == -1){
 
1692
        perror_plus("dup2(devnull, STDIN_FILENO)");
 
1693
        _exit(EX_OSERR);
 
1694
      }
 
1695
      ret = close(devnull);
 
1696
      if(ret == -1){
 
1697
        perror_plus("close");
 
1698
        _exit(EX_OSERR);
 
1699
      }
 
1700
      ret = dup2(STDERR_FILENO, STDOUT_FILENO);
 
1701
      if(ret == -1){
 
1702
        perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
 
1703
        _exit(EX_OSERR);
 
1704
      }
1954
1705
      ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1955
1706
      if(ret == -1){
1956
1707
        perror_plus("setenv");
1991
1742
          _exit(EX_OSERR);
1992
1743
        }
1993
1744
      }
1994
 
      int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
1995
 
                                                   direntry->d_name,
1996
 
                                                   O_RDONLY));
 
1745
      int hook_fd = openat(hookdir_fd, direntry->d_name, O_RDONLY);
1997
1746
      if(hook_fd == -1){
1998
1747
        perror_plus("openat");
1999
1748
        _exit(EXIT_FAILURE);
2000
1749
      }
2001
 
      if(close(hookdir_fd) == -1){
 
1750
      if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
2002
1751
        perror_plus("close");
2003
1752
        _exit(EXIT_FAILURE);
2004
1753
      }
2005
 
      ret = dup2(devnull, STDIN_FILENO);
2006
 
      if(ret == -1){
2007
 
        perror_plus("dup2(devnull, STDIN_FILENO)");
2008
 
        _exit(EX_OSERR);
2009
 
      }
2010
 
      ret = close(devnull);
2011
 
      if(ret == -1){
2012
 
        perror_plus("close");
2013
 
        _exit(EX_OSERR);
2014
 
      }
2015
 
      ret = dup2(STDERR_FILENO, STDOUT_FILENO);
2016
 
      if(ret == -1){
2017
 
        perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
2018
 
        _exit(EX_OSERR);
2019
 
      }
2020
1754
      if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
2021
1755
                 environ) == -1){
2022
1756
        perror_plus("fexecve");
2062
1796
    free(direntry);
2063
1797
  }
2064
1798
  free(direntries);
2065
 
  if(close(hookdir_fd) == -1){
 
1799
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
2066
1800
    perror_plus("close");
2067
1801
  } else {
2068
1802
    hookdir_fd = -1;
2108
1842
    }
2109
1843
    
2110
1844
    if(quit_now){
2111
 
      ret = close(sd);
 
1845
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
2112
1846
      if(ret == -1){
2113
1847
        perror_plus("close");
2114
1848
      }
2164
1898
    }
2165
1899
    
2166
1900
    /* Close the socket */
2167
 
    ret = close(sd);
 
1901
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
2168
1902
    if(ret == -1){
2169
1903
      perror_plus("close");
2170
1904
    }
2252
1986
    }
2253
1987
    
2254
1988
    /* Close the socket */
2255
 
    int ret = close(sd);
 
1989
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
2256
1990
    if(ret == -1){
2257
1991
      perror_plus("close");
2258
1992
    }
2274
2008
 
2275
2009
int main(int argc, char *argv[]){
2276
2010
  mandos_context mc = { .server = NULL, .dh_bits = 0,
2277
 
                        .priority = "SECURE256:!CTYPE-X.509"
2278
 
                        ":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
2279
 
                        .current_server = NULL, .interfaces = NULL,
2280
 
                        .interfaces_size = 0 };
 
2011
                        .priority = "SECURE256:!CTYPE-X.509:"
 
2012
                        "+CTYPE-OPENPGP:!RSA", .current_server = NULL,
 
2013
                        .interfaces = NULL, .interfaces_size = 0 };
2281
2014
  AvahiSServiceBrowser *sb = NULL;
2282
2015
  error_t ret_errno;
2283
2016
  int ret;
2292
2025
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2293
2026
  const char *seckey = PATHDIR "/" SECKEY;
2294
2027
  const char *pubkey = PATHDIR "/" PUBKEY;
2295
 
  const char *dh_params_file = NULL;
2296
2028
  char *interfaces_hooks = NULL;
2297
2029
  
2298
2030
  bool gnutls_initialized = false;
2351
2083
        .doc = "Bit length of the prime number used in the"
2352
2084
        " Diffie-Hellman key exchange",
2353
2085
        .group = 2 },
2354
 
      { .name = "dh-params", .key = 134,
2355
 
        .arg = "FILE",
2356
 
        .doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
2357
 
        " for the Diffie-Hellman key exchange",
2358
 
        .group = 2 },
2359
2086
      { .name = "priority", .key = 130,
2360
2087
        .arg = "STRING",
2361
2088
        .doc = "GnuTLS priority string for the TLS handshake",
2416
2143
        }
2417
2144
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2418
2145
        break;
2419
 
      case 134:                 /* --dh-params */
2420
 
        dh_params_file = arg;
2421
 
        break;
2422
2146
      case 130:                 /* --priority */
2423
2147
        mc.priority = arg;
2424
2148
        break;
2480
2204
      goto end;
2481
2205
    }
2482
2206
  }
2483
 
  
 
2207
    
2484
2208
  {
2485
2209
    /* Work around Debian bug #633582:
2486
2210
       <http://bugs.debian.org/633582> */
2510
2234
              }
2511
2235
            }
2512
2236
          }
2513
 
          close(seckey_fd);
 
2237
          TEMP_FAILURE_RETRY(close(seckey_fd));
2514
2238
        }
2515
2239
      }
2516
 
      
 
2240
    
2517
2241
      if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2518
2242
        int pubkey_fd = open(pubkey, O_RDONLY);
2519
2243
        if(pubkey_fd == -1){
2531
2255
              }
2532
2256
            }
2533
2257
          }
2534
 
          close(pubkey_fd);
2535
 
        }
2536
 
      }
2537
 
      
2538
 
      if(dh_params_file != NULL
2539
 
         and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2540
 
        int dhparams_fd = open(dh_params_file, O_RDONLY);
2541
 
        if(dhparams_fd == -1){
2542
 
          perror_plus("open");
2543
 
        } else {
2544
 
          ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
2545
 
          if(ret == -1){
2546
 
            perror_plus("fstat");
2547
 
          } else {
2548
 
            if(S_ISREG(st.st_mode)
2549
 
               and st.st_uid == 0 and st.st_gid == 0){
2550
 
              ret = fchown(dhparams_fd, uid, gid);
2551
 
              if(ret == -1){
2552
 
                perror_plus("fchown");
2553
 
              }
2554
 
            }
2555
 
          }
2556
 
          close(dhparams_fd);
2557
 
        }
2558
 
      }
2559
 
      
 
2258
          TEMP_FAILURE_RETRY(close(pubkey_fd));
 
2259
        }
 
2260
      }
 
2261
    
2560
2262
      /* Lower privileges */
2561
2263
      ret_errno = lower_privileges();
2562
2264
      if(ret_errno != 0){
2736
2438
      errno = bring_up_interface(interface, delay);
2737
2439
      if(not interface_was_up){
2738
2440
        if(errno != 0){
2739
 
          fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
2740
 
                       " %s\n", interface, strerror(errno));
 
2441
          perror_plus("Failed to bring up interface");
2741
2442
        } else {
2742
2443
          errno = argz_add(&interfaces_to_take_down,
2743
2444
                           &interfaces_to_take_down_size,
2766
2467
    goto end;
2767
2468
  }
2768
2469
  
2769
 
  ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
 
2470
  ret = init_gnutls_global(pubkey, seckey, &mc);
2770
2471
  if(ret == -1){
2771
2472
    fprintf_plus(stderr, "init_gnutls_global failed\n");
2772
2473
    exitcode = EX_UNAVAILABLE;
2961
2662
  
2962
2663
  if(gnutls_initialized){
2963
2664
    gnutls_certificate_free_credentials(mc.cred);
 
2665
    gnutls_global_deinit();
2964
2666
    gnutls_dh_params_deinit(mc.dh_params);
2965
2667
  }
2966
2668
  
3028
2730
  free(interfaces_to_take_down);
3029
2731
  free(interfaces_hooks);
3030
2732
  
3031
 
  void clean_dir_at(int base, const char * const dirname,
3032
 
                    uintmax_t level){
3033
 
    struct dirent **direntries = NULL;
3034
 
    int dret;
3035
 
    int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3036
 
                                                O_RDONLY
3037
 
                                                | O_NOFOLLOW
3038
 
                                                | O_DIRECTORY
3039
 
                                                | O_PATH));
3040
 
    if(dir_fd == -1){
3041
 
      perror_plus("open");
3042
 
    }
3043
 
    int numentries = scandirat(dir_fd, ".", &direntries,
3044
 
                               notdotentries, alphasort);
3045
 
    if(numentries >= 0){
3046
 
      for(int i = 0; i < numentries; i++){
3047
 
        if(debug){
3048
 
          fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3049
 
                       dirname, direntries[i]->d_name);
3050
 
        }
3051
 
        dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3052
 
        if(dret == -1){
3053
 
          if(errno == EISDIR){
3054
 
              dret = unlinkat(dir_fd, direntries[i]->d_name,
3055
 
                              AT_REMOVEDIR);
3056
 
          }         
3057
 
          if((dret == -1) and (errno == ENOTEMPTY)
3058
 
             and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3059
 
                  == 0) and (level == 0)){
3060
 
            /* Recurse only in this special case */
3061
 
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3062
 
            dret = 0;
3063
 
          }
3064
 
          if(dret == -1){
3065
 
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3066
 
                         direntries[i]->d_name, strerror(errno));
3067
 
          }
3068
 
        }
3069
 
        free(direntries[i]);
3070
 
      }
3071
 
      
3072
 
      /* need to clean even if 0 because man page doesn't specify */
3073
 
      free(direntries);
3074
 
      if(numentries == -1){
3075
 
        perror_plus("scandirat");
3076
 
      }
3077
 
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
3078
 
      if(dret == -1 and errno != ENOENT){
3079
 
        perror_plus("rmdir");
3080
 
      }
3081
 
    } else {
3082
 
      perror_plus("scandirat");
3083
 
    }
3084
 
    close(dir_fd);
3085
 
  }
3086
 
  
3087
2733
  /* Removes the GPGME temp directory and all files inside */
3088
2734
  if(tempdir != NULL){
3089
 
    clean_dir_at(-1, tempdir, 0);
 
2735
    struct dirent **direntries = NULL;
 
2736
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY |
 
2737
                                                  O_NOFOLLOW));
 
2738
    if(tempdir_fd == -1){
 
2739
      perror_plus("open");
 
2740
    } else {
 
2741
#ifdef __GLIBC__
 
2742
#if __GLIBC_PREREQ(2, 15)
 
2743
      int numentries = scandirat(tempdir_fd, ".", &direntries,
 
2744
                                 notdotentries, alphasort);
 
2745
#else  /* not __GLIBC_PREREQ(2, 15) */
 
2746
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2747
                               alphasort);
 
2748
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
2749
#else   /* not __GLIBC__ */
 
2750
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2751
                               alphasort);
 
2752
#endif  /* not __GLIBC__ */
 
2753
      if(numentries >= 0){
 
2754
        for(int i = 0; i < numentries; i++){
 
2755
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
 
2756
          if(ret == -1){
 
2757
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
 
2758
                         " \"%s\", 0): %s\n", tempdir,
 
2759
                         direntries[i]->d_name, strerror(errno));
 
2760
          }
 
2761
          free(direntries[i]);
 
2762
        }
 
2763
        
 
2764
        /* need to clean even if 0 because man page doesn't specify */
 
2765
        free(direntries);
 
2766
        if(numentries == -1){
 
2767
          perror_plus("scandir");
 
2768
        }
 
2769
        ret = rmdir(tempdir);
 
2770
        if(ret == -1 and errno != ENOENT){
 
2771
          perror_plus("rmdir");
 
2772
        }
 
2773
      }
 
2774
      TEMP_FAILURE_RETRY(close(tempdir_fd));
 
2775
    }
3090
2776
  }
3091
2777
  
3092
2778
  if(quit_now){