Changeset f5342b1 in mod_gnutls for src/gnutls_hooks.c


Ignore:
Timestamp:
Apr 16, 2018, 8:42:39 PM (3 years ago)
Author:
Daniel Kahn Gillmor <dkg@…>
Branches:
debian/master, debian/stretch-backports, upstream
Children:
300ae82, f4ac9ccd
Parents:
e105d3e (diff), 2a912c3 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

New upstream version 0.8.3

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/gnutls_hooks.c

    re105d3e rf5342b1  
    44 *  Copyright 2011 Dash Shendy
    55 *  Copyright 2013-2014 Daniel Kahn Gillmor
    6  *  Copyright 2015-2016 Thomas Klute
     6 *  Copyright 2015-2017 Thomas Klute
    77 *
    88 *  Licensed under the Apache License, Version 2.0 (the "License");
     
    7070    session_ticket_key.data = NULL;
    7171    session_ticket_key.size = 0;
    72         /* Deinitialize GnuTLS Library */
    73     gnutls_global_deinit();
    7472    return APR_SUCCESS;
    7573}
     
    118116    }
    119117
    120         /* Initialize GnuTLS Library */
    121     ret = gnutls_global_init();
    122     if (ret < 0) {
    123                 ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, plog, "gnutls_global_init: %s", gnutls_strerror(ret));
    124                 return DONE;
    125     }
    126 
    127118        /* Generate a Session Key */
    128119    ret = gnutls_session_ticket_key_generate(&session_ticket_key);
     
    143134}
    144135
    145 static int mgs_select_virtual_server_cb(gnutls_session_t session) {
    146 
    147     mgs_handle_t *ctxt = NULL;
    148     mgs_srvconf_rec *tsc = NULL;
     136/**
     137 * Post client hello function for GnuTLS, used to configure the TLS
     138 * server based on virtual host configuration. Uses SNI to select the
     139 * virtual host if available.
     140 *
     141 * @param session the TLS session
     142 *
     143 * @return zero or a GnuTLS error code, as required by GnuTLS hook
     144 * definition
     145 */
     146static int mgs_select_virtual_server_cb(gnutls_session_t session)
     147{
    149148    int ret = 0;
    150 
    151     _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
    152 
    153     ctxt = gnutls_transport_get_ptr(session);
    154 
    155     /* find the virtual server */
    156     tsc = mgs_find_sni_server(session);
    157 
    158     if (tsc != NULL) {
    159         // Found a TLS vhost based on the SNI from the client; use it instead.
     149    mgs_handle_t *ctxt = gnutls_session_get_ptr(session);
     150
     151    /* try to find a virtual host */
     152    mgs_srvconf_rec *tsc = mgs_find_sni_server(session);
     153    if (tsc != NULL)
     154    {
     155        /* Found a TLS vhost based on the SNI, configure the
     156         * connection context. */
    160157        ctxt->sc = tsc;
    161158        }
     
    186183     * negotiation.
    187184     */
    188 
    189185    ret = gnutls_priority_set(session, ctxt->sc->priorities);
     186
    190187    /* actually it shouldn't fail since we have checked at startup */
    191188    return ret;
    192 
    193189}
    194190
     
    313309}
    314310
     311
     312
     313#if GNUTLS_VERSION_NUMBER >= 0x030506
     314#define HAVE_KNOWN_DH_GROUPS 1
     315#endif
     316#ifdef HAVE_KNOWN_DH_GROUPS
     317/**
     318 * Try to estimate a GnuTLS security parameter based on the given
     319 * private key. Any errors are logged.
     320 *
     321 * @param s The `server_rec` to use for logging
     322 *
     323 * @param key The private key to use
     324 *
     325 * @return `gnutls_sec_param_t` as returned by
     326 * `gnutls_pk_bits_to_sec_param` for the key properties, or
     327 * GNUTLS_SEC_PARAM_UNKNOWN in case of error
     328 */
     329static gnutls_sec_param_t sec_param_from_privkey(server_rec *server,
     330                                                 gnutls_privkey_t key)
     331{
     332    unsigned int bits = 0;
     333    int pk_algo = gnutls_privkey_get_pk_algorithm(key, &bits);
     334    if (pk_algo < 0)
     335    {
     336        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, server,
     337                     "%s: Could not get private key parameters: %s (%d)",
     338                     __func__, gnutls_strerror(pk_algo), pk_algo);
     339        return GNUTLS_SEC_PARAM_UNKNOWN;
     340    }
     341    return gnutls_pk_bits_to_sec_param(pk_algo, bits);
     342}
     343#else
     344/** ffdhe2048 DH group as defined in RFC 7919, Appendix A.1. This is
     345 * the default DH group if mod_gnutls is compiled agains a GnuTLS
     346 * version that does not provide known DH groups based on security
     347 * parameters (before 3.5.6). */
     348static const char FFDHE2048_PKCS3[] =
     349    "-----BEGIN DH PARAMETERS-----\n"
     350    "MIIBDAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz\n"
     351    "+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a\n"
     352    "87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7\n"
     353    "YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi\n"
     354    "7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD\n"
     355    "ssbzSibBsu/6iGtCOGEoXJf//////////wIBAgICAQA=\n"
     356    "-----END DH PARAMETERS-----\n";
     357const gnutls_datum_t default_dh_params = {
     358    (void *) FFDHE2048_PKCS3,
     359    sizeof(FFDHE2048_PKCS3)
     360};
     361#endif
     362
     363
     364
     365/**
     366 * Configure the default DH groups to use for the given server. When
     367 * compiled against GnuTLS version 3.5.6 or newer the known DH group
     368 * matching the GnuTLS security parameter estimated from the private
     369 * key is used. Otherwise the ffdhe2048 DH group as defined in RFC
     370 * 7919, Appendix A.1 is the default.
     371 *
     372 * @param server the host to configure
     373 *
     374 * @return `OK` on success, `HTTP_UNAUTHORIZED` otherwise
     375 */
     376static int set_default_dh_param(server_rec *server)
     377{
     378    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
     379        ap_get_module_config(server->module_config, &gnutls_module);
     380
     381#ifdef HAVE_KNOWN_DH_GROUPS
     382    gnutls_sec_param_t seclevel = GNUTLS_SEC_PARAM_UNKNOWN;
     383    if (sc->privkey_x509)
     384    {
     385        seclevel = sec_param_from_privkey(server, sc->privkey_x509);
     386        ap_log_error(APLOG_MARK, APLOG_TRACE1, APR_SUCCESS, server,
     387                     "%s: GnuTLS security param estimated based on "
     388                     "private key '%s': %s",
     389                     __func__, sc->x509_key_file,
     390                     gnutls_sec_param_get_name(seclevel));
     391    }
     392
     393    if (seclevel == GNUTLS_SEC_PARAM_UNKNOWN)
     394        seclevel = GNUTLS_SEC_PARAM_MEDIUM;
     395    ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, server,
     396                 "%s: Setting DH params for security level '%s'.",
     397                 __func__, gnutls_sec_param_get_name(seclevel));
     398
     399    int ret = gnutls_certificate_set_known_dh_params(sc->certs, seclevel);
     400    if (ret < 0)
     401    {
     402        ap_log_error(APLOG_MARK, APLOG_EMERG, APR_EGENERAL, server,
     403                     "%s: setting known DH params failed: %s (%d)",
     404                     __func__, gnutls_strerror(ret), ret);
     405        return HTTP_UNAUTHORIZED;
     406    }
     407    ret = gnutls_anon_set_server_known_dh_params(sc->anon_creds, seclevel);
     408    if (ret < 0)
     409    {
     410        ap_log_error(APLOG_MARK, APLOG_EMERG, APR_EGENERAL, server,
     411                     "%s: setting known DH params failed: %s (%d)",
     412                     __func__, gnutls_strerror(ret), ret);
     413        return HTTP_UNAUTHORIZED;
     414    }
     415#else
     416    int ret = gnutls_dh_params_init(&sc->dh_params);
     417    if (ret < 0)
     418    {
     419        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, server,
     420                     "%s: Failed to initialize DH params structure: "
     421                     "%s (%d)", __func__, gnutls_strerror(ret), ret);
     422        return HTTP_UNAUTHORIZED;
     423    }
     424    ret = gnutls_dh_params_import_pkcs3(sc->dh_params, &default_dh_params,
     425                                        GNUTLS_X509_FMT_PEM);
     426    if (ret < 0)
     427    {
     428        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, server,
     429                     "%s: Failed to import default DH params: %s (%d)",
     430                     __func__, gnutls_strerror(ret), ret);
     431        return HTTP_UNAUTHORIZED;
     432    }
     433
     434    gnutls_certificate_set_dh_params(sc->certs, sc->dh_params);
     435    gnutls_anon_set_server_dh_params(sc->anon_creds, sc->dh_params);
     436#endif
     437
     438    return OK;
     439}
     440
     441
     442
    315443/**
    316444 * Post config hook.
     
    328456    int rv;
    329457    server_rec *s;
    330     gnutls_dh_params_t dh_params = NULL;
    331458    mgs_srvconf_rec *sc;
    332459    mgs_srvconf_rec *sc_base;
     
    359486                                    base_server, pconf, 0);
    360487        if (rv != APR_SUCCESS)
    361         {
    362             ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, base_server,
    363                          "Failed to create mutex '" MGS_OCSP_MUTEX_NAME
    364                          "'.");
    365             return HTTP_INTERNAL_SERVER_ERROR;
    366         }
     488            return rv;
    367489    }
    368490
     
    443565        }
    444566
    445         /* Check if DH params have been set per host */
     567        /* Set host DH params from user configuration or defaults */
    446568        if (sc->dh_params != NULL) {
    447569            gnutls_certificate_set_dh_params(sc->certs, sc->dh_params);
    448570            gnutls_anon_set_server_dh_params(sc->anon_creds, sc->dh_params);
    449         } else if (dh_params) {
    450             gnutls_certificate_set_dh_params(sc->certs, dh_params);
    451             gnutls_anon_set_server_dh_params(sc->anon_creds, dh_params);
     571        } else {
     572            rv = set_default_dh_param(s);
     573            if (rv != OK)
     574                return rv;
    452575        }
    453576
     
    484607        }
    485608
     609        /* If OpenPGP support is already disabled in the loaded GnuTLS
     610         * library startup will fail if the configuration tries to
     611         * load PGP credentials. Otherwise warn affected users about
     612         * deprecation. */
     613        if (sc->pgp_cert_file || sc->pgp_key_file || sc->pgp_ring_file)
     614            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
     615                         "Host '%s:%d' is configured to use OpenPGP auth. "
     616                         "OpenPGP support has been deprecated in GnuTLS "
     617                         "since version 3.5.9 and will be removed from "
     618                         "mod_gnutls in a future release.",
     619                         s->server_hostname, s->port);
     620
    486621        if (sc->enabled == GNUTLS_ENABLED_TRUE) {
    487622            rv = -1;
     
    606741}
    607742
    608 #define MAX_HOST_LEN 255
     743/**
     744 * Default buffer size for SNI data, including the terminating NULL
     745 * byte. The size matches what gnutls-cli uses initially.
     746 */
     747#define DEFAULT_SNI_HOST_LEN 256
    609748
    610749typedef struct {
     
    699838}
    700839
     840/**
     841 * Get SNI data from GnuTLS (if any) and search for a matching virtual
     842 * host configuration. This method is called from the post client
     843 * hello function.
     844 *
     845 * @param session the GnuTLS session
     846 *
     847 * @return either the matching mod_gnutls server config, or `NULL`
     848 */
    701849mgs_srvconf_rec *mgs_find_sni_server(gnutls_session_t session)
    702850{
    703     int rv;
     851    mgs_handle_t *ctxt = gnutls_session_get_ptr(session);
     852
     853    char *sni_name = apr_palloc(ctxt->c->pool, DEFAULT_SNI_HOST_LEN);
     854    size_t sni_len = DEFAULT_SNI_HOST_LEN;
    704855    unsigned int sni_type;
    705     size_t data_len = MAX_HOST_LEN;
    706     char sni_name[MAX_HOST_LEN];
    707     mgs_handle_t *ctxt;
    708     vhost_cb_rec cbx;
    709 
    710     if (session == NULL)
     856
     857    /* Search for a DNS SNI element. Note that RFC 6066 prohibits more
     858     * than one server name per type. */
     859    int sni_index = -1;
     860    int rv = 0;
     861    do {
     862        /* The sni_index is incremented before each use, so if the
     863         * loop terminates with a type match we will have the right
     864         * one stored. */
     865        rv = gnutls_server_name_get(session, sni_name,
     866                                    &sni_len, &sni_type, ++sni_index);
     867        if (rv == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
     868        {
     869            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, APR_EGENERAL, ctxt->c,
     870                          "%s: no DNS SNI found (last index: %d).",
     871                          __func__, sni_index);
     872            return NULL;
     873        }
     874    } while (sni_type != GNUTLS_NAME_DNS);
     875    /* The (rv == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) path inside
     876     * the loop above returns, so if we reach this point we have a DNS
     877     * SNI at the current index. */
     878
     879    if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER)
     880    {
     881        /* Allocate a new buffer of the right size and retry */
     882        sni_name = apr_palloc(ctxt->c->pool, sni_len);
     883        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, APR_SUCCESS, ctxt->c,
     884                      "%s: reallocated SNI data buffer for %" APR_SIZE_T_FMT
     885                      " bytes.", __func__, sni_len);
     886        rv = gnutls_server_name_get(session, sni_name,
     887                                    &sni_len, &sni_type, sni_index);
     888    }
     889
     890    /* Unless there's a bug in the GnuTLS API only GNUTLS_E_IDNA_ERROR
     891     * can occur here, but a catch all is safer and no more
     892     * complicated. */
     893    if (rv != GNUTLS_E_SUCCESS)
     894    {
     895        ap_log_cerror(APLOG_MARK, APLOG_INFO, APR_EGENERAL, ctxt->c,
     896                      "%s: error while getting SNI DNS data: '%s' (%d).",
     897                      __func__, gnutls_strerror(rv), rv);
    711898        return NULL;
    712 
    713     _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
    714     ctxt = gnutls_transport_get_ptr(session);
    715 
    716     rv = gnutls_server_name_get(ctxt->session, sni_name,
    717             &data_len, &sni_type, 0);
    718 
    719     if (rv != 0) {
    720         return NULL;
    721     }
    722 
    723     if (sni_type != GNUTLS_NAME_DNS) {
    724         ap_log_cerror(APLOG_MARK, APLOG_CRIT, 0, ctxt->c,
    725                       "GnuTLS: Unknown type '%d' for SNI: '%s'",
    726                       sni_type, sni_name);
    727         return NULL;
    728     }
    729 
    730     /**
    731      * Code in the Core already sets up the c->base_server as the base
    732      * for this IP/Port combo.  Trust that the core did the 'right' thing.
    733      */
    734     cbx.ctxt = ctxt;
    735     cbx.sc = NULL;
    736     cbx.sni_name = sni_name;
    737 
     899    }
     900
     901    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, APR_SUCCESS, ctxt->c,
     902                  "%s: client requested server '%s'.",
     903                  __func__, sni_name);
     904
     905    /* Search for vhosts matching connection parameters and the
     906     * SNI. If a match is found, cbx.sc will contain the mod_gnutls
     907     * server config for the vhost. */
     908    vhost_cb_rec cbx = {
     909        .ctxt = ctxt,
     910        .sc = NULL,
     911        .sni_name = sni_name
     912    };
    738913    rv = ap_vhost_iterate_given_conn(ctxt->c, vhost_cb, &cbx);
    739914    if (rv == 1) {
     
    824999            ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
    8251000                          "gnutls_init for proxy connection failed: %s (%d)",
    826                           gnutls_strerror(err), err);
    827         err = gnutls_session_ticket_enable_client(ctxt->session);
    828         if (err != GNUTLS_E_SUCCESS)
    829             ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
    830                           "gnutls_session_ticket_enable_client failed: %s (%d)",
    8311001                          gnutls_strerror(err), err);
    8321002    }
     
    17911961    /* Get peer hostname from a note left by mod_proxy */
    17921962    const char *peer_hostname =
    1793         apr_table_get(ctxt->c->notes, "proxy-request-hostname");
     1963        apr_table_get(ctxt->c->notes, PROXY_SNI_NOTE);
    17941964    if (peer_hostname == NULL)
    17951965        ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, ctxt->c,
    1796                       "%s: proxy-request-hostname is NULL, cannot check "
     1966                      "%s: " PROXY_SNI_NOTE " NULL, cannot check "
    17971967                      "peer's hostname", __func__);
    17981968
Note: See TracChangeset for help on using the changeset viewer.