/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-07-20 03:03:33 UTC
  • Revision ID: teddy@recompile.se-20150720030333-203m2aeblypcsfte
Bug fix for GnuTLS 3: be compatible with old 2048-bit DSA keys.

The mandos-keygen program in Mandos version 1.6.0 and older generated
2048-bit DSA keys, and when GnuTLS uses these it has trouble
connecting using the Mandos default priority string.  This was
previously fixed in Mandos 1.6.2, but the bug reappeared when using
GnuTLS 3, so the default priority string has to change again; this
time also the Mandos client has to change its default, so now the
server and the client should use the same default priority string:

SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256

* mandos (main/server_defaults): Changed default priority string.
* mandos-options.xml (/section/para[id="priority_compat"]): Removed.
  (/section/para[id="priority"]): Changed default priority string.
* mandos.conf ([DEFAULT]/priority): - '' -
* mandos.conf.xml (OPTIONS/priority): Refer to the id "priority"
                                      instead of "priority_compat".
* mandos.xml (OPTIONS/--priority): - '' -
* plugins.d/mandos-client.c (main): Changed default priority string.

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-2015 Teddy Hogeborn
 
13
 * Copyright © 2008-2015 Björn Påhlsson
 
14
 * 
 
15
 * This program is free software: you can redistribute it and/or
 
16
 * modify it under the terms of the GNU General Public License as
 
17
 * published by the Free Software Foundation, either version 3 of the
 
18
 * License, or (at your option) any later version.
 
19
 * 
 
20
 * This program is distributed in the hope that it will be useful, but
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
 */
48
47
                                   strtof(), abort() */
49
48
#include <stdbool.h>            /* bool, false, true */
50
49
#include <string.h>             /* strcmp(), strlen(), strerror(),
51
 
                                   asprintf(), strncpy(), strsignal()
52
 
                                */
 
50
                                   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,
420
414
      if(result == NULL){
421
415
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
422
416
      } else {
423
 
        if(result->unsupported_algorithm != NULL) {
424
 
          fprintf_plus(stderr, "Unsupported algorithm: %s\n",
425
 
                       result->unsupported_algorithm);
426
 
        }
427
 
        fprintf_plus(stderr, "Wrong key usage: %s\n",
428
 
                     result->wrong_key_usage ? "Yes" : "No");
 
417
        fprintf_plus(stderr, "Unsupported algorithm: %s\n",
 
418
                     result->unsupported_algorithm);
 
419
        fprintf_plus(stderr, "Wrong key usage: %u\n",
 
420
                     result->wrong_key_usage);
429
421
        if(result->file_name != NULL){
430
422
          fprintf_plus(stderr, "File name: %s\n", result->file_name);
431
423
        }
521
513
  fprintf_plus(stderr, "GnuTLS: %s", string);
522
514
}
523
515
 
524
 
__attribute__((nonnull(1, 2, 4), warn_unused_result))
 
516
__attribute__((nonnull, warn_unused_result))
525
517
static int init_gnutls_global(const char *pubkeyfilename,
526
518
                              const char *seckeyfilename,
527
519
                              const char *dhparamsfilename,
533
525
    fprintf_plus(stderr, "Initializing GnuTLS\n");
534
526
  }
535
527
  
 
528
  ret = gnutls_global_init();
 
529
  if(ret != GNUTLS_E_SUCCESS){
 
530
    fprintf_plus(stderr, "GnuTLS global_init: %s\n",
 
531
                 safer_gnutls_strerror(ret));
 
532
    return -1;
 
533
  }
 
534
  
