/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:
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(), strcpy() */
51
51
#include <sys/ioctl.h>          /* ioctl */
52
52
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
53
53
                                   sockaddr_in6, PF_INET6,
305
305
      return false;
306
306
    }
307
307
    
308
 
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
 
308
    ret = close(fd);
309
309
    if(ret == -1){
310
310
      perror_plus("close");
311
311
    }
516
516
__attribute__((nonnull, warn_unused_result))
517
517
static int init_gnutls_global(const char *pubkeyfilename,
518
518
                              const char *seckeyfilename,
 
519
                              const char *dhparamsfilename,
519
520
                              mandos_context *mc){
520
521
  int ret;
521
522
  unsigned int uret;
575
576
                 safer_gnutls_strerror(ret));
576
577
    goto globalfail;
577
578
  }
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;
 
579
  /* If a Diffie-Hellman parameters file was given, try to use it */
 
580
  if(dhparamsfilename != NULL){
 
581
    gnutls_datum_t params = { .data = NULL, .size = 0 };
 
582
    do {
 
583
      int dhpfile = open(dhparamsfilename, O_RDONLY);
 
584
      if(dhpfile == -1){
 
585
        perror_plus("open");
 
586
        dhparamsfilename = NULL;
 
587
        break;
 
588
      }
 
589
      size_t params_capacity = 0;
585
590
      while(true){
586
 
        buffer_capacity = incbuffer((char **)&buffer.data,
587
 
                                    (size_t)buffer.size,
588
 
                                    (size_t)buffer_capacity);
589
 
        if(buffer_capacity == 0){
 
591
        params_capacity = incbuffer((char **)&params.data,
 
592
                                    (size_t)params.size,
 
593
                                    (size_t)params_capacity);
 
594
        if(params_capacity == 0){
590
595
          perror_plus("incbuffer");
591
 
          free(buffer.data);
592
 
          buffer.data = NULL;
 
596
          free(params.data);
 
597
          params.data = NULL;
 
598
          dhparamsfilename = NULL;
593
599
          break;
594
600
        }
595
 
        ssize_t bytes_read = read(secfile, buffer.data + buffer.size,
 
601
        ssize_t bytes_read = read(dhpfile,
 
602
                                  params.data + params.size,
596
603
                                  BUFFER_SIZE);
597
604
        /* EOF */
598
605
        if(bytes_read == 0){
601
608
        /* check bytes_read for failure */
602
609
        if(bytes_read < 0){
603
610
          perror_plus("read");
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);
 
611
          free(params.data);
 
612
          params.data = NULL;
 
613
          dhparamsfilename = NULL;
 
614
          break;
 
615
        }
 
616
        params.size += (unsigned int)bytes_read;
 
617
      }
 
618
      if(params.data == NULL){
 
619
        dhparamsfilename = NULL;
 
620
      }
 
621
      if(dhparamsfilename == NULL){
 
622
        break;
 
623
      }
 
624
      ret = gnutls_dh_params_import_pkcs3(mc->dh_params, &params,
 
625
                                          GNUTLS_X509_FMT_PEM);
 
626
      if(ret != GNUTLS_E_SUCCESS){
 
627
        fprintf_plus(stderr, "Failed to parse DH parameters in file"
 
628
                     " \"%s\": %s\n", dhparamsfilename,
 
629
                     safer_gnutls_strerror(ret));
 
630
        dhparamsfilename = NULL;
 
631
      }
 
632
    } while(false);
 
633
  }
 
634
  if(dhparamsfilename == NULL){
 
635
    if(mc->dh_bits == 0){
 
636
      /* Find out the optimal number of DH bits */
 
637
      /* Try to read the private key file */
 
638
      gnutls_datum_t buffer = { .data = NULL, .size = 0 };
 
639
      do {
 
640
        int secfile = open(seckeyfilename, O_RDONLY);
 
641
        if(secfile == -1){
 
642
          perror_plus("open");
 
643
          break;
 
644
        }
 
645
        size_t buffer_capacity = 0;
 
646
        while(true){
 
647
          buffer_capacity = incbuffer((char **)&buffer.data,
 
648
                                      (size_t)buffer.size,
 
649
                                      (size_t)buffer_capacity);
 
650
          if(buffer_capacity == 0){
 
651
            perror_plus("incbuffer");
 
652
            free(buffer.data);
 
653
            buffer.data = NULL;
 
654
            break;
 
655
          }
 
656
          ssize_t bytes_read = read(secfile,
 
657
                                    buffer.data + buffer.size,
 
658
                                    BUFFER_SIZE);
 
659
          /* EOF */
 
660
          if(bytes_read == 0){
 
661
            break;
 
662
          }
 
663
          /* check bytes_read for failure */
 
664
          if(bytes_read < 0){
 
665
            perror_plus("read");
 
666
            free(buffer.data);
 
667
            buffer.data = NULL;
 
668
            break;
 
669
          }
 
670
          buffer.size += (unsigned int)bytes_read;
 
671
        }
 
672
        close(secfile);
 
673
      } while(false);
 
674
      /* If successful, use buffer to parse private key */
 
675
      gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
676
      if(buffer.data != NULL){
 
677
        {
 
678
          gnutls_openpgp_privkey_t privkey = NULL;
 
679
          ret = gnutls_openpgp_privkey_init(&privkey);
627
680
          if(ret != GNUTLS_E_SUCCESS){
628
 
            fprintf_plus(stderr, "Error importing OpenPGP key : %s",
 
681
            fprintf_plus(stderr, "Error initializing OpenPGP key"
 
682
                         " structure: %s",
629
683
                         safer_gnutls_strerror(ret));
630
 
            privkey = NULL;
 
684
            free(buffer.data);
 
685
            buffer.data = NULL;
 
686
          } else {
 
687
            ret = gnutls_openpgp_privkey_import
 
688
              (privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
 
689
            if(ret != GNUTLS_E_SUCCESS){
 
690
              fprintf_plus(stderr, "Error importing OpenPGP key : %s",
 
691
                           safer_gnutls_strerror(ret));
 
692
              privkey = NULL;
 
693
            }
 
694
            free(buffer.data);
 
695
            buffer.data = NULL;
 
696
            if(privkey != NULL){
 
697
              /* Use private key to suggest an appropriate
 
698
                 sec_param */
 
699
              sec_param = gnutls_openpgp_privkey_sec_param(privkey);
 
700
              gnutls_openpgp_privkey_deinit(privkey);
 
701
              if(debug){
 
702
                fprintf_plus(stderr, "This OpenPGP key implies using"
 
703
                             " a GnuTLS security parameter \"%s\".\n",
 
704
                             safe_string(gnutls_sec_param_get_name
 
705
                                         (sec_param)));
 
706
              }
 
707
            }
631
708
          }
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
 
            }
 
709
        }
 
710
        if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
 
711
          /* Err on the side of caution */
 
712
          sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
713
          if(debug){
 
714
            fprintf_plus(stderr, "Falling back to security parameter"
 
715
                         " \"%s\"\n",
 
716
                         safe_string(gnutls_sec_param_get_name
 
717
                                     (sec_param)));
644
718
          }
645
719
        }
646
720
      }
647
 
      if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
648
 
        /* Err on the side of caution */
649
 
        sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
721
      uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
 
722
      if(uret != 0){
 
723
        mc->dh_bits = uret;
650
724
        if(debug){
651
 
          fprintf_plus(stderr, "Falling back to security parameter"
652
 
                       " \"%s\"\n",
 
725
          fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
 
726
                       " implies %u DH bits; using that.\n",
653
727
                       safe_string(gnutls_sec_param_get_name
654
 
                                   (sec_param)));
 
728
                                   (sec_param)),
 
729
                       mc->dh_bits);
655
730
        }
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",
 
731
      } else {
 
732
        fprintf_plus(stderr, "Failed to get implied number of DH"
 
733
                     " bits for security parameter \"%s\"): %s\n",
664
734
                     safe_string(gnutls_sec_param_get_name
665
735
                                 (sec_param)),
666
 
                     mc->dh_bits);
 
736
                     safer_gnutls_strerror(ret));
 
737
        goto globalfail;
667
738
      }
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)),
 
739
    } else if(debug){
 
740
      fprintf_plus(stderr, "DH bits explicitly set to %u\n",
 
741
                   mc->dh_bits);
 
742
    }
 
743
    ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
 
744
    if(ret != GNUTLS_E_SUCCESS){
 
745
      fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
 
746
                   " bits): %s\n", mc->dh_bits,
672
747
                   safer_gnutls_strerror(ret));
673
748
      goto globalfail;
674
749
    }
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
 
  
 
750
  }
686
751
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
687
752
  
688
753
  return 0;
748
813
  /* ignore client certificate if any. */
749
814
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
750
815
  
751
 
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
752
 
  
753
816
  return 0;
754
817
}
755
818
 
868
931
      perror_plus("dup2(devnull, STDIN_FILENO)");
869
932
      _exit(EX_OSERR);
870
933
    }
871
 
    ret = (int)TEMP_FAILURE_RETRY(close(devnull));
 
934
    ret = close(devnull);
872
935
    if(ret == -1){
873
936
      perror_plus("close");
874
937
      _exit(EX_OSERR);
891
954
                                                   helper, O_RDONLY));
892
955
    if(helper_fd == -1){
893
956
      perror_plus("openat");
 
957
      close(helperdir_fd);
894
958
      _exit(EX_UNAVAILABLE);
895
959
    }
896
 
    TEMP_FAILURE_RETRY(close(helperdir_fd));
 
960
    close(helperdir_fd);
897
961
#ifdef __GNUC__
898
962
#pragma GCC diagnostic push
899
963
#pragma GCC diagnostic ignored "-Wcast-qual"
1067
1131
    goto mandos_end;
1068
1132
  }
1069
1133
  
1070
 
  memset(&to, 0, sizeof(to));
1071
1134
  if(af == AF_INET6){
1072
 
    ((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
1073
 
    ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
 
1135
    struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
 
1136
    *to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
 
1137
    ret = inet_pton(af, ip, &to6->sin6_addr);
1074
1138
  } else {                      /* IPv4 */
1075
 
    ((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
1076
 
    ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
 
1139
    struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
 
1140
    *to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
 
1141
    ret = inet_pton(af, ip, &to4->sin_addr);
1077
1142
  }
1078
1143
  if(ret < 0 ){
1079
1144
    int e = errno;
1402
1467
    free(decrypted_buffer);
1403
1468
    free(buffer);
1404
1469
    if(tcp_sd >= 0){
1405
 
      ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
 
1470
      ret = close(tcp_sd);
1406
1471
    }
1407
1472
    if(ret == -1){
1408
1473
      if(e == 0){
1947
2012
        perror_plus("openat");
1948
2013
        _exit(EXIT_FAILURE);
1949
2014
      }
1950
 
      if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
2015
      if(close(hookdir_fd) == -1){
1951
2016
        perror_plus("close");
1952
2017
        _exit(EXIT_FAILURE);
1953
2018
      }
1956
2021
        perror_plus("dup2(devnull, STDIN_FILENO)");
1957
2022
        _exit(EX_OSERR);
1958
2023
      }
1959
 
      ret = (int)TEMP_FAILURE_RETRY(close(devnull));
 
2024
      ret = close(devnull);
1960
2025
      if(ret == -1){
1961
2026
        perror_plus("close");
1962
2027
        _exit(EX_OSERR);
2011
2076
    free(direntry);
2012
2077
  }
2013
2078
  free(direntries);
2014
 
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
2079
  if(close(hookdir_fd) == -1){
2015
2080
    perror_plus("close");
2016
2081
  } else {
2017
2082
    hookdir_fd = -1;
2057
2122
    }
2058
2123
    
2059
2124
    if(quit_now){
2060
 
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2125
      ret = close(sd);
2061
2126
      if(ret == -1){
2062
2127
        perror_plus("close");
2063
2128
      }
2113
2178
    }
2114
2179
    
2115
2180
    /* Close the socket */
2116
 
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2181
    ret = close(sd);
2117
2182
    if(ret == -1){
2118
2183
      perror_plus("close");
2119
2184
    }
2201
2266
    }
2202
2267
    
2203
2268
    /* Close the socket */
2204
 
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2269
    int ret = close(sd);
2205
2270
    if(ret == -1){
2206
2271
      perror_plus("close");
2207
2272
    }
2223
2288
 
2224
2289
int main(int argc, char *argv[]){
2225
2290
  mandos_context mc = { .server = NULL, .dh_bits = 0,
2226
 
                        .priority = "SECURE256:!CTYPE-X.509:"
2227
 
                        "+CTYPE-OPENPGP:!RSA", .current_server = NULL,
2228
 
                        .interfaces = NULL, .interfaces_size = 0 };
 
2291
                        .priority = "SECURE256:!CTYPE-X.509"
 
2292
                        ":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
 
2293
                        .current_server = NULL, .interfaces = NULL,
 
2294
                        .interfaces_size = 0 };
2229
2295
  AvahiSServiceBrowser *sb = NULL;
2230
2296
  error_t ret_errno;
2231
2297
  int ret;
2240
2306
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2241
2307
  const char *seckey = PATHDIR "/" SECKEY;
2242
2308
  const char *pubkey = PATHDIR "/" PUBKEY;
 
2309
  const char *dh_params_file = NULL;
2243
2310
  char *interfaces_hooks = NULL;
2244
2311
  
2245
2312
  bool gnutls_initialized = false;
2298
2365
        .doc = "Bit length of the prime number used in the"
2299
2366
        " Diffie-Hellman key exchange",
2300
2367
        .group = 2 },
 
2368
      { .name = "dh-params", .key = 134,
 
2369
        .arg = "FILE",
 
2370
        .doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
 
2371
        " for the Diffie-Hellman key exchange",
 
2372
        .group = 2 },
2301
2373
      { .name = "priority", .key = 130,
2302
2374
        .arg = "STRING",
2303
2375
        .doc = "GnuTLS priority string for the TLS handshake",
2358
2430
        }
2359
2431
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2360
2432
        break;
 
2433
      case 134:                 /* --dh-params */
 
2434
        dh_params_file = arg;
 
2435
        break;
2361
2436
      case 130:                 /* --priority */
2362
2437
        mc.priority = arg;
2363
2438
        break;
2449
2524
              }
2450
2525
            }
2451
2526
          }
2452
 
          TEMP_FAILURE_RETRY(close(seckey_fd));
 
2527
          close(seckey_fd);
2453
2528
        }
2454
2529
      }
2455
2530
      
2470
2545
              }
2471
2546
            }
2472
2547
          }
2473
 
          TEMP_FAILURE_RETRY(close(pubkey_fd));
 
2548
          close(pubkey_fd);
 
2549
        }
 
2550
      }
 
2551
      
 
2552
      if(dh_params_file != NULL
 
2553
         and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
 
2554
        int dhparams_fd = open(dh_params_file, O_RDONLY);
 
2555
        if(dhparams_fd == -1){
 
2556
          perror_plus("open");
 
2557
        } else {
 
2558
          ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
 
2559
          if(ret == -1){
 
2560
            perror_plus("fstat");
 
2561
          } else {
 
2562
            if(S_ISREG(st.st_mode)
 
2563
               and st.st_uid == 0 and st.st_gid == 0){
 
2564
              ret = fchown(dhparams_fd, uid, gid);
 
2565
              if(ret == -1){
 
2566
                perror_plus("fchown");
 
2567
              }
 
2568
            }
 
2569
          }
 
2570
          close(dhparams_fd);
2474
2571
        }
2475
2572
      }
2476
2573
      
2653
2750
      errno = bring_up_interface(interface, delay);
2654
2751
      if(not interface_was_up){
2655
2752
        if(errno != 0){
2656
 
          perror_plus("Failed to bring up interface");
 
2753
          fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
 
2754
                       " %s\n", interface, strerror(errno));
2657
2755
        } else {
2658
2756
          errno = argz_add(&interfaces_to_take_down,
2659
2757
                           &interfaces_to_take_down_size,
2682
2780
    goto end;
2683
2781
  }
2684
2782
  
2685
 
  ret = init_gnutls_global(pubkey, seckey, &mc);
 
2783
  ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
2686
2784
  if(ret == -1){
2687
2785
    fprintf_plus(stderr, "init_gnutls_global failed\n");
2688
2786
    exitcode = EX_UNAVAILABLE;
2988
3086
          perror_plus("rmdir");
2989
3087
        }
2990
3088
      }
2991
 
      TEMP_FAILURE_RETRY(close(tempdir_fd));
 
3089
      close(tempdir_fd);
2992
3090
    }
2993
3091
  }
2994
3092