1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* 18 * based on mod_tls.c - Apache SSL/TLS module for NetWare by Mike Gardiner. 19 * 20 * This module gives Apache the ability to do SSL/TLS with a minimum amount 21 * of effort. All of the SSL/TLS logic is already on NetWare versions 5 and 22 * above and is interfaced through WinSock on NetWare. As you can see in 23 * the code below SSL/TLS sockets can be created with three WinSock calls. 24 * 25 * To load, simply place the module in the modules directory under the main 26 * apache tree. Then add a "SecureListen" with two arguments. The first 27 * argument is an address and/or port. The second argument is the key pair 28 * name as created in ConsoleOne. 29 * 30 * Examples: 31 * 32 * SecureListen 443 "SSL CertificateIP" 33 * SecureListen 123.45.67.89:443 mycert 34 * 35 * The module also supports RFC 2817 / TLS Upgrade for HTTP 1.1. 36 * For this add a "NWSSLUpgradeable" with two arguments. The first 37 * argument is an address and/or port. The second argument is the key pair 38 * name as created in ConsoleOne. 39 * 40 * Examples: 41 * 42 * NWSSLUpgradeable 8080 "SSL CertificateIP" 43 * NWSSLUpgradeable 123.45.67.89:8080 mycert 44 * 45 */ 46 47#define WS_SSL 48 49#define MAX_ADDRESS 512 50#define MAX_KEY 80 51 52 53#include "httpd.h" 54#include "http_config.h" 55#include "http_connection.h" 56#include "http_core.h" 57#include "http_log.h" 58#include "http_protocol.h" 59#include "http_request.h" 60#include "ap_listen.h" 61#include "apr_strings.h" 62#include "apr_portable.h" 63#include "apr_optional.h" 64 65#include <unilib.h> 66 67#ifndef SO_TLS_UNCLEAN_SHUTDOWN 68#define SO_TLS_UNCLEAN_SHUTDOWN 0 69#endif 70 71/* The ssl_var_lookup() optional function retrieves SSL environment 72 * variables. */ 73APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup, 74 (apr_pool_t *, server_rec *, 75 conn_rec *, request_rec *, 76 char *)); 77 78/* An optional function which returns non-zero if the given connection 79 * is using SSL/TLS. */ 80APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec *)); 81 82/* The ssl_proxy_enable() and ssl_engine_disable() optional functions 83 * are used by mod_proxy to enable use of SSL for outgoing 84 * connections. */ 85APR_DECLARE_OPTIONAL_FN(int, ssl_proxy_enable, (conn_rec *)); 86APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *)); 87 88#define strEQ(s1,s2) (strcmp(s1,s2) == 0) 89#define strNE(s1,s2) (strcmp(s1,s2) != 0) 90#define strEQn(s1,s2,n) (strncmp(s1,s2,n) == 0) 91#define strNEn(s1,s2,n) (strncmp(s1,s2,n) != 0) 92 93#define strcEQ(s1,s2) (strcasecmp(s1,s2) == 0) 94#define strcNE(s1,s2) (strcasecmp(s1,s2) != 0) 95#define strcEQn(s1,s2,n) (strncasecmp(s1,s2,n) == 0) 96#define strcNEn(s1,s2,n) (strncasecmp(s1,s2,n) != 0) 97 98#define strIsEmpty(s) (s == NULL || s[0] == NUL) 99 100 101module AP_MODULE_DECLARE_DATA nwssl_module; 102 103typedef struct NWSSLSrvConfigRec NWSSLSrvConfigRec; 104typedef struct seclisten_rec seclisten_rec; 105typedef struct seclistenup_rec seclistenup_rec; 106typedef struct secsocket_data secsocket_data; 107 108struct seclisten_rec { 109 seclisten_rec *next; 110 struct sockaddr_in local_addr; /* local IP address and port */ 111 int fd; 112 int used; /* Only used during restart */ 113 char key[MAX_KEY]; 114 int mutual; 115 char *addr; 116 apr_port_t port; 117}; 118 119struct seclistenup_rec { 120 seclistenup_rec *next; 121 char key[MAX_KEY]; 122 char *addr; 123 apr_port_t port; 124}; 125 126struct NWSSLSrvConfigRec { 127 apr_table_t *sltable; 128 apr_table_t *slutable; 129 apr_pool_t *pPool; 130}; 131 132struct secsocket_data { 133 apr_socket_t* csd; 134 int is_secure; 135}; 136 137static apr_array_header_t *certlist = NULL; 138static unicode_t** certarray = NULL; 139static int numcerts = 0; 140static seclisten_rec* ap_seclisteners = NULL; 141static seclistenup_rec* ap_seclistenersup = NULL; 142 143static ap_listen_rec *nw_old_listeners; 144 145#define get_nwssl_cfg(srv) (NWSSLSrvConfigRec *) ap_get_module_config(srv->module_config, &nwssl_module) 146 147 148static void build_cert_list(apr_pool_t *p) 149{ 150 int i; 151 char **rootcerts = (char **)certlist->elts; 152 153 numcerts = certlist->nelts; 154 certarray = apr_palloc(p, sizeof(unicode_t*)*numcerts); 155 156 for (i = 0; i < numcerts; ++i) { 157 unicode_t *unistr; 158 unistr = (unicode_t*)apr_palloc(p, strlen(rootcerts[i])*4); 159 loc2uni (UNI_LOCAL_DEFAULT, unistr, rootcerts[i], 0, 2); 160 certarray[i] = unistr; 161 } 162} 163 164/* 165 * Parses a host of the form <address>[:port] 166 * :port is permitted if 'port' is not NULL 167 */ 168static unsigned long parse_addr(const char *w, unsigned short *ports) 169{ 170 struct hostent *hep; 171 unsigned long my_addr; 172 char *p; 173 174 p = strchr(w, ':'); 175 if (ports != NULL) { 176 *ports = 0; 177 if (p != NULL && strcmp(p + 1, "*") != 0) 178 *ports = atoi(p + 1); 179 } 180 181 if (p != NULL) 182 *p = '\0'; 183 if (strcmp(w, "*") == 0) { 184 if (p != NULL) 185 *p = ':'; 186 return htonl(INADDR_ANY); 187 } 188 189 my_addr = apr_inet_addr((char *)w); 190 if (my_addr != INADDR_NONE) { 191 if (p != NULL) 192 *p = ':'; 193 return my_addr; 194 } 195 196 hep = gethostbyname(w); 197 198 if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) { 199 /* XXX Should be echoing by h_errno the actual failure, no? 200 * ap_log_error would be good here. Better yet - APRize. 201 */ 202 fprintf(stderr, "Cannot resolve host name %s --- exiting!\n", w); 203 exit(1); 204 } 205 206 if (hep->h_addr_list[1]) { 207 fprintf(stderr, "Host %s has multiple addresses ---\n", w); 208 fprintf(stderr, "you must choose one explicitly for use as\n"); 209 fprintf(stderr, "a secure port. Exiting!!!\n"); 210 exit(1); 211 } 212 213 if (p != NULL) 214 *p = ':'; 215 216 return ((struct in_addr *) (hep->h_addr))->s_addr; 217} 218 219static int find_secure_listener(seclisten_rec *lr) 220{ 221 seclisten_rec *sl; 222 223 for (sl = ap_seclisteners; sl; sl = sl->next) { 224 if (!memcmp(&sl->local_addr, &lr->local_addr, sizeof(sl->local_addr))) { 225 sl->used = 1; 226 return sl->fd; 227 } 228 } 229 return -1; 230} 231 232static char *get_port_key(conn_rec *c) 233{ 234 seclistenup_rec *sl; 235 236 for (sl = ap_seclistenersup; sl; sl = sl->next) { 237 if ((sl->port == (c->local_addr)->port) && 238 ((strcmp(sl->addr, "0.0.0.0") == 0) || 239 (strcmp(sl->addr, c->local_ip) == 0))) { 240 return sl->key; 241 } 242 } 243 return NULL; 244} 245 246static int make_secure_socket(apr_pool_t *pconf, 247 const struct sockaddr_in *server, 248 char* key, int mutual, server_rec *sconf) 249{ 250 int s; 251 char addr[MAX_ADDRESS]; 252 struct sslserveropts opts; 253 unsigned int optParam; 254 WSAPROTOCOL_INFO SecureProtoInfo; 255 256 if (server->sin_addr.s_addr != htonl(INADDR_ANY)) 257 apr_snprintf(addr, sizeof(addr), "address %s port %d", 258 inet_ntoa(server->sin_addr), ntohs(server->sin_port)); 259 else 260 apr_snprintf(addr, sizeof(addr), "port %d", ntohs(server->sin_port)); 261 262 /* note that because we're about to slack we don't use psocket */ 263 memset(&SecureProtoInfo, 0, sizeof(WSAPROTOCOL_INFO)); 264 265 SecureProtoInfo.iAddressFamily = AF_INET; 266 SecureProtoInfo.iSocketType = SOCK_STREAM; 267 SecureProtoInfo.iProtocol = IPPROTO_TCP; 268 SecureProtoInfo.iSecurityScheme = SECURITY_PROTOCOL_SSL; 269 270 s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 271 (LPWSAPROTOCOL_INFO)&SecureProtoInfo, 0, 0); 272 273 if (s == INVALID_SOCKET) { 274 ap_log_error(APLOG_MARK, APLOG_CRIT, WSAGetLastError(), sconf, 275 APLOGNO(02120) 276 "make_secure_socket: failed to get a socket for %s", 277 addr); 278 return -1; 279 } 280 281 if (!mutual) { 282 optParam = SO_SSL_ENABLE | SO_SSL_SERVER; 283 284 if (WSAIoctl(s, SO_SSL_SET_FLAGS, (char *)&optParam, 285 sizeof(optParam), NULL, 0, NULL, NULL, NULL)) { 286 ap_log_error(APLOG_MARK, APLOG_CRIT, WSAGetLastError(), sconf, 287 APLOGNO(02121) 288 "make_secure_socket: for %s, WSAIoctl: " 289 "(SO_SSL_SET_FLAGS)", addr); 290 return -1; 291 } 292 } 293 294 opts.cert = key; 295 opts.certlen = strlen(key); 296 opts.sidtimeout = 0; 297 opts.sidentries = 0; 298 opts.siddir = NULL; 299 300 if (WSAIoctl(s, SO_SSL_SET_SERVER, (char *)&opts, sizeof(opts), 301 NULL, 0, NULL, NULL, NULL) != 0) { 302 ap_log_error(APLOG_MARK, APLOG_CRIT, WSAGetLastError(), sconf, 303 APLOGNO(02122) 304 "make_secure_socket: for %s, WSAIoctl: " 305 "(SO_SSL_SET_SERVER)", addr); 306 return -1; 307 } 308 309 if (mutual) { 310 optParam = 0x07; /* SO_SSL_AUTH_CLIENT */ 311 312 if (WSAIoctl(s, SO_SSL_SET_FLAGS, (char*)&optParam, sizeof(optParam), 313 NULL, 0, NULL, NULL, NULL)) { 314 ap_log_error(APLOG_MARK, APLOG_CRIT, WSAGetLastError(), sconf, 315 APLOGNO(02123) 316 "make_secure_socket: for %s, WSAIoctl: " 317 "(SO_SSL_SET_FLAGS)", addr); 318 return -1; 319 } 320 } 321 322 optParam = SO_TLS_UNCLEAN_SHUTDOWN; 323 WSAIoctl(s, SO_SSL_SET_FLAGS, (char *)&optParam, sizeof(optParam), 324 NULL, 0, NULL, NULL, NULL); 325 326 return s; 327} 328 329static int convert_secure_socket(conn_rec *c, apr_socket_t *csd) 330{ 331 int rcode; 332 struct tlsclientopts sWS2Opts; 333 struct nwtlsopts sNWTLSOpts; 334 struct sslserveropts opts; 335 unsigned long ulFlags; 336 SOCKET sock; 337 unicode_t keyFileName[60]; 338 339 apr_os_sock_get(&sock, csd); 340 341 /* zero out buffers */ 342 memset((char *)&sWS2Opts, 0, sizeof(struct tlsclientopts)); 343 memset((char *)&sNWTLSOpts, 0, sizeof(struct nwtlsopts)); 344 345 /* turn on ssl for the socket */ 346 ulFlags = (numcerts ? SO_TLS_ENABLE : SO_TLS_ENABLE | SO_TLS_BLIND_ACCEPT); 347 rcode = WSAIoctl(sock, SO_TLS_SET_FLAGS, &ulFlags, sizeof(unsigned long), 348 NULL, 0, NULL, NULL, NULL); 349 if (SOCKET_ERROR == rcode) { 350 ap_log_error(APLOG_MARK, APLOG_ERR, 0, c->base_server, APLOGNO(02124) 351 "Error: %d with WSAIoctl(flag SO_TLS_ENABLE)", 352 WSAGetLastError()); 353 return rcode; 354 } 355 356 ulFlags = SO_TLS_UNCLEAN_SHUTDOWN; 357 WSAIoctl(sock, SO_TLS_SET_FLAGS, &ulFlags, sizeof(unsigned long), 358 NULL, 0, NULL, NULL, NULL); 359 360 /* setup the socket for SSL */ 361 memset (&sWS2Opts, 0, sizeof(sWS2Opts)); 362 memset (&sNWTLSOpts, 0, sizeof(sNWTLSOpts)); 363 sWS2Opts.options = &sNWTLSOpts; 364 365 if (numcerts) { 366 sNWTLSOpts.walletProvider = WAL_PROV_DER; /* the wallet provider defined in wdefs.h */ 367 sNWTLSOpts.TrustedRootList = certarray; /* array of certs in UNICODE format */ 368 sNWTLSOpts.numElementsInTRList = numcerts; /* number of certs in TRList */ 369 } 370 else { 371 /* setup the socket for SSL */ 372 unicpy(keyFileName, L"SSL CertificateIP"); 373 sWS2Opts.wallet = keyFileName; /* no client certificate */ 374 sWS2Opts.walletlen = unilen(keyFileName); 375 376 sNWTLSOpts.walletProvider = WAL_PROV_KMO; /* the wallet provider defined in wdefs.h */ 377 } 378 379 /* make the IOCTL call */ 380 rcode = WSAIoctl(sock, SO_TLS_SET_CLIENT, &sWS2Opts, 381 sizeof(struct tlsclientopts), NULL, 0, NULL, 382 NULL, NULL); 383 384 /* make sure that it was successful */ 385 if (SOCKET_ERROR == rcode ) { 386 ap_log_error(APLOG_MARK, APLOG_ERR, 0, c->base_server, APLOGNO(02125) 387 "Error: %d with WSAIoctl(SO_TLS_SET_CLIENT)", 388 WSAGetLastError()); 389 } 390 return rcode; 391} 392 393static int SSLize_Socket(SOCKET socketHnd, char *key, request_rec *r) 394{ 395 int rcode; 396 struct tlsserveropts sWS2Opts; 397 struct nwtlsopts sNWTLSOpts; 398 unicode_t SASKey[512]; 399 unsigned long ulFlag; 400 401 memset((char *)&sWS2Opts, 0, sizeof(struct tlsserveropts)); 402 memset((char *)&sNWTLSOpts, 0, sizeof(struct nwtlsopts)); 403 404 ulFlag = SO_TLS_ENABLE; 405 rcode = WSAIoctl(socketHnd, SO_TLS_SET_FLAGS, &ulFlag, 406 sizeof(unsigned long), NULL, 0, NULL, NULL, NULL); 407 if(rcode) { 408 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, APLOGNO(02126) 409 "Error: %d with WSAIoctl(SO_TLS_SET_FLAGS, SO_TLS_ENABLE)", 410 WSAGetLastError()); 411 goto ERR; 412 } 413 414 415 ulFlag = SO_TLS_SERVER; 416 rcode = WSAIoctl(socketHnd, SO_TLS_SET_FLAGS, &ulFlag, 417 sizeof(unsigned long),NULL, 0, NULL, NULL, NULL); 418 419 if (rcode) { 420 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, APLOGNO(02127) 421 "Error: %d with WSAIoctl(SO_TLS_SET_FLAGS, SO_TLS_SERVER)", 422 WSAGetLastError()); 423 goto ERR; 424 } 425 426 loc2uni(UNI_LOCAL_DEFAULT, SASKey, key, 0, 0); 427 428 /* setup the tlsserveropts struct */ 429 sWS2Opts.wallet = SASKey; 430 sWS2Opts.walletlen = unilen(SASKey); 431 sWS2Opts.sidtimeout = 0; 432 sWS2Opts.sidentries = 0; 433 sWS2Opts.siddir = NULL; 434 sWS2Opts.options = &sNWTLSOpts; 435 436 /* setup the nwtlsopts structure */ 437 438 sNWTLSOpts.walletProvider = WAL_PROV_KMO; 439 sNWTLSOpts.keysList = NULL; 440 sNWTLSOpts.numElementsInKeyList = 0; 441 sNWTLSOpts.reservedforfutureuse = NULL; 442 sNWTLSOpts.reservedforfutureCRL = NULL; 443 sNWTLSOpts.reservedforfutureCRLLen = 0; 444 sNWTLSOpts.reserved1 = NULL; 445 sNWTLSOpts.reserved2 = NULL; 446 sNWTLSOpts.reserved3 = NULL; 447 448 rcode = WSAIoctl(socketHnd, 449 SO_TLS_SET_SERVER, 450 &sWS2Opts, 451 sizeof(struct tlsserveropts), 452 NULL, 453 0, 454 NULL, 455 NULL, 456 NULL); 457 if (SOCKET_ERROR == rcode) { 458 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, APLOGNO(02128) 459 "Error: %d with WSAIoctl(SO_TLS_SET_SERVER)", WSAGetLastError()); 460 goto ERR; 461 } 462 463ERR: 464 return rcode; 465} 466 467static const char *set_secure_listener(cmd_parms *cmd, void *dummy, 468 const char *ips, const char* key, 469 const char* mutual) 470{ 471 NWSSLSrvConfigRec* sc = get_nwssl_cfg(cmd->server); 472 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); 473 char *ports, *addr; 474 unsigned short port; 475 seclisten_rec *new; 476 ap_listen_rec **walk; 477 apr_sockaddr_t *sa; 478 int found_listener = 0; 479 480 481 if (err != NULL) 482 return err; 483 484 ports = strchr(ips, ':'); 485 486 if (ports != NULL) { 487 if (ports == ips) 488 return "Missing IP address"; 489 else if (ports[1] == '\0') 490 return "Address must end in :<port-number>"; 491 492 *(ports++) = '\0'; 493 } 494 else { 495 ports = (char*)ips; 496 } 497 498 new = apr_pcalloc(cmd->server->process->pool, sizeof(seclisten_rec)); 499 new->local_addr.sin_family = AF_INET; 500 501 if (ports == ips) { 502 new->local_addr.sin_addr.s_addr = htonl(INADDR_ANY); 503 addr = apr_pstrdup(cmd->server->process->pool, "0.0.0.0"); 504 } 505 else { 506 new->local_addr.sin_addr.s_addr = parse_addr(ips, NULL); 507 addr = apr_pstrdup(cmd->server->process->pool, ips); 508 } 509 510 port = atoi(ports); 511 512 if (!port) 513 return "Port must be numeric"; 514 515 /* If the specified addr:port was created previously, put the listen 516 socket record back on the ap_listeners list so that the socket 517 will be reused rather than recreated */ 518 for (walk = &nw_old_listeners; *walk;) { 519 sa = (*walk)->bind_addr; 520 if (sa) { 521 ap_listen_rec *new; 522 apr_port_t oldport; 523 524 oldport = sa->port; 525 /* If both ports are equivalent, then if their names are equivalent, 526 * then we will re-use the existing record. 527 */ 528 if (port == oldport && 529 ((!addr && !sa->hostname) || 530 ((addr && sa->hostname) && !strcmp(sa->hostname, addr)))) { 531 new = *walk; 532 *walk = new->next; 533 new->next = ap_listeners; 534 ap_listeners = new; 535 found_listener = 1; 536 continue; 537 } 538 } 539 540 walk = &(*walk)->next; 541 } 542 543 apr_table_add(sc->sltable, ports, addr); 544 545 /* If we found a pre-existing listen socket record, then there 546 is no need to create a new secure listen socket record. */ 547 if (found_listener) { 548 return NULL; 549 } 550 551 new->local_addr.sin_port = htons(port); 552 new->fd = -1; 553 new->used = 0; 554 new->next = ap_seclisteners; 555 strcpy(new->key, key); 556 new->mutual = (mutual) ? 1 : 0; 557 new->addr = addr; 558 new->port = port; 559 ap_seclisteners = new; 560 return NULL; 561} 562 563static const char *set_secure_upgradeable_listener(cmd_parms *cmd, void *dummy, 564 const char *ips, const char* key) 565{ 566 NWSSLSrvConfigRec* sc = get_nwssl_cfg(cmd->server); 567 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); 568 char *ports, *addr; 569 unsigned short port; 570 seclistenup_rec *new; 571 572 if (err != NULL) 573 return err; 574 575 ports = strchr(ips, ':'); 576 577 if (ports != NULL) { 578 if (ports == ips) 579 return "Missing IP address"; 580 else if (ports[1] == '\0') 581 return "Address must end in :<port-number>"; 582 583 *(ports++) = '\0'; 584 } 585 else { 586 ports = (char*)ips; 587 } 588 589 if (ports == ips) { 590 addr = apr_pstrdup(cmd->pool, "0.0.0.0"); 591 } 592 else { 593 addr = apr_pstrdup(cmd->pool, ips); 594 } 595 596 port = atoi(ports); 597 598 if (!port) 599 return "Port must be numeric"; 600 601 apr_table_set(sc->slutable, ports, addr); 602 603 new = apr_pcalloc(cmd->pool, sizeof(seclistenup_rec)); 604 new->next = ap_seclistenersup; 605 strcpy(new->key, key); 606 new->addr = addr; 607 new->port = port; 608 ap_seclistenersup = new; 609 610 return err; 611} 612 613static apr_status_t nwssl_socket_cleanup(void *data) 614{ 615 ap_listen_rec* slr = (ap_listen_rec*)data; 616 ap_listen_rec* lr; 617 618 /* Remove our secure listener from the listener list */ 619 for (lr = ap_listeners; lr; lr = lr->next) { 620 /* slr is at the head of the list */ 621 if (lr == slr) { 622 ap_listeners = slr->next; 623 break; 624 } 625 /* slr is somewhere in between or at the end*/ 626 if (lr->next == slr) { 627 lr->next = slr->next; 628 break; 629 } 630 } 631 return APR_SUCCESS; 632} 633 634static const char *set_trusted_certs(cmd_parms *cmd, void *dummy, char *arg) 635{ 636 char **ptr = (char **)apr_array_push(certlist); 637 638 *ptr = apr_pstrdup(cmd->pool, arg); 639 return NULL; 640} 641 642static int nwssl_pre_config(apr_pool_t *pconf, apr_pool_t *plog, 643 apr_pool_t *ptemp) 644{ 645 seclisten_rec* ap_old_seclisteners; 646 ap_listen_rec **walk; 647 seclisten_rec **secwalk; 648 apr_sockaddr_t *sa; 649 int found; 650 651 /* Pull all of the listeners that were created by mod_nw_ssl out of the 652 ap_listeners list so that the normal listen socket processing does 653 automatically close them */ 654 nw_old_listeners = NULL; 655 ap_old_seclisteners = NULL; 656 657 for (secwalk = &ap_seclisteners; *secwalk;) { 658 found = 0; 659 for (walk = &ap_listeners; *walk;) { 660 sa = (*walk)->bind_addr; 661 if (sa) { 662 ap_listen_rec *new; 663 seclisten_rec *secnew; 664 apr_port_t oldport; 665 666 oldport = sa->port; 667 /* If both ports are equivalent, then if their names are equivalent, 668 * then we will re-use the existing record. 669 */ 670 if ((*secwalk)->port == oldport && 671 ((!(*secwalk)->addr && !sa->hostname) || 672 (((*secwalk)->addr && sa->hostname) && !strcmp(sa->hostname, (*secwalk)->addr)))) { 673 /* Move the listen socket from ap_listeners to nw_old_listeners */ 674 new = *walk; 675 *walk = new->next; 676 new->next = nw_old_listeners; 677 nw_old_listeners = new; 678 679 /* Move the secure socket record to ap_old_seclisterners */ 680 secnew = *secwalk; 681 *secwalk = secnew->next; 682 secnew->next = ap_old_seclisteners; 683 ap_old_seclisteners = secnew; 684 found = 1; 685 break; 686 } 687 } 688 689 walk = &(*walk)->next; 690 } 691 if (!found && &(*secwalk)->next) { 692 secwalk = &(*secwalk)->next; 693 } 694 } 695 696 /* Restore the secure socket records list so that the post config can 697 process all of the sockets normally */ 698 ap_seclisteners = ap_old_seclisteners; 699 ap_seclistenersup = NULL; 700 certlist = apr_array_make(pconf, 1, sizeof(char *)); 701 702 /* Now that we have removed all of the mod_nw_ssl created socket records, 703 allow the normal listen socket handling to occur. 704 NOTE: If for any reason mod_nw_ssl is removed as a built-in module, 705 the following call must be put back into the pre-config handler of the 706 MPM. It is only here to ensure that mod_nw_ssl fixes up the listen 707 socket list before anything else looks at it. */ 708 ap_listen_pre_config(); 709 710 return OK; 711} 712 713static int nwssl_pre_connection(conn_rec *c, void *csd) 714{ 715 716 if (apr_table_get(c->notes, "nwconv-ssl")) { 717 convert_secure_socket(c, (apr_socket_t*)csd); 718 } 719 else { 720 secsocket_data *csd_data = apr_palloc(c->pool, sizeof(secsocket_data)); 721 722 csd_data->csd = (apr_socket_t*)csd; 723 csd_data->is_secure = 0; 724 ap_set_module_config(c->conn_config, &nwssl_module, (void*)csd_data); 725 } 726 727 return OK; 728} 729 730static int nwssl_post_config(apr_pool_t *pconf, apr_pool_t *plog, 731 apr_pool_t *ptemp, server_rec *s) 732{ 733 seclisten_rec* sl; 734 ap_listen_rec* lr; 735 apr_socket_t* sd; 736 apr_status_t status; 737 seclistenup_rec *slu; 738 int found; 739 ap_listen_rec *walk; 740 seclisten_rec *secwalk, *lastsecwalk; 741 apr_sockaddr_t *sa; 742 743 /* Walk the old listeners list and compare it to the secure 744 listeners list and remove any secure listener records that 745 are not being reused */ 746 for (walk = nw_old_listeners; walk; walk = walk->next) { 747 sa = walk->bind_addr; 748 if (sa) { 749 ap_listen_rec *new; 750 apr_port_t oldport; 751 752 oldport = sa->port; 753 for (secwalk = ap_seclisteners, lastsecwalk = ap_seclisteners; secwalk; secwalk = lastsecwalk->next) { 754 unsigned short port = secwalk->port; 755 char *addr = secwalk->addr; 756 /* If both ports are equivalent, then if their names are equivalent, 757 * then we will re-use the existing record. 758 */ 759 if (port == oldport && 760 ((!addr && !sa->hostname) || 761 ((addr && sa->hostname) && !strcmp(sa->hostname, addr)))) { 762 if (secwalk == ap_seclisteners) { 763 ap_seclisteners = secwalk->next; 764 } 765 else { 766 lastsecwalk->next = secwalk->next; 767 } 768 apr_socket_close(walk->sd); 769 walk->active = 0; 770 break; 771 } 772 else { 773 lastsecwalk = secwalk; 774 } 775 } 776 } 777 } 778 779 for (sl = ap_seclisteners; sl != NULL; sl = sl->next) { 780 /* If we find a pre-existing listen socket and it has already been 781 created, then no neeed to go any further, just reuse it. */ 782 if (((sl->fd = find_secure_listener(sl)) >= 0) && (sl->used)) { 783 continue; 784 } 785 786 if (sl->fd < 0) 787 sl->fd = make_secure_socket(s->process->pool, &sl->local_addr, sl->key, sl->mutual, s); 788 789 if (sl->fd >= 0) { 790 apr_os_sock_info_t sock_info; 791 792 sock_info.os_sock = &(sl->fd); 793 sock_info.local = (struct sockaddr*)&(sl->local_addr); 794 sock_info.remote = NULL; 795 sock_info.family = APR_INET; 796 sock_info.type = SOCK_STREAM; 797 798 apr_os_sock_make(&sd, &sock_info, s->process->pool); 799 800 lr = apr_pcalloc(s->process->pool, sizeof(ap_listen_rec)); 801 802 if (lr) { 803 lr->sd = sd; 804 if ((status = apr_sockaddr_info_get(&lr->bind_addr, sl->addr, APR_UNSPEC, sl->port, 0, 805 s->process->pool)) != APR_SUCCESS) { 806 ap_log_perror(APLOG_MARK, APLOG_CRIT, status, pconf, APLOGNO(02129) 807 "alloc_listener: failed to set up sockaddr for %s:%d", sl->addr, sl->port); 808 return HTTP_INTERNAL_SERVER_ERROR; 809 } 810 lr->next = ap_listeners; 811 ap_listeners = lr; 812 apr_pool_cleanup_register(s->process->pool, lr, nwssl_socket_cleanup, apr_pool_cleanup_null); 813 } 814 } else { 815 return HTTP_INTERNAL_SERVER_ERROR; 816 } 817 } 818 819 for (slu = ap_seclistenersup; slu; slu = slu->next) { 820 /* Check the listener list for a matching upgradeable listener */ 821 found = 0; 822 for (lr = ap_listeners; lr; lr = lr->next) { 823 if (slu->port == lr->bind_addr->port) { 824 found = 1; 825 break; 826 } 827 } 828 if (!found) { 829 ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, plog, APLOGNO(02130) 830 "No Listen directive found for upgradeable listener %s:%d", slu->addr, slu->port); 831 } 832 } 833 834 build_cert_list(s->process->pool); 835 836 return OK; 837} 838 839static void *nwssl_config_server_create(apr_pool_t *p, server_rec *s) 840{ 841 NWSSLSrvConfigRec *new = apr_palloc(p, sizeof(NWSSLSrvConfigRec)); 842 new->sltable = apr_table_make(p, 5); 843 new->slutable = apr_table_make(p, 5); 844 return new; 845} 846 847static void *nwssl_config_server_merge(apr_pool_t *p, void *basev, void *addv) 848{ 849 NWSSLSrvConfigRec *base = (NWSSLSrvConfigRec *)basev; 850 NWSSLSrvConfigRec *add = (NWSSLSrvConfigRec *)addv; 851 NWSSLSrvConfigRec *merged = (NWSSLSrvConfigRec *)apr_palloc(p, sizeof(NWSSLSrvConfigRec)); 852 return merged; 853} 854 855static int compare_ipports(void *rec, const char *key, const char *value) 856{ 857 conn_rec *c = (conn_rec*)rec; 858 859 if (value && 860 ((strcmp(value, "0.0.0.0") == 0) || (strcmp(value, c->local_ip) == 0))) 861 { 862 return 0; 863 } 864 return 1; 865} 866 867static int isSecureConnEx (const server_rec *s, const conn_rec *c, const apr_table_t *t) 868{ 869 char port[8]; 870 871 itoa((c->local_addr)->port, port, 10); 872 if (!apr_table_do(compare_ipports, (void*)c, t, port, NULL)) { 873 return 1; 874 } 875 876 return 0; 877} 878 879static int isSecureConn (const server_rec *s, const conn_rec *c) 880{ 881 NWSSLSrvConfigRec *sc = get_nwssl_cfg(s); 882 883 return isSecureConnEx (s, c, sc->sltable); 884} 885 886static int isSecureConnUpgradeable (const server_rec *s, const conn_rec *c) 887{ 888 NWSSLSrvConfigRec *sc = get_nwssl_cfg(s); 889 890 return isSecureConnEx (s, c, sc->slutable); 891} 892 893static int isSecure (const request_rec *r) 894{ 895 return isSecureConn (r->server, r->connection); 896} 897 898static int isSecureUpgradeable (const request_rec *r) 899{ 900 return isSecureConnUpgradeable (r->server, r->connection); 901} 902 903static int isSecureUpgraded (const request_rec *r) 904{ 905 secsocket_data *csd_data = (secsocket_data*)ap_get_module_config(r->connection->conn_config, &nwssl_module); 906 907 return csd_data->is_secure; 908} 909 910static int nwssl_hook_Fixup(request_rec *r) 911{ 912 if (!isSecure(r) && !isSecureUpgraded(r)) 913 return DECLINED; 914 915 apr_table_setn(r->subprocess_env, "HTTPS", "on"); 916 917 return DECLINED; 918} 919 920static const char *nwssl_hook_http_scheme(const request_rec *r) 921{ 922 if (isSecure(r) && !isSecureUpgraded(r)) 923 return "https"; 924 925 return NULL; 926} 927 928static apr_port_t nwssl_hook_default_port(const request_rec *r) 929{ 930 if (isSecure(r)) 931 return DEFAULT_HTTPS_PORT; 932 933 return 0; 934} 935 936int ssl_proxy_enable(conn_rec *c) 937{ 938 apr_table_setn(c->notes, "nwconv-ssl", "Y"); 939 940 return 1; 941} 942 943int ssl_engine_disable(conn_rec *c) 944{ 945 return 1; 946} 947 948static int ssl_is_https(conn_rec *c) 949{ 950 secsocket_data *csd_data = (secsocket_data*)ap_get_module_config(c->conn_config, &nwssl_module); 951 952 return isSecureConn (c->base_server, c) || (csd_data && csd_data->is_secure); 953} 954 955/* This function must remain safe to use for a non-SSL connection. */ 956char *ssl_var_lookup(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, char *var) 957{ 958 NWSSLSrvConfigRec *mc = get_nwssl_cfg(s); 959 const char *result; 960 BOOL resdup; 961 apr_time_exp_t tm; 962 963 result = NULL; 964 resdup = TRUE; 965 966 /* 967 * When no pool is given try to find one 968 */ 969 if (p == NULL) { 970 if (r != NULL) 971 p = r->pool; 972 else if (c != NULL) 973 p = c->pool; 974 else 975 p = mc->pPool; 976 } 977 978 /* 979 * Request dependent stuff 980 */ 981 if (r != NULL) { 982 switch (var[0]) { 983 case 'H': 984 case 'h': 985 if (strcEQ(var, "HTTP_USER_AGENT")) 986 result = apr_table_get(r->headers_in, "User-Agent"); 987 else if (strcEQ(var, "HTTP_REFERER")) 988 result = apr_table_get(r->headers_in, "Referer"); 989 else if (strcEQ(var, "HTTP_COOKIE")) 990 result = apr_table_get(r->headers_in, "Cookie"); 991 else if (strcEQ(var, "HTTP_FORWARDED")) 992 result = apr_table_get(r->headers_in, "Forwarded"); 993 else if (strcEQ(var, "HTTP_HOST")) 994 result = apr_table_get(r->headers_in, "Host"); 995 else if (strcEQ(var, "HTTP_PROXY_CONNECTION")) 996 result = apr_table_get(r->headers_in, "Proxy-Connection"); 997 else if (strcEQ(var, "HTTP_ACCEPT")) 998 result = apr_table_get(r->headers_in, "Accept"); 999 else if (strcEQ(var, "HTTPS")) { 1000 if (isSecure(r) || isSecureUpgraded(r)) 1001 result = "on"; 1002 else 1003 result = "off"; 1004 } 1005 else if (strlen(var) > 5 && strcEQn(var, "HTTP:", 5)) 1006 /* all other headers from which we are still not know about */ 1007 result = apr_table_get(r->headers_in, var+5); 1008 break; 1009 1010 case 'R': 1011 case 'r': 1012 if (strcEQ(var, "REQUEST_METHOD")) 1013 result = r->method; 1014 else if (strcEQ(var, "REQUEST_SCHEME")) 1015 result = ap_http_scheme(r); 1016 else if (strcEQ(var, "REQUEST_URI")) 1017 result = r->uri; 1018 else if (strcEQ(var, "REQUEST_FILENAME")) 1019 result = r->filename; 1020 else if (strcEQ(var, "REMOTE_ADDR")) 1021 result = r->useragent_ip; 1022 else if (strcEQ(var, "REMOTE_HOST")) 1023 result = ap_get_remote_host(r->connection, r->per_dir_config, 1024 REMOTE_NAME, NULL); 1025 else if (strcEQ(var, "REMOTE_IDENT")) 1026 result = ap_get_remote_logname(r); 1027 else if (strcEQ(var, "REMOTE_USER")) 1028 result = r->user; 1029 break; 1030 1031 case 'S': 1032 case 's': 1033 if (strcEQn(var, "SSL", 3)) break; /* shortcut common case */ 1034 1035 if (strcEQ(var, "SERVER_ADMIN")) 1036 result = r->server->server_admin; 1037 else if (strcEQ(var, "SERVER_NAME")) 1038 result = ap_get_server_name_for_url(r); 1039 else if (strcEQ(var, "SERVER_PORT")) 1040 result = apr_psprintf(p, "%u", ap_get_server_port(r)); 1041 else if (strcEQ(var, "SERVER_PROTOCOL")) 1042 result = r->protocol; 1043 else if (strcEQ(var, "SCRIPT_FILENAME")) 1044 result = r->filename; 1045 break; 1046 1047 default: 1048 if (strcEQ(var, "PATH_INFO")) 1049 result = r->path_info; 1050 else if (strcEQ(var, "QUERY_STRING")) 1051 result = r->args; 1052 else if (strcEQ(var, "IS_SUBREQ")) 1053 result = (r->main != NULL ? "true" : "false"); 1054 else if (strcEQ(var, "DOCUMENT_ROOT")) 1055 result = ap_document_root(r); 1056 else if (strcEQ(var, "AUTH_TYPE")) 1057 result = r->ap_auth_type; 1058 else if (strcEQ(var, "THE_REQUEST")) 1059 result = r->the_request; 1060 break; 1061 } 1062 } 1063 1064 /* 1065 * Connection stuff 1066 */ 1067 if (result == NULL && c != NULL) { 1068 /* XXX-Can't get specific SSL info from NetWare */ 1069 /* SSLConnRec *sslconn = myConnConfig(c); 1070 if (strlen(var) > 4 && strcEQn(var, "SSL_", 4) 1071 && sslconn && sslconn->ssl) 1072 result = ssl_var_lookup_ssl(p, c, var+4);*/ 1073 1074 if (strlen(var) > 4 && strcEQn(var, "SSL_", 4)) 1075 result = NULL; 1076 } 1077 1078 /* 1079 * Totally independent stuff 1080 */ 1081 if (result == NULL) { 1082 if (strlen(var) > 12 && strcEQn(var, "SSL_VERSION_", 12)) 1083 result = NULL; 1084 /* XXX-Can't get specific SSL info from NetWare */ 1085 /*result = ssl_var_lookup_ssl_version(p, var+12);*/ 1086 else if (strcEQ(var, "SERVER_SOFTWARE")) 1087 result = ap_get_server_banner(); 1088 else if (strcEQ(var, "API_VERSION")) { 1089 result = apr_itoa(p, MODULE_MAGIC_NUMBER); 1090 resdup = FALSE; 1091 } 1092 else if (strcEQ(var, "TIME_YEAR")) { 1093 apr_time_exp_lt(&tm, apr_time_now()); 1094 result = apr_psprintf(p, "%02d%02d", 1095 (tm.tm_year / 100) + 19, tm.tm_year % 100); 1096 resdup = FALSE; 1097 } 1098#define MKTIMESTR(format, tmfield) \ 1099 apr_time_exp_lt(&tm, apr_time_now()); \ 1100 result = apr_psprintf(p, format, tm.tmfield); \ 1101 resdup = FALSE; 1102 else if (strcEQ(var, "TIME_MON")) { 1103 MKTIMESTR("%02d", tm_mon+1) 1104 } 1105 else if (strcEQ(var, "TIME_DAY")) { 1106 MKTIMESTR("%02d", tm_mday) 1107 } 1108 else if (strcEQ(var, "TIME_HOUR")) { 1109 MKTIMESTR("%02d", tm_hour) 1110 } 1111 else if (strcEQ(var, "TIME_MIN")) { 1112 MKTIMESTR("%02d", tm_min) 1113 } 1114 else if (strcEQ(var, "TIME_SEC")) { 1115 MKTIMESTR("%02d", tm_sec) 1116 } 1117 else if (strcEQ(var, "TIME_WDAY")) { 1118 MKTIMESTR("%d", tm_wday) 1119 } 1120 else if (strcEQ(var, "TIME")) { 1121 apr_time_exp_lt(&tm, apr_time_now()); 1122 result = apr_psprintf(p, 1123 "%02d%02d%02d%02d%02d%02d%02d", (tm.tm_year / 100) + 19, 1124 (tm.tm_year % 100), tm.tm_mon+1, tm.tm_mday, 1125 tm.tm_hour, tm.tm_min, tm.tm_sec); 1126 resdup = FALSE; 1127 } 1128 /* all other env-variables from the parent Apache process */ 1129 else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) { 1130 result = apr_table_get(r->notes, var+4); 1131 if (result == NULL) 1132 result = apr_table_get(r->subprocess_env, var+4); 1133 if (result == NULL) 1134 result = getenv(var+4); 1135 } 1136 } 1137 1138 if (result != NULL && resdup) 1139 result = apr_pstrdup(p, result); 1140 if (result == NULL) 1141 result = ""; 1142 return (char *)result; 1143} 1144 1145#define SWITCH_STATUS_LINE "HTTP/1.1 101 Switching Protocols" 1146#define UPGRADE_HEADER "Upgrade: TLS/1.0, HTTP/1.1" 1147#define CONNECTION_HEADER "Connection: Upgrade" 1148 1149static apr_status_t ssl_io_filter_Upgrade(ap_filter_t *f, 1150 apr_bucket_brigade *bb) 1151 1152{ 1153 const char *upgrade; 1154 apr_bucket_brigade *upgradebb; 1155 request_rec *r = f->r; 1156 apr_socket_t *csd = NULL; 1157 char *key; 1158 int ret; 1159 secsocket_data *csd_data; 1160 apr_bucket *b; 1161 apr_status_t rv; 1162 1163 /* Just remove the filter, if it doesn't work the first time, it won't 1164 * work at all for this request. 1165 */ 1166 ap_remove_output_filter(f); 1167 1168 if (!r) { 1169 /* 1170 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, APLOGNO(02131) 1171 "Unable to get upgradeable socket handle"); 1172 */ 1173 return ap_pass_brigade(f->next, bb); 1174 } 1175 1176 /* No need to ensure that this is a server with optional SSL, the filter 1177 * is only inserted if that is true. 1178 */ 1179 1180 upgrade = apr_table_get(r->headers_in, "Upgrade"); 1181 if (upgrade == NULL 1182 || strcmp(ap_getword(r->pool, &upgrade, ','), "TLS/1.0")) { 1183 /* "Upgrade: TLS/1.0, ..." header not found, don't do Upgrade */ 1184 return ap_pass_brigade(f->next, bb); 1185 } 1186 1187 apr_table_unset(r->headers_out, "Upgrade"); 1188 1189 csd_data = (secsocket_data*)ap_get_module_config(r->connection->conn_config, &nwssl_module); 1190 csd = csd_data->csd; 1191 1192 /* Send the interim 101 response. */ 1193 upgradebb = apr_brigade_create(r->pool, f->c->bucket_alloc); 1194 1195 ap_fputstrs(f->next, upgradebb, SWITCH_STATUS_LINE, CRLF, 1196 UPGRADE_HEADER, CRLF, CONNECTION_HEADER, CRLF, CRLF, NULL); 1197 1198 b = apr_bucket_flush_create(f->c->bucket_alloc); 1199 APR_BRIGADE_INSERT_TAIL(upgradebb, b); 1200 1201 rv = ap_pass_brigade(f->next, upgradebb); 1202 if (rv) { 1203 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02132) 1204 "could not send interim 101 Upgrade response"); 1205 return AP_FILTER_ERROR; 1206 } 1207 1208 key = get_port_key(r->connection); 1209 1210 if (csd && key) { 1211 int sockdes; 1212 apr_os_sock_get(&sockdes, csd); 1213 1214 1215 ret = SSLize_Socket(sockdes, key, r); 1216 if (!ret) { 1217 csd_data->is_secure = 1; 1218 } 1219 } 1220 else { 1221 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, APLOGNO(02133) 1222 "Upgradeable socket handle not found"); 1223 return AP_FILTER_ERROR; 1224 } 1225 1226 ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server, APLOGNO(02134) 1227 "Awaiting re-negotiation handshake"); 1228 1229 /* Now that we have initialized the ssl connection which added the ssl_io_filter, 1230 pass the brigade off to the connection based output filters so that the 1231 request can complete encrypted */ 1232 return ap_pass_brigade(f->c->output_filters, bb); 1233} 1234 1235static void ssl_hook_Insert_Filter(request_rec *r) 1236{ 1237 NWSSLSrvConfigRec *sc = get_nwssl_cfg(r->server); 1238 1239 if (isSecureUpgradeable (r)) { 1240 ap_add_output_filter("UPGRADE_FILTER", NULL, r, r->connection); 1241 } 1242} 1243 1244static const command_rec nwssl_module_cmds[] = 1245{ 1246 AP_INIT_TAKE23("SecureListen", set_secure_listener, NULL, RSRC_CONF, 1247 "specify an address and/or port with a key pair name.\n" 1248 "Optional third parameter of MUTUAL configures the port for mutual authentication."), 1249 AP_INIT_TAKE2("NWSSLUpgradeable", set_secure_upgradeable_listener, NULL, RSRC_CONF, 1250 "specify an address and/or port with a key pair name, that can be upgraded to an SSL connection.\n" 1251 "The address and/or port must have already be defined using a Listen directive."), 1252 AP_INIT_ITERATE("NWSSLTrustedCerts", set_trusted_certs, NULL, RSRC_CONF, 1253 "Adds trusted certificates that are used to create secure connections to proxied servers"), 1254 {NULL} 1255}; 1256 1257static void register_hooks(apr_pool_t *p) 1258{ 1259 ap_register_output_filter ("UPGRADE_FILTER", ssl_io_filter_Upgrade, NULL, AP_FTYPE_PROTOCOL + 5); 1260 1261 ap_hook_pre_config(nwssl_pre_config, NULL, NULL, APR_HOOK_MIDDLE); 1262 ap_hook_pre_connection(nwssl_pre_connection, NULL, NULL, APR_HOOK_MIDDLE); 1263 ap_hook_post_config(nwssl_post_config, NULL, NULL, APR_HOOK_MIDDLE); 1264 ap_hook_fixups(nwssl_hook_Fixup, NULL, NULL, APR_HOOK_MIDDLE); 1265 ap_hook_http_scheme(nwssl_hook_http_scheme, NULL, NULL, APR_HOOK_MIDDLE); 1266 ap_hook_default_port(nwssl_hook_default_port, NULL, NULL, APR_HOOK_MIDDLE); 1267 ap_hook_insert_filter(ssl_hook_Insert_Filter, NULL, NULL, APR_HOOK_MIDDLE); 1268 1269 APR_REGISTER_OPTIONAL_FN(ssl_is_https); 1270 APR_REGISTER_OPTIONAL_FN(ssl_var_lookup); 1271 1272 APR_REGISTER_OPTIONAL_FN(ssl_proxy_enable); 1273 APR_REGISTER_OPTIONAL_FN(ssl_engine_disable); 1274} 1275 1276AP_DECLARE_MODULE(nwssl) = 1277{ 1278 STANDARD20_MODULE_STUFF, 1279 NULL, /* dir config creater */ 1280 NULL, /* dir merger --- default is to override */ 1281 nwssl_config_server_create, /* server config */ 1282 nwssl_config_server_merge, /* merge server config */ 1283 nwssl_module_cmds, /* command apr_table_t */ 1284 register_hooks 1285}; 1286 1287