/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(), strsignal()
51
 
                                */
 
50
                                   asprintf(), strcpy() */
52
51
#include <sys/ioctl.h>          /* ioctl */
53
52
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
54
53
                                   sockaddr_in6, PF_INET6,
58
57
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
59
58
                                   inet_pton(), connect(),
60
59
                                   getnameinfo() */
61
 
#include <fcntl.h>              /* open(), unlinkat(), AT_REMOVEDIR */
 
60
#include <fcntl.h>              /* open(), unlinkat() */
62
61
#include <dirent.h>             /* opendir(), struct dirent, readdir()
63
62
                                 */
64
63
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
65
64
                                   strtoimax() */
66
 
#include <errno.h>              /* perror(), errno, EINTR, EINVAL,
67
 
                                   EAI_SYSTEM, ENETUNREACH,
68
 
                                   EHOSTUNREACH, ECONNREFUSED, EPROTO,
69
 
                                   EIO, ENOENT, ENXIO, ENOMEM, EISDIR,
70
 
                                   ENOTEMPTY,
 
65
#include <errno.h>              /* perror(), errno,
71
66
                                   program_invocation_short_name */
72
67
#include <time.h>               /* nanosleep(), time(), sleep() */
73
68
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
518
513
  fprintf_plus(stderr, "GnuTLS: %s", string);
519
514
}
520
515
 
521
 
__attribute__((nonnull(1, 2, 4), warn_unused_result))
 
516
__attribute__((nonnull, warn_unused_result))
522
517
static int init_gnutls_global(const char *pubkeyfilename,
523
518
                              const char *seckeyfilename,
524
519
                              const char *dhparamsfilename,
530
525
    fprintf_plus(stderr, "Initializing GnuTLS\n");
531
526
  }
532
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
  
