#define CORE_PRIVATE
#define WS_SSL
#define MAX_ADDRESS 512
#define MAX_KEY 80
#include "httpd.h"
#include "http_config.h"
#include "http_conf_globals.h"
#include "http_log.h"
#include "http_main.h"
module MODULE_VAR_EXPORT tls_module;
typedef
struct
TLSSrvConfigRec TLSSrvConfigRec;
typedef
struct
seclisten_rec seclisten_rec;
static
fd_set listenfds;
struct
seclisten_rec {
seclisten_rec *next;
struct
sockaddr_in local_addr;
int
fd;
int
used;
char
key[MAX_KEY];
int
mutual;
};
struct
TLSSrvConfigRec {
table *sltable;
};
static
seclisten_rec* ap_seclisteners = NULL;
#define get_tls_cfg(srv) (TLSSrvConfigRec *) ap_get_module_config(srv->module_config, &tls_module)
static
int
find_secure_listener(seclisten_rec *lr)
{
seclisten_rec *sl;
for
(sl = ap_seclisteners; sl; sl = sl->next) {
if
(!
memcmp
(&sl->local_addr, &lr->local_addr,
sizeof
(sl->local_addr))) {
sl->used = 1;
return
sl->fd;
}
}
return
-1;
}
static
int
make_secure_socket(pool *p,
const
struct
sockaddr_in *server,
char
* key,
int
mutual, server_rec *server_conf)
{
int
s;
int
one = 1;
char
addr[MAX_ADDRESS];
struct
sslserveropts opts;
struct
linger li;
unsigned
int
optParam;
WSAPROTOCOL_INFO SecureProtoInfo;
int
no = 1;
if
(server->sin_addr.s_addr != htonl(INADDR_ANY))
ap_snprintf(addr,
sizeof
(addr),
"address %s port %d"
,
inet_ntoa(server->sin_addr), ntohs(server->sin_port));
else
ap_snprintf(addr,
sizeof
(addr),
"port %d"
, ntohs(server->sin_port));
ap_block_alarms();
memset
(&SecureProtoInfo, 0,
sizeof
(WSAPROTOCOL_INFO));
SecureProtoInfo.iAddressFamily = AF_INET;
SecureProtoInfo.iSocketType = SOCK_STREAM;
SecureProtoInfo.iProtocol = IPPROTO_TCP;
SecureProtoInfo.iSecurityScheme = SECURITY_PROTOCOL_SSL;
s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP,
(LPWSAPROTOCOL_INFO)&SecureProtoInfo, 0, 0);
if
(s == INVALID_SOCKET) {
errno
= WSAGetLastError();
ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
"make_secure_socket: failed to get a socket for %s"
, addr);
ap_unblock_alarms();
return
-1;
}
if
(!mutual) {
optParam = SO_SSL_ENABLE | SO_SSL_SERVER;
if
(WSAIoctl(s, SO_SSL_SET_FLAGS, (
char
*)&optParam,
sizeof
(optParam), NULL, 0, NULL, NULL, NULL)) {
errno
= WSAGetLastError();
ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
"make_secure_socket: for %s, WSAIoctl: (SO_SSL_SET_FLAGS)"
, addr);
ap_unblock_alarms();
return
-1;
}
}
opts.cert = key;
opts.certlen =
strlen
(key);
opts.sidtimeout = 0;
opts.sidentries = 0;
opts.siddir = NULL;
if
(WSAIoctl(s, SO_SSL_SET_SERVER, (
char
*)&opts,
sizeof
(opts),
NULL, 0, NULL, NULL, NULL) != 0) {
errno
= WSAGetLastError();
ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
"make_secure_socket: for %s, WSAIoctl: (SO_SSL_SET_SERVER)"
, addr);
ap_unblock_alarms();
return
-1;
}
if
(mutual) {
optParam = 0x07;
if
(WSAIoctl(s, SO_SSL_SET_FLAGS, (
char
*)&optParam,
sizeof
(optParam), NULL, 0, NULL, NULL, NULL)) {
errno
= WSAGetLastError();
ap_log_error( APLOG_MARK, APLOG_CRIT, server_conf,
"make_secure_socket: for %s, WSAIoctl: (SO_SSL_SET_FLAGS)"
, addr );
ap_unblock_alarms();
return
-1;
}
}
if
(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (
char
*) &one,
sizeof
(
int
)) < 0) {
errno
= WSAGetLastError();
ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
"make_secure_socket: for %s, setsockopt: (SO_REUSEADDR)"
, addr);
ap_unblock_alarms();
return
-1;
}
one = 1;
#ifdef SO_KEEPALIVE
if
(setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (
char
*) &one,
sizeof
(
int
)) < 0) {
errno
= WSAGetLastError();
ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
"make_secure_socket: for %s, setsockopt: (SO_KEEPALIVE)"
, addr);
#endif
ap_unblock_alarms();
return
-1;
}
if
(server_conf->send_buffer_size) {
if
(setsockopt(s, SOL_SOCKET, SO_SNDBUF,
(
char
*) &server_conf->send_buffer_size,
sizeof
(
int
)) < 0) {
errno
= WSAGetLastError();
ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf,
"make_secure_socket: failed to set SendBufferSize for %s, "
"using default"
, addr);
ap_unblock_alarms();
return
-1;
}
}
if
(bind(s, (
struct
sockaddr *) server,
sizeof
(
struct
sockaddr_in)) == -1) {
errno
= WSAGetLastError();
ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
"make_secure_socket: could not bind to %s"
, addr);
ap_unblock_alarms();
return
-1;
}
if
(setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (
char
*) &no,
sizeof
(
int
)) < 0) {
errno
= WSAGetLastError();
ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf,
"setsockopt: (TCP_NODELAY)"
);
}
if
(listen(s, ap_listenbacklog) == -1) {
errno
= WSAGetLastError();
ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
"make_secure_socket: unable to listen for connections on %s"
, addr);
ap_unblock_alarms();
return
-1;
}
ap_unblock_alarms();
return
s;
}
static
const
char
*set_secure_listener(cmd_parms *cmd,
void
*dummy,
char
*ips,
char
* key,
char
* mutual)
{
TLSSrvConfigRec* sc = get_tls_cfg(cmd->server);
const
char
*err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
char
*ports;
unsigned
short
port;
seclisten_rec *
new
;
if
(err != NULL)
return
err;
ports =
strchr
(ips,
':'
);
if
(ports != NULL) {
if
(ports == ips)
return
"Missing IP address"
;
else
if
(ports[1] ==
'\0'
)
return
"Address must end in :<port-number>"
;
*(ports++) =
'\0'
;
}
else
{
ports = ips;
}
new
= ap_pcalloc(cmd->pool,
sizeof
(seclisten_rec));
new
->local_addr.sin_family = AF_INET;
if
(ports == ips)
new
->local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
else
new
->local_addr.sin_addr.s_addr = ap_get_virthost_addr(ips, NULL);
port =
atoi
(ports);
if
(!port)
return
"Port must be numeric"
;
ap_table_set(sc->sltable, ports,
"T"
);
new
->local_addr.sin_port = htons(port);
new
->fd = -1;
new
->used = 0;
new
->next = ap_seclisteners;
strcpy
(
new
->key, key);
new
->mutual = (mutual) ? 1 : 0;
ap_seclisteners =
new
;
return
NULL;
}
static
void
InitTLS(server_rec *s, pool *p)
{
seclisten_rec* sl;
listen_rec* lr;
for
(sl = ap_seclisteners; sl != NULL; sl = sl->next) {
sl->fd = find_secure_listener(sl);
if
(sl->fd < 0)
sl->fd = make_secure_socket(p, &sl->local_addr, sl->key, sl->mutual, s);
else
ap_note_cleanups_for_socket(p, sl->fd);
if
(sl->fd >= 0) {
FD_SET(sl->fd, &listenfds);
ap_note_cleanups_for_socket(p, sl->fd);
lr = ap_pcalloc(p,
sizeof
(listen_rec));
if
(lr) {
lr->local_addr = sl->local_addr;
lr->used = 0;
lr->fd = sl->fd;
lr->next = ap_listeners;
ap_listeners = lr;
}
}
else
{
clean_parent_exit(1);
}
}
}
void
*tls_config_server_create(pool *p, server_rec *s)
{
TLSSrvConfigRec *
new
= ap_palloc(p,
sizeof
(TLSSrvConfigRec));
new
->sltable = ap_make_table(p, 5);
return
new
;
}
void
*tls_config_server_merge(pool *p,
void
*basev,
void
*addv)
{
TLSSrvConfigRec *base = (TLSSrvConfigRec *)basev;
TLSSrvConfigRec *add = (TLSSrvConfigRec *)addv;
TLSSrvConfigRec *merged = (TLSSrvConfigRec *)ap_palloc(p,
sizeof
(TLSSrvConfigRec));
return
merged;
}
int
tls_hook_Fixup(request_rec *r)
{
TLSSrvConfigRec *sc = get_tls_cfg(r->server);
table *e = r->subprocess_env;
const
char
*s_secure;
char
port[8];
itoa(ntohs(((r->connection)->local_addr).sin_port), port, 10);
s_secure = ap_table_get(sc->sltable, port);
if
(!s_secure)
return
DECLINED;
ap_table_set(e,
"HTTPS"
,
"on"
);
return
DECLINED;
}
static
const
command_rec tls_module_cmds[] = {
{
"SecureListen"
, set_secure_listener, NULL, RSRC_CONF, TAKE23,
"specify an address and/or port with a key pair name.\n"
"Optional third parameter of MUTUAL configures the port for mutual authentication."
},
{ NULL }
};
module MODULE_VAR_EXPORT tls_module =
{
STANDARD_MODULE_STUFF,
InitTLS,
NULL,
NULL,
tls_config_server_create,
tls_config_server_merge,
tls_module_cmds,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
tls_hook_Fixup
};