/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: 2016-03-17 20:40:55 UTC
  • Revision ID: teddy@recompile.se-20160317204055-bhsh5xsidq7w5cxu
Client: Fix plymouth agent; broken since 1.7.2.

Fix an very old memory bug in the plymouth agent (which has been
present since its apperance in version 1.2), but which was only
recently detected at run time due to the new -fsanitize=address
compile- time flag, which has been used since version 1.7.2.  This
detection of a memory access violation causes the program to abort,
making the Plymouth graphical boot system unable to accept interactive
input of passwords when using the Mandos client.

* plugins.d/plymouth.c (exec_and_wait): Fix memory allocation bug when
  allocating new_argv.  Also tolerate a zero-length argv.

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-2015 Teddy Hogeborn
13
 
 * Copyright © 2008-2015 Björn Påhlsson
 
12
 * Copyright © 2008-2016 Teddy Hogeborn
 
13
 * Copyright © 2008-2016 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>             /* memset(), strcmp(), strlen(),
50
 
                                   strerror(), asprintf(), strcpy() */
 
49
#include <string.h>             /* strcmp(), strlen(), strerror(),
 
50
                                   asprintf(), strncpy() */
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() */
 
60
#include <fcntl.h>              /* open(), unlinkat(), AT_REMOVEDIR */
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,
 
65
#include <errno.h>              /* perror(), errno, EINTR, EINVAL,
 
66
                                   EAI_SYSTEM, ENETUNREACH,
 
67
                                   EHOSTUNREACH, ECONNREFUSED, EPROTO,
 
68
                                   EIO, ENOENT, ENXIO, ENOMEM, EISDIR,
 
69
                                   ENOTEMPTY,
66
70
                                   program_invocation_short_name */
67
71
#include <time.h>               /* nanosleep(), time(), sleep() */
68
72
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
305
309
      return false;
306
310
    }
307
311
    
308
 
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
 
312
    ret = close(fd);
309
313
    if(ret == -1){
310
314
      perror_plus("close");
311
315
    }
513
517
  fprintf_plus(stderr, "GnuTLS: %s", string);
514
518
}
515
519
 
516
 
__attribute__((nonnull, warn_unused_result))
 
520
__attribute__((nonnull(1, 2, 4), warn_unused_result))
517
521
static int init_gnutls_global(const char *pubkeyfilename,
518
522
                              const char *seckeyfilename,
519
523
                              const char *dhparamsfilename,
525
529
    fprintf_plus(stderr, "Initializing GnuTLS\n");
526
530
  }