533
535
  if(debug){
534
536
    /* "Use a log level over 10 to enable all debugging options."
535
537
     * - GnuTLS manual
543
545
  if(ret != GNUTLS_E_SUCCESS){
544
546
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
545
547
                 safer_gnutls_strerror(ret));
 
548
    gnutls_global_deinit();
546
549
    return -1;
547
550
  }
548
551
  
626
629
                     safer_gnutls_strerror(ret));
627
630
        dhparamsfilename = NULL;
628
631
      }
629
 
      free(params.data);
630
632
    } while(false);
631
633
  }
632
634
  if(dhparamsfilename == NULL){
753
755
 globalfail:
754
756
  
755
757
  gnutls_certificate_free_credentials(mc->cred);
 
758
  gnutls_global_deinit();
756
759
  gnutls_dh_params_deinit(mc->dh_params);
757
760
  return -1;
758
761
}
819
822
 
820
823
/* Set effective uid to 0, return errno */
821
824
__attribute__((warn_unused_result))
822
 
int raise_privileges(void){
823
 
  int old_errno = errno;
824
 
  int ret = 0;
 
825
error_t raise_privileges(void){
 
826
  error_t old_errno = errno;
 
827
  error_t ret_errno = 0;
825
828
  if(seteuid(0) == -1){
826
 
    ret = errno;
 
829
    ret_errno = errno;
827
830
  }
828
831
  errno = old_errno;
829
 
  return ret;
 
832
  return ret_errno;
830
833
}
831
834
 
832
835
/* Set effective and real user ID to 0.  Return errno. */
833
836
__attribute__((warn_unused_result))
834
 
int raise_privileges_permanently(void){
835
 
  int old_errno = errno;
836
 
  int ret = raise_privileges();
837
 
  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){
838
841
    errno = old_errno;
839
 
    return ret;
 
842
    return ret_errno;
840
843
  }
841
844
  if(setuid(0) == -1){
842
 
    ret = errno;
 
845
    ret_errno = errno;
843
846
  }
844
847
  errno = old_errno;
845
 
  return ret;
 
848
  return ret_errno;
846
849
}
847
850
 
848
851
/* Set effective user ID to unprivileged saved user ID */
849
852
__attribute__((warn_unused_result))
850
 
int lower_privileges(void){
851
 
  int old_errno = errno;
852
 
  int ret = 0;
 
853
error_t lower_privileges(void){
 
854
  error_t old_errno = errno;
 
855
  error_t ret_errno = 0;
853
856
  if(seteuid(uid) == -1){
854
 
    ret = errno;
 
857
    ret_errno = errno;
855
858
  }
856
859
  errno = old_errno;
857
 
  return ret;
 
860
  return ret_errno;
858
861
}
859
862
 
860
863
/* Lower privileges permanently */
861
864
__attribute__((warn_unused_result))
862
 
int lower_privileges_permanently(void){
863
 
  int old_errno = errno;
864
 
  int ret = 0;
 
865
error_t lower_privileges_permanently(void){
 
866
  error_t old_errno = errno;
 
867
  error_t ret_errno = 0;
865
868
  if(setuid(uid) == -1){
866
 
    ret = errno;
 
869
    ret_errno = errno;
867
870
  }
868
871
  errno = old_errno;
869
 
  return ret;
 
872
  return ret_errno;
870
873
}
871
874
 
872
875
/* Helper function to add_local_route() and delete_local_route() */
1220
1223
                    sizeof(struct sockaddr_in));
1221
1224
    }
1222
1225
    if(ret < 0){
1223
 
      if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
 
1226
      if(errno == ENETUNREACH
1224
1227
         and if_index != AVAHI_IF_UNSPEC
1225
1228
         and connect_to == NULL
1226
1229
         and not route_added and
1239
1242
           with an explicit route added with the server's address.
1240
1243
           
1241
1244
           Avahi bug reference:
1242
 
           https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
 
1245
           http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1243
1246
           https://bugs.debian.org/587961
1244
1247
        */
1245
1248
        if(debug){
1425
1428
                                               &decrypted_buffer, mc);
1426
1429
    if(decrypted_buffer_size >= 0){
1427
1430
      
1428
 
      clearerr(stdout);
1429
1431
      written = 0;
1430
1432
      while(written < (size_t) decrypted_buffer_size){
1431
1433
        if(quit_now){
1447
1449
        }
1448
1450
        written += (size_t)ret;
1449
1451
      }
1450
 
      ret = fflush(stdout);
1451
 
      if(ret != 0){
1452
 
        int e = errno;
1453
 
        if(debug){
1454
 
          fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1455
 
                       strerror(errno));
1456
 
        }
1457
 
        errno = e;
1458
 
        goto mandos_end;
1459
 
      }
1460
1452
      retval = 0;
1461
1453
    }
1462
1454
  }
1636
1628
__attribute__((nonnull, warn_unused_result))
1637
1629
bool get_flags(const char *ifname, struct ifreq *ifr){
1638
1630
  int ret;
1639
 
  int old_errno;
 
1631
  error_t ret_errno;
1640
1632
  
1641
1633
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1642
1634
  if(s < 0){
1643
 
    old_errno = errno;
 
1635
    ret_errno = errno;
1644
1636
    perror_plus("socket");
1645
 
    errno = old_errno;
 
1637
    errno = ret_errno;
1646
1638
    return false;
1647
1639
  }
1648
 
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1649
 
  ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
 
1640
  strcpy(ifr->ifr_name, ifname);
1650
1641
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1651
1642
  if(ret == -1){
1652
1643
    if(debug){
1653
 
      old_errno = errno;
 
1644
      ret_errno = errno;
1654
1645
      perror_plus("ioctl SIOCGIFFLAGS");
1655
 
      errno = old_errno;
 
1646
      errno = ret_errno;
1656
1647
    }
1657
1648
    return false;
1658
1649
  }
1922
1913
      return;
1923
1914
    }
1924
1915
  }
 
1916
#ifdef __GLIBC__
 
1917
#if __GLIBC_PREREQ(2, 15)
1925
1918
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
1926
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__ */
1927
1928
  if(numhooks == -1){
1928
1929
    perror_plus("scandir");
1929
1930
    return;
2084
2085
}
2085
2086
 
2086
2087
__attribute__((nonnull, warn_unused_result))
2087
 
