/* Copyright 2001-2004 The Apache Software Foundation * * Licensed 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_ssl_error: * * Certificate validation error trapping * * Developed by Marc Stern, for Approach Belgium / CSC / Belgian Government * * This module was developed to support the Belgian Electronic Identity Card * * Version: 1.0.4 * */ /* ``We are born naked, wet and hungry. Then things get worse.´´ */ #include "apr.h" #include "ap_config.h" #include "apr_optional.h" #include "httpd.h" #include "http_config.h" #include "util_filter.h" #include "mod_ssl.h" #include "ssl_private.h" #define LOG( l_, t_ ) {ap_log_error(APLOG_MARK, l_, 0, r->server, t_);} #define LOG1( l_, t_, p1_ ) {ap_log_error(APLOG_MARK, l_, 0, r->server, t_, p1_);} #define LOG2( l_, t_, p1_, p2_ ) {ap_log_error(APLOG_MARK, l_, 0, r->server, t_, p1_, p2_);} #define LOG3( l_, t_, p1_, p2_, p3_ ) {ap_log_error(APLOG_MARK, l_, 0, r->server, t_, p1_, p2_, p3_);} /* Functions from mod_ssl to retrieve variables containing status */ static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *ptr_ssl_lookup = NULL; module AP_MODULE_DECLARE_DATA ssl_error_module; #define strend( string ) ( (string) + strlen(string) ) #define CheckURL(url) if ( !ap_is_url(url) && (*url != '/') ) return "Redirect page is not a URL" /*if ( strEQn(url, "https:", 6) ) return "Redirect page cannot use HTTPS"*/ typedef struct { const char *defaultURL; const char *URL[X509_V_ERR_APPLICATION_VERIFICATION]; } SSL_Error_conf; const char *SSL_Error_cmd_DefaultURL( cmd_parms *cmd, void *dcfg, const char *url ) { // SSL_Error_conf *mcfg = (SSL_Error_conf*)ap_get_module_config(cmd->server->module_config, &ssl_error_module); SSL_Error_conf *cfg = (SSL_Error_conf*)dcfg; CheckURL(url); cfg->defaultURL = url; return NULL; } const char *SSL_Error_cmd_URL( cmd_parms *cmd, void *dcfg, const char *err, const char *url ) { // SSL_Error_conf* cfg = (SSL_Error_conf*)ap_get_module_config(cmd->server->module_config, &ssl_error_module); SSL_Error_conf *cfg = (SSL_Error_conf*)dcfg; int num = atoi(err); if ( (num <= 0) || (num >= X509_V_ERR_APPLICATION_VERIFICATION) ) return "Bad error number"; CheckURL(url); cfg->URL[num] = url; return NULL; } static void redirect( request_rec *r, const char *url, int error_nb, const char *errorMsg, const char *dn, const char *serial ) { static const char p0[] = "errorNb="; static const char p1[] = "&error="; static const char p2[] = "&dn="; static const char p3[] = "&serial="; static const char http[] = "http://"; char *buf; if ( !errorMsg || strEQ(errorMsg, "(null)") ) errorMsg = ""; if ( !dn || strEQ(dn, "(null)") ) dn = ""; if ( !serial || strEQ(serial, "(null)") ) serial = ""; buf = malloc( strlen(http) + strlen(r->hostname) + strlen(url) + 1 + strlen(p0) + 4 + strlen(p1) + strlen(errorMsg) + strlen(p2) + strlen(dn) + strlen(p3) + strlen(serial) + 1 ); if ( buf ) { *buf = 0; if (*url == '/') { /* Add hostname */ strcat( buf, http ); strcat( buf, r->hostname ); } #if 0 if ( strEQn(url, "https:", 6) ) strcpy(url+4, url+5); /* HTTPS -> HTTP */ #endif strcat( buf, url ); if ( (error_nb >= 0) || *errorMsg || *dn || *serial ) strcat( buf, "?" ); strcat( buf, p0 ); if ( error_nb >= 0 ) sprintf(strend(buf), "%d", error_nb); else strcat( buf, "?" ); /* if ( *errorMsg ) { strcat( buf, p1 ); strcat( buf, errorMsg ); } */ if ( *dn ) { strcat( buf, p2 ); strcat( buf, dn ); } if ( *serial ) { strcat( buf, p3 ); strcat( buf, serial ); } } else buf = (char *)url; /* in case of not enough memory */ apr_table_set(r->headers_out, "Location", buf); if ( buf != url ) free(buf); } static int get_SSL_error_nb( const char *verify ) { int i; for ( i = 0; i <= X509_V_ERR_APPLICATION_VERIFICATION; i++ ) if ( strEQ(verify, X509_verify_cert_error_string(i)) ) return i; return -1; } int ssl_error_handler( apr_pool_t *pool, server_rec *server, conn_rec *connection, request_rec *r ) { const char *verify, *dn, *serial, *redir_url = NULL; int err_nb = -1; // SSL_Error_conf *cfg = (SSL_Error_conf*)ap_get_module_config(server->module_config, &ssl_error_module); SSL_Error_conf *cfg = (SSL_Error_conf*)ap_get_module_config(r->per_dir_config, &ssl_error_module); //LOG(APLOG_DEBUG, "SSL_error entry point"); if ( ! ptr_ssl_lookup ) { /* SSL not configured */ LOG(APLOG_DEBUG, "SSL not configured"); return DECLINED; } verify = ptr_ssl_lookup( pool, server, connection, r, apr_pstrdup(pool, "SSL_CLIENT_VERIFY") ); dn = ptr_ssl_lookup( pool, server, connection, r, apr_pstrdup(pool, "SSL_CLIENT_S_DN") ); serial = ptr_ssl_lookup( pool, server, connection, r, apr_pstrdup(pool, "SSL_CLIENT_M_SERIAL") ); if ( ! verify ) { LOG(APLOG_ERR, "Unable to get SSL Verification STATUS"); return HTTP_INTERNAL_SERVER_ERROR; } LOG1(APLOG_DEBUG, "Certificate verification: %s", verify); if ( ! *verify ) return DECLINED; /* not in a SSL session */ if ( strEQ(verify, "NONE") ) return DECLINED; /* SSL negociation not finished or no client verify */ if ( strEQ(verify, "SUCCESS") ) return DECLINED; if ( strEQn(verify, "GENEROUS", 8) ) return DECLINED; /* apr_table_unset(r->notes, "HTTPS"); /* needed ? */ if ( strEQn(verify, "FAILED:", 7) ) { verify += 7; err_nb = get_SSL_error_nb(verify); LOG1(APLOG_DEBUG, "SSL error number: %d", err_nb); if ( err_nb == 0 ) return DECLINED; if ( err_nb > 0 ) redir_url = cfg->URL[err_nb]; } if ( !redir_url ) redir_url = cfg->defaultURL; if ( redir_url ) { LOG1(APLOG_DEBUG, "redirect URL: %s", redir_url); redirect( r, redir_url, err_nb, verify, dn, serial ); return HTTP_TEMPORARY_REDIRECT; } return HTTP_FORBIDDEN; } /* ------- Module Initialisation ----------------- */ const command_rec ssl_error_config_cmds[] = { /* Global (main-server) context configuration directives */ AP_INIT_TAKE1("SSL_Error_DefaultURL", SSL_Error_cmd_DefaultURL, NULL, RSRC_CONF|OR_AUTHCFG, "Default error page for certificate validation failure"), AP_INIT_TAKE2("SSL_Error_URL", SSL_Error_cmd_URL, NULL, RSRC_CONF|OR_AUTHCFG, "Error page for specific certificate validation failure"), { NULL } }; static void *config_dir_create( apr_pool_t *p, char *dir ) { SSL_Error_conf *ret = apr_pcalloc(p, sizeof(SSL_Error_conf) ); /* filled with 0 */ return (void *)ret ; /* structure containing internal parameters (user options) */ } static void *config_server_create( apr_pool_t *p, server_rec *s ) { ptr_ssl_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup); return config_dir_create( p, "" ); /* structure containing internal parameters (user options) */ } #define cfgMerge(el,unset) mrg->el = (add->el == (unset)) ? base->el : add->el #define cfgMergeString(el) cfgMerge(el, NULL) #define cfgMergeBool(el) cfgMerge(el, UNSET) #define cfgMergeInt(el) cfgMerge(el, UNSET) static void *config_server_merge( apr_pool_t *p, void *basev, void *addv ) { SSL_Error_conf *base = (SSL_Error_conf *)basev; SSL_Error_conf *add = (SSL_Error_conf *)addv; SSL_Error_conf *mrg = (SSL_Error_conf *)apr_palloc(p, sizeof(SSL_Error_conf)); int i; cfgMergeString(defaultURL); for ( i = 0; i < sizeof(mrg->URL) / sizeof(char*); ++i ) cfgMergeString(URL[i]); return (void *)mrg; /* structure containing internal parameters (user options) */ } static void *config_dir_merge( apr_pool_t *p, void *basev, void *addv ) { return (void *)config_server_merge( p, basev, addv ); /* structure containing internal parameters (user options) */ } int ssl_error_hook_Access( request_rec *r ) { return ssl_error_handler( r->pool, r->server, r->connection, r ); } void register_hooks( apr_pool_t *p ) { ap_hook_access_checker(ssl_error_hook_Access, NULL, NULL, APR_HOOK_MIDDLE); // used during session renegociation // ? ap_hook_auth_checker(ssl_error_hook_Access, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_post_read_request(ssl_error_hook_Access, NULL, NULL, APR_HOOK_MIDDLE); } extern module AP_MODULE_DECLARE_DATA ssl_error_module; module AP_MODULE_DECLARE_DATA ssl_error_module = { STANDARD20_MODULE_STUFF, config_dir_create, /* create per-dir config structures */ config_dir_merge, /* merge per-dir config structures */ config_server_create, /* create per-server config structures */ config_server_merge, /* merge per-server config structures */ ssl_error_config_cmds, /* table of configuration directives (to store user options) */ register_hooks /* register hooks */ };