527
531
  
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
 
  
535
532
  if(debug){
536
533
    /* "Use a log level over 10 to enable all debugging options."
537
534
     * - GnuTLS manual
545
542
  if(ret != GNUTLS_E_SUCCESS){
546
543
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
547
544
                 safer_gnutls_strerror(ret));
548
 
    gnutls_global_deinit();
549
545
    return -1;
550
546
  }
551
547
  
755
751
 globalfail:
756
752
  
757
753
  gnutls_certificate_free_credentials(mc->cred);
758
 
  gnutls_global_deinit();
759
754
  gnutls_dh_params_deinit(mc->dh_params);
760
755
  return -1;
761
756
}
822
817
 
823
818
/* Set effective uid to 0, return errno */
824
819
__attribute__((warn_unused_result))
825
 
error_t raise_privileges(void){
826
 
  error_t old_errno = errno;
827
 
  error_t ret_errno = 0;
 
820
int raise_privileges(void){
 
821
  int old_errno = errno;
 
822
  int ret = 0;
828
823
  if(seteuid(0) == -1){
829
 
    ret_errno = errno;
 
824
    ret = errno;
830
825
  }
831
826
  errno = old_errno;
832
 
  return ret_errno;
 
827
  return ret;
833
828
}
834
829
 
835
830
/* Set effective and real user ID to 0.  Return errno. */
836
831
__attribute__((warn_unused_result))
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){
 
832
int raise_privileges_permanently(void){
 
833
  int old_errno = errno;
 
834
  int ret = raise_privileges();
 
835
  if(ret != 0){
841
836
    errno = old_errno;
842
 
    return ret_errno;
 
837
    return ret;
843
838
  }
844
839
  if(setuid(0) == -1){
845
 
    ret_errno = errno;
 
840
    ret = errno;
846
841
  }
847
842
  errno = old_errno;
848
 
  return ret_errno;
 
843
  return ret;
849
844
}
850
845
 
851
846
/* Set effective user ID to unprivileged saved user ID */
852
847
__attribute__((warn_unused_result))
853
 
error_t lower_privileges(void){
854
 
  error_t old_errno = errno;
855
 
  error_t ret_errno = 0;
 
848
int lower_privileges(void){
 
849
  int old_errno = errno;
 
850
  int ret = 0;
856
851
  if(seteuid(uid) == -1){
857
 
    ret_errno = errno;
 
852
    ret = errno;
858
853
  }
859
854
  errno = old_errno;
860
 
  return ret_errno;
 
855
  return ret;
861
856
}
862
857
 
863
858
/* Lower privileges permanently */
864
859
__attribute__((warn_unused_result))
865
 
error_t lower_privileges_permanently(void){
866
 
  error_t old_errno = errno;
867
 
  error_t ret_errno = 0;
 
860
int lower_privileges_permanently(void){
 
861
  int old_errno = errno;
 
862
  int ret = 0;
868
863
  if(setuid(uid) == -1){
869
 
    ret_errno = errno;
 
864
    ret = errno;
870
865
  }
871
866
  errno = old_errno;
872
 
  return ret_errno;
 
867
  return ret;
873
868
}
874
869
 
875
870
/* Helper function to add_local_route() and delete_local_route() */
931
926
      perror_plus("dup2(devnull, STDIN_FILENO)");
932
927
      _exit(EX_OSERR);
933
928
    }
934
 
    ret = (int)TEMP_FAILURE_RETRY(close(devnull));
 
929
    ret = close(devnull);
935
930
    if(ret == -1){
936
931
      perror_plus("close");
937
932
      _exit(EX_OSERR);
954
949
                                                   helper, O_RDONLY));
955
950
    if(helper_fd == -1){
956
951
      perror_plus("openat");
 
952
      close(helperdir_fd);
957
953
      _exit(EX_UNAVAILABLE);
958
954
    }
959
 
    TEMP_FAILURE_RETRY(close(helperdir_fd));
 
955
    close(helperdir_fd);
960
956
#ifdef __GNUC__
961
957
#pragma GCC diagnostic push
962
958
#pragma GCC diagnostic ignored "-Wcast-qual"
1130
1126
    goto mandos_end;
1131
1127
  }
1132
1128
  
1133
 
  memset(&to, 0, sizeof(to));
1134
1129
  if(af == AF_INET6){
1135
 
    ((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
1136
 
    ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
 
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);
1137
1133
  } else {                      /* IPv4 */
1138
 
    ((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
1139
 
    ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
 
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);
1140
1137
  }
1141
1138
  if(ret < 0 ){
1142
1139
    int e = errno;
1221
1218
                    sizeof(struct sockaddr_in));
1222
1219
    }
1223
1220
    if(ret < 0){
1224
 
      if(errno == ENETUNREACH
 
1221
      if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1225
1222
         and if_index != AVAHI_IF_UNSPEC
1226
1223
         and connect_to == NULL
1227
1224
         and not route_added and
1465
1462
    free(decrypted_buffer);
1466
1463
    free(buffer);
1467
1464
    if(tcp_sd >= 0){
1468
 
      ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
 
1465
      ret = close(tcp_sd);
1469
1466
    }
1470
1467
    if(ret == -1){
1471
1468
      if(e == 0){
1626
1623
__attribute__((nonnull, warn_unused_result))
1627
1624
bool get_flags(const char *ifname, struct ifreq *ifr){
1628
1625
  int ret;
1629
 
  error_t ret_errno;
 
1626
  int old_errno;
1630
1627
  
1631
1628
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1632
1629
  if(s < 0){
1633
 
    ret_errno = errno;
 
1630
    old_errno = errno;
1634
1631
    perror_plus("socket");
1635
 
    errno = ret_errno;
 
1632
    errno = old_errno;
1636
1633
    return false;
1637
1634
  }
1638
 
  strcpy(ifr->ifr_name, ifname);
 
1635
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
 
1636
  ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1639
1637
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1640
1638
  if(ret == -1){
1641
1639
    if(debug){
1642
 
      ret_errno = errno;
 
1640
      old_errno = errno;
1643
1641
      perror_plus("ioctl SIOCGIFFLAGS");
1644
 
      errno = ret_errno;
 
1642
      errno = old_errno;
1645
1643
    }
1646
1644
    return false;
1647
1645
  }
1911
1909
      return;
1912
1910
    }
1913
1911
  }
1914
 
#ifdef __GLIBC__
1915
 
#if __GLIBC_PREREQ(2, 15)
1916
1912
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
1917
1913
                           runnable_hook, alphasort);
1918
 
#else  /* not __GLIBC_PREREQ(2, 15) */
1919
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1920
 
                         alphasort);
1921
 
#endif  /* not __GLIBC_PREREQ(2, 15) */
1922
 
#else   /* not __GLIBC__ */
1923
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1924
 
                         alphasort);
1925
 
#endif  /* not __GLIBC__ */
1926
1914
  if(numhooks == -1){
1927
1915
    perror_plus("scandir");
1928
1916
    return;
2010
1998
        perror_plus("openat");
2011
1999
        _exit(EXIT_FAILURE);
2012
2000
      }
2013
 
      if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
2001
      if(close(hookdir_fd) == -1){
2014
2002
        perror_plus("close");
2015
2003
        _exit(EXIT_FAILURE);
2016
2004
      }
2019
2007
        perror_plus("dup2(devnull, STDIN_FILENO)");
2020
2008
        _exit(EX_OSERR);
2021
2009
      }
2022
 
      ret = (int)TEMP_FAILURE_RETRY(close(devnull));
 
2010
      ret = close(devnull);
2023
2011
      if(ret == -1){
2024
2012
        perror_plus("close");
2025
2013
        _exit(EX_OSERR);
2074
2062
    free(direntry);
2075
2063
  }
2076
2064
  free(direntries);
2077
 
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
2065
  if(close(hookdir_fd) == -1){
2078
2066
    perror_plus("close");
2079
2067
  } else {
2080
2068
    hookdir_fd = -1;
2083
2071
}
2084
2072
 
2085
2073
__attribute__((nonnull, warn_unused_result))
2086
 
error_t bring_up_interface(const char *const interface,
2087
 
                           const float delay){
2088
 
  error_t old_errno = errno;
 
2074
int bring_up_interface(const char *const interface,
 
2075
                       const float delay){
 
2076
  int old_errno = errno;
2089
2077
  int ret;
2090
2078
  struct ifreq network;
2091
2079
  unsigned int if_index = if_nametoindex(interface);
2101
2089
  }
2102
2090
  
2103
2091
  if(not interface_is_up(interface)){
2104
 
    error_t ret_errno = 0, ioctl_errno = 0;
 
2092
    int ret_errno = 0;
 
2093
    int ioctl_errno = 0;
2105
2094
    if(not get_flags(interface, &network)){
2106
2095
      ret_errno = errno;
2107
2096
      fprintf_plus(stderr, "Failed to get flags for interface "
2120
2109
    }
2121
2110
    
2122
2111
    if(quit_now){
2123
 
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2112
      ret = close(sd);
2124
2113
      if(ret == -1){
2125
2114
        perror_plus("close");
2126
2115
      }
2176
2165
    }
2177
2166
    
2178
2167
    /* Close the socket */
2179
 
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2168
    ret = close(sd);
2180
2169
    if(ret == -1){
2181
2170
      perror_plus("close");
2182
2171
    }
2210
2199
}
2211
2200
 
2212
2201
__attribute__((nonnull, warn_unused_result))
2213
 
error_t take_down_interface(const char *const interface){
2214
 
  error_t old_errno = errno;
 
2202
int take_down_interface(const char *const interface){
 
2203
  int old_errno = errno;
2215
2204
  struct ifreq network;
2216
2205
  unsigned int if_index = if_nametoindex(interface);
2217
2206
  if(if_index == 0){
2220
2209
    return ENXIO;
2221
2210
  }
2222
2211
  if(interface_is_up(interface)){
2223
 
    error_t ret_errno = 0, ioctl_errno = 0;
 
2212
    int ret_errno = 0;
 
2213
    int ioctl_errno = 0;
2224
2214
    if(not get_flags(interface, &network) and debug){
2225
2215
      ret_errno = errno;
2226
2216
      fprintf_plus(stderr, "Failed to get flags for interface "
2264
2254
    }
2265
2255
    
2266
2256
    /* Close the socket */
2267
 
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2257
    int ret = close(sd);
2268
2258
    if(ret == -1){
2269
2259
      perror_plus("close");
2270
2260
    }
2286
2276
 
2287
2277
int main(int argc, char *argv[]){
2288
2278
  mandos_context mc = { .server = NULL, .dh_bits = 0,
2289
 
                        .priority = "SECURE256:!CTYPE-X.509:"
2290
 
                        "+CTYPE-OPENPGP:!RSA", .current_server = NULL,
2291
 
                        .interfaces = NULL, .interfaces_size = 0 };
 
2279
                        .priority = "SECURE256:!CTYPE-X.509"
 
2280
                        ":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
 
2281
                        .current_server = NULL, .interfaces = NULL,
 
2282
                        .interfaces_size = 0 };
2292
2283
  AvahiSServiceBrowser *sb = NULL;
2293
2284
  error_t ret_errno;
2294
2285
  int ret;
2475
2466
                         .args_doc = "",
2476
2467
                         .doc = "Mandos client -- Get and decrypt"
2477
2468
                         " passwords from a Mandos server" };
2478
 
    ret = argp_parse(&argp, argc, argv,
2479
 
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2480
 
    switch(ret){
 
2469
    ret_errno = argp_parse(&argp, argc, argv,
 
2470
                           ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2471
    switch(ret_errno){
2481
2472
    case 0:
2482
2473
      break;
2483
2474
    case ENOMEM:
2484
2475
    default:
2485
 
      errno = ret;
 
2476
      errno = ret_errno;
2486
2477
      perror_plus("argp_parse");
2487
2478
      exitcode = EX_OSERR;
2488
2479
      goto end;
2497
2488
       <http://bugs.debian.org/633582> */
2498
2489
    
2499
2490
    /* Re-raise privileges */
2500
 
    ret_errno = raise_privileges();
2501
 
    if(ret_errno != 0){
2502
 
      errno = ret_errno;
 
2491
    ret = raise_privileges();
 
2492
    if(ret != 0){
 
2493
      errno = ret;
2503
2494
      perror_plus("Failed to raise privileges");
2504
2495
    } else {
2505
2496
      struct stat st;
2521
2512
              }
2522
2513
            }
2523
2514
          }
2524
 
          TEMP_FAILURE_RETRY(close(seckey_fd));
 
2515
          close(seckey_fd);
2525
2516
        }
2526
2517
      }
2527
2518
      
2542
2533
              }
2543
2534
            }
2544
2535
          }
2545
 
          TEMP_FAILURE_RETRY(close(pubkey_fd));
 
2536
          close(pubkey_fd);
 
2537
        }
 
2538
      }
 
2539
      
 
2540
      if(dh_params_file != NULL
 
2541
         and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
 
2542
        int dhparams_fd = open(dh_params_file, O_RDONLY);
 
2543
        if(dhparams_fd == -1){
 
2544
          perror_plus("open");
 
2545
        } else {
 
2546
          ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
 
2547
          if(ret == -1){
 
2548
            perror_plus("fstat");
 
2549
          } else {
 
2550
            if(S_ISREG(st.st_mode)
 
2551
               and st.st_uid == 0 and st.st_gid == 0){
 
2552
              ret = fchown(dhparams_fd, uid, gid);
 
2553
              if(ret == -1){
 
2554
                perror_plus("fchown");
 
2555
              }
 
2556
            }
 
2557
          }
 
2558
          close(dhparams_fd);
2546
2559
        }
2547
2560
      }
2548
2561
      
2549
2562
      /* Lower privileges */
2550
 
      ret_errno = lower_privileges();
2551
 
      if(ret_errno != 0){
2552
 
        errno = ret_errno;
 
2563
      ret = lower_privileges();
 
2564
      if(ret != 0){
 
2565
        errno = ret;
2553
2566
        perror_plus("Failed to lower privileges");
2554
2567
      }
2555
2568
    }
2725
2738
      errno = bring_up_interface(interface, delay);
2726
2739
      if(not interface_was_up){
2727
2740
        if(errno != 0){
2728
 
          perror_plus("Failed to bring up interface");
 
2741
          fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
 
2742
                       " %s\n", interface, strerror(errno));
2729
2743
        } else {
2730
2744
          errno = argz_add(&interfaces_to_take_down,
2731
2745
                           &interfaces_to_take_down_size,
2882
2896
    
2883
2897
    /* Allocate a new server */
2884
2898
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2885
 
                                 &config, NULL, NULL, &ret_errno);
 
2899
                                 &config, NULL, NULL, &ret);
2886
2900
    
2887
2901
    /* Free the Avahi configuration data */
2888
2902
    avahi_server_config_free(&config);
2891
2905
  /* Check if creating the Avahi server object succeeded */
2892
2906
  if(mc.server == NULL){
2893
2907
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2894
 
                 avahi_strerror(ret_errno));
 
2908
                 avahi_strerror(ret));
2895
2909
    exitcode = EX_UNAVAILABLE;
2896
2910
    goto end;
2897
2911
  }
