Changeset 180e49f in mod_gnutls for src/gnutls_io.c
- Timestamp:
- Jan 11, 2013, 12:58:31 AM (8 years ago)
- Branches:
- debian/master, debian/stretch-backports, jessie-backports, upstream
- Children:
- 2b1118c, ae29683
- Parents:
- 17eb1a1
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/gnutls_io.c
r17eb1a1 r180e49f 33 33 34 34 static apr_status_t gnutls_io_filter_error(ap_filter_t * f, 35 apr_bucket_brigade * bb, 36 apr_status_t status) 37 { 38 mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx; 39 apr_bucket *bucket; 40 41 switch (status) { 42 case HTTP_BAD_REQUEST: 43 /* log the situation */ 44 ap_log_error(APLOG_MARK, APLOG_INFO, 0, 45 f->c->base_server, 46 "GnuTLS handshake failed: HTTP spoken on HTTPS port; " 47 "trying to send HTML error page"); 48 49 ctxt->status = -1; 50 51 /* fake the request line */ 52 bucket = HTTP_ON_HTTPS_PORT_BUCKET(f->c->bucket_alloc); 53 break; 54 55 default: 56 return status; 57 } 58 59 APR_BRIGADE_INSERT_TAIL(bb, bucket); 60 bucket = apr_bucket_eos_create(f->c->bucket_alloc); 61 APR_BRIGADE_INSERT_TAIL(bb, bucket); 62 63 return APR_SUCCESS; 64 } 65 66 static int char_buffer_read(mgs_char_buffer_t * buffer, char *in, 67 int inl) 68 { 69 if (!buffer->length) { 70 return 0; 71 } 72 73 if (buffer->length > inl) { 74 /* we have have enough to fill the caller's buffer */ 75 memmove(in, buffer->value, inl); 76 buffer->value += inl; 77 buffer->length -= inl; 78 } 79 else { 80 /* swallow remainder of the buffer */ 81 memmove(in, buffer->value, buffer->length); 82 inl = buffer->length; 83 buffer->value = NULL; 84 buffer->length = 0; 85 } 86 87 return inl; 88 } 89 90 static int char_buffer_write(mgs_char_buffer_t * buffer, char *in, 91 int inl) 92 { 93 buffer->value = in; 94 buffer->length = inl; 95 return inl; 35 apr_bucket_brigade * bb, 36 apr_status_t status) 37 { 38 mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx; 39 apr_bucket *bucket; 40 41 switch (status) { 42 case HTTP_BAD_REQUEST: 43 /* log the situation */ 44 ap_log_error(APLOG_MARK, APLOG_INFO, 0, 45 f->c->base_server, 46 "GnuTLS handshake failed: HTTP spoken on HTTPS port; " 47 "trying to send HTML error page"); 48 49 ctxt->status = -1; 50 51 /* fake the request line */ 52 bucket = HTTP_ON_HTTPS_PORT_BUCKET(f->c->bucket_alloc); 53 break; 54 55 default: 56 return status; 57 } 58 59 APR_BRIGADE_INSERT_TAIL(bb, bucket); 60 bucket = apr_bucket_eos_create(f->c->bucket_alloc); 61 APR_BRIGADE_INSERT_TAIL(bb, bucket); 62 63 return APR_SUCCESS; 64 } 65 66 static int char_buffer_read(mgs_char_buffer_t * buffer, char *in, int inl) 67 { 68 if (!buffer->length) { 69 return 0; 70 } 71 72 if (buffer->length > inl) { 73 /* we have have enough to fill the caller's buffer */ 74 memmove(in, buffer->value, inl); 75 buffer->value += inl; 76 buffer->length -= inl; 77 } else { 78 /* swallow remainder of the buffer */ 79 memmove(in, buffer->value, buffer->length); 80 inl = buffer->length; 81 buffer->value = NULL; 82 buffer->length = 0; 83 } 84 85 return inl; 86 } 87 88 static int char_buffer_write(mgs_char_buffer_t * buffer, char *in, int inl) 89 { 90 buffer->value = in; 91 buffer->length = inl; 92 return inl; 96 93 } 97 94 … … 102 99 */ 103 100 static apr_status_t brigade_consume(apr_bucket_brigade * bb, 104 105 106 { 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 consume = (str_len + actual > *len) ? *len - actual : str_len; 152 153 memcpy(c, str, consume);154 155 c += consume;156 actual += consume; 157 158 if (consume >= b->length) { 159 /* This physical bucket was consumed */ 160 apr_bucket_delete(b); 161 } 162 else { 163 /* Only part of this physical bucket was consumed */ 164 b->start += consume; 165 b->length -= consume;166 } 167 168 169 170 171 172 173 174 175 176 177 178 179 180 101 apr_read_type_e block, 102 char *c, apr_size_t * len) 103 { 104 apr_size_t actual = 0; 105 apr_status_t status = APR_SUCCESS; 106 107 while (!APR_BRIGADE_EMPTY(bb)) { 108 apr_bucket *b = APR_BRIGADE_FIRST(bb); 109 const char *str; 110 apr_size_t str_len; 111 apr_size_t consume; 112 113 /* Justin points out this is an http-ism that might 114 * not fit if brigade_consume is added to APR. Perhaps 115 * apr_bucket_read(eos_bucket) should return APR_EOF? 116 * Then this becomes mainline instead of a one-off. 117 */ 118 if (APR_BUCKET_IS_EOS(b)) { 119 status = APR_EOF; 120 break; 121 } 122 123 /* The reason I'm not offering brigade_consume yet 124 * across to apr-util is that the following call 125 * illustrates how borked that API really is. For 126 * this sort of case (caller provided buffer) it 127 * would be much more trivial for apr_bucket_consume 128 * to do all the work that follows, based on the 129 * particular characteristics of the bucket we are 130 * consuming here. 131 */ 132 status = apr_bucket_read(b, &str, &str_len, block); 133 134 if (status != APR_SUCCESS) { 135 if (APR_STATUS_IS_EOF(status)) { 136 /* This stream bucket was consumed */ 137 apr_bucket_delete(b); 138 continue; 139 } 140 break; 141 } 142 143 if (str_len > 0) { 144 /* Do not block once some data has been consumed */ 145 block = APR_NONBLOCK_READ; 146 147 /* Assure we don't overflow. */ 148 consume = 149 (str_len + actual > 150 *len) ? *len - actual : str_len; 151 152 memcpy(c, str, consume); 153 154 c += consume; 155 actual += consume; 156 157 if (consume >= b->length) { 158 /* This physical bucket was consumed */ 159 apr_bucket_delete(b); 160 } else { 161 /* Only part of this physical bucket was consumed */ 162 b->start += consume; 163 b->length -= consume; 164 } 165 } else if (b->length == 0) { 166 apr_bucket_delete(b); 167 } 168 169 /* This could probably be actual == *len, but be safe from stray 170 * photons. */ 171 if (actual >= *len) { 172 break; 173 } 174 } 175 176 *len = actual; 177 return status; 181 178 } 182 179 183 180 184 181 static apr_status_t gnutls_io_input_read(mgs_handle_t * ctxt, 185 char *buf, apr_size_t * len) 186 { 187 apr_size_t wanted = *len; 188 apr_size_t bytes = 0; 189 int rc; 190 191 *len = 0; 192 193 /* If we have something leftover from last time, try that first. */ 194 if ((bytes = char_buffer_read(&ctxt->input_cbuf, buf, wanted))) { 195 *len = bytes; 196 if (ctxt->input_mode == AP_MODE_SPECULATIVE) { 197 /* We want to rollback this read. */ 198 if (ctxt->input_cbuf.length > 0) { 199 ctxt->input_cbuf.value -= bytes; 200 ctxt->input_cbuf.length += bytes; 201 } 202 else { 203 char_buffer_write(&ctxt->input_cbuf, buf, (int) bytes); 204 } 205 return APR_SUCCESS; 206 } 207 /* This could probably be *len == wanted, but be safe from stray 208 * photons. 209 */ 210 if (*len >= wanted) { 211 return APR_SUCCESS; 212 } 213 if (ctxt->input_mode == AP_MODE_GETLINE) { 214 if (memchr(buf, APR_ASCII_LF, *len)) { 215 return APR_SUCCESS; 216 } 217 } 218 else { 219 /* Down to a nonblock pattern as we have some data already 220 */ 221 ctxt->input_block = APR_NONBLOCK_READ; 222 } 223 } 224 225 if (ctxt->session == NULL) { 226 return APR_EGENERAL; 227 } 228 229 while (1) { 230 231 rc = gnutls_record_recv(ctxt->session, buf + bytes, wanted - bytes); 232 233 if (rc > 0) { 234 *len += rc; 235 if (ctxt->input_mode == AP_MODE_SPECULATIVE) { 236 /* We want to rollback this read. */ 237 char_buffer_write(&ctxt->input_cbuf, buf, rc); 238 } 239 return ctxt->input_rc; 240 } 241 else if (rc == 0) { 242 /* If EAGAIN, we will loop given a blocking read, 243 * otherwise consider ourselves at EOF. 244 */ 245 if (APR_STATUS_IS_EAGAIN(ctxt->input_rc) 246 || APR_STATUS_IS_EINTR(ctxt->input_rc)) { 247 /* Already read something, return APR_SUCCESS instead. 248 * On win32 in particular, but perhaps on other kernels, 249 * a blocking call isn't 'always' blocking. 250 */ 251 if (*len > 0) { 252 ctxt->input_rc = APR_SUCCESS; 253 break; 254 } 255 if (ctxt->input_block == APR_NONBLOCK_READ) { 256 break; 257 } 258 } 259 else { 260 if (*len > 0) { 261 ctxt->input_rc = APR_SUCCESS; 262 } 263 else { 264 ctxt->input_rc = APR_EOF; 265 } 266 break; 267 } 268 } 269 else { /* (rc < 0) */ 270 271 if (rc == GNUTLS_E_REHANDSHAKE) { 272 /* A client has asked for a new Hankshake. Currently, we don't do it */ 273 ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->input_rc, 274 ctxt->c->base_server, 275 "GnuTLS: Error reading data. Client Requested a New Handshake." 276 " (%d) '%s'", rc, gnutls_strerror(rc)); 277 } 278 else if (rc == GNUTLS_E_WARNING_ALERT_RECEIVED) { 279 rc = gnutls_alert_get(ctxt->session); 280 ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->input_rc, 281 ctxt->c->base_server, 282 "GnuTLS: Warning Alert From Client: " 283 " (%d) '%s'", rc, gnutls_alert_get_name(rc)); 284 } 285 else if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED) { 286 rc = gnutls_alert_get(ctxt->session); 287 ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->input_rc, 288 ctxt->c->base_server, 289 "GnuTLS: Fatal Alert From Client: " 290 "(%d) '%s'", rc, gnutls_alert_get_name(rc)); 291 ctxt->input_rc = APR_EGENERAL; 292 break; 293 } 294 else { 295 /* Some Other Error. Report it. Die. */ 296 if(gnutls_error_is_fatal(rc)) { 297 ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->input_rc, 298 ctxt->c->base_server, 299 "GnuTLS: Error reading data. (%d) '%s'", rc, 300 gnutls_strerror(rc)); 301 } 302 else if(*len > 0) { 303 ctxt->input_rc = APR_SUCCESS; 304 break; 305 } 306 } 307 308 if (ctxt->input_rc == APR_SUCCESS) { 309 ctxt->input_rc = APR_EGENERAL; 310 } 311 break; 312 } 313 } 314 return ctxt->input_rc; 182 char *buf, apr_size_t * len) 183 { 184 apr_size_t wanted = *len; 185 apr_size_t bytes = 0; 186 int rc; 187 188 *len = 0; 189 190 /* If we have something leftover from last time, try that first. */ 191 if ((bytes = char_buffer_read(&ctxt->input_cbuf, buf, wanted))) { 192 *len = bytes; 193 if (ctxt->input_mode == AP_MODE_SPECULATIVE) { 194 /* We want to rollback this read. */ 195 if (ctxt->input_cbuf.length > 0) { 196 ctxt->input_cbuf.value -= bytes; 197 ctxt->input_cbuf.length += bytes; 198 } else { 199 char_buffer_write(&ctxt->input_cbuf, buf, 200 (int) bytes); 201 } 202 return APR_SUCCESS; 203 } 204 /* This could probably be *len == wanted, but be safe from stray 205 * photons. 206 */ 207 if (*len >= wanted) { 208 return APR_SUCCESS; 209 } 210 if (ctxt->input_mode == AP_MODE_GETLINE) { 211 if (memchr(buf, APR_ASCII_LF, *len)) { 212 return APR_SUCCESS; 213 } 214 } else { 215 /* Down to a nonblock pattern as we have some data already 216 */ 217 ctxt->input_block = APR_NONBLOCK_READ; 218 } 219 } 220 221 if (ctxt->session == NULL) { 222 return APR_EGENERAL; 223 } 224 225 while (1) { 226 227 rc = gnutls_record_recv(ctxt->session, buf + bytes, 228 wanted - bytes); 229 230 if (rc > 0) { 231 *len += rc; 232 if (ctxt->input_mode == AP_MODE_SPECULATIVE) { 233 /* We want to rollback this read. */ 234 char_buffer_write(&ctxt->input_cbuf, buf, 235 rc); 236 } 237 return ctxt->input_rc; 238 } else if (rc == 0) { 239 /* If EAGAIN, we will loop given a blocking read, 240 * otherwise consider ourselves at EOF. 241 */ 242 if (APR_STATUS_IS_EAGAIN(ctxt->input_rc) 243 || APR_STATUS_IS_EINTR(ctxt->input_rc)) { 244 /* Already read something, return APR_SUCCESS instead. 245 * On win32 in particular, but perhaps on other kernels, 246 * a blocking call isn't 'always' blocking. 247 */ 248 if (*len > 0) { 249 ctxt->input_rc = APR_SUCCESS; 250 break; 251 } 252 if (ctxt->input_block == APR_NONBLOCK_READ) { 253 break; 254 } 255 } else { 256 if (*len > 0) { 257 ctxt->input_rc = APR_SUCCESS; 258 } else { 259 ctxt->input_rc = APR_EOF; 260 } 261 break; 262 } 263 } else { /* (rc < 0) */ 264 265 if (rc == GNUTLS_E_REHANDSHAKE) { 266 /* A client has asked for a new Hankshake. Currently, we don't do it */ 267 ap_log_error(APLOG_MARK, APLOG_INFO, 268 ctxt->input_rc, 269 ctxt->c->base_server, 270 "GnuTLS: Error reading data. Client Requested a New Handshake." 271 " (%d) '%s'", rc, 272 gnutls_strerror(rc)); 273 } else if (rc == GNUTLS_E_WARNING_ALERT_RECEIVED) { 274 rc = gnutls_alert_get(ctxt->session); 275 ap_log_error(APLOG_MARK, APLOG_INFO, 276 ctxt->input_rc, 277 ctxt->c->base_server, 278 "GnuTLS: Warning Alert From Client: " 279 " (%d) '%s'", rc, 280 gnutls_alert_get_name(rc)); 281 } else if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED) { 282 rc = gnutls_alert_get(ctxt->session); 283 ap_log_error(APLOG_MARK, APLOG_INFO, 284 ctxt->input_rc, 285 ctxt->c->base_server, 286 "GnuTLS: Fatal Alert From Client: " 287 "(%d) '%s'", rc, 288 gnutls_alert_get_name(rc)); 289 ctxt->input_rc = APR_EGENERAL; 290 break; 291 } else { 292 /* Some Other Error. Report it. Die. */ 293 if (gnutls_error_is_fatal(rc)) { 294 ap_log_error(APLOG_MARK, 295 APLOG_INFO, 296 ctxt->input_rc, 297 ctxt->c->base_server, 298 "GnuTLS: Error reading data. (%d) '%s'", 299 rc, 300 gnutls_strerror(rc)); 301 } else if (*len > 0) { 302 ctxt->input_rc = APR_SUCCESS; 303 break; 304 } 305 } 306 307 if (ctxt->input_rc == APR_SUCCESS) { 308 ctxt->input_rc = APR_EGENERAL; 309 } 310 break; 311 } 312 } 313 return ctxt->input_rc; 315 314 } 316 315 317 316 static apr_status_t gnutls_io_input_getline(mgs_handle_t * ctxt, 318 319 { 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 317 char *buf, apr_size_t * len) 318 { 319 const char *pos = NULL; 320 apr_status_t status; 321 apr_size_t tmplen = *len, buflen = *len, offset = 0; 322 323 *len = 0; 324 325 while (tmplen > 0) { 326 status = gnutls_io_input_read(ctxt, buf + offset, &tmplen); 327 328 if (status != APR_SUCCESS) { 329 return status; 330 } 331 332 *len += tmplen; 333 334 if ((pos = memchr(buf, APR_ASCII_LF, *len))) { 335 break; 336 } 337 338 offset += tmplen; 339 tmplen = buflen - offset; 340 } 341 342 if (pos) { 343 char *value; 344 int length; 345 apr_size_t bytes = pos - buf; 346 347 bytes += 1; 348 value = buf + bytes; 349 length = *len - bytes; 350 351 char_buffer_write(&ctxt->input_cbuf, value, length); 352 353 *len = bytes; 354 } 355 356 return APR_SUCCESS; 358 357 } 359 358 … … 361 360 static int gnutls_do_handshake(mgs_handle_t * ctxt) 362 361 { 363 int ret; 364 int errcode; 365 int maxtries = HANDSHAKE_MAX_TRIES; 366 367 if (ctxt->status != 0 || ctxt->session == NULL) { 368 return -1; 369 } 370 371 tryagain: 372 do { 373 ret = gnutls_handshake(ctxt->session); 374 maxtries--; 375 } while ((ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) && maxtries > 0); 376 377 if (maxtries < 1) { 378 ctxt->status = -1; 362 int ret; 363 int errcode; 364 int maxtries = HANDSHAKE_MAX_TRIES; 365 366 if (ctxt->status != 0 || ctxt->session == NULL) { 367 return -1; 368 } 369 370 tryagain: 371 do { 372 ret = gnutls_handshake(ctxt->session); 373 maxtries--; 374 } while ((ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) 375 && maxtries > 0); 376 377 if (maxtries < 1) { 378 ctxt->status = -1; 379 379 #if USING_2_1_RECENT 380 381 380 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, ctxt->c, 381 "GnuTLS: Handshake Failed. Hit Maximum Attempts"); 382 382 #else 383 ap_log_error(APLOG_MARK, APLOG_ERR, 0, ctxt->c->base_server, 384 "GnuTLS: Handshake Failed. Hit Maximum Attempts"); 383 ap_log_error(APLOG_MARK, APLOG_ERR, 0, 384 ctxt->c->base_server, 385 "GnuTLS: Handshake Failed. Hit Maximum Attempts"); 385 386 #endif 386 if (ctxt->session) { 387 gnutls_alert_send(ctxt->session, GNUTLS_AL_FATAL, 388 gnutls_error_to_alert(GNUTLS_E_INTERNAL_ERROR, NULL)); 389 gnutls_deinit(ctxt->session); 387 if (ctxt->session) { 388 gnutls_alert_send(ctxt->session, GNUTLS_AL_FATAL, 389 gnutls_error_to_alert 390 (GNUTLS_E_INTERNAL_ERROR, NULL)); 391 gnutls_deinit(ctxt->session); 392 } 393 ctxt->session = NULL; 394 return -1; 395 } 396 397 if (ret < 0) { 398 if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED 399 || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) { 400 errcode = gnutls_alert_get(ctxt->session); 401 ap_log_error(APLOG_MARK, APLOG_INFO, 0, 402 ctxt->c->base_server, 403 "GnuTLS: Hanshake Alert (%d) '%s'.", 404 errcode, 405 gnutls_alert_get_name(errcode)); 406 } 407 408 if (!gnutls_error_is_fatal(ret)) { 409 ap_log_error(APLOG_MARK, APLOG_INFO, 0, 410 ctxt->c->base_server, 411 "GnuTLS: Non-Fatal Handshake Error: (%d) '%s'", 412 ret, gnutls_strerror(ret)); 413 goto tryagain; 414 } 415 #if USING_2_1_RECENT 416 ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, ctxt->c, 417 "GnuTLS: Handshake Failed (%d) '%s'", ret, 418 gnutls_strerror(ret)); 419 #else 420 ap_log_error(APLOG_MARK, APLOG_INFO, 0, 421 ctxt->c->base_server, 422 "GnuTLS: Handshake Failed (%d) '%s'", ret, 423 gnutls_strerror(ret)); 424 #endif 425 ctxt->status = -1; 426 if (ctxt->session) { 427 gnutls_alert_send(ctxt->session, GNUTLS_AL_FATAL, 428 gnutls_error_to_alert(ret, 429 NULL)); 430 gnutls_deinit(ctxt->session); 431 } 432 ctxt->session = NULL; 433 return ret; 434 } else { 435 /* all done with the handshake */ 436 ctxt->status = 1; 437 /* If the session was resumed, we did not set the correct 438 * server_rec in ctxt->sc. Go Find it. (ick!) 439 */ 440 if (gnutls_session_is_resumed(ctxt->session)) { 441 mgs_srvconf_rec *sc; 442 sc = mgs_find_sni_server(ctxt->session); 443 if (sc) { 444 ctxt->sc = sc; 445 } 446 } 447 return 0; 448 } 449 } 450 451 int mgs_rehandshake(mgs_handle_t * ctxt) 452 { 453 int rv; 454 455 if (ctxt->session == NULL) 456 return -1; 457 458 rv = gnutls_rehandshake(ctxt->session); 459 460 if (rv != 0) { 461 /* the client did not want to rehandshake. goodbye */ 462 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, 463 ctxt->c->base_server, 464 "GnuTLS: Client Refused Rehandshake request."); 465 return -1; 466 } 467 468 ctxt->status = 0; 469 470 rv = gnutls_do_handshake(ctxt); 471 472 return rv; 473 } 474 475 476 apr_status_t mgs_filter_input(ap_filter_t * f, 477 apr_bucket_brigade * bb, 478 ap_input_mode_t mode, 479 apr_read_type_e block, apr_off_t readbytes) 480 { 481 apr_status_t status = APR_SUCCESS; 482 mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx; 483 apr_size_t len = sizeof(ctxt->input_buffer); 484 485 if (f->c->aborted) { 486 apr_bucket *bucket = 487 apr_bucket_eos_create(f->c->bucket_alloc); 488 APR_BRIGADE_INSERT_TAIL(bb, bucket); 489 return APR_ECONNABORTED; 490 } 491 492 if (ctxt->status == 0) { 493 gnutls_do_handshake(ctxt); 494 } 495 496 if (ctxt->status < 0) { 497 return ap_get_brigade(f->next, bb, mode, block, readbytes); 498 } 499 500 /* XXX: we don't currently support anything other than these modes. */ 501 if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE && 502 mode != AP_MODE_SPECULATIVE && mode != AP_MODE_INIT) { 503 return APR_ENOTIMPL; 504 } 505 506 ctxt->input_mode = mode; 507 ctxt->input_block = block; 508 509 if (ctxt->input_mode == AP_MODE_READBYTES || 510 ctxt->input_mode == AP_MODE_SPECULATIVE) { 511 /* Err. This is bad. readbytes *can* be a 64bit int! len.. is NOT */ 512 if (readbytes < len) { 513 len = (apr_size_t) readbytes; 514 } 515 status = 516 gnutls_io_input_read(ctxt, ctxt->input_buffer, &len); 517 } else if (ctxt->input_mode == AP_MODE_GETLINE) { 518 status = 519 gnutls_io_input_getline(ctxt, ctxt->input_buffer, 520 &len); 521 } else { 522 /* We have no idea what you are talking about, so return an error. */ 523 return APR_ENOTIMPL; 524 } 525 526 if (status != APR_SUCCESS) { 527 return gnutls_io_filter_error(f, bb, status); 528 } 529 530 /* Create a transient bucket out of the decrypted data. */ 531 if (len > 0) { 532 apr_bucket *bucket = 533 apr_bucket_transient_create(ctxt->input_buffer, len, 534 f->c->bucket_alloc); 535 APR_BRIGADE_INSERT_TAIL(bb, bucket); 536 } 537 538 return status; 539 } 540 541 apr_status_t mgs_filter_output(ap_filter_t * f, apr_bucket_brigade * bb) 542 { 543 apr_size_t ret; 544 mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx; 545 apr_status_t status = APR_SUCCESS; 546 apr_read_type_e rblock = APR_NONBLOCK_READ; 547 548 if (f->c->aborted) { 549 apr_brigade_cleanup(bb); 550 return APR_ECONNABORTED; 551 } 552 553 if (ctxt->status == 0) { 554 gnutls_do_handshake(ctxt); 555 } 556 557 if (ctxt->status < 0) { 558 return ap_pass_brigade(f->next, bb); 559 } 560 561 while (!APR_BRIGADE_EMPTY(bb)) { 562 apr_bucket *bucket = APR_BRIGADE_FIRST(bb); 563 564 if (APR_BUCKET_IS_EOS(bucket)) { 565 return ap_pass_brigade(f->next, bb); 566 } else if (APR_BUCKET_IS_FLUSH(bucket)) { 567 /* Try Flush */ 568 if( write_flush(ctxt) < 0) { 569 /* Flush Error */ 570 return ctxt->output_rc; 571 } 572 /* cleanup! */ 573 apr_bucket_delete(bucket); 574 } else if (AP_BUCKET_IS_EOC(bucket)) { 575 /* End Of Connection */ 576 if (ctxt->session != NULL) { 577 /* Try A Clean Shutdown */ 578 do { 579 ret = gnutls_bye( ctxt->session, 580 GNUTLS_SHUT_WR); 581 } while(ret == GNUTLS_E_INTERRUPTED || 582 ret == GNUTLS_E_AGAIN); 583 /* De-Initialize Session */ 584 gnutls_deinit(ctxt->session); 585 ctxt->session = NULL; 586 } 587 /* Pass next brigade! */ 588 return ap_pass_brigade(f->next, bb); 589 } else { 590 /* filter output */ 591 const char *data; 592 apr_size_t len; 593 594 status = 595 apr_bucket_read(bucket, &data, &len, rblock); 596 597 if (APR_STATUS_IS_EAGAIN(status)) { 598 /* No data available so Flush! */ 599 if (write_flush(ctxt) < 0) { 600 return ctxt->output_rc; 601 } 602 /* Try again with a blocking read. */ 603 rblock = APR_BLOCK_READ; 604 continue; 605 } 606 607 rblock = APR_NONBLOCK_READ; 608 609 if (!APR_STATUS_IS_EOF(status) 610 && (status != APR_SUCCESS)) { 611 return status; 612 } 613 614 if (len > 0) { 615 616 if (ctxt->session == NULL) { 617 ret = GNUTLS_E_INVALID_REQUEST; 618 } else { 619 do { 620 ret = 621 gnutls_record_send 622 (ctxt->session, data, 623 len); 624 } 625 while (ret == GNUTLS_E_INTERRUPTED 626 || ret == GNUTLS_E_AGAIN); 627 } 628 629 if (ret < 0) { 630 /* error sending output */ 631 ap_log_error(APLOG_MARK, 632 APLOG_INFO, 633 ctxt->output_rc, 634 ctxt->c->base_server, 635 "GnuTLS: Error writing data." 636 " (%d) '%s'", 637 (int) ret, 638 gnutls_strerror(ret)); 639 if (ctxt->output_rc == APR_SUCCESS) { 640 ctxt->output_rc = 641 APR_EGENERAL; 642 return ctxt->output_rc; 643 } 644 } else if (ret != len) { 645 /* Not able to send the entire bucket, 646 split it and send it again. */ 647 apr_bucket_split(bucket, ret); 648 } 649 } 650 651 apr_bucket_delete(bucket); 652 } 653 } 654 655 return status; 656 } 657 658 ssize_t mgs_transport_read(gnutls_transport_ptr_t ptr, 659 void *buffer, size_t len) 660 { 661 mgs_handle_t *ctxt = ptr; 662 apr_status_t rc; 663 apr_size_t in = len; 664 apr_read_type_e block = ctxt->input_block; 665 666 ctxt->input_rc = APR_SUCCESS; 667 668 /* If Len = 0, we don't do anything. */ 669 if (!len || buffer == NULL) { 670 return 0; 390 671 } 391 ctxt->session = NULL; 392 return -1; 393 } 394 395 if (ret < 0) { 396 if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED 397 || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) { 398 errcode = gnutls_alert_get(ctxt->session); 399 ap_log_error(APLOG_MARK, APLOG_ERR, 0, ctxt->c->base_server, 400 "GnuTLS: Hanshake Alert (%d) '%s'.", errcode, 401 gnutls_alert_get_name(errcode)); 402 } 403 404 if (!gnutls_error_is_fatal(ret)) { 405 ap_log_error(APLOG_MARK, APLOG_INFO, 0, ctxt->c->base_server, 406 "GnuTLS: Non-Fatal Handshake Error: (%d) '%s'", ret, 407 gnutls_strerror(ret)); 408 goto tryagain; 409 } 410 #if USING_2_1_RECENT 411 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, ctxt->c, 412 "GnuTLS: Handshake Failed (%d) '%s'", ret, 413 gnutls_strerror(ret)); 414 #else 415 ap_log_error(APLOG_MARK, APLOG_ERR, 0, ctxt->c->base_server, 416 "GnuTLS: Handshake Failed (%d) '%s'", ret, 417 gnutls_strerror(ret)); 418 #endif 419 ctxt->status = -1; 420 if (ctxt->session) { 421 gnutls_alert_send(ctxt->session, GNUTLS_AL_FATAL, 422 gnutls_error_to_alert(ret, NULL)); 423 gnutls_deinit(ctxt->session); 424 } 425 ctxt->session = NULL; 426 return ret; 427 } 428 else { 429 /* all done with the handshake */ 430 ctxt->status = 1; 431 /* If the session was resumed, we did not set the correct 432 * server_rec in ctxt->sc. Go Find it. (ick!) 433 */ 434 if (gnutls_session_is_resumed(ctxt->session)) { 435 mgs_srvconf_rec* sc; 436 sc = mgs_find_sni_server(ctxt->session); 437 if (sc) { 438 ctxt->sc = sc; 439 } 440 } 441 return 0; 442 } 443 } 444 445 int mgs_rehandshake(mgs_handle_t * ctxt) 446 { 447 int rv; 448 449 if (ctxt->session == NULL) 450 return -1; 451 452 rv = gnutls_rehandshake(ctxt->session); 453 454 if (rv != 0) { 455 /* the client did not want to rehandshake. goodbye */ 456 ap_log_error(APLOG_MARK, APLOG_ERR, 0, ctxt->c->base_server, 457 "GnuTLS: Client Refused Rehandshake request."); 458 return -1; 459 } 460 461 ctxt->status = 0; 462 463 rv = gnutls_do_handshake(ctxt); 464 465 return rv; 466 } 467 468 469 apr_status_t mgs_filter_input(ap_filter_t* f, 470 apr_bucket_brigade * bb, 471 ap_input_mode_t mode, 472 apr_read_type_e block, 473 apr_off_t readbytes) 474 { 475 apr_status_t status = APR_SUCCESS; 476 mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx; 477 apr_size_t len = sizeof(ctxt->input_buffer); 478 479 if (f->c->aborted) { 480 apr_bucket *bucket = apr_bucket_eos_create(f->c->bucket_alloc); 481 APR_BRIGADE_INSERT_TAIL(bb, bucket); 482 return APR_ECONNABORTED; 483 } 484 485 if (ctxt->status == 0) { 486 gnutls_do_handshake(ctxt); 487 } 488 489 if (ctxt->status < 0) { 490 return ap_get_brigade(f->next, bb, mode, block, readbytes); 491 } 492 493 /* XXX: we don't currently support anything other than these modes. */ 494 if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE && 495 mode != AP_MODE_SPECULATIVE && mode != AP_MODE_INIT) { 496 return APR_ENOTIMPL; 497 } 498 499 ctxt->input_mode = mode; 500 ctxt->input_block = block; 501 502 if (ctxt->input_mode == AP_MODE_READBYTES || 503 ctxt->input_mode == AP_MODE_SPECULATIVE) { 504 /* Err. This is bad. readbytes *can* be a 64bit int! len.. is NOT */ 505 if (readbytes < len) { 506 len = (apr_size_t) readbytes; 507 } 508 status = gnutls_io_input_read(ctxt, ctxt->input_buffer, &len); 509 } 510 else if (ctxt->input_mode == AP_MODE_GETLINE) { 511 status = gnutls_io_input_getline(ctxt, ctxt->input_buffer, &len); 512 } 513 else { 514 /* We have no idea what you are talking about, so return an error. */ 515 return APR_ENOTIMPL; 516 } 517 518 if (status != APR_SUCCESS) { 519 return gnutls_io_filter_error(f, bb, status); 520 } 521 522 /* Create a transient bucket out of the decrypted data. */ 523 if (len > 0) { 524 apr_bucket *bucket = 525 apr_bucket_transient_create(ctxt->input_buffer, len, 526 f->c->bucket_alloc); 527 APR_BRIGADE_INSERT_TAIL(bb, bucket); 528 } 529 530 return status; 531 } 532 533 apr_status_t mgs_filter_output(ap_filter_t * f, 534 apr_bucket_brigade * bb) 535 { 536 apr_size_t ret; 537 apr_bucket* e; 538 mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx; 539 apr_status_t status = APR_SUCCESS; 540 apr_read_type_e rblock = APR_NONBLOCK_READ; 541 542 if (f->c->aborted) { 543 apr_brigade_cleanup(bb); 544 return APR_ECONNABORTED; 545 } 546 547 if (ctxt->status == 0) { 548 gnutls_do_handshake(ctxt); 549 } 550 551 if (ctxt->status < 0) { 552 return ap_pass_brigade(f->next, bb); 553 } 554 555 while (!APR_BRIGADE_EMPTY(bb)) { 556 apr_bucket *bucket = APR_BRIGADE_FIRST(bb); 557 558 if (AP_BUCKET_IS_EOC(bucket)) { 559 if (ctxt->session != NULL) { 560 do { 561 ret = gnutls_bye( ctxt->session, GNUTLS_SHUT_WR); 562 } while(ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN); 563 } 564 565 apr_bucket_copy(bucket, &e); 566 APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, e); 567 568 if ((status = ap_pass_brigade(f->next, ctxt->output_bb)) != APR_SUCCESS) { 569 apr_brigade_cleanup(ctxt->output_bb); 570 return status; 571 } 572 573 apr_brigade_cleanup(ctxt->output_bb); 574 if (ctxt->session) { 575 gnutls_deinit(ctxt->session); 576 ctxt->session = NULL; 577 } 578 continue; 579 } else if (APR_BUCKET_IS_FLUSH(bucket) || APR_BUCKET_IS_EOS(bucket)) { 580 581 apr_bucket_copy(bucket, &e); 582 APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, e); 583 if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) { 584 apr_brigade_cleanup(ctxt->output_bb); 585 return status; 586 } 587 588 apr_brigade_cleanup(ctxt->output_bb); 589 continue; 590 } 591 else { 592 /* filter output */ 593 const char *data; 594 apr_size_t len; 595 596 status = apr_bucket_read(bucket, &data, &len, rblock); 597 598 if (APR_STATUS_IS_EAGAIN(status)) { 599 rblock = APR_BLOCK_READ; 600 continue; /* and try again with a blocking read. */ 601 } 602 603 rblock = APR_NONBLOCK_READ; 604 605 if (!APR_STATUS_IS_EOF(status) && (status != APR_SUCCESS)) { 606 break; 607 } 608 609 if (len > 0) { 610 611 if (ctxt->session == NULL) { 612 ret = GNUTLS_E_INVALID_REQUEST; 613 } else { 614 do { 615 ret = gnutls_record_send(ctxt->session, data, len); 616 } 617 while(ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN); 618 } 619 620 if (ret < 0) { 621 /* error sending output */ 622 ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->output_rc, 623 ctxt->c->base_server, 624 "GnuTLS: Error writing data." 625 " (%d) '%s'", (int)ret, gnutls_strerror(ret)); 626 if (ctxt->output_rc == APR_SUCCESS) { 627 ctxt->output_rc = APR_EGENERAL; 628 } 629 } 630 else if (ret != len) { 631 /* Not able to send the entire bucket, 632 split it and send it again. */ 633 apr_bucket_split(bucket, ret); 634 } 635 } 636 637 apr_bucket_delete(bucket); 638 639 if (ctxt->output_rc != APR_SUCCESS) { 640 break; 641 } 642 } 643 } 644 645 return status; 646 } 647 648 ssize_t mgs_transport_read(gnutls_transport_ptr_t ptr, 649 void *buffer, size_t len) 650 { 651 mgs_handle_t *ctxt = ptr; 652 apr_status_t rc; 653 apr_size_t in = len; 654 apr_read_type_e block = ctxt->input_block; 655 656 ctxt->input_rc = APR_SUCCESS; 657 658 /* If Len = 0, we don't do anything. */ 659 if (!len) 660 return 0; 661 662 if (!ctxt->input_bb) { 663 ctxt->input_rc = APR_EOF; 664 return -1; 665 } 666 667 if (APR_BRIGADE_EMPTY(ctxt->input_bb)) { 668 669 rc = ap_get_brigade(ctxt->input_filter->next, ctxt->input_bb, 670 AP_MODE_READBYTES, ctxt->input_block, in); 671 672 /* Not a problem, there was simply no data ready yet. 673 */ 674 if (APR_STATUS_IS_EAGAIN(rc) || APR_STATUS_IS_EINTR(rc) 675 || (rc == APR_SUCCESS && APR_BRIGADE_EMPTY(ctxt->input_bb))) { 676 677 if (APR_STATUS_IS_EOF(ctxt->input_rc)) { 678 return 0; 679 } else { 680 if (ctxt->session) 681 gnutls_transport_set_errno(ctxt->session, EINTR); 682 return -1; 683 } 684 } 685 686 687 if (rc != APR_SUCCESS) { 688 /* Unexpected errors discard the brigade */ 689 apr_brigade_cleanup(ctxt->input_bb); 690 ctxt->input_bb = NULL; 691 return -1; 692 } 693 } 694 695 ctxt->input_rc = brigade_consume(ctxt->input_bb, block, buffer, &len); 696 697 if (ctxt->input_rc == APR_SUCCESS) { 698 return (ssize_t) len; 699 } 700 701 if (APR_STATUS_IS_EAGAIN(ctxt->input_rc) 702 || APR_STATUS_IS_EINTR(ctxt->input_rc)) { 703 if (len == 0) { 704 if (ctxt->session) 705 gnutls_transport_set_errno(ctxt->session, EINTR); 706 return -1; 707 } 708 709 return (ssize_t) len; 710 } 711 712 /* Unexpected errors and APR_EOF clean out the brigade. 713 * Subsequent calls will return APR_EOF. 714 */ 715 apr_brigade_cleanup(ctxt->input_bb); 716 ctxt->input_bb = NULL; 717 718 if (APR_STATUS_IS_EOF(ctxt->input_rc) && len) { 719 /* Provide the results of this read pass, 720 * without resetting the BIO retry_read flag 721 */ 722 return (ssize_t) len; 723 } 724 725 return -1; 672 if (!ctxt->input_bb) { 673 ctxt->input_rc = APR_EOF; 674 return -1; 675 } 676 677 if (APR_BRIGADE_EMPTY(ctxt->input_bb)) { 678 679 rc = ap_get_brigade(ctxt->input_filter->next, 680 ctxt->input_bb, AP_MODE_READBYTES, 681 ctxt->input_block, in); 682 683 /* Not a problem, there was simply no data ready yet. 684 */ 685 if (APR_STATUS_IS_EAGAIN(rc) || APR_STATUS_IS_EINTR(rc) 686 || (rc == APR_SUCCESS 687 && APR_BRIGADE_EMPTY(ctxt->input_bb))) { 688 689 if (APR_STATUS_IS_EOF(ctxt->input_rc)) { 690 return 0; 691 } else { 692 if (ctxt->session) 693 gnutls_transport_set_errno(ctxt-> 694 session, 695 EINTR); 696 return -1; 697 } 698 } 699 700 701 if (rc != APR_SUCCESS) { 702 /* Unexpected errors discard the brigade */ 703 apr_brigade_cleanup(ctxt->input_bb); 704 ctxt->input_bb = NULL; 705 return -1; 706 } 707 } 708 709 ctxt->input_rc = 710 brigade_consume(ctxt->input_bb, block, buffer, &len); 711 712 if (ctxt->input_rc == APR_SUCCESS) { 713 return (ssize_t) len; 714 } 715 716 if (APR_STATUS_IS_EAGAIN(ctxt->input_rc) 717 || APR_STATUS_IS_EINTR(ctxt->input_rc)) { 718 if (len == 0) { 719 if (ctxt->session) 720 gnutls_transport_set_errno(ctxt->session, 721 EINTR); 722 return -1; 723 } 724 725 return (ssize_t) len; 726 } 727 728 /* Unexpected errors and APR_EOF clean out the brigade. 729 * Subsequent calls will return APR_EOF. 730 */ 731 apr_brigade_cleanup(ctxt->input_bb); 732 ctxt->input_bb = NULL; 733 734 if (APR_STATUS_IS_EOF(ctxt->input_rc) && len) { 735 /* Provide the results of this read pass, 736 * without resetting the BIO retry_read flag 737 */ 738 return (ssize_t) len; 739 } 740 741 return -1; 726 742 } 727 743 … … 729 745 static ssize_t write_flush(mgs_handle_t * ctxt) 730 746 { 731 apr_bucket *e; 732 733 if (!(ctxt->output_blen || ctxt->output_length)) { 734 ctxt->output_rc = APR_SUCCESS; 735 return 1; 736 } 737 738 if (ctxt->output_blen) { 739 e = apr_bucket_transient_create(ctxt->output_buffer, 740 ctxt->output_blen, 741 ctxt->output_bb->bucket_alloc); 742 /* we filled this buffer first so add it to the 743 * head of the brigade 744 */ 745 APR_BRIGADE_INSERT_HEAD(ctxt->output_bb, e); 746 ctxt->output_blen = 0; 747 } 748 749 ctxt->output_length = 0; 750 e = apr_bucket_flush_create(ctxt->output_bb->bucket_alloc); 751 APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, e); 752 753 ctxt->output_rc = ap_pass_brigade(ctxt->output_filter->next, 754 ctxt->output_bb); 755 /* clear the brigade to be ready for next time */ 756 apr_brigade_cleanup(ctxt->output_bb); 757 758 return (ctxt->output_rc == APR_SUCCESS) ? 1 : -1; 747 apr_bucket *e; 748 749 if (!(ctxt->output_blen || ctxt->output_length)) { 750 ctxt->output_rc = APR_SUCCESS; 751 return 1; 752 } 753 754 if (ctxt->output_blen) { 755 e = apr_bucket_transient_create(ctxt->output_buffer, 756 ctxt->output_blen, 757 ctxt->output_bb-> 758 bucket_alloc); 759 /* we filled this buffer first so add it to the 760 * head of the brigade 761 */ 762 APR_BRIGADE_INSERT_HEAD(ctxt->output_bb, e); 763 ctxt->output_blen = 0; 764 } 765 766 ctxt->output_length = 0; 767 e = apr_bucket_flush_create(ctxt->output_bb->bucket_alloc); 768 APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, e); 769 770 ctxt->output_rc = ap_pass_brigade(ctxt->output_filter->next, 771 ctxt->output_bb); 772 /* clear the brigade to be ready for next time */ 773 apr_brigade_cleanup(ctxt->output_bb); 774 775 return (ctxt->output_rc == APR_SUCCESS) ? 1 : -1; 759 776 } 760 777 761 778 ssize_t mgs_transport_write(gnutls_transport_ptr_t ptr, 762 const void *buffer, size_t len) 763 { 764 mgs_handle_t *ctxt = ptr; 765 766 /* pass along the encrypted data 767 * need to flush since we're using SSL's malloc-ed buffer 768 * which will be overwritten once we leave here 769 */ 770 apr_bucket *bucket = apr_bucket_transient_create(buffer, len, 771 ctxt->output_bb->bucket_alloc); 772 ctxt->output_length += len; 773 APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, bucket); 774 775 if (write_flush(ctxt) < 0) { 776 return -1; 777 } 778 return len; 779 } 779 const void *buffer, size_t len) 780 { 781 mgs_handle_t *ctxt = ptr; 782 783 /* pass along the encrypted data 784 * need to flush since we're using SSL's malloc-ed buffer 785 * which will be overwritten once we leave here 786 */ 787 apr_bucket *bucket = apr_bucket_transient_create(buffer, len, 788 ctxt->output_bb-> 789 bucket_alloc); 790 ctxt->output_length += len; 791 APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, bucket); 792 793 if (write_flush(ctxt) < 0) { 794 return -1; 795 } 796 return len; 797 }
Note: See TracChangeset
for help on using the changeset viewer.