mod_nw_ssl.c revision 3e520e9f095fbbcaa3c216c8ea56e89bd6fd58b4
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele/* Licensed to the Apache Software Foundation (ASF) under one or more
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele * contributor license agreements. See the NOTICE file distributed with
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele * this work for additional information regarding copyright ownership.
5f5d1b4cc970b7f06ff8ef6526128e9a27303d88nd * The ASF licenses this file to You under the Apache License, Version 2.0
acc36ab93565d2880447d535da6ca6e5feac7a70nd * (the "License"); you may not use this file except in compliance with
acc36ab93565d2880447d535da6ca6e5feac7a70nd * the License. You may obtain a copy of the License at
db479b48bd4d75423ed4a45e15b75089d1a8ad72fielding *
db479b48bd4d75423ed4a45e15b75089d1a8ad72fielding * http://www.apache.org/licenses/LICENSE-2.0
db479b48bd4d75423ed4a45e15b75089d1a8ad72fielding *
db479b48bd4d75423ed4a45e15b75089d1a8ad72fielding * Unless required by applicable law or agreed to in writing, software
db479b48bd4d75423ed4a45e15b75089d1a8ad72fielding * distributed under the License is distributed on an "AS IS" BASIS,
db479b48bd4d75423ed4a45e15b75089d1a8ad72fielding * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
acc36ab93565d2880447d535da6ca6e5feac7a70nd * See the License for the specific language governing permissions and
acc36ab93565d2880447d535da6ca6e5feac7a70nd * limitations under the License.
acc36ab93565d2880447d535da6ca6e5feac7a70nd */
acc36ab93565d2880447d535da6ca6e5feac7a70nd
acc36ab93565d2880447d535da6ca6e5feac7a70nd/*
acc36ab93565d2880447d535da6ca6e5feac7a70nd * mod_tls.c - Apache SSL/TLS module for NetWare by Mike Gardiner.
acc36ab93565d2880447d535da6ca6e5feac7a70nd *
acc36ab93565d2880447d535da6ca6e5feac7a70nd * This module gives Apache the ability to do SSL/TLS with a minimum amount
acc36ab93565d2880447d535da6ca6e5feac7a70nd * of effort. All of the SSL/TLS logic is already on NetWare versions 5 and
acc36ab93565d2880447d535da6ca6e5feac7a70nd * above and is interfaced through WinSock on NetWare. As you can see in
7db9f691a00ead175b03335457ca296a33ddf31bnd * the code below SSL/TLS sockets can be created with three WinSock calls.
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele *
3ca6ee111e6044cb463e6dc45b9adcfa3050ff00rbowen * To load, simply place the module in the modules directory under the main
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele * apache tree. Then add a "SecureListen" with two arguments. The first
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele * argument is an address and/or port. The second argument is the key pair
e50df6c711553f98103f1e0802f7de8c59be7cddslive * name as created in ConsoleOne.
e50df6c711553f98103f1e0802f7de8c59be7cddslive *
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele * Examples:
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele *
c449ffd09c6af936695e858cff98916215ff76bckess * SecureListen 443 "SSL CertificateIP"
e50df6c711553f98103f1e0802f7de8c59be7cddslive * SecureListen 123.45.67.89:443 mycert
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele */
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele#define WS_SSL
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
e50df6c711553f98103f1e0802f7de8c59be7cddslive#define MAX_ADDRESS 512
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele#define MAX_KEY 80
91dbfe27f56a07b53ec19068fdb47581476d5c3brbowen
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele#include "httpd.h"
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele#include "http_config.h"
e50df6c711553f98103f1e0802f7de8c59be7cddslive#include "http_connection.h"
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele#include "http_core.h"
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele#include "http_log.h"
19e48954d3cfb4f573a99866b0071b6aaa62723ckess#include "http_protocol.h"
91dbfe27f56a07b53ec19068fdb47581476d5c3brbowen#include "http_request.h"
19e48954d3cfb4f573a99866b0071b6aaa62723ckess#include "ap_listen.h"
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele#include "apr_strings.h"
e50df6c711553f98103f1e0802f7de8c59be7cddslive#include "apr_portable.h"
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele#include "apr_optional.h"
91dbfe27f56a07b53ec19068fdb47581476d5c3brbowen
3f2fa24c621652779fd6706116a35b49c6dc26cand#include <unilib.h>
3f2fa24c621652779fd6706116a35b49c6dc26cand
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele#ifndef SO_TLS_UNCLEAN_SHUTDOWN
e50df6c711553f98103f1e0802f7de8c59be7cddslive#define SO_TLS_UNCLEAN_SHUTDOWN 0
e50df6c711553f98103f1e0802f7de8c59be7cddslive#endif
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
91dbfe27f56a07b53ec19068fdb47581476d5c3brbowen/* The ssl_var_lookup() optional function retrieves SSL environment
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele * variables. */
252b32956857ad89fc9ee708c4c6eb36097a647cerikabeleAPR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup,
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele (apr_pool_t *, server_rec *,
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele conn_rec *, request_rec *,
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele char *));
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele/* An optional function which returns non-zero if the given connection
91dbfe27f56a07b53ec19068fdb47581476d5c3brbowen * is using SSL/TLS. */
252b32956857ad89fc9ee708c4c6eb36097a647cerikabeleAPR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec *));
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele/* The ssl_proxy_enable() and ssl_engine_disable() optional functions
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele * are used by mod_proxy to enable use of SSL for outgoing
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele * connections. */
252b32956857ad89fc9ee708c4c6eb36097a647cerikabeleAPR_DECLARE_OPTIONAL_FN(int, ssl_proxy_enable, (conn_rec *));
91dbfe27f56a07b53ec19068fdb47581476d5c3brbowenAPR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *));
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
e50df6c711553f98103f1e0802f7de8c59be7cddslive#define strEQ(s1,s2) (strcmp(s1,s2) == 0)
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele#define strNE(s1,s2) (strcmp(s1,s2) != 0)
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele#define strEQn(s1,s2,n) (strncmp(s1,s2,n) == 0)
91dbfe27f56a07b53ec19068fdb47581476d5c3brbowen#define strNEn(s1,s2,n) (strncmp(s1,s2,n) != 0)
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele#define strcEQ(s1,s2) (strcasecmp(s1,s2) == 0)
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele#define strcNE(s1,s2) (strcasecmp(s1,s2) != 0)
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele#define strcEQn(s1,s2,n) (strncasecmp(s1,s2,n) == 0)
19e48954d3cfb4f573a99866b0071b6aaa62723ckess#define strcNEn(s1,s2,n) (strncasecmp(s1,s2,n) != 0)
cf02129aebf73dd0bdf369b172eb481ff76ac5f6colm
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele#define strIsEmpty(s) (s == NULL || s[0] == NUL)
e50df6c711553f98103f1e0802f7de8c59be7cddslive
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
91dbfe27f56a07b53ec19068fdb47581476d5c3brbowenmodule AP_MODULE_DECLARE_DATA nwssl_module;
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
e50df6c711553f98103f1e0802f7de8c59be7cddslivetypedef struct NWSSLSrvConfigRec NWSSLSrvConfigRec;
19e48954d3cfb4f573a99866b0071b6aaa62723ckesstypedef struct seclisten_rec seclisten_rec;
19e48954d3cfb4f573a99866b0071b6aaa62723ckesstypedef struct seclistenup_rec seclistenup_rec;
19e48954d3cfb4f573a99866b0071b6aaa62723ckesstypedef struct secsocket_data secsocket_data;
19e48954d3cfb4f573a99866b0071b6aaa62723ckess
e50df6c711553f98103f1e0802f7de8c59be7cddslivestruct seclisten_rec {
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele seclisten_rec *next;
e50df6c711553f98103f1e0802f7de8c59be7cddslive struct sockaddr_in local_addr; /* local IP address and port */
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele int fd;
19e48954d3cfb4f573a99866b0071b6aaa62723ckess int used; /* Only used during restart */
19e48954d3cfb4f573a99866b0071b6aaa62723ckess char key[MAX_KEY];
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele int mutual;
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele char *addr;
e50df6c711553f98103f1e0802f7de8c59be7cddslive apr_port_t port;
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele};
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
19e48954d3cfb4f573a99866b0071b6aaa62723ckessstruct seclistenup_rec {
19e48954d3cfb4f573a99866b0071b6aaa62723ckess seclistenup_rec *next;
19e48954d3cfb4f573a99866b0071b6aaa62723ckess char key[MAX_KEY];
e50df6c711553f98103f1e0802f7de8c59be7cddslive char *addr;
e50df6c711553f98103f1e0802f7de8c59be7cddslive apr_port_t port;
e50df6c711553f98103f1e0802f7de8c59be7cddslive};
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
e50df6c711553f98103f1e0802f7de8c59be7cddslivestruct NWSSLSrvConfigRec {
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele apr_table_t *sltable;
91dbfe27f56a07b53ec19068fdb47581476d5c3brbowen apr_table_t *slutable;
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele apr_pool_t *pPool;
19e48954d3cfb4f573a99866b0071b6aaa62723ckess};
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
19e48954d3cfb4f573a99866b0071b6aaa62723ckessstruct secsocket_data {
19e48954d3cfb4f573a99866b0071b6aaa62723ckess apr_socket_t* csd;
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele int is_secure;
e50df6c711553f98103f1e0802f7de8c59be7cddslive};
e50df6c711553f98103f1e0802f7de8c59be7cddslive
e50df6c711553f98103f1e0802f7de8c59be7cddslivestatic apr_array_header_t *certlist = NULL;
91dbfe27f56a07b53ec19068fdb47581476d5c3brbowenstatic unicode_t** certarray = NULL;
e50df6c711553f98103f1e0802f7de8c59be7cddslivestatic int numcerts = 0;
e50df6c711553f98103f1e0802f7de8c59be7cddslivestatic seclisten_rec* ap_seclisteners = NULL;
e50df6c711553f98103f1e0802f7de8c59be7cddslivestatic seclistenup_rec* ap_seclistenersup = NULL;
e50df6c711553f98103f1e0802f7de8c59be7cddslive
252b32956857ad89fc9ee708c4c6eb36097a647cerikabelestatic ap_listen_rec *nw_old_listeners;
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele#define get_nwssl_cfg(srv) (NWSSLSrvConfigRec *) ap_get_module_config(srv->module_config, &nwssl_module)
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
252b32956857ad89fc9ee708c4c6eb36097a647cerikabelestatic void build_cert_list(apr_pool_t *p)
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele{
e50df6c711553f98103f1e0802f7de8c59be7cddslive int i;
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele char **rootcerts = (char **)certlist->elts;
91dbfe27f56a07b53ec19068fdb47581476d5c3brbowen
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele numcerts = certlist->nelts;
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele certarray = apr_palloc(p, sizeof(unicode_t*)*numcerts);
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
e50df6c711553f98103f1e0802f7de8c59be7cddslive for (i = 0; i < numcerts; ++i) {
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele unicode_t *unistr;
91dbfe27f56a07b53ec19068fdb47581476d5c3brbowen unistr = (unicode_t*)apr_palloc(p, strlen(rootcerts[i])*4);
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele loc2uni (UNI_LOCAL_DEFAULT, unistr, rootcerts[i], 0, 2);
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele certarray[i] = unistr;
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele }
e50df6c711553f98103f1e0802f7de8c59be7cddslive}
e50df6c711553f98103f1e0802f7de8c59be7cddslive
91dbfe27f56a07b53ec19068fdb47581476d5c3brbowen/*
e50df6c711553f98103f1e0802f7de8c59be7cddslive * Parses a host of the form <address>[:port]
e50df6c711553f98103f1e0802f7de8c59be7cddslive * :port is permitted if 'port' is not NULL
e50df6c711553f98103f1e0802f7de8c59be7cddslive */
a6fc6b44b7f8ad7390864b3555341d3abf867f7endstatic unsigned long parse_addr(const char *w, unsigned short *ports)
e50df6c711553f98103f1e0802f7de8c59be7cddslive{
91dbfe27f56a07b53ec19068fdb47581476d5c3brbowen struct hostent *hep;
e50df6c711553f98103f1e0802f7de8c59be7cddslive unsigned long my_addr;
e50df6c711553f98103f1e0802f7de8c59be7cddslive char *p;
e50df6c711553f98103f1e0802f7de8c59be7cddslive
e50df6c711553f98103f1e0802f7de8c59be7cddslive p = strchr(w, ':');
e50df6c711553f98103f1e0802f7de8c59be7cddslive if (ports != NULL) {
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele *ports = 0;
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele if (p != NULL && strcmp(p + 1, "*") != 0)
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele *ports = atoi(p + 1);
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele }
e50df6c711553f98103f1e0802f7de8c59be7cddslive
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele if (p != NULL)
91dbfe27f56a07b53ec19068fdb47581476d5c3brbowen *p = '\0';
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele if (strcmp(w, "*") == 0) {
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele if (p != NULL)
e50df6c711553f98103f1e0802f7de8c59be7cddslive *p = ':';
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele return htonl(INADDR_ANY);
91dbfe27f56a07b53ec19068fdb47581476d5c3brbowen }
91dbfe27f56a07b53ec19068fdb47581476d5c3brbowen
91dbfe27f56a07b53ec19068fdb47581476d5c3brbowen my_addr = apr_inet_addr((char *)w);
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele if (my_addr != INADDR_NONE) {
e50df6c711553f98103f1e0802f7de8c59be7cddslive if (p != NULL)
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele *p = ':';
91dbfe27f56a07b53ec19068fdb47581476d5c3brbowen return my_addr;
e50df6c711553f98103f1e0802f7de8c59be7cddslive }
e50df6c711553f98103f1e0802f7de8c59be7cddslive
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele hep = gethostbyname(w);
e50df6c711553f98103f1e0802f7de8c59be7cddslive
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) {
e50df6c711553f98103f1e0802f7de8c59be7cddslive /* XXX Should be echoing by h_errno the actual failure, no?
e50df6c711553f98103f1e0802f7de8c59be7cddslive * ap_log_error would be good here. Better yet - APRize.
e50df6c711553f98103f1e0802f7de8c59be7cddslive */
e50df6c711553f98103f1e0802f7de8c59be7cddslive fprintf(stderr, "Cannot resolve host name %s --- exiting!\n", w);
e50df6c711553f98103f1e0802f7de8c59be7cddslive exit(1);
19e48954d3cfb4f573a99866b0071b6aaa62723ckess }
19e48954d3cfb4f573a99866b0071b6aaa62723ckess
19e48954d3cfb4f573a99866b0071b6aaa62723ckess if (hep->h_addr_list[1]) {
19e48954d3cfb4f573a99866b0071b6aaa62723ckess fprintf(stderr, "Host %s has multiple addresses ---\n", w);
19e48954d3cfb4f573a99866b0071b6aaa62723ckess fprintf(stderr, "you must choose one explicitly for use as\n");
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele fprintf(stderr, "a secure port. Exiting!!!\n");
e50df6c711553f98103f1e0802f7de8c59be7cddslive exit(1);
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele }
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele if (p != NULL)
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele *p = ':';
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele return ((struct in_addr *) (hep->h_addr))->s_addr;
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele}
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
252b32956857ad89fc9ee708c4c6eb36097a647cerikabelestatic int find_secure_listener(seclisten_rec *lr)
e50df6c711553f98103f1e0802f7de8c59be7cddslive{
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele seclisten_rec *sl;
19e48954d3cfb4f573a99866b0071b6aaa62723ckess
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele for (sl = ap_seclisteners; sl; sl = sl->next) {
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele if (!memcmp(&sl->local_addr, &lr->local_addr, sizeof(sl->local_addr))) {
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele sl->used = 1;
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele return sl->fd;
e50df6c711553f98103f1e0802f7de8c59be7cddslive }
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele }
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele return -1;
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele}
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
252b32956857ad89fc9ee708c4c6eb36097a647cerikabelestatic char *get_port_key(conn_rec *c)
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele{
19e48954d3cfb4f573a99866b0071b6aaa62723ckess seclistenup_rec *sl;
19e48954d3cfb4f573a99866b0071b6aaa62723ckess
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele for (sl = ap_seclistenersup; sl; sl = sl->next) {
a6fc6b44b7f8ad7390864b3555341d3abf867f7end if ((sl->port == (c->local_addr)->port) &&
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele ((strcmp(sl->addr, "0.0.0.0") == 0) || (strcmp(sl->addr, c->local_ip) == 0))) {
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele return sl->key;
f50095bc3874806a9cc5e7d283dd9051ef8a0df8nd }
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele }
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele return NULL;
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele}
252b32956857ad89fc9ee708c4c6eb36097a647cerikabele
static int make_secure_socket(apr_pool_t *pconf, const struct sockaddr_in *server,
char* key, int mutual, server_rec *sconf)
{
int s;
char addr[MAX_ADDRESS];
struct sslserveropts opts;
unsigned int optParam;
WSAPROTOCOL_INFO SecureProtoInfo;
if (server->sin_addr.s_addr != htonl(INADDR_ANY))
apr_snprintf(addr, sizeof(addr), "address %s port %d",
inet_ntoa(server->sin_addr), ntohs(server->sin_port));
else
apr_snprintf(addr, sizeof(addr), "port %d", ntohs(server->sin_port));
/* note that because we're about to slack we don't use psocket */
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) {
ap_log_error(APLOG_MARK, APLOG_CRIT, WSAGetLastError(), sconf,
"make_secure_socket: failed to get a socket for %s",
addr);
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)) {
ap_log_error(APLOG_MARK, APLOG_CRIT, WSAGetLastError(), sconf,
"make_secure_socket: for %s, WSAIoctl: "
"(SO_SSL_SET_FLAGS)", addr);
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) {
ap_log_error(APLOG_MARK, APLOG_CRIT, WSAGetLastError(), sconf,
"make_secure_socket: for %s, WSAIoctl: "
"(SO_SSL_SET_SERVER)", addr);
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)) {
ap_log_error(APLOG_MARK, APLOG_CRIT, WSAGetLastError(), sconf,
"make_secure_socket: for %s, WSAIoctl: "
"(SO_SSL_SET_FLAGS)", addr);
return -1;
}
}
optParam = SO_TLS_UNCLEAN_SHUTDOWN;
WSAIoctl(s, SO_SSL_SET_FLAGS, (char *)&optParam, sizeof(optParam),
NULL, 0, NULL, NULL, NULL);
return s;
}
static int convert_secure_socket(conn_rec *c, apr_socket_t *csd)
{
int rcode;
struct tlsclientopts sWS2Opts;
struct nwtlsopts sNWTLSOpts;
struct sslserveropts opts;
unsigned long ulFlags;
SOCKET sock;
unicode_t keyFileName[60];
apr_os_sock_get(&sock, csd);
/* zero out buffers */
memset((char *)&sWS2Opts, 0, sizeof(struct tlsclientopts));
memset((char *)&sNWTLSOpts, 0, sizeof(struct nwtlsopts));
/* turn on ssl for the socket */
ulFlags = (numcerts ? SO_TLS_ENABLE : SO_TLS_ENABLE | SO_TLS_BLIND_ACCEPT);
rcode = WSAIoctl(sock, SO_TLS_SET_FLAGS, &ulFlags, sizeof(unsigned long),
NULL, 0, NULL, NULL, NULL);
if (SOCKET_ERROR == rcode)
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, c->base_server,
"Error: %d with ioctlsocket(flag SO_TLS_ENABLE)", WSAGetLastError());
return rcode;
}
ulFlags = SO_TLS_UNCLEAN_SHUTDOWN;
WSAIoctl(sock, SO_TLS_SET_FLAGS, &ulFlags, sizeof(unsigned long),
NULL, 0, NULL, NULL, NULL);
/* setup the socket for SSL */
memset (&sWS2Opts, 0, sizeof(sWS2Opts));
memset (&sNWTLSOpts, 0, sizeof(sNWTLSOpts));
sWS2Opts.options = &sNWTLSOpts;
if (numcerts) {
sNWTLSOpts.walletProvider = WAL_PROV_DER; /* the wallet provider defined in wdefs.h */
sNWTLSOpts.TrustedRootList = certarray; /* array of certs in UNICODE format */
sNWTLSOpts.numElementsInTRList = numcerts; /* number of certs in TRList */
}
else {
/* setup the socket for SSL */
unicpy(keyFileName, L"SSL CertificateIP");
sWS2Opts.wallet = keyFileName; /* no client certificate */
sWS2Opts.walletlen = unilen(keyFileName);
sNWTLSOpts.walletProvider = WAL_PROV_KMO; /* the wallet provider defined in wdefs.h */
}
/* make the IOCTL call */
rcode = WSAIoctl(sock, SO_TLS_SET_CLIENT, &sWS2Opts,
sizeof(struct tlsclientopts), NULL, 0, NULL,
NULL, NULL);
/* make sure that it was successful */
if(SOCKET_ERROR == rcode ){
ap_log_error(APLOG_MARK, APLOG_ERR, 0, c->base_server,
"Error: %d with ioctl (SO_TLS_SET_CLIENT)", WSAGetLastError());
}
return rcode;
}
static int SSLize_Socket(SOCKET socketHnd, char *key, request_rec *r)
{
int rcode;
struct tlsserveropts sWS2Opts;
struct nwtlsopts sNWTLSOpts;
unicode_t SASKey[512];
unsigned long ulFlag;
memset((char *)&sWS2Opts, 0, sizeof(struct tlsserveropts));
memset((char *)&sNWTLSOpts, 0, sizeof(struct nwtlsopts));
ulFlag = SO_TLS_ENABLE;
rcode = WSAIoctl(socketHnd, SO_TLS_SET_FLAGS, &ulFlag, sizeof(unsigned long), NULL, 0, NULL, NULL, NULL);
if(rcode)
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
"Error: %d with WSAIoctl(SO_TLS_SET_FLAGS, SO_TLS_ENABLE)", WSAGetLastError());
goto ERR;
}
ulFlag = SO_TLS_SERVER;
rcode = WSAIoctl(socketHnd, SO_TLS_SET_FLAGS, &ulFlag, sizeof(unsigned long),NULL, 0, NULL, NULL, NULL);
if(rcode)
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
"Error: %d with WSAIoctl(SO_TLS_SET_FLAGS, SO_TLS_SERVER)", WSAGetLastError());
goto ERR;
}
loc2uni(UNI_LOCAL_DEFAULT, SASKey, key, 0, 0);
/* setup the tlsserveropts struct */
sWS2Opts.wallet = SASKey;
sWS2Opts.walletlen = unilen(SASKey);
sWS2Opts.sidtimeout = 0;
sWS2Opts.sidentries = 0;
sWS2Opts.siddir = NULL;
sWS2Opts.options = &sNWTLSOpts;
/* setup the nwtlsopts structure */
sNWTLSOpts.walletProvider = WAL_PROV_KMO;
sNWTLSOpts.keysList = NULL;
sNWTLSOpts.numElementsInKeyList = 0;
sNWTLSOpts.reservedforfutureuse = NULL;
sNWTLSOpts.reservedforfutureCRL = NULL;
sNWTLSOpts.reservedforfutureCRLLen = 0;
sNWTLSOpts.reserved1 = NULL;
sNWTLSOpts.reserved2 = NULL;
sNWTLSOpts.reserved3 = NULL;
rcode = WSAIoctl(socketHnd,
SO_TLS_SET_SERVER,
&sWS2Opts,
sizeof(struct tlsserveropts),
NULL,
0,
NULL,
NULL,
NULL);
if(SOCKET_ERROR == rcode) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
"Error: %d with WSAIoctl(SO_TLS_SET_SERVER)", WSAGetLastError());
goto ERR;
}
ERR:
return rcode;
}
static const char *set_secure_listener(cmd_parms *cmd, void *dummy,
const char *ips, const char* key,
const char* mutual)
{
NWSSLSrvConfigRec* sc = get_nwssl_cfg(cmd->server);
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
char *ports, *addr;
unsigned short port;
seclisten_rec *new;
ap_listen_rec **walk;
apr_sockaddr_t *sa;
int found_listener = 0;
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 = (char*)ips;
}
new = apr_pcalloc(cmd->server->process->pool, sizeof(seclisten_rec));
new->local_addr.sin_family = AF_INET;
if (ports == ips) {
new->local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr = apr_pstrdup(cmd->server->process->pool, "0.0.0.0");
}
else {
new->local_addr.sin_addr.s_addr = parse_addr(ips, NULL);
addr = apr_pstrdup(cmd->server->process->pool, ips);
}
port = atoi(ports);
if (!port)
return "Port must be numeric";
/* If the specified addr:port was created previously, put the listen
socket record back on the ap_listeners list so that the socket
will be reused rather than recreated */
for (walk = &nw_old_listeners; *walk;) {
sa = (*walk)->bind_addr;
if (sa) {
ap_listen_rec *new;
apr_port_t oldport;
oldport = sa->port;
/* If both ports are equivalent, then if their names are equivalent,
* then we will re-use the existing record.
*/
if (port == oldport &&
((!addr && !sa->hostname) ||
((addr && sa->hostname) && !strcmp(sa->hostname, addr)))) {
new = *walk;
*walk = new->next;
new->next = ap_listeners;
ap_listeners = new;
found_listener = 1;
continue;
}
}
walk = &(*walk)->next;
}
apr_table_add(sc->sltable, ports, addr);
/* If we found a pre-existing listen socket record, then there
is no need to create a new secure listen socket record. */
if (found_listener) {
return NULL;
}
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;
new->addr = addr;
new->port = port;
ap_seclisteners = new;
return NULL;
}
static const char *set_secure_upgradeable_listener(cmd_parms *cmd, void *dummy,
const char *ips, const char* key)
{
NWSSLSrvConfigRec* sc = get_nwssl_cfg(cmd->server);
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
char *ports, *addr;
unsigned short port;
seclistenup_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 = (char*)ips;
}
if (ports == ips) {
addr = apr_pstrdup(cmd->pool, "0.0.0.0");
}
else {
addr = apr_pstrdup(cmd->pool, ips);
}
port = atoi(ports);
if (!port)
return "Port must be numeric";
apr_table_set(sc->slutable, ports, addr);
new = apr_pcalloc(cmd->pool, sizeof(seclistenup_rec));
new->next = ap_seclistenersup;
strcpy(new->key, key);
new->addr = addr;
new->port = port;
ap_seclistenersup = new;
return err;
}
static apr_status_t nwssl_socket_cleanup(void *data)
{
ap_listen_rec* slr = (ap_listen_rec*)data;
ap_listen_rec* lr;
/* Remove our secure listener from the listener list */
for (lr = ap_listeners; lr; lr = lr->next) {
/* slr is at the head of the list */
if (lr == slr) {
ap_listeners = slr->next;
break;
}
/* slr is somewhere in between or at the end*/
if (lr->next == slr) {
lr->next = slr->next;
break;
}
}
return APR_SUCCESS;
}
static const char *set_trusted_certs(cmd_parms *cmd, void *dummy, char *arg)
{
char **ptr = (char **)apr_array_push(certlist);
*ptr = apr_pstrdup(cmd->pool, arg);
return NULL;
}
static int nwssl_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
apr_pool_t *ptemp)
{
seclisten_rec* ap_old_seclisteners;
ap_listen_rec **walk;
seclisten_rec **secwalk;
apr_sockaddr_t *sa;
int found;
/* Pull all of the listeners that were created by mod_nw_ssl out of the
ap_listeners list so that the normal listen socket processing does
automatically close them */
nw_old_listeners = NULL;
ap_old_seclisteners = NULL;
for (secwalk = &ap_seclisteners; *secwalk;) {
found = 0;
for (walk = &ap_listeners; *walk;) {
sa = (*walk)->bind_addr;
if (sa) {
ap_listen_rec *new;
seclisten_rec *secnew;
apr_port_t oldport;
oldport = sa->port;
/* If both ports are equivalent, then if their names are equivalent,
* then we will re-use the existing record.
*/
if ((*secwalk)->port == oldport &&
((!(*secwalk)->addr && !sa->hostname) ||
(((*secwalk)->addr && sa->hostname) && !strcmp(sa->hostname, (*secwalk)->addr)))) {
/* Move the listen socket from ap_listeners to nw_old_listeners */
new = *walk;
*walk = new->next;
new->next = nw_old_listeners;
nw_old_listeners = new;
/* Move the secure socket record to ap_old_seclisterners */
secnew = *secwalk;
*secwalk = secnew->next;
secnew->next = ap_old_seclisteners;
ap_old_seclisteners = secnew;
found = 1;
break;
}
}
walk = &(*walk)->next;
}
if (!found && &(*secwalk)->next) {
secwalk = &(*secwalk)->next;
}
}
/* Restore the secure socket records list so that the post config can
process all of the sockets normally */
ap_seclisteners = ap_old_seclisteners;
ap_seclistenersup = NULL;
certlist = apr_array_make(pconf, 1, sizeof(char *));
/* Now that we have removed all of the mod_nw_ssl created socket records,
allow the normal listen socket handling to occur.
NOTE: If for any reason mod_nw_ssl is removed as a built-in module,
the following call must be put back into the pre-config handler of the
MPM. It is only here to ensure that mod_nw_ssl fixes up the listen
socket list before anything else looks at it. */
ap_listen_pre_config();
return OK;
}
static int nwssl_pre_connection(conn_rec *c, void *csd)
{
if (apr_table_get(c->notes, "nwconv-ssl")) {
convert_secure_socket(c, (apr_socket_t*)csd);
}
else {
secsocket_data *csd_data = apr_palloc(c->pool, sizeof(secsocket_data));
csd_data->csd = (apr_socket_t*)csd;
csd_data->is_secure = 0;
ap_set_module_config(c->conn_config, &nwssl_module, (void*)csd_data);
}
return OK;
}
static int nwssl_post_config(apr_pool_t *pconf, apr_pool_t *plog,
apr_pool_t *ptemp, server_rec *s)
{
seclisten_rec* sl;
ap_listen_rec* lr;
apr_socket_t* sd;
apr_status_t status;
seclistenup_rec *slu;
int found;
ap_listen_rec *walk;
seclisten_rec *secwalk, *lastsecwalk;
apr_sockaddr_t *sa;
/* Walk the old listeners list and compare it to the secure
listeners list and remove any secure listener records that
are not being reused */
for (walk = nw_old_listeners; walk; walk = walk->next) {
sa = walk->bind_addr;
if (sa) {
ap_listen_rec *new;
apr_port_t oldport;
oldport = sa->port;
for (secwalk = ap_seclisteners, lastsecwalk = ap_seclisteners; secwalk; secwalk = lastsecwalk->next) {
unsigned short port = secwalk->port;
char *addr = secwalk->addr;
/* If both ports are equivalent, then if their names are equivalent,
* then we will re-use the existing record.
*/
if (port == oldport &&
((!addr && !sa->hostname) ||
((addr && sa->hostname) && !strcmp(sa->hostname, addr)))) {
if (secwalk == ap_seclisteners) {
ap_seclisteners = secwalk->next;
}
else {
lastsecwalk->next = secwalk->next;
}
apr_socket_close(walk->sd);
walk->active = 0;
break;
}
else {
lastsecwalk = secwalk;
}
}
}
}
for (sl = ap_seclisteners; sl != NULL; sl = sl->next) {
/* If we find a pre-existing listen socket and it has already been
created, then no neeed to go any further, just reuse it. */
if (((sl->fd = find_secure_listener(sl)) >= 0) && (sl->used)) {
continue;
}
if (sl->fd < 0)
sl->fd = make_secure_socket(s->process->pool, &sl->local_addr, sl->key, sl->mutual, s);
if (sl->fd >= 0) {
apr_os_sock_info_t sock_info;
sock_info.os_sock = &(sl->fd);
sock_info.local = (struct sockaddr*)&(sl->local_addr);
sock_info.remote = NULL;
sock_info.family = APR_INET;
sock_info.type = SOCK_STREAM;
apr_os_sock_make(&sd, &sock_info, s->process->pool);
lr = apr_pcalloc(s->process->pool, sizeof(ap_listen_rec));
if (lr) {
lr->sd = sd;
if ((status = apr_sockaddr_info_get(&lr->bind_addr, sl->addr, APR_UNSPEC, sl->port, 0,
s->process->pool)) != APR_SUCCESS) {
ap_log_perror(APLOG_MARK, APLOG_CRIT, status, pconf,
"alloc_listener: failed to set up sockaddr for %s:%d", sl->addr, sl->port);
return HTTP_INTERNAL_SERVER_ERROR;
}
lr->next = ap_listeners;
ap_listeners = lr;
apr_pool_cleanup_register(s->process->pool, lr, nwssl_socket_cleanup, apr_pool_cleanup_null);
}
} else {
return HTTP_INTERNAL_SERVER_ERROR;
}
}
for (slu = ap_seclistenersup; slu; slu = slu->next) {
/* Check the listener list for a matching upgradeable listener */
found = 0;
for (lr = ap_listeners; lr; lr = lr->next) {
if (slu->port == lr->bind_addr->port) {
found = 1;
break;
}
}
if (!found) {
ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, plog,
"No Listen directive found for upgradeable listener %s:%d", slu->addr, slu->port);
}
}
build_cert_list(s->process->pool);
return OK;
}
static void *nwssl_config_server_create(apr_pool_t *p, server_rec *s)
{
NWSSLSrvConfigRec *new = apr_palloc(p, sizeof(NWSSLSrvConfigRec));
new->sltable = apr_table_make(p, 5);
new->slutable = apr_table_make(p, 5);
return new;
}
static void *nwssl_config_server_merge(apr_pool_t *p, void *basev, void *addv)
{
NWSSLSrvConfigRec *base = (NWSSLSrvConfigRec *)basev;
NWSSLSrvConfigRec *add = (NWSSLSrvConfigRec *)addv;
NWSSLSrvConfigRec *merged = (NWSSLSrvConfigRec *)apr_palloc(p, sizeof(NWSSLSrvConfigRec));
return merged;
}
static int compare_ipports(void *rec, const char *key, const char *value)
{
conn_rec *c = (conn_rec*)rec;
if (value &&
((strcmp(value, "0.0.0.0") == 0) || (strcmp(value, c->local_ip) == 0)))
{
return 0;
}
return 1;
}
static int isSecureConnEx (const server_rec *s, const conn_rec *c, const apr_table_t *t)
{
char port[8];
itoa((c->local_addr)->port, port, 10);
if (!apr_table_do(compare_ipports, (void*)c, t, port, NULL))
{
return 1;
}
return 0;
}
static int isSecureConn (const server_rec *s, const conn_rec *c)
{
NWSSLSrvConfigRec *sc = get_nwssl_cfg(s);
return isSecureConnEx (s, c, sc->sltable);
}
static int isSecureConnUpgradeable (const server_rec *s, const conn_rec *c)
{
NWSSLSrvConfigRec *sc = get_nwssl_cfg(s);
return isSecureConnEx (s, c, sc->slutable);
}
static int isSecure (const request_rec *r)
{
return isSecureConn (r->server, r->connection);
}
static int isSecureUpgradeable (const request_rec *r)
{
return isSecureConnUpgradeable (r->server, r->connection);
}
static int isSecureUpgraded (const request_rec *r)
{
secsocket_data *csd_data = (secsocket_data*)ap_get_module_config(r->connection->conn_config, &nwssl_module);
return csd_data->is_secure;
}
static int nwssl_hook_Fixup(request_rec *r)
{
if (!isSecure(r) && !isSecureUpgraded(r))
return DECLINED;
apr_table_setn(r->subprocess_env, "HTTPS", "on");
return DECLINED;
}
static const char *nwssl_hook_http_scheme(const request_rec *r)
{
if (isSecure(r) && !isSecureUpgraded(r))
return "https";
return NULL;
}
static apr_port_t nwssl_hook_default_port(const request_rec *r)
{
if (isSecure(r))
return DEFAULT_HTTPS_PORT;
return 0;
}
int ssl_proxy_enable(conn_rec *c)
{
apr_table_setn(c->notes, "nwconv-ssl", "Y");
return 1;
}
int ssl_engine_disable(conn_rec *c)
{
return 1;
}
static int ssl_is_https(conn_rec *c)
{
secsocket_data *csd_data = (secsocket_data*)ap_get_module_config(c->conn_config, &nwssl_module);
return isSecureConn (c->base_server, c) || (csd_data && csd_data->is_secure);
}
/* This function must remain safe to use for a non-SSL connection. */
char *ssl_var_lookup(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, char *var)
{
NWSSLSrvConfigRec *mc = get_nwssl_cfg(s);
const char *result;
BOOL resdup;
apr_time_exp_t tm;
result = NULL;
resdup = TRUE;
/*
* When no pool is given try to find one
*/
if (p == NULL) {
if (r != NULL)
p = r->pool;
else if (c != NULL)
p = c->pool;
else
p = mc->pPool;
}
/*
* Request dependent stuff
*/
if (r != NULL) {
switch (var[0]) {
case 'H':
case 'h':
if (strcEQ(var, "HTTP_USER_AGENT"))
result = apr_table_get(r->headers_in, "User-Agent");
else if (strcEQ(var, "HTTP_REFERER"))
result = apr_table_get(r->headers_in, "Referer");
else if (strcEQ(var, "HTTP_COOKIE"))
result = apr_table_get(r->headers_in, "Cookie");
else if (strcEQ(var, "HTTP_FORWARDED"))
result = apr_table_get(r->headers_in, "Forwarded");
else if (strcEQ(var, "HTTP_HOST"))
result = apr_table_get(r->headers_in, "Host");
else if (strcEQ(var, "HTTP_PROXY_CONNECTION"))
result = apr_table_get(r->headers_in, "Proxy-Connection");
else if (strcEQ(var, "HTTP_ACCEPT"))
result = apr_table_get(r->headers_in, "Accept");
else if (strcEQ(var, "HTTPS")) {
if (isSecure(r) || isSecureUpgraded(r))
result = "on";
else
result = "off";
}
else if (strlen(var) > 5 && strcEQn(var, "HTTP:", 5))
/* all other headers from which we are still not know about */
result = apr_table_get(r->headers_in, var+5);
break;
case 'R':
case 'r':
if (strcEQ(var, "REQUEST_METHOD"))
result = r->method;
else if (strcEQ(var, "REQUEST_SCHEME"))
result = ap_http_scheme(r);
else if (strcEQ(var, "REQUEST_URI"))
result = r->uri;
else if (strcEQ(var, "REQUEST_FILENAME"))
result = r->filename;
else if (strcEQ(var, "REMOTE_HOST"))
result = ap_get_remote_host(r->connection, r->per_dir_config,
REMOTE_NAME, NULL);
else if (strcEQ(var, "REMOTE_IDENT"))
result = ap_get_remote_logname(r);
else if (strcEQ(var, "REMOTE_USER"))
result = r->user;
break;
case 'S':
case 's':
if (strcEQn(var, "SSL", 3)) break; /* shortcut common case */
if (strcEQ(var, "SERVER_ADMIN"))
result = r->server->server_admin;
else if (strcEQ(var, "SERVER_NAME"))
result = ap_get_server_name_for_url(r);
else if (strcEQ(var, "SERVER_PORT"))
result = apr_psprintf(p, "%u", ap_get_server_port(r));
else if (strcEQ(var, "SERVER_PROTOCOL"))
result = r->protocol;
else if (strcEQ(var, "SCRIPT_FILENAME"))
result = r->filename;
break;
default:
if (strcEQ(var, "PATH_INFO"))
result = r->path_info;
else if (strcEQ(var, "QUERY_STRING"))
result = r->args;
else if (strcEQ(var, "IS_SUBREQ"))
result = (r->main != NULL ? "true" : "false");
else if (strcEQ(var, "DOCUMENT_ROOT"))
result = ap_document_root(r);
else if (strcEQ(var, "AUTH_TYPE"))
result = r->ap_auth_type;
else if (strcEQ(var, "THE_REQUEST"))
result = r->the_request;
break;
}
}
/*
* Connection stuff
*/
if (result == NULL && c != NULL) {
/* XXX-Can't get specific SSL info from NetWare */
/* SSLConnRec *sslconn = myConnConfig(c);
if (strlen(var) > 4 && strcEQn(var, "SSL_", 4)
&& sslconn && sslconn->ssl)
result = ssl_var_lookup_ssl(p, c, var+4);*/
if (strlen(var) > 4 && strcEQn(var, "SSL_", 4))
result = NULL;
else if (strcEQ(var, "REMOTE_ADDR"))
result = c->remote_ip;
}
/*
* Totally independent stuff
*/
if (result == NULL) {
if (strlen(var) > 12 && strcEQn(var, "SSL_VERSION_", 12))
result = NULL;
/* XXX-Can't get specific SSL info from NetWare */
/*result = ssl_var_lookup_ssl_version(p, var+12);*/
else if (strcEQ(var, "SERVER_SOFTWARE"))
result = ap_get_server_banner();
else if (strcEQ(var, "API_VERSION")) {
result = apr_itoa(p, MODULE_MAGIC_NUMBER);
resdup = FALSE;
}
else if (strcEQ(var, "TIME_YEAR")) {
apr_time_exp_lt(&tm, apr_time_now());
result = apr_psprintf(p, "%02d%02d",
(tm.tm_year / 100) + 19, tm.tm_year % 100);
resdup = FALSE;
}
#define MKTIMESTR(format, tmfield) \
apr_time_exp_lt(&tm, apr_time_now()); \
result = apr_psprintf(p, format, tm.tmfield); \
resdup = FALSE;
else if (strcEQ(var, "TIME_MON")) {
MKTIMESTR("%02d", tm_mon+1)
}
else if (strcEQ(var, "TIME_DAY")) {
MKTIMESTR("%02d", tm_mday)
}
else if (strcEQ(var, "TIME_HOUR")) {
MKTIMESTR("%02d", tm_hour)
}
else if (strcEQ(var, "TIME_MIN")) {
MKTIMESTR("%02d", tm_min)
}
else if (strcEQ(var, "TIME_SEC")) {
MKTIMESTR("%02d", tm_sec)
}
else if (strcEQ(var, "TIME_WDAY")) {
MKTIMESTR("%d", tm_wday)
}
else if (strcEQ(var, "TIME")) {
apr_time_exp_lt(&tm, apr_time_now());
result = apr_psprintf(p,
"%02d%02d%02d%02d%02d%02d%02d", (tm.tm_year / 100) + 19,
(tm.tm_year % 100), tm.tm_mon+1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
resdup = FALSE;
}
/* all other env-variables from the parent Apache process */
else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) {
result = apr_table_get(r->notes, var+4);
if (result == NULL)
result = apr_table_get(r->subprocess_env, var+4);
if (result == NULL)
result = getenv(var+4);
}
}
if (result != NULL && resdup)
result = apr_pstrdup(p, result);
if (result == NULL)
result = "";
return (char *)result;
}
#define SWITCH_STATUS_LINE "HTTP/1.1 101 Switching Protocols"
#define UPGRADE_HEADER "Upgrade: TLS/1.0, HTTP/1.1"
#define CONNECTION_HEADER "Connection: Upgrade"
static apr_status_t ssl_io_filter_Upgrade(ap_filter_t *f,
apr_bucket_brigade *bb)
{
const char *upgrade;
apr_bucket_brigade *upgradebb;
request_rec *r = f->r;
apr_socket_t *csd = NULL;
char *key;
int ret;
secsocket_data *csd_data;
apr_bucket *b;
apr_status_t rv;
/* Just remove the filter, if it doesn't work the first time, it won't
* work at all for this request.
*/
ap_remove_output_filter(f);
/* No need to ensure that this is a server with optional SSL, the filter
* is only inserted if that is true.
*/
upgrade = apr_table_get(r->headers_in, "Upgrade");
if (upgrade == NULL
|| strcmp(ap_getword(r->pool, &upgrade, ','), "TLS/1.0")) {
/* "Upgrade: TLS/1.0, ..." header not found, don't do Upgrade */
return ap_pass_brigade(f->next, bb);
}
apr_table_unset(r->headers_out, "Upgrade");
if (r) {
csd_data = (secsocket_data*)ap_get_module_config(r->connection->conn_config, &nwssl_module);
csd = csd_data->csd;
}
else {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
"Unable to get upgradeable socket handle");
return ap_pass_brigade(f->next, bb);
}
/* Send the interim 101 response. */
upgradebb = apr_brigade_create(r->pool, f->c->bucket_alloc);
ap_fputstrs(f->next, upgradebb, SWITCH_STATUS_LINE, CRLF,
UPGRADE_HEADER, CRLF, CONNECTION_HEADER, CRLF, CRLF, NULL);
b = apr_bucket_flush_create(f->c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(upgradebb, b);
rv = ap_pass_brigade(f->next, upgradebb);
if (rv) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
"could not send interim 101 Upgrade response");
return AP_FILTER_ERROR;
}
key = get_port_key(r->connection);
if (csd && key) {
int sockdes;
apr_os_sock_get(&sockdes, csd);
ret = SSLize_Socket(sockdes, key, r);
if (!ret) {
csd_data->is_secure = 1;
}
}
else {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
"Upgradeable socket handle not found");
return AP_FILTER_ERROR;
}
ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
"Awaiting re-negotiation handshake");
/* Now that we have initialized the ssl connection which added the ssl_io_filter,
pass the brigade off to the connection based output filters so that the
request can complete encrypted */
return ap_pass_brigade(f->c->output_filters, bb);
}
static void ssl_hook_Insert_Filter(request_rec *r)
{
NWSSLSrvConfigRec *sc = get_nwssl_cfg(r->server);
if (isSecureUpgradeable (r)) {
ap_add_output_filter("UPGRADE_FILTER", NULL, r, r->connection);
}
}
static const command_rec nwssl_module_cmds[] =
{
AP_INIT_TAKE23("SecureListen", set_secure_listener, NULL, RSRC_CONF,
"specify an address and/or port with a key pair name.\n"
"Optional third parameter of MUTUAL configures the port for mutual authentication."),
AP_INIT_TAKE2("NWSSLUpgradeable", set_secure_upgradeable_listener, NULL, RSRC_CONF,
"specify an address and/or port with a key pair name, that can be upgraded to an SSL connection.\n"
"The address and/or port must have already be defined using a Listen directive."),
AP_INIT_ITERATE("NWSSLTrustedCerts", set_trusted_certs, NULL, RSRC_CONF,
"Adds trusted certificates that are used to create secure connections to proxied servers"),
{NULL}
};
static void register_hooks(apr_pool_t *p)
{
ap_register_output_filter ("UPGRADE_FILTER", ssl_io_filter_Upgrade, NULL, AP_FTYPE_PROTOCOL + 5);
ap_hook_pre_config(nwssl_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_pre_connection(nwssl_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_post_config(nwssl_post_config, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_fixups(nwssl_hook_Fixup, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_http_scheme(nwssl_hook_http_scheme, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_default_port(nwssl_hook_default_port, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_insert_filter(ssl_hook_Insert_Filter, NULL, NULL, APR_HOOK_MIDDLE);
APR_REGISTER_OPTIONAL_FN(ssl_is_https);
APR_REGISTER_OPTIONAL_FN(ssl_var_lookup);
APR_REGISTER_OPTIONAL_FN(ssl_proxy_enable);
APR_REGISTER_OPTIONAL_FN(ssl_engine_disable);
}
AP_DECLARE_MODULE(nwssl) =
{
STANDARD20_MODULE_STUFF,
NULL, /* dir config creater */
NULL, /* dir merger --- default is to override */
nwssl_config_server_create, /* server config */
nwssl_config_server_merge, /* merge server config */
nwssl_module_cmds, /* command apr_table_t */
register_hooks
};