blob: f711c6a6f77de004e3abeb54fc4130b6e96a0dcc [file] [log] [blame]
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* mod_tls.c - Apache SSL/TLS module for NetWare by Mike Gardiner.
*
* This module gives Apache the ability to do SSL/TLS with a minimum amount
* of effort. All of the SSL/TLS logic is already on NetWare versions 5 and
* above and is interfaced through WinSock on NetWare. As you can see in
* the code below SSL/TLS sockets can be created with three WinSock calls.
*
* To load, simply place the module in the modules directory under the main
* apache tree. Then add a "SecureListen" with two arguments. The first
* argument is an address and/or port. The second argument is the key pair
* name as created in ConsoleOne.
*
* Examples:
*
* SecureListen 443 "SSL CertificateIP"
* SecureListen 123.45.67.89:443 mycert
*/
#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; /* local IP address and port */
int fd;
int used; /* Only used during restart */
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));
/* note that because we're about to slack we don't use psocket */
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; // SO_SSL_AUTH_CLIENT
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];
/* For some reason r->server->port always return 80 rather than
* the current port. So for now we will get it straight from
* the horses mouth.
*/
/* itoa(r->server->port, port, 10); */
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, /* initializer */
NULL, /* dir config creater */
NULL, /* dir merger --- default is to override */
tls_config_server_create, /* server config */
tls_config_server_merge, /* merge server config */
tls_module_cmds, /* command table */
NULL, /* handlers */
NULL, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
NULL, /* fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
tls_hook_Fixup /* post read request */
};