2949
2963
  
2950
2964
  if(gnutls_initialized){
2951
2965
    gnutls_certificate_free_credentials(mc.cred);
2952
 
    gnutls_global_deinit();
2953
2966
    gnutls_dh_params_deinit(mc.dh_params);
2954
2967
  }
2955
2968
  
2978
2991
  
2979
2992
  /* Re-raise privileges */
2980
2993
  {
2981
 
    ret_errno = raise_privileges();
2982
 
    if(ret_errno != 0){
2983
 
      errno = ret_errno;
 
2994
    ret = raise_privileges();
 
2995
    if(ret != 0){
 
2996
      errno = ret;
2984
2997
      perror_plus("Failed to raise privileges");
2985
2998
    } else {
2986
2999
      
2994
3007
        while((interface=argz_next(interfaces_to_take_down,
2995
3008
                                   interfaces_to_take_down_size,
2996
3009
                                   interface))){
2997
 
          ret_errno = take_down_interface(interface);
2998
 
          if(ret_errno != 0){
2999
 
            errno = ret_errno;
 
3010
          ret = take_down_interface(interface);
 
3011
          if(ret != 0){
 
3012
            errno = ret;
3000
3013
            perror_plus("Failed to take down interface");
3001
3014
          }
3002
3015
        }
3007
3020
      }
3008
3021
    }
3009
3022
    
3010
 
    ret_errno = lower_privileges_permanently();
3011
 
    if(ret_errno != 0){
3012
 
      errno = ret_errno;
 
3023
    ret = lower_privileges_permanently();
 
3024
    if(ret != 0){
 
3025
      errno = ret;
3013
3026
      perror_plus("Failed to lower privileges permanently");
3014
3027
    }
3015
3028
  }
3017
3030
  free(interfaces_to_take_down);
3018
3031
  free(interfaces_hooks);
3019
3032
  
 
3033
  void clean_dir_at(int base, const char * const dirname,
 
3034
                    uintmax_t level){
 
3035
    struct dirent **direntries = NULL;
 
3036
    int dret;
 
3037
    int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
 
3038
                                                O_RDONLY
 
3039
                                                | O_NOFOLLOW
 
3040
                                                | O_DIRECTORY
 
3041
                                                | O_PATH));
 
3042
    if(dir_fd == -1){
 
3043
      perror_plus("open");
 
3044
    }
 
3045
    int numentries = scandirat(dir_fd, ".", &direntries,
 
3046
                               notdotentries, alphasort);
 
3047
    if(numentries >= 0){
 
3048
      for(int i = 0; i < numentries; i++){
 
3049
        if(debug){
 
3050
          fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
 
3051
                       dirname, direntries[i]->d_name);
 
3052
        }
 
3053
        dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
 
3054
        if(dret == -1){
 
3055
          if(errno == EISDIR){
 
3056
              dret = unlinkat(dir_fd, direntries[i]->d_name,
 
3057
                              AT_REMOVEDIR);
 
3058
          }         
 
3059
          if((dret == -1) and (errno == ENOTEMPTY)
 
3060
             and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
 
3061
                  == 0) and (level == 0)){
 
3062
            /* Recurse only in this special case */
 
3063
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
 
3064
            dret = 0;
 
3065
          }
 
3066
          if(dret == -1){
 
3067
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
 
3068
                         direntries[i]->d_name, strerror(errno));
 
3069
          }
 
3070
        }
 
3071
        free(direntries[i]);
 
3072
      }
 
3073
      
 
3074
      /* need to clean even if 0 because man page doesn't specify */
 
3075
      free(direntries);
 
3076
      if(numentries == -1){
 
3077
        perror_plus("scandirat");
 
3078
      }
 
3079
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
 
3080
      if(dret == -1 and errno != ENOENT){
 
3081
        perror_plus("rmdir");
 
3082
      }
 
3083
    } else {
 
3084
      perror_plus("scandirat");
 
3085
    }
 
3086
    close(dir_fd);
 
3087
  }
 
3088
  
3020
3089
  /* Removes the GPGME temp directory and all files inside */
3021
3090
  if(tempdir != NULL){
3022
 
    struct dirent **direntries = NULL;
3023
 
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
3024
 
                                                  | O_NOFOLLOW
3025
 
                                                  | O_DIRECTORY
3026
 
                                                  | O_PATH));
3027
 
    if(tempdir_fd == -1){
3028
 
      perror_plus("open");
3029
 
    } else {
3030
 
#ifdef __GLIBC__
3031
 
#if __GLIBC_PREREQ(2, 15)
3032
 
      int numentries = scandirat(tempdir_fd, ".", &direntries,
3033
 
                                 notdotentries, alphasort);
3034
 
#else  /* not __GLIBC_PREREQ(2, 15) */
3035
 
      int numentries = scandir(tempdir, &direntries, notdotentries,
3036
 
                               alphasort);
3037
 
#endif  /* not __GLIBC_PREREQ(2, 15) */
3038
 
#else   /* not __GLIBC__ */
3039
 
      int numentries = scandir(tempdir, &direntries, notdotentries,
3040
 
                               alphasort);
3041
 
#endif  /* not __GLIBC__ */
3042
 
      if(numentries >= 0){
3043
 
        for(int i = 0; i < numentries; i++){
3044
 
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
3045
 
          if(ret == -1){
3046
 
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
3047
 
                         " \"%s\", 0): %s\n", tempdir,
3048
 
                         direntries[i]->d_name, strerror(errno));
3049
 
          }
3050
 
          free(direntries[i]);
3051
 
        }
3052
 
        
3053
 
        /* need to clean even if 0 because man page doesn't specify */
3054
 
        free(direntries);
3055
 
        if(numentries == -1){
3056
 
          perror_plus("scandir");
3057
 
        }
3058
 
        ret = rmdir(tempdir);
3059
 
        if(ret == -1 and errno != ENOENT){
3060
 
          perror_plus("rmdir");
3061
 
        }
3062
 
      }
3063
 
      TEMP_FAILURE_RETRY(close(tempdir_fd));
3064
 
    }
 
3091
    clean_dir_at(-1, tempdir, 0);
3065
3092
  }
3066
3093
  
3067
3094
  if(quit_now){