int bring_up_interface(const char *const interface,
2088
 
                       const float delay){
2089
 
  int old_errno = errno;
 
2088
error_t bring_up_interface(const char *const interface,
 
2089
                           const float delay){
 
2090
  error_t old_errno = errno;
2090
2091
  int ret;
2091
2092
  struct ifreq network;
2092
2093
  unsigned int if_index = if_nametoindex(interface);
2102
2103
  }
2103
2104
  
2104
2105
  if(not interface_is_up(interface)){
2105
 
    int ret_errno = 0;
2106
 
    int ioctl_errno = 0;
 
2106
    error_t ret_errno = 0, ioctl_errno = 0;
2107
2107
    if(not get_flags(interface, &network)){
2108
2108
      ret_errno = errno;
2109
2109
      fprintf_plus(stderr, "Failed to get flags for interface "
2212
2212
}
2213
2213
 
2214
2214
__attribute__((nonnull, warn_unused_result))
2215
 
int take_down_interface(const char *const interface){
2216
 
  int old_errno = errno;
 
2215
error_t take_down_interface(const char *const interface){
 
2216
  error_t old_errno = errno;
2217
2217
  struct ifreq network;
2218
2218
  unsigned int if_index = if_nametoindex(interface);
2219
2219
  if(if_index == 0){
2222
2222
    return ENXIO;
2223
2223
  }
2224
2224
  if(interface_is_up(interface)){
2225
 
    int ret_errno = 0;
2226
 
    int ioctl_errno = 0;
 
2225
    error_t ret_errno = 0, ioctl_errno = 0;
2227
2226
    if(not get_flags(interface, &network) and debug){
2228
2227
      ret_errno = errno;
2229
2228
      fprintf_plus(stderr, "Failed to get flags for interface "
2479
2478
                         .args_doc = "",
2480
2479
                         .doc = "Mandos client -- Get and decrypt"
2481
2480
                         " passwords from a Mandos server" };
2482
 
    ret_errno = argp_parse(&argp, argc, argv,
2483
 
                           ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2484
 
    switch(ret_errno){
 
2481
    ret = argp_parse(&argp, argc, argv,
 
2482
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2483
    switch(ret){
2485
2484
    case 0:
2486
2485
      break;
2487
2486
    case ENOMEM:
2488
2487
    default:
2489
 
      errno = ret_errno;
 
2488
      errno = ret;
2490
2489
      perror_plus("argp_parse");
2491
2490
      exitcode = EX_OSERR;
2492
2491
      goto end;
2498
2497
  
2499
2498
  {
2500
2499
    /* Work around Debian bug #633582:
2501
 
       <https://bugs.debian.org/633582> */
 
2500
       <http://bugs.debian.org/633582> */
2502
2501
    
2503
2502
    /* Re-raise privileges */
2504
 
    ret = raise_privileges();
2505
 
    if(ret != 0){
2506
 
      errno = ret;
 
2503
    ret_errno = raise_privileges();
 
2504
    if(ret_errno != 0){
 
2505
      errno = ret_errno;
2507
2506
      perror_plus("Failed to raise privileges");
2508
2507
    } else {
2509
2508
      struct stat st;
2573
2572
      }
2574
2573
      
2575
2574
      /* Lower privileges */
2576
 
      ret = lower_privileges();
2577
 
      if(ret != 0){
2578
 
        errno = ret;
 
2575
      ret_errno = lower_privileges();
 
2576
      if(ret_errno != 0){
 
2577
        errno = ret_errno;
2579
2578
        perror_plus("Failed to lower privileges");
2580
2579
      }
2581
2580
    }
2909
2908
    
2910
2909
    /* Allocate a new server */
2911
2910
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2912
 
                                 &config, NULL, NULL, &ret);
 
2911
                                 &config, NULL, NULL, &ret_errno);
2913
2912
    
2914
2913
    /* Free the Avahi configuration data */
2915
2914
    avahi_server_config_free(&config);
2918
2917
  /* Check if creating the Avahi server object succeeded */
2919
2918
  if(mc.server == NULL){
2920
2919
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2921
 
                 avahi_strerror(ret));
 
2920
                 avahi_strerror(ret_errno));
2922
2921
    exitcode = EX_UNAVAILABLE;
2923
2922
    goto end;
2924
2923
  }
2959
2958
 end:
2960
2959
  
2961
2960
  if(debug){
2962
 
    if(signal_received){
2963
 
      fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
2964
 
                   argv[0], signal_received,
2965
 
                   strsignal(signal_received));
2966
 
    } else {
2967
 
      fprintf_plus(stderr, "%s exiting\n", argv[0]);
2968
 
    }
 
2961
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
2969
2962
  }
2970
2963
  
2971
2964
  /* Cleanup things */
2982
2975
  
2983
2976
  if(gnutls_initialized){
2984
2977
    gnutls_certificate_free_credentials(mc.cred);
 
2978
    gnutls_global_deinit();
2985
2979
    gnutls_dh_params_deinit(mc.dh_params);
2986
2980
  }
2987
2981
  
3010
3004
  
3011
3005
  /* Re-raise privileges */
3012
3006
  {
3013
 
    ret = raise_privileges();
3014
 
    if(ret != 0){
3015
 
      errno = ret;
 
3007
    ret_errno = raise_privileges();
 
3008
    if(ret_errno != 0){
 
3009
      errno = ret_errno;
3016
3010
      perror_plus("Failed to raise privileges");
3017
3011
    } else {
3018
3012
      
3026
3020
        while((interface=argz_next(interfaces_to_take_down,
3027
3021
                                   interfaces_to_take_down_size,
3028
3022
                                   interface))){
3029
 
          ret = take_down_interface(interface);
3030
 
          if(ret != 0){
3031
 
            errno = ret;
 
3023
          ret_errno = take_down_interface(interface);
 
3024
          if(ret_errno != 0){
 
3025
            errno = ret_errno;
3032
3026
            perror_plus("Failed to take down interface");
3033
3027
          }
3034
3028
        }
3039
3033
      }
3040
3034
    }
3041
3035
    
3042
 
    ret = lower_privileges_permanently();
3043
 
    if(ret != 0){
3044
 
      errno = ret;
 
3036
    ret_errno = lower_privileges_permanently();
 
3037
    if(ret_errno != 0){
 
3038
      errno = ret_errno;
3045
3039
      perror_plus("Failed to lower privileges permanently");
3046
3040
    }
3047
3041
  }
3049
3043
  free(interfaces_to_take_down);
3050
3044
  free(interfaces_hooks);
3051
3045
  
3052
 
  void clean_dir_at(int base, const char * const dirname,
3053
 
                    uintmax_t level){
3054
 
    struct dirent **direntries = NULL;
3055
 
    int dret;
3056
 
    int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3057
 
                                                O_RDONLY
3058
 
                                                | O_NOFOLLOW
3059
 
                                                | O_DIRECTORY
3060
 
                                                | O_PATH));
3061
 
    if(dir_fd == -1){
3062
 
      perror_plus("open");
3063
 
    }
3064
 
    int numentries = scandirat(dir_fd, ".", &direntries,
3065
 
                               notdotentries, alphasort);
3066
 
    if(numentries >= 0){
3067
 
      for(int i = 0; i < numentries; i++){
3068
 
        if(debug){
3069
 
          fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3070
 
                       dirname, direntries[i]->d_name);
3071
 
        }
3072
 
        dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3073
 
        if(dret == -1){
3074
 
          if(errno == EISDIR){
3075
 
              dret = unlinkat(dir_fd, direntries[i]->d_name,
3076
 
                              AT_REMOVEDIR);
3077
 
          }         
3078
 
          if((dret == -1) and (errno == ENOTEMPTY)
3079
 
             and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3080
 
                  == 0) and (level == 0)){
3081
 
            /* Recurse only in this special case */
3082
 
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3083
 
            dret = 0;
3084
 
          }
3085
 
          if(dret == -1){
3086
 
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3087
 
                         direntries[i]->d_name, strerror(errno));
3088
 
          }
3089
 
        }
3090
 
        free(direntries[i]);
3091
 
      }
3092
 
      
3093
 
      /* need to clean even if 0 because man page doesn't specify */
3094
 
      free(direntries);
3095
 
      if(numentries == -1){
3096
 
        perror_plus("scandirat");
3097
 
      }
3098
 
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
3099
 
      if(dret == -1 and errno != ENOENT){
3100
 
        perror_plus("rmdir");
3101
 
      }
3102
 
    } else {
3103
 
      perror_plus("scandirat");
3104
 
    }
3105
 
    close(dir_fd);
3106
 
  }
3107
 
  
3108
3046
  /* Removes the GPGME temp directory and all files inside */
3109
3047
  if(tempdir != NULL){
3110
 
    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
    }
3111
3091
  }
3112
3092
  
3113
3093
  if(quit_now){