/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-2016 Teddy Hogeborn
13
 
 * Copyright © 2008-2016 Björn Påhlsson
 
12
 * Copyright © 2008-2015 Teddy Hogeborn
 
13
 * Copyright © 2008-2015 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
47
47
                                   strtof(), abort() */
48
48
#include <stdbool.h>            /* bool, false, true */
49
49
#include <string.h>             /* strcmp(), strlen(), strerror(),
50
 
                                   asprintf(), strncpy() */
 
50
                                   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,
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
519
                              const char *dhparamsfilename,
529
525
    fprintf_plus(stderr, "Initializing GnuTLS\n");
530
526
  }
531
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
  
532
535
  if(debug){
533
536
    /* "Use a log level over 10 to enable all debugging options."
534
537
     * - GnuTLS manual
542
545
  if(ret != GNUTLS_E_SUCCESS){
543
546
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
544
547
                 safer_gnutls_strerror(ret));
 
548
    gnutls_global_deinit();
545
549
    return -1;
546
550
  }
547
551
  
751
755
 globalfail:
752
756
  
753
757
  gnutls_certificate_free_credentials(mc->cred);
 
758
  gnutls_global_deinit();
754
759
  gnutls_dh_params_deinit(mc->dh_params);
755
760
  return -1;
756
761
}
817
822
 
818
823
/* Set effective uid to 0, return errno */
819
824
__attribute__((warn_unused_result))
820
 
int raise_privileges(void){
821
 
  int old_errno = errno;
822
 
  int ret = 0;
 
825
error_t raise_privileges(void){
 
826
  error_t old_errno = errno;
 
827
  error_t ret_errno = 0;
823
828
  if(seteuid(0) == -1){
824
 
    ret = errno;
 
829
    ret_errno = errno;
825
830
  }
826
831
  errno = old_errno;
827
 
  return ret;
 
832
  return ret_errno;
828
833
}
829
834
 
830
835
/* Set effective and real user ID to 0.  Return errno. */
831
836
__attribute__((warn_unused_result))
832
 
int raise_privileges_permanently(void){
833
 
  int old_errno = errno;
834
 
  int ret = raise_privileges();
835
 
  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){
836
841
    errno = old_errno;
837
 
    return ret;
 
842
    return ret_errno;
838
843
  }
839
844
  if(setuid(0) == -1){
840
 
    ret = errno;
 
845
    ret_errno = errno;
841
846
  }
842
847
  errno = old_errno;
843
 
  return ret;
 
848
  return ret_errno;
844
849
}
845
850
 
846
851
/* Set effective user ID to unprivileged saved user ID */
847
852
__attribute__((warn_unused_result))
848
 
int lower_privileges(void){
849
 
  int old_errno = errno;
850
 
  int ret = 0;
 
853
error_t lower_privileges(void){
 
854
  error_t old_errno = errno;
 
855
  error_t ret_errno = 0;
851
856
  if(seteuid(uid) == -1){
852
 
    ret = errno;
 
857
    ret_errno = errno;
853
858
  }
854
859
  errno = old_errno;
855
 
  return ret;
 
860
  return ret_errno;
856
861
}
857
862
 
858
863
/* Lower privileges permanently */
859
864
__attribute__((warn_unused_result))
860
 
int lower_privileges_permanently(void){
861
 
  int old_errno = errno;
862
 
  int ret = 0;
 
865
error_t lower_privileges_permanently(void){
 
866
  error_t old_errno = errno;
 
867
  error_t ret_errno = 0;
863
868
  if(setuid(uid) == -1){
864
 
    ret = errno;
 
869
    ret_errno = errno;
865
870
  }
866
871
  errno = old_errno;
867
 
  return ret;
 
872
  return ret_errno;
868
873
}
869
874
 
870
875
/* Helper function to add_local_route() and delete_local_route() */
1218
1223
                    sizeof(struct sockaddr_in));
1219
1224
    }
1220
1225
    if(ret < 0){
1221
 
      if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
 
1226
      if(errno == ENETUNREACH
1222
1227
         and if_index != AVAHI_IF_UNSPEC
1223
1228
         and connect_to == NULL
1224
1229
         and not route_added and
1623
1628
__attribute__((nonnull, warn_unused_result))
1624
1629
bool get_flags(const char *ifname, struct ifreq *ifr){
1625
1630
  int ret;
1626
 
  int old_errno;
 
1631
  error_t ret_errno;
1627
1632
  
1628
1633
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1629
1634
  if(s < 0){
1630
 
    old_errno = errno;
 
1635
    ret_errno = errno;
1631
1636
    perror_plus("socket");
1632
 
    errno = old_errno;
 
1637
    errno = ret_errno;
1633
1638
    return false;
1634
1639
  }
1635
 
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1636
 
  ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
 
1640
  strcpy(ifr->ifr_name, ifname);
1637
1641
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1638
1642
  if(ret == -1){
1639
1643
    if(debug){
1640
 
      old_errno = errno;
 
1644
      ret_errno = errno;
1641
1645
      perror_plus("ioctl SIOCGIFFLAGS");
1642
 
      errno = old_errno;
 
1646
      errno = ret_errno;
1643
1647
    }
1644
1648
    return false;
1645
1649
  }
1909
1913
      return;
1910
1914
    }
1911
1915
  }
 
1916
#ifdef __GLIBC__
 
1917
#if __GLIBC_PREREQ(2, 15)
1912
1918
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
1913
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__ */
1914
1928
  if(numhooks == -1){
1915
1929
    perror_plus("scandir");
1916
1930
    return;
2071
2085
}
2072
2086
 
2073
2087
__attribute__((nonnull, warn_unused_result))
2074
 
int bring_up_interface(const char *const interface,
2075
 
                       const float delay){
2076
 
  int old_errno = errno;
 
2088
error_t bring_up_interface(const char *const interface,
 
2089
                           const float delay){
 
2090
  error_t old_errno = errno;
2077
2091
  int ret;
2078
2092
  struct ifreq network;
2079
2093
  unsigned int if_index = if_nametoindex(interface);
2089
2103
  }
2090
2104
  
2091
2105
  if(not interface_is_up(interface)){
2092
 
    int ret_errno = 0;
2093
 
    int ioctl_errno = 0;
 
2106
    error_t ret_errno = 0, ioctl_errno = 0;
2094
2107
    if(not get_flags(interface, &network)){
2095
2108
      ret_errno = errno;
2096
2109
      fprintf_plus(stderr, "Failed to get flags for interface "
2199
2212
}
2200
2213
 
2201
2214
__attribute__((nonnull, warn_unused_result))
2202
 
int take_down_interface(const char *const interface){
2203
 
  int old_errno = errno;
 
2215
error_t take_down_interface(const char *const interface){
 
2216
  error_t old_errno = errno;
2204
2217
  struct ifreq network;
2205
2218
  unsigned int if_index = if_nametoindex(interface);
2206
2219
  if(if_index == 0){
2209
2222
    return ENXIO;
2210
2223
  }
2211
2224
  if(interface_is_up(interface)){
2212
 
    int ret_errno = 0;
2213
 
    int ioctl_errno = 0;
 
2225
    error_t ret_errno = 0, ioctl_errno = 0;
2214
2226
    if(not get_flags(interface, &network) and debug){
2215
2227
      ret_errno = errno;
2216
2228
      fprintf_plus(stderr, "Failed to get flags for interface "
2466
2478
                         .args_doc = "",
2467
2479
                         .doc = "Mandos client -- Get and decrypt"
2468
2480
                         " passwords from a Mandos server" };
2469
 
    ret_errno = argp_parse(&argp, argc, argv,
2470
 
                           ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2471
 
    switch(ret_errno){
 
2481
    ret = argp_parse(&argp, argc, argv,
 
2482
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2483
    switch(ret){
2472
2484
    case 0:
2473
2485
      break;
2474
2486
    case ENOMEM:
2475
2487
    default:
2476
 
      errno = ret_errno;
 
2488
      errno = ret;
2477
2489
      perror_plus("argp_parse");
2478
2490
      exitcode = EX_OSERR;
2479
2491
      goto end;
2488
2500
       <http://bugs.debian.org/633582> */
2489
2501
    
2490
2502
    /* Re-raise privileges */
2491
 
    ret = raise_privileges();
2492
 
    if(ret != 0){
2493
 
      errno = ret;
 
2503
    ret_errno = raise_privileges();
 
2504
    if(ret_errno != 0){
 
2505
      errno = ret_errno;
2494
2506
      perror_plus("Failed to raise privileges");
2495
2507
    } else {
2496
2508
      struct stat st;
2560
2572
      }
2561
2573
      
2562
2574
      /* Lower privileges */
2563
 
      ret = lower_privileges();
2564
 
      if(ret != 0){
2565
 
        errno = ret;
 
2575
      ret_errno = lower_privileges();
 
2576
      if(ret_errno != 0){
 
2577
        errno = ret_errno;
2566
2578
        perror_plus("Failed to lower privileges");
2567
2579
      }
2568
2580
    }
2896
2908
    
2897
2909
    /* Allocate a new server */
2898
2910
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2899
 
                                 &config, NULL, NULL, &ret);
 
2911
                                 &config, NULL, NULL, &ret_errno);
2900
2912
    
2901
2913
    /* Free the Avahi configuration data */
2902
2914
    avahi_server_config_free(&config);
2905
2917
  /* Check if creating the Avahi server object succeeded */
2906
2918
  if(mc.server == NULL){
2907
2919
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2908
 
                 avahi_strerror(ret));
 
2920
                 avahi_strerror(ret_errno));
2909
2921
    exitcode = EX_UNAVAILABLE;
2910
2922
    goto end;
2911
2923
  }
2963
2975
  
2964
2976
  if(gnutls_initialized){
2965
2977
    gnutls_certificate_free_credentials(mc.cred);
 
2978
    gnutls_global_deinit();
2966
2979
    gnutls_dh_params_deinit(mc.dh_params);
2967
2980
  }
2968
2981
  
2991
3004
  
2992
3005
  /* Re-raise privileges */
2993
3006
  {
2994
 
    ret = raise_privileges();
2995
 
    if(ret != 0){
2996
 
      errno = ret;
 
3007
    ret_errno = raise_privileges();
 
3008
    if(ret_errno != 0){
 
3009
      errno = ret_errno;
2997
3010
      perror_plus("Failed to raise privileges");
2998
3011
    } else {
2999
3012
      
3007
3020
        while((interface=argz_next(interfaces_to_take_down,
3008
3021
                                   interfaces_to_take_down_size,
3009
3022
                                   interface))){
3010
 
          ret = take_down_interface(interface);
3011
 
          if(ret != 0){
3012
 
            errno = ret;
 
3023
          ret_errno = take_down_interface(interface);
 
3024
          if(ret_errno != 0){
 
3025
            errno = ret_errno;
3013
3026
            perror_plus("Failed to take down interface");
3014
3027
          }
3015
3028
        }
3020
3033
      }
3021
3034
    }
3022
3035
    
3023
 
    ret = lower_privileges_permanently();
3024
 
    if(ret != 0){
3025
 
      errno = ret;
 
3036
    ret_errno = lower_privileges_permanently();
 
3037
    if(ret_errno != 0){
 
3038
      errno = ret_errno;
3026
3039
      perror_plus("Failed to lower privileges permanently");
3027
3040
    }
3028
3041
  }
3030
3043
  free(interfaces_to_take_down);
3031
3044
  free(interfaces_hooks);
3032
3045
  
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
 
  
3089
3046
  /* Removes the GPGME temp directory and all files inside */
3090
3047
  if(tempdir != NULL){
3091
 
    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
    }
3092
3091
  }
3093
3092
  
3094
3093
  if(quit_now){