536
535
  if(debug){
537
536
    /* "Use a log level over 10 to enable all debugging options."
538
537
     * - GnuTLS manual
546
545
  if(ret != GNUTLS_E_SUCCESS){
547
546
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
548
547
                 safer_gnutls_strerror(ret));
 
548
    gnutls_global_deinit();
549
549
    return -1;
550
550
  }
551
551
  
615
615
        }
616
616
        params.size += (unsigned int)bytes_read;
617
617
      }
618
 
      ret = close(dhpfile);
619
 
      if(ret == -1){
620
 
        perror_plus("close");
621
 
      }
622
618
      if(params.data == NULL){
623
619
        dhparamsfilename = NULL;
624
620
      }
633
629
                     safer_gnutls_strerror(ret));
634
630
        dhparamsfilename = NULL;
635
631
      }
636
 
      free(params.data);
637
632
    } while(false);
638
633
  }
639
634
  if(dhparamsfilename == NULL){
760
755
 globalfail:
761
756
  
762
757
  gnutls_certificate_free_credentials(mc->cred);
 
758
  gnutls_global_deinit();
763
759
  gnutls_dh_params_deinit(mc->dh_params);
764
760
  return -1;
765
761
}
826
822
 
827
823
/* Set effective uid to 0, return errno */
828
824
__attribute__((warn_unused_result))
829
 
int raise_privileges(void){
830
 
  int old_errno = errno;
831
 
  int ret = 0;
 
825
error_t raise_privileges(void){
 
826
  error_t old_errno = errno;
 
827
  error_t ret_errno = 0;
832
828
  if(seteuid(0) == -1){
833
 
    ret = errno;
 
829
    ret_errno = errno;
834
830
  }
835
831
  errno = old_errno;
836
 
  return ret;
 
832
  return ret_errno;
837
833
}
838
834
 
839
835
/* Set effective and real user ID to 0.  Return errno. */
840
836
__attribute__((warn_unused_result))
841
 
int raise_privileges_permanently(void){
842
 
  int old_errno = errno;
843
 
  int ret = raise_privileges();
844
 
  if(ret != 0){
 
837
error_t raise_privileges_permanently(void){
 
838
  error_t old_errno = errno;
 
839
  error_t ret_errno = raise_privileges();
 
840
  if(ret_errno != 0){
845
841
    errno = old_errno;
846
 
    return ret;
 
842
    return ret_errno;
847
843
  }
848
844
  if(setuid(0) == -1){
849
 
    ret = errno;
 
845
    ret_errno = errno;
850
846
  }
851
847
  errno = old_errno;
852
 
  return ret;
 
848
  return ret_errno;
853
849
}
854
850
 
855
851
/* Set effective user ID to unprivileged saved user ID */
856
852
__attribute__((warn_unused_result))
857
 
int lower_privileges(void){
858
 
  int old_errno = errno;
859
 
  int ret = 0;
 
853
error_t lower_privileges(void){
 
854
  error_t old_errno = errno;
 
855
  error_t ret_errno = 0;
860
856
  if(seteuid(uid) == -1){
861
 
    ret = errno;
 
857
    ret_errno = errno;
862
858
  }
863
859
  errno = old_errno;
864
 
  return ret;
 
860
  return ret_errno;
865
861
}
866
862
 
867
863
/* Lower privileges permanently */
868
864
__attribute__((warn_unused_result))
869
 
int lower_privileges_permanently(void){
870
 
  int old_errno = errno;
871
 
  int ret = 0;
 
865
error_t lower_privileges_permanently(void){
 
866
  error_t old_errno = errno;
 
867
  error_t ret_errno = 0;
872
868
  if(setuid(uid) == -1){
873
 
    ret = errno;
 
869
    ret_errno = errno;
874
870
  }
875
871
  errno = old_errno;
876
 
  return ret;
 
872
  return ret_errno;
877
873
}
878
874
 
879
875
/* Helper function to add_local_route() and delete_local_route() */
1086
1082
    bool match = false;
1087
1083
    {
1088
1084
      char *interface = NULL;
1089
 
      while((interface = argz_next(mc->interfaces,
1090
 
                                   mc->interfaces_size,
1091
 
                                   interface))){
 
1085
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
 
1086
                                 interface))){
1092
1087
        if(if_nametoindex(interface) == (unsigned int)if_index){
1093
1088
          match = true;
1094
1089
          break;
1228
1223
                    sizeof(struct sockaddr_in));
1229
1224
    }
1230
1225
    if(ret < 0){
1231
 
      if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
 
1226
      if(errno == ENETUNREACH
1232
1227
         and if_index != AVAHI_IF_UNSPEC
1233
1228
         and connect_to == NULL
1234
1229
         and not route_added and
1247
1242
           with an explicit route added with the server's address.
1248
1243
           
1249
1244
           Avahi bug reference:
1250
 
           https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
 
1245
           http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1251
1246
           https://bugs.debian.org/587961
1252
1247
        */
1253
1248
        if(debug){
1433
1428
                                               &decrypted_buffer, mc);
1434
1429
    if(decrypted_buffer_size >= 0){
1435
1430
      
1436
 
      clearerr(stdout);
1437
1431
      written = 0;
1438
1432
      while(written < (size_t) decrypted_buffer_size){
1439
1433
        if(quit_now){
1455
1449
        }
1456
1450
        written += (size_t)ret;
1457
1451
      }
1458
 
      ret = fflush(stdout);
1459
 
      if(ret != 0){
1460
 
        int e = errno;
1461
 
        if(debug){
1462
 
          fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1463
 
                       strerror(errno));
1464
 
        }
1465
 
        errno = e;
1466
 
        goto mandos_end;
1467
 
      }
1468
1452
      retval = 0;
1469
1453
    }
1470
1454
  }
1501
1485
  return retval;
1502
1486
}
1503
1487
 
 
1488
__attribute__((nonnull))
1504
1489
static void resolve_callback(AvahiSServiceResolver *r,
1505
1490
                             AvahiIfIndex interface,
1506
1491
                             AvahiProtocol proto,
1643
1628
__attribute__((nonnull, warn_unused_result))
1644
1629
bool get_flags(const char *ifname, struct ifreq *ifr){
1645
1630
  int ret;
1646
 
  int old_errno;
 
1631
  error_t ret_errno;
1647
1632
  
1648
1633
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1649
1634
  if(s < 0){
1650
 
    old_errno = errno;
 
1635
    ret_errno = errno;
1651
1636
    perror_plus("socket");
1652
 
    errno = old_errno;
 
1637
    errno = ret_errno;
1653
1638
    return false;
1654
1639
  }
1655
 
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1656
 
  ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
 
1640
  strcpy(ifr->ifr_name, ifname);
1657
1641
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1658
1642
  if(ret == -1){
1659
1643
    if(debug){
1660
 
      old_errno = errno;
 
1644
      ret_errno = errno;
1661
1645
      perror_plus("ioctl SIOCGIFFLAGS");
1662
 
      errno = old_errno;
1663
 
    }
1664
 
    if((close(s) == -1) and debug){
1665
 
      old_errno = errno;
1666
 
      perror_plus("close");
1667
 
      errno = old_errno;
 
1646
      errno = ret_errno;
1668
1647
    }
1669
1648
    return false;
1670
1649
  }
1671
 
  if((close(s) == -1) and debug){
1672
 
    old_errno = errno;
1673
 
    perror_plus("close");
1674
 
    errno = old_errno;
1675
 
  }
1676
1650
  return true;
1677
1651
}
1678
1652
 
1939
1913
      return;
1940
1914
    }
1941
1915
  }
1942
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1943
 
  if(devnull == -1){
1944
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
1945
 
    return;
1946
 
  }
 
1916
#ifdef __GLIBC__
 
1917
#if __GLIBC_PREREQ(2, 15)
1947
1918
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
1948
1919
                           runnable_hook, alphasort);
 
1920
#else  /* not __GLIBC_PREREQ(2, 15) */
 
1921
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
1922
                         alphasort);
 
1923
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
1924
#else   /* not __GLIBC__ */
 
1925
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
1926
                         alphasort);
 
1927
#endif  /* not __GLIBC__ */
1949
1928
  if(numhooks == -1){
1950
1929
    perror_plus("scandir");
1951
 
    close(devnull);
1952
1930
    return;
1953
1931
  }
1954
1932
  struct dirent *direntry;
1955
1933
  int ret;
 
1934
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
 
1935
  if(devnull == -1){
 
1936
    perror_plus("open(\"/dev/null\", O_RDONLY)");
 
1937
    return;
 
1938
  }
1956
1939
  for(int i = 0; i < numhooks; i++){
1957
1940
    direntry = direntries[i];
1958
1941
    if(debug){
2102
2085
}
2103
2086
 
2104
2087
__attribute__((nonnull, warn_unused_result))
2105
 
int bring_up_interface(const char *const interface,
2106
 
                       const float delay){
2107
 
  int old_errno = errno;
 
2088
error_t bring_up_interface(const char *const interface,
 
2089
                           const float delay){
 
2090
  error_t old_errno = errno;
2108
2091
  int ret;
2109
2092
  struct ifreq network;
2110
2093
  unsigned int if_index = if_nametoindex(interface);
2120
2103
  }
2121
2104
  
2122
2105
  if(not interface_is_up(interface)){
2123
 
    int ret_errno = 0;
2124
 
    int ioctl_errno = 0;
 
2106
    error_t ret_errno = 0, ioctl_errno = 0;
2125
2107
    if(not get_flags(interface, &network)){
2126
2108
      ret_errno = errno;
2127
2109
      fprintf_plus(stderr, "Failed to get flags for interface "
2214
2196
  
2215
2197
  /* Sleep checking until interface is running.
2216
2198
     Check every 0.25s, up to total time of delay */
2217
 
  for(int i = 0; i < delay * 4; i++){
 
2199
  for(int i=0; i < delay * 4; i++){
2218
2200
    if(interface_is_running(interface)){
2219
2201
      break;
2220
2202
    }
2230
2212
}
2231
2213
 
2232
2214
__attribute__((nonnull, warn_unused_result))
2233
 
int take_down_interface(const char *const interface){
2234
 
  int old_errno = errno;
 
2215
error_t take_down_interface(const char *const interface){
 
2216
  error_t old_errno = errno;
2235
2217
  struct ifreq network;
2236
2218
  unsigned int if_index = if_nametoindex(interface);
2237
2219
  if(if_index == 0){
2240
2222
    return ENXIO;
2241
2223
  }
2242
2224
  if(interface_is_up(interface)){
2243
 
    int ret_errno = 0;
2244
 
    int ioctl_errno = 0;
 
2225
    error_t ret_errno = 0, ioctl_errno = 0;
2245
2226
    if(not get_flags(interface, &network) and debug){
2246
2227
      ret_errno = errno;
2247
2228
      fprintf_plus(stderr, "Failed to get flags for interface "
2497
2478
                         .args_doc = "",
2498
2479
                         .doc = "Mandos client -- Get and decrypt"
2499
2480
                         " passwords from a Mandos server" };
2500
 
    ret_errno = argp_parse(&argp, argc, argv,
2501
 
                           ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2502
 
    switch(ret_errno){
 
2481
    ret = argp_parse(&argp, argc, argv,
 
2482
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2483
    switch(ret){
2503
2484
    case 0:
2504
2485
      break;
2505
2486
    case ENOMEM:
2506
2487
    default:
2507
 
      errno = ret_errno;
 
2488
      errno = ret;
2508
2489
      perror_plus("argp_parse");
2509
2490
      exitcode = EX_OSERR;
2510
2491
      goto end;
2516
2497
  
2517
2498
  {
2518
2499
    /* Work around Debian bug #633582:
2519
 
       <https://bugs.debian.org/633582> */
 
2500
       <http://bugs.debian.org/633582> */
2520
2501
    
2521
2502
    /* Re-raise privileges */
2522
 
    ret = raise_privileges();
2523
 
    if(ret != 0){
2524
 
      errno = ret;
 
2503
    ret_errno = raise_privileges();
 
2504
    if(ret_errno != 0){
 
2505
      errno = ret_errno;
2525
2506
      perror_plus("Failed to raise privileges");
2526
2507
    } else {
2527
2508
      struct stat st;
2591
2572
      }
2592
2573
      
2593
2574
      /* Lower privileges */
2594
 
      ret = lower_privileges();
2595
 
      if(ret != 0){
2596
 
        errno = ret;
 
2575
      ret_errno = lower_privileges();
 
2576
      if(ret_errno != 0){
 
2577
        errno = ret_errno;
2597
2578
        perror_plus("Failed to lower privileges");
2598
2579
      }
2599
2580
    }
2927
2908
    
2928
2909
    /* Allocate a new server */
2929
2910
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2930
 
                                 &config, NULL, NULL, &ret);
 
2911
                                 &config, NULL, NULL, &ret_errno);
2931
2912
    
2932
2913
    /* Free the Avahi configuration data */
2933
2914
    avahi_server_config_free(&config);
2936
2917
  /* Check if creating the Avahi server object succeeded */
2937
2918
  if(mc.server == NULL){
2938
2919
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2939
 
                 avahi_strerror(ret));
 
2920
                 avahi_strerror(ret_errno));
2940
2921
    exitcode = EX_UNAVAILABLE;
2941
2922
    goto end;
2942
2923
  }
2977
2958
 end:
2978
2959
  
2979
2960
  if(debug){
2980
 
    if(signal_received){
2981
 
      fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
2982
 
                   argv[0], signal_received,
2983
 
                   strsignal(signal_received));
2984
 
    } else {
2985
 
      fprintf_plus(stderr, "%s exiting\n", argv[0]);
2986
 
    }
 
2961
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
2987
2962
  }
2988
2963
  
2989
2964
  /* Cleanup things */
3000
2975
  
3001
2976
  if(gnutls_initialized){
3002
2977
    gnutls_certificate_free_credentials(mc.cred);
 
2978
    gnutls_global_deinit();
3003
2979
    gnutls_dh_params_deinit(mc.dh_params);
3004
2980
  }
3005
2981
  
3028
3004
  
3029
3005
  /* Re-raise privileges */
3030
3006
  {
3031
 
    ret = raise_privileges();
3032
 
    if(ret != 0){
3033
 
      errno = ret;
 
3007
    ret_errno = raise_privileges();
 
3008
    if(ret_errno != 0){
 
3009
      errno = ret_errno;
3034
3010
      perror_plus("Failed to raise privileges");
3035
3011
    } else {
3036
3012
      
3041
3017
      /* Take down the network interfaces which were brought up */
3042
3018
      {
3043
3019
        char *interface = NULL;
3044
 
        while((interface = argz_next(interfaces_to_take_down,
3045
 
                                     interfaces_to_take_down_size,
3046
 
                                     interface))){
3047
 
          ret = take_down_interface(interface);
3048
 
          if(ret != 0){
3049
 
            errno = ret;
 
3020
        while((interface=argz_next(interfaces_to_take_down,
 
3021
                                   interfaces_to_take_down_size,
 
3022
                                   interface))){
 
3023
          ret_errno = take_down_interface(interface);
 
3024
          if(ret_errno != 0){
 
3025
            errno = ret_errno;
3050
3026
            perror_plus("Failed to take down interface");
3051
3027
          }
3052
3028
        }
3057
3033
      }
3058
3034
    }
3059
3035
    
3060
 
    ret = lower_privileges_permanently();
3061
 
    if(ret != 0){
3062
 
      errno = ret;
 
3036
    ret_errno = lower_privileges_permanently();
 
3037
    if(ret_errno != 0){
 
3038
      errno = ret_errno;
3063
3039
      perror_plus("Failed to lower privileges permanently");
3064
3040
    }
3065
3041
  }
3067
3043
  free(interfaces_to_take_down);
3068
3044
  free(interfaces_hooks);
3069
3045
  
3070
 
  void clean_dir_at(int base, const char * const dirname,
3071
 
                    uintmax_t level){
3072
 
    struct dirent **direntries = NULL;
3073
 
    int dret;
3074
 
    int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3075
 
                                                O_RDONLY
3076
 
                                                | O_NOFOLLOW
3077
 
                                                | O_DIRECTORY
3078
 
                                                | O_PATH));
3079
 
    if(dir_fd == -1){
3080
 
      perror_plus("open");
3081
 
      return;
3082
 
    }
3083
 
    int numentries = scandirat(dir_fd, ".", &direntries,
3084
 
                               notdotentries, alphasort);
3085
 
    if(numentries >= 0){
3086
 
      for(int i = 0; i < numentries; i++){
3087
 
        if(debug){
3088
 
          fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3089
 
                       dirname, direntries[i]->d_name);
3090
 
        }
3091
 
        dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3092
 
        if(dret == -1){
3093
 
          if(errno == EISDIR){
3094
 
              dret = unlinkat(dir_fd, direntries[i]->d_name,
3095
 
                              AT_REMOVEDIR);
3096
 
          }         
3097
 
          if((dret == -1) and (errno == ENOTEMPTY)
3098
 
             and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3099
 
                  == 0) and (level == 0)){
3100
 
            /* Recurse only in this special case */
3101
 
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3102
 
            dret = 0;
3103
 
          }
3104
 
          if((dret == -1) and (errno != ENOENT)){
3105
 
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3106
 
                         direntries[i]->d_name, strerror(errno));
3107
 
          }
3108
 
        }
3109
 
        free(direntries[i]);
3110
 
      }
3111
 
      
3112
 
      /* need to clean even if 0 because man page doesn't specify */
3113
 
      free(direntries);
3114
 
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
3115
 
      if(dret == -1 and errno != ENOENT){
3116
 
        perror_plus("rmdir");
3117
 
      }
3118
 
    } else {
3119
 
      perror_plus("scandirat");
3120
 
    }
3121
 
    close(dir_fd);
3122
 
  }
3123
 
  
3124
3046
  /* Removes the GPGME temp directory and all files inside */
3125
3047
  if(tempdir != NULL){
3126
 
    clean_dir_at(-1, tempdir, 0);
 
3048
    struct dirent **direntries = NULL;
 
3049
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
 
3050
                                                  | O_NOFOLLOW
 
3051
                                                  | O_DIRECTORY
 
3052
                                                  | O_PATH));
 
3053
    if(tempdir_fd == -1){
 
3054
      perror_plus("open");
 
3055
    } else {
 
3056
#ifdef __GLIBC__
 
3057
#if __GLIBC_PREREQ(2, 15)
 
3058
      int numentries = scandirat(tempdir_fd, ".", &direntries,
 
3059
                                 notdotentries, alphasort);
 
3060
#else  /* not __GLIBC_PREREQ(2, 15) */
 
3061
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
3062
                               alphasort);
 
3063
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
3064
#else   /* not __GLIBC__ */
 
3065
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
3066
                               alphasort);
 
3067
#endif  /* not __GLIBC__ */
 
3068
      if(numentries >= 0){
 
3069
        for(int i = 0; i < numentries; i++){
 
3070
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
 
3071
          if(ret == -1){
 
3072
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
 
3073
                         " \"%s\", 0): %s\n", tempdir,
 
3074
                         direntries[i]->d_name, strerror(errno));
 
3075
          }
 
3076
          free(direntries[i]);
 
3077
        }
 
3078
        
 
3079
        /* need to clean even if 0 because man page doesn't specify */
 
3080
        free(direntries);
 
3081
        if(numentries == -1){
 
3082
          perror_plus("scandir");
 
3083
        }
 
3084
        ret = rmdir(tempdir);
 
3085
        if(ret == -1 and errno != ENOENT){
 
3086
          perror_plus("rmdir");
 
3087
        }
 
3088
      }
 
3089
      close(tempdir_fd);
 
3090
    }
3127
3091
  }
3128
3092
  
3129
3093
  if(quit_now){