- Timestamp:
- Apr 11, 2018, 1:41:07 AM (3 years ago)
- Branches:
- asyncio, debian/master, debian/stretch-backports, master, proxy-ticket, upstream
- Children:
- d50dac5
- Parents:
- 2f10643
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/gnutls_hooks.c
r2f10643 r9cee2e9 135 135 } 136 136 137 138 139 /** 140 * Get the list of available protocols for this connection and add it 141 * to the GnuTLS session. Must run before the client hello function. 142 */ 143 static void prepare_alpn_proposals(mgs_handle_t *ctxt) 144 { 145 /* Check if any protocol upgrades are available 146 * 147 * The "report_all" parameter to ap_get_protocol_upgrades() is 0 148 * (report only more preferable protocols) because setting it to 1 149 * doesn't actually report ALL protocols, but only all except the 150 * current one. This way we can at least list the current one as 151 * available by appending it without potentially negotiating a 152 * less preferred protocol. */ 153 const apr_array_header_t *pupgrades = NULL; 154 apr_status_t ret = 155 ap_get_protocol_upgrades(ctxt->c, NULL, ctxt->c->base_server, 156 /*report_all*/ 0, &pupgrades); 157 if (ret != APR_SUCCESS) 158 { 159 ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, ctxt->c, 160 "%s: ap_get_protocol_upgrades() failed, " 161 "cannot configure ALPN!", __func__); 162 return; 163 } 164 165 if (pupgrades == NULL || pupgrades->nelts == 0) 166 { 167 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, ctxt->c, 168 "%s: No protocol upgrades available.", __func__); 169 return; 170 } 171 172 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c, 173 "%s: Found %d protocol upgrade(s) for ALPN: %s", 174 __func__, pupgrades->nelts, 175 apr_array_pstrcat(ctxt->c->pool, pupgrades, ',')); 176 gnutls_datum_t *alpn_protos = 177 apr_palloc(ctxt->c->pool, 178 (pupgrades->nelts + 1) * sizeof(gnutls_datum_t)); 179 for (int i = 0; i < pupgrades->nelts; i++) 180 { 181 alpn_protos[i].data = (void *) APR_ARRAY_IDX(pupgrades, i, char *); 182 alpn_protos[i].size = 183 strnlen(APR_ARRAY_IDX(pupgrades, i, char *), 184 pupgrades->elt_size); 185 } 186 187 /* Add the current (default) protocol at the end of the list */ 188 alpn_protos[pupgrades->nelts].data = 189 (void*) apr_pstrdup(ctxt->c->pool, ap_get_protocol(ctxt->c)); 190 alpn_protos[pupgrades->nelts].size = 191 strlen((char*) alpn_protos[pupgrades->nelts].data); 192 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, ctxt->c, 193 "%s: Adding current protocol %s to ALPN set.", 194 __func__, alpn_protos[pupgrades->nelts].data); 195 196 gnutls_alpn_set_protocols(ctxt->session, 197 alpn_protos, 198 pupgrades->nelts, 199 GNUTLS_ALPN_SERVER_PRECEDENCE); 200 } 201 202 203 204 /** 205 * Check if ALPN selected any protocol upgrade, try to switch if so. 206 */ 207 static int process_alpn_result(mgs_handle_t *ctxt) 208 { 209 int ret = 0; 210 gnutls_datum_t alpn_proto; 211 ret = gnutls_alpn_get_selected_protocol(ctxt->session, &alpn_proto); 212 if (ret != GNUTLS_E_SUCCESS) 213 { 214 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, APR_SUCCESS, ctxt->c, 215 "%s: No ALPN result: %s (%d)", 216 __func__, gnutls_strerror(ret), ret); 217 return GNUTLS_E_SUCCESS; 218 } 219 220 apr_array_header_t *client_protos = 221 apr_array_make(ctxt->c->pool, 1, sizeof(char *)); 222 /* apr_pstrndup to ensure that the protocol is null terminated */ 223 APR_ARRAY_PUSH(client_protos, char *) = 224 apr_pstrndup(ctxt->c->pool, (char*) alpn_proto.data, alpn_proto.size); 225 const char *selected = 226 ap_select_protocol(ctxt->c, NULL, ctxt->c->base_server, 227 client_protos); 228 229 /* ap_select_protocol() will return NULL if none of the ALPN 230 * proposals matched. GnuTLS negotiated alpn_proto based on the 231 * list provided by the server, but the vhost might have changed 232 * based on SNI. Apache seems to adjust the proposal list to avoid 233 * such issues though. 234 * 235 * GnuTLS will return a fatal "no_application_protocol" alert as 236 * required by RFC 7301 if the post client hello function returns 237 * GNUTLS_E_NO_APPLICATION_PROTOCOL. */ 238 if (!selected) 239 { 240 ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c, 241 "%s: ap_select_protocol() returned NULL! Please " 242 "make sure any overlapping vhosts have the same " 243 "protocols available.", 244 __func__); 245 return GNUTLS_E_NO_APPLICATION_PROTOCOL; 246 } 247 248 if (strcmp(selected, ap_get_protocol(ctxt->c)) == 0) 249 { 250 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, APR_SUCCESS, ctxt->c, 251 "%s: Already using protocol '%s', nothing to do.", 252 __func__, selected); 253 return GNUTLS_E_SUCCESS; 254 } 255 256 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ctxt->c, 257 "%s: Switching protocol to '%s' based on ALPN.", 258 __func__, selected); 259 apr_status_t status = ap_switch_protocol(ctxt->c, NULL, 260 ctxt->c->base_server, 261 selected); 262 if (status != APR_SUCCESS) 263 { 264 ap_log_cerror(APLOG_MARK, APLOG_ERR, status, ctxt->c, 265 "%s: Protocol switch to '%s' failed!", 266 __func__, selected); 267 return GNUTLS_E_NO_APPLICATION_PROTOCOL; 268 } 269 /* ALPN done! */ 270 return GNUTLS_E_SUCCESS; 271 } 272 273 274 137 275 /** 138 276 * Post client hello function for GnuTLS, used to configure the TLS … … 173 311 #endif 174 312 175 /* update the priorities - to avoid negotiating a ciphersuite that is not 313 ret = process_alpn_result(ctxt); 314 if (ret != GNUTLS_E_SUCCESS) 315 return ret; 316 317 /* Update the priorities - to avoid negotiating a ciphersuite that is not 176 318 * enabled on this virtual server. Note that here we ignore the version 177 * negotiation. 178 */ 319 * negotiation. */ 179 320 ret = gnutls_priority_set(session, ctxt->sc->priorities); 180 321 … … 1040 1181 } 1041 1182 1183 prepare_alpn_proposals(ctxt); 1184 1042 1185 /* Initialize Session Cache */ 1043 1186 mgs_cache_session_init(ctxt);
Note: See TracChangeset
for help on using the changeset viewer.