/* the SSL_read replacement routine which knows about the suck buffer */ static int ssl_io_suck_read(SSL *ssl, char *buf, int len) { ap_ctx *actx; struct ssl_io_suck_st *ss; request_rec *r = NULL; int rv; actx = (ap_ctx *)SSL_get_app_data2(ssl); if (actx != NULL) r = (request_rec *)ap_ctx_get(actx, "ssl::request_rec"); rv = -1; if (r != NULL && r->ctx != NULL) { ss = ap_ctx_get(r->ctx, "ssl::io::suck"); if (ss != NULL) { if (ss->active && ss->pendlen > 0) { /* ok, there is pre-sucked data */ len = (ss->pendlen > len ? len : ss->pendlen); memcpy(buf, ss->pendptr, len); ss->pendptr += len; ss->pendlen -= len; ssl_log(r->server, SSL_LOG_TRACE, "I/O: injecting %d bytes of pre-sucked data " "into Apache I/O layer", len); rv = len; } } } if (rv == -1) rv = SSL_read(ssl, buf, len); return rv; }
static int SSL_recvwithtimeout(BUFF *fb, char *buf, int len) { int iostate = 1; fd_set fdset; struct timeval tv; int err = WSAEWOULDBLOCK; int rv; int sock = fb->fd_in; SSL *ssl; int retry; ssl = ap_ctx_get(fb->ctx, "ssl"); if (!(tv.tv_sec = ap_check_alarm())) return (SSL_read(ssl, buf, len)); rv = ioctlsocket(sock, FIONBIO, &iostate); iostate = 0; ap_assert(!rv); rv = SSL_read(ssl, buf, len); if (rv <= 0) { if (BIO_sock_should_retry(rv)) { do { retry = 0; FD_ZERO(&fdset); FD_SET((unsigned int)sock, &fdset); tv.tv_usec = 0; rv = select(FD_SETSIZE, &fdset, NULL, NULL, &tv); if (rv == SOCKET_ERROR) err = WSAGetLastError(); else if (rv == 0) { ioctlsocket(sock, FIONBIO, &iostate); ap_check_alarm(); WSASetLastError(WSAEWOULDBLOCK); return (SOCKET_ERROR); } else { rv = SSL_read(ssl, buf, len); if (rv == SOCKET_ERROR) { if (BIO_sock_should_retry(rv)) { ap_log_error(APLOG_MARK,APLOG_DEBUG, NULL, "select claimed we could read, " "but in fact we couldn't. " "This is a bug in Windows."); retry = 1; Sleep(100); } else { err = WSAGetLastError(); } } } } while(retry); } } ioctlsocket(sock, FIONBIO, &iostate); if (rv == SOCKET_ERROR) WSASetLastError(err); return (rv); }
static int ssl_io_hook_writev(BUFF *fb, const struct iovec *iov, int iovcnt) { SSL *ssl; conn_rec *c; int rc; if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL) { rc = SSL_writev(ssl, iov, iovcnt); /* * Simulate an EINTR in case OpenSSL wants to write more. */ if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_WANT_WRITE) errno = EINTR; /* * Log SSL errors */ if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_SSL) { c = (conn_rec *)SSL_get_app_data(ssl); ssl_log(c->server, SSL_LOG_ERROR|SSL_ADD_SSLERR, "SSL error on writing data"); } /* * writev(2) returns only the generic error number -1 */ if (rc < 0) rc = -1; } else rc = writev(fb->fd, iov, iovcnt); return rc; }
static int ssl_io_hook_read(BUFF *fb, char *buf, int len) { SSL *ssl; conn_rec *c; int rc; if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL) { rc = SSL_read(ssl, buf, len); /* * Simulate an EINTR in case OpenSSL wants to read more. * (This is usually the case when the client forces an SSL * renegotation which is handled implicitly by OpenSSL.) */ if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_WANT_READ) errno = EINTR; /* * Log SSL errors */ if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_SSL) { c = (conn_rec *)SSL_get_app_data(ssl); ssl_log(c->server, SSL_LOG_ERROR|SSL_ADD_SSLERR, "SSL error on reading data"); } /* * read(2) returns only the generic error number -1 */ if (rc < 0) rc = -1; } else rc = read(fb->fd_in, buf, len); return rc; }
/* record a sucked input chunk */ static void ssl_io_suck_record(request_rec *r, char *buf, int len) { struct ssl_io_suck_st *ss; if ((ss = ap_ctx_get(r->ctx, "ssl::io::suck")) == NULL) return; if (((ss->bufptr + ss->buflen) - (ss->pendptr + ss->pendlen)) < len) { /* "expand" buffer: actually we cannot really expand the buffer here, because Apache's pool system doesn't support expanding chunks of memory. Instead we have to either reuse processed data or allocate a new chunk of memory in advance if we really need more memory. */ int newlen; char *newptr; if (( (ss->pendptr - ss->bufptr) + ((ss->bufptr + ss->buflen) - (ss->pendptr + ss->pendlen)) ) >= len) { /* make memory available by reusing already processed data */ memmove(ss->bufptr, ss->pendptr, ss->pendlen); ss->pendptr = ss->bufptr; } else { /* too bad, we have to allocate a new larger buffer */ newlen = (ss->buflen * 2) + len; newptr = ap_palloc(r->pool, newlen); ss->bufptr = newptr; ss->buflen = newlen; memcpy(ss->bufptr, ss->pendptr, ss->pendlen); ss->pendptr = ss->bufptr; } } memcpy(ss->pendptr+ss->pendlen, buf, len); ss->pendlen += len; return; }
static int ssl_io_hook_sendwithtimeout(BUFF *fb, const char *buf, int len) { SSL *ssl; int rc; if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL) rc = SSL_sendwithtimeout(fb, buf, len); else rc = ap_sendwithtimeout(fb->fd, buf, len, 0); return rc; }
/* finish request_rec after input sucking */ static void ssl_io_suck_end(request_rec *r) { struct ssl_io_suck_st *ss; if ((ss = ap_ctx_get(r->ctx, "ssl::io::suck")) == NULL) return; ss->active = TRUE; r->read_body = REQUEST_NO_BODY; r->read_length = 0; r->read_chunked = 0; r->remaining = 0; ap_bsetflag(r->connection->client, B_CHUNK, 0); return; }
void ssl_config_global_create(void) { pool *pPool; SSLModConfigRec *mc; mc = ap_ctx_get(ap_global_ctx, "ssl_module"); if (mc == NULL) { /* * allocate an own subpool which survives server restarts */ pPool = ap_make_sub_pool(NULL); mc = (SSLModConfigRec *)ap_palloc(pPool, sizeof(SSLModConfigRec)); mc->pPool = pPool; mc->bFixed = FALSE; /* * initialize per-module configuration */ mc->nInitCount = 0; mc->nSessionCacheMode = SSL_SCMODE_UNSET; mc->szSessionCacheDataFile = NULL; mc->nSessionCacheDataSize = 0; mc->pSessionCacheDataMM = NULL; mc->tSessionCacheDataTable = NULL; mc->nMutexMode = SSL_MUTEXMODE_UNSET; mc->szMutexFile = NULL; mc->nMutexFD = -1; mc->nMutexSEMID = -1; mc->aRandSeed = ap_make_array(pPool, 4, sizeof(ssl_randseed_t)); mc->tPrivateKey = ssl_ds_table_make(pPool, sizeof(ssl_asn1_t)); mc->tPublicCert = ssl_ds_table_make(pPool, sizeof(ssl_asn1_t)); mc->tTmpKeys = ssl_ds_table_make(pPool, sizeof(ssl_asn1_t)); #ifdef SSL_EXPERIMENTAL_ENGINE mc->szCryptoDevice = NULL; #endif (void)memset(mc->pTmpKeys, 0, SSL_TKPIDX_MAX*sizeof(void *)); #ifdef SSL_VENDOR mc->ctx = ap_ctx_new(pPool); ap_hook_use("ap::mod_ssl::vendor::config_global_create", AP_HOOK_SIG2(void,ptr), AP_HOOK_MODE_ALL, mc); #endif /* * And push it into Apache's global context */ ap_ctx_set(ap_global_ctx, "ssl_module", mc); }
/* prepare request_rec structure for input sucking */ static void ssl_io_suck_start(request_rec *r) { struct ssl_io_suck_st *ss; ss = ap_ctx_get(r->ctx, "ssl::io::suck"); if (ss == NULL) { ss = ap_palloc(r->pool, sizeof(struct ssl_io_suck_st)); ap_ctx_set(r->ctx, "ssl::io::suck", ss); ss->buflen = 8192; ss->bufptr = ap_palloc(r->pool, ss->buflen); } ss->pendptr = ss->bufptr; ss->pendlen = 0; ss->active = FALSE; return; }