static void test_buffer_to_lower_upper(void) { buffer *psrc = buffer_init(); buffer_copy_string_len(psrc, CONST_STR_LEN("0123456789abcdefghijklmnopqrstuvwxyz")); buffer_to_lower(psrc); assert(buffer_is_equal_string(psrc, CONST_STR_LEN("0123456789abcdefghijklmnopqrstuvwxyz"))); buffer_to_upper(psrc); assert(buffer_is_equal_string(psrc, CONST_STR_LEN("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"))); buffer_to_upper(psrc); assert(buffer_is_equal_string(psrc, CONST_STR_LEN("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"))); buffer_to_lower(psrc); assert(buffer_is_equal_string(psrc, CONST_STR_LEN("0123456789abcdefghijklmnopqrstuvwxyz"))); buffer_free(psrc); }
void http_response_xsendfile (server *srv, connection *con, buffer *path, const array *xdocroot) { const int status = con->http_status; int valid = 1; /* reset Content-Length, if set by backend * Content-Length might later be set to size of X-Sendfile static file, * determined by open(), fstat() to reduces race conditions if the file * is modified between stat() (stat_cache_get_entry()) and open(). */ if (con->parsed_response & HTTP_CONTENT_LENGTH) { data_string *ds = (data_string *) array_get_element(con->response.headers, "Content-Length"); if (ds) buffer_reset(ds->value); con->parsed_response &= ~HTTP_CONTENT_LENGTH; con->response.content_length = -1; } buffer_urldecode_path(path); buffer_path_simplify(path, path); if (con->conf.force_lowercase_filenames) { buffer_to_lower(path); } /* check that path is under xdocroot(s) * - xdocroot should have trailing slash appended at config time * - con->conf.force_lowercase_filenames is not a server-wide setting, * and so can not be definitively applied to xdocroot at config time*/ if (xdocroot->used) { size_t i, xlen = buffer_string_length(path); for (i = 0; i < xdocroot->used; ++i) { data_string *ds = (data_string *)xdocroot->data[i]; size_t dlen = buffer_string_length(ds->value); if (dlen <= xlen && (!con->conf.force_lowercase_filenames ? 0 == memcmp(path->ptr, ds->value->ptr, dlen) : 0 == strncasecmp(path->ptr, ds->value->ptr, dlen))) { break; } } if (i == xdocroot->used) { log_error_write(srv, __FILE__, __LINE__, "SBs", "X-Sendfile (", path, ") not under configured x-sendfile-docroot(s)"); con->http_status = 403; valid = 0; } } if (valid) http_response_send_file(srv, con, path); if (con->http_status >= 400 && status < 300) { con->mode = DIRECT; } else if (0 != status && 200 != status) { con->http_status = status; } }
static int network_ssl_servername_callback(SSL *ssl, int *al, server *srv) { const char *servername; connection *con = (connection *) SSL_get_app_data(ssl); UNUSED(al); buffer_copy_string(con->uri.scheme, "https"); if (NULL == (servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) { #if 0 /* this "error" just means the client didn't support it */ log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", "failed to get TLS server name"); #endif return SSL_TLSEXT_ERR_NOACK; } buffer_copy_string(con->tlsext_server_name, servername); buffer_to_lower(con->tlsext_server_name); /* Sometimes this is still set, confusing COMP_HTTP_HOST */ buffer_reset(con->uri.authority); config_cond_cache_reset(srv, con); config_setup_connection(srv, con); config_patch_connection(srv, con, COMP_SERVER_SOCKET); config_patch_connection(srv, con, COMP_HTTP_SCHEME); config_patch_connection(srv, con, COMP_HTTP_HOST); if (NULL == con->conf.ssl_ctx) { /* ssl_ctx <=> pemfile was set <=> ssl_ctx got patched: so this should never happen */ log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", "null SSL_CTX for TLS server name", con->tlsext_server_name); return SSL_TLSEXT_ERR_ALERT_FATAL; } /* switch to new SSL_CTX in reaction to a client's server_name extension */ if (con->conf.ssl_ctx != SSL_set_SSL_CTX(ssl, con->conf.ssl_ctx)) { log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", "failed to set SSL_CTX for TLS server name", con->tlsext_server_name); return SSL_TLSEXT_ERR_ALERT_FATAL; } return SSL_TLSEXT_ERR_OK; }
handler_t http_response_prepare(server *srv, connection *con) { handler_t r; Cdbg(DBE, "enter http_response_prepare..mode=[%d], status=[%d][%s]", con->mode, con->http_status, connection_get_state(con->http_status)); /* looks like someone has already done a decision */ if ( (con->mode == DIRECT || con->mode == SMB_BASIC || con->mode == SMB_NTLM) && (con->http_status != 0 && con->http_status != 200)) { /* remove a packets in the queue */ if (con->file_finished == 0) { chunkqueue_reset(con->write_queue); } return HANDLER_FINISHED; } /* no decision yet, build conf->filename */ if ( (con->mode == DIRECT || con->mode == SMB_BASIC || con->mode == SMB_NTLM) && con->physical.path->used == 0) { char *qstr; /* we only come here when we have the parse the full request again * * a HANDLER_COMEBACK from mod_rewrite and mod_fastcgi might be a * problem here as mod_setenv might get called multiple times * * fastcgi-auth might lead to a COMEBACK too * fastcgi again dead server too * * mod_compress might add headers twice too * * */ config_cond_cache_reset(srv, con); config_setup_connection(srv, con); /* Perhaps this could be removed at other places. */ if (con->conf.log_condition_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "run condition"); } config_patch_connection(srv, con, COMP_SERVER_SOCKET); /* SERVERsocket */ /** * prepare strings * * - uri.path_raw * - uri.path (secure) * - uri.query * */ /** * Name according to RFC 2396 * * - scheme * - authority * - path * - query * * (scheme)://(authority)(path)?(query)#fragment * * */ if (con->conf.is_ssl) { buffer_copy_string_len(con->uri.scheme, CONST_STR_LEN("https")); } else { buffer_copy_string_len(con->uri.scheme, CONST_STR_LEN("http")); } buffer_copy_string_buffer(con->uri.authority, con->request.http_host); buffer_to_lower(con->uri.authority); config_patch_connection(srv, con, COMP_HTTP_SCHEME); /* Scheme: */ config_patch_connection(srv, con, COMP_HTTP_HOST); /* Host: */ config_patch_connection(srv, con, COMP_HTTP_REMOTE_IP); /* Client-IP */ config_patch_connection(srv, con, COMP_HTTP_REFERER); /* Referer: */ config_patch_connection(srv, con, COMP_HTTP_USER_AGENT);/* User-Agent: */ config_patch_connection(srv, con, COMP_HTTP_LANGUAGE); /* Accept-Language: */ config_patch_connection(srv, con, COMP_HTTP_COOKIE); /* Cookie: */ config_patch_connection(srv, con, COMP_HTTP_REQUEST_METHOD); /* REQUEST_METHOD */ /** their might be a fragment which has to be cut away */ if (NULL != (qstr = strchr(con->request.uri->ptr, '#'))) { con->request.uri->used = qstr - con->request.uri->ptr; con->request.uri->ptr[con->request.uri->used++] = '\0'; } /** extract query string from request.uri */ if (NULL != (qstr = strchr(con->request.uri->ptr, '?'))) { buffer_copy_string(con->uri.query, qstr + 1); buffer_copy_string_len(con->uri.path_raw, con->request.uri->ptr, qstr - con->request.uri->ptr); } else { buffer_reset(con->uri.query); buffer_copy_string_buffer(con->uri.path_raw, con->request.uri); } if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- splitting Request-URI"); log_error_write(srv, __FILE__, __LINE__, "sb", "Request-URI : ", con->request.uri); log_error_write(srv, __FILE__, __LINE__, "sb", "URI-scheme : ", con->uri.scheme); log_error_write(srv, __FILE__, __LINE__, "sb", "URI-authority: ", con->uri.authority); log_error_write(srv, __FILE__, __LINE__, "sb", "URI-path : ", con->uri.path_raw); log_error_write(srv, __FILE__, __LINE__, "sb", "URI-query : ", con->uri.query); } /** * * call plugins * * - based on the raw URL * */ switch(r = plugins_call_handle_uri_raw(srv, con)) { case HANDLER_GO_ON: break; case HANDLER_FINISHED: case HANDLER_COMEBACK: case HANDLER_WAIT_FOR_EVENT: case HANDLER_ERROR: return r; default: log_error_write(srv, __FILE__, __LINE__, "sd", "handle_uri_raw: unknown return value", r); break; } /* build filename * * - decode url-encodings (e.g. %20 -> ' ') * - remove path-modifiers (e.g. /../) */ if (con->request.http_method == HTTP_METHOD_OPTIONS && con->uri.path_raw->ptr[0] == '*' && con->uri.path_raw->ptr[1] == '\0') { /* OPTIONS * ... */ buffer_copy_string_buffer(con->uri.path, con->uri.path_raw); } else { buffer_copy_string_buffer(srv->tmp_buf, con->uri.path_raw); buffer_urldecode_path(srv->tmp_buf); buffer_path_simplify(con->uri.path, srv->tmp_buf); } if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- sanatising URI"); log_error_write(srv, __FILE__, __LINE__, "sb", "URI-path : ", con->uri.path); } #ifdef USE_OPENSSL if (con->conf.is_ssl && con->conf.ssl_verifyclient) { https_add_ssl_entries(con); } #endif /** * * call plugins * * - based on the clean URL * */ config_patch_connection(srv, con, COMP_HTTP_URL); /* HTTPurl */ config_patch_connection(srv, con, COMP_HTTP_QUERY_STRING); /* HTTPqs */ /* do we have to downgrade to 1.0 ? */ if (!con->conf.allow_http11) { con->request.http_version = HTTP_VERSION_1_0; } switch(r = plugins_call_handle_uri_clean(srv, con)) { case HANDLER_GO_ON: break; case HANDLER_FINISHED: case HANDLER_COMEBACK: case HANDLER_WAIT_FOR_EVENT: case HANDLER_ERROR: return r; default: log_error_write(srv, __FILE__, __LINE__, ""); break; } if (con->request.http_method == HTTP_METHOD_OPTIONS && con->uri.path->ptr[0] == '*' && con->uri.path_raw->ptr[1] == '\0') { /* option requests are handled directly without checking of the path */ response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST")); con->http_status = 200; con->file_finished = 1; return HANDLER_FINISHED; } /*** * * border * * logical filename (URI) becomes a physical filename here * * * */ /* 1. stat() * ... ISREG() -> ok, go on * ... ISDIR() -> index-file -> redirect * * 2. pathinfo() * ... ISREG() * * 3. -> 404 * */ /* * SEARCH DOCUMENT ROOT */ /* set a default */ buffer_copy_string_buffer(con->physical.doc_root, con->conf.document_root); buffer_copy_string_buffer(con->physical.rel_path, con->uri.path); #if defined(__WIN32) || defined(__CYGWIN__) /* strip dots from the end and spaces * * windows/dos handle those filenames as the same file * * foo == foo. == foo..... == "foo... " == "foo.. ./" * * This will affect in some cases PATHINFO * * on native windows we could prepend the filename with \\?\ to circumvent * this behaviour. I have no idea how to push this through cygwin * * */ if (con->physical.rel_path->used > 1) { buffer *b = con->physical.rel_path; size_t i; if (b->used > 2 && b->ptr[b->used-2] == '/' && (b->ptr[b->used-3] == ' ' || b->ptr[b->used-3] == '.')) { b->ptr[b->used--] = '\0'; } for (i = b->used - 2; b->used > 1; i--) { if (b->ptr[i] == ' ' || b->ptr[i] == '.') { b->ptr[b->used--] = '\0'; } else { break; } } } #endif if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- before doc_root"); log_error_write(srv, __FILE__, __LINE__, "sb", "Doc-Root :", con->physical.doc_root); log_error_write(srv, __FILE__, __LINE__, "sb", "Rel-Path :", con->physical.rel_path); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } /* the docroot plugin should set the doc_root and might also set the physical.path * for us (all vhost-plugins are supposed to set the doc_root) * */ switch(r = plugins_call_handle_docroot(srv, con)) { case HANDLER_GO_ON: break; case HANDLER_FINISHED: case HANDLER_COMEBACK: case HANDLER_WAIT_FOR_EVENT: case HANDLER_ERROR: return r; default: log_error_write(srv, __FILE__, __LINE__, ""); break; } /* MacOS X and Windows can't distiguish between upper and lower-case * * convert to lower-case */ if (con->conf.force_lowercase_filenames) { buffer_to_lower(con->physical.rel_path); } /* the docroot plugins might set the servername, if they don't we take http-host */ if (buffer_is_empty(con->server_name)) { buffer_copy_string_buffer(con->server_name, con->uri.authority); } /** * create physical filename * -> physical.path = docroot + rel_path * */ buffer_copy_string_buffer(con->physical.path, con->physical.doc_root); BUFFER_APPEND_SLASH(con->physical.path); buffer_copy_string_buffer(con->physical.basedir, con->physical.path); if (con->physical.rel_path->used && con->physical.rel_path->ptr[0] == '/') { buffer_append_string_len(con->physical.path, con->physical.rel_path->ptr + 1, con->physical.rel_path->used - 2); //buffer_append_string_encoded(con->physical.path, con->physical.rel_path->ptr + 1, con->physical.rel_path->used - 2, ENCODING_REL_URI); //Cdbg(1,"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa %s", con->physical.path->ptr); } else { buffer_append_string_buffer(con->physical.path, con->physical.rel_path); } if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- after doc_root"); log_error_write(srv, __FILE__, __LINE__, "sb", "Doc-Root :", con->physical.doc_root); log_error_write(srv, __FILE__, __LINE__, "sb", "Rel-Path :", con->physical.rel_path); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } switch(r = plugins_call_handle_physical(srv, con)) { case HANDLER_GO_ON: break; case HANDLER_FINISHED: case HANDLER_COMEBACK: case HANDLER_WAIT_FOR_EVENT: case HANDLER_ERROR: return r; default: log_error_write(srv, __FILE__, __LINE__, ""); break; } if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- logical -> physical"); log_error_write(srv, __FILE__, __LINE__, "sb", "Doc-Root :", con->physical.doc_root); log_error_write(srv, __FILE__, __LINE__, "sb", "Rel-Path :", con->physical.rel_path); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } } /* * Noone catched away the file from normal path of execution yet (like mod_access) * * Go on and check of the file exists at all */ if (con->mode == DIRECT || con->mode == SMB_BASIC || con->mode == SMB_NTLM) { char *slash = NULL; char *pathinfo = NULL; int found = 0; stat_cache_entry *sce = NULL; if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- handling physical path"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } if ( HANDLER_ERROR != stat_cache_get_entry(srv, con, smbc_wrapper_physical_url_path(srv, con), &sce)) { /* file exists */ if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- file found"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } #ifdef HAVE_LSTAT if ((sce->is_symlink != 0) && !con->conf.follow_symlink) { con->http_status = 403; if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied due symlink restriction"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } buffer_reset(con->physical.path); return HANDLER_FINISHED; }; #endif if (S_ISDIR(sce->st.st_mode)) { if (con->uri.path->ptr[con->uri.path->used - 2] != '/') { /* redirect to .../ */ http_response_redirect_to_directory(srv, con); return HANDLER_FINISHED; } #ifdef HAVE_LSTAT } else if (!S_ISREG(sce->st.st_mode) && !sce->is_symlink) { #else } else if (!S_ISREG(sce->st.st_mode)) { #endif /* any special handling of non-reg files ?*/ } } else { switch (errno) { case EACCES: con->http_status = 403; if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } buffer_reset(con->physical.path); return HANDLER_FINISHED; case ENOENT: con->http_status = 404; if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- file not found"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } buffer_reset(con->physical.path); return HANDLER_FINISHED; case ENOTDIR: /* PATH_INFO ! :) */ break; default: /* we have no idea what happend. let's tell the user so. */ con->http_status = 500; buffer_reset(con->physical.path); log_error_write(srv, __FILE__, __LINE__, "ssbsb", "file not found ... or so: ", strerror(errno), con->uri.path, "->", con->physical.path); return HANDLER_FINISHED; } /* not found, perhaps PATHINFO */ buffer_copy_string_buffer(srv->tmp_buf, con->physical.path); do { if (slash) { buffer_copy_string_len(con->physical.path, srv->tmp_buf->ptr, slash - srv->tmp_buf->ptr); } else { buffer_copy_string_buffer(con->physical.path, srv->tmp_buf); } if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { found = S_ISREG(sce->st.st_mode); break; } if (pathinfo != NULL) { *pathinfo = '\0'; } slash = strrchr(srv->tmp_buf->ptr, '/'); if (pathinfo != NULL) { /* restore '/' */ *pathinfo = '/'; } if (slash) pathinfo = slash; } while ((found == 0) && (slash != NULL) && ((size_t)(slash - srv->tmp_buf->ptr) > (con->physical.basedir->used - 2))); if (found == 0) { /* no it really doesn't exists */ con->http_status = 404; if (con->conf.log_file_not_found) { log_error_write(srv, __FILE__, __LINE__, "sbsb", "file not found:", con->uri.path, "->", con->physical.path); } buffer_reset(con->physical.path); return HANDLER_FINISHED; } #ifdef HAVE_LSTAT if ((sce->is_symlink != 0) && !con->conf.follow_symlink) { con->http_status = 403; if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied due symlink restriction"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } buffer_reset(con->physical.path); return HANDLER_FINISHED; }; #endif /* we have a PATHINFO */ if (pathinfo) { buffer_copy_string(con->request.pathinfo, pathinfo); /* * shorten uri.path */ con->uri.path->used -= strlen(pathinfo); con->uri.path->ptr[con->uri.path->used - 1] = '\0'; } if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- after pathinfo check"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); log_error_write(srv, __FILE__, __LINE__, "sb", "URI :", con->uri.path); log_error_write(srv, __FILE__, __LINE__, "sb", "Pathinfo :", con->request.pathinfo); } } if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- handling subrequest"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } /* call the handlers */ switch(r = plugins_call_handle_subrequest_start(srv, con)) { case HANDLER_GO_ON: /* request was not handled */ break; case HANDLER_FINISHED: default: if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- subrequest finished"); } /* something strange happend */ return r; } /* if we are still here, no one wanted the file, status 403 is ok I think */ if ((con->mode == DIRECT || con->mode == SMB_BASIC || con->mode == SMB_NTLM) && con->http_status == 0) { switch (con->request.http_method) { case HTTP_METHOD_OPTIONS: con->http_status = 200; break; default: con->http_status = 403; } return HANDLER_FINISHED; } } switch(r = plugins_call_handle_subrequest(srv, con)) { case HANDLER_GO_ON: /* request was not handled, looks like we are done */ return HANDLER_FINISHED; case HANDLER_FINISHED: /* request is finished */ default: /* something strange happend */ return r; } /* can't happen */ return HANDLER_COMEBACK; }
int config_set_defaults(server *srv) { size_t i; specific_config *s = srv->config_storage[0]; struct stat st1, st2; struct ev_map { fdevent_handler_t et; const char *name; } event_handlers[] = { /* - poll is most reliable * - select works everywhere * - linux-* are experimental */ #ifdef USE_POLL { FDEVENT_HANDLER_POLL, "poll" }, #endif #ifdef USE_SELECT { FDEVENT_HANDLER_SELECT, "select" }, #endif #ifdef USE_LINUX_EPOLL { FDEVENT_HANDLER_LINUX_SYSEPOLL, "linux-sysepoll" }, #endif #ifdef USE_LINUX_SIGIO { FDEVENT_HANDLER_LINUX_RTSIG, "linux-rtsig" }, #endif #ifdef USE_SOLARIS_DEVPOLL { FDEVENT_HANDLER_SOLARIS_DEVPOLL,"solaris-devpoll" }, #endif #ifdef USE_FREEBSD_KQUEUE { FDEVENT_HANDLER_FREEBSD_KQUEUE, "freebsd-kqueue" }, { FDEVENT_HANDLER_FREEBSD_KQUEUE, "kqueue" }, #endif { FDEVENT_HANDLER_UNSET, NULL } }; if (buffer_is_empty(s->document_root)) { log_error_write(srv, __FILE__, __LINE__, "s", "a default document-root has to be set"); return -1; } if (buffer_is_empty(srv->srvconf.changeroot)) { if (-1 == stat(s->document_root->ptr, &st1)) { log_error_write(srv, __FILE__, __LINE__, "sb", "base-docroot doesn't exist:", s->document_root); return -1; } } else { buffer_copy_string_buffer(srv->tmp_buf, srv->srvconf.changeroot); buffer_append_string_buffer(srv->tmp_buf, s->document_root); if (-1 == stat(srv->tmp_buf->ptr, &st1)) { log_error_write(srv, __FILE__, __LINE__, "sb", "base-docroot doesn't exist:", srv->tmp_buf); return -1; } } buffer_copy_string_buffer(srv->tmp_buf, s->document_root); buffer_to_lower(srv->tmp_buf); if (0 == stat(srv->tmp_buf->ptr, &st1)) { int is_lower = 0; is_lower = buffer_is_equal(srv->tmp_buf, s->document_root); /* lower-case existed, check upper-case */ buffer_copy_string_buffer(srv->tmp_buf, s->document_root); buffer_to_upper(srv->tmp_buf); /* we have to handle the special case that upper and lower-casing results in the same filename * as in server.document-root = "/" or "/12345/" */ if (is_lower && buffer_is_equal(srv->tmp_buf, s->document_root)) { /* lower-casing and upper-casing didn't result in * an other filename, no need to stat(), * just assume it is case-sensitive. */ s->force_lowercase_filenames = 0; } else if (0 == stat(srv->tmp_buf->ptr, &st2)) { /* upper case exists too, doesn't the FS handle this ? */ /* upper and lower have the same inode -> case-insensitve FS */ if (st1.st_ino == st2.st_ino) { /* upper and lower have the same inode -> case-insensitve FS */ s->force_lowercase_filenames = 1; } } } if (srv->srvconf.port == 0) { srv->srvconf.port = s->is_ssl ? 443 : 80; } if (srv->srvconf.event_handler->used == 0) { /* choose a good default * * the event_handler list is sorted by 'goodness' * taking the first available should be the best solution */ srv->event_handler = event_handlers[0].et; if (FDEVENT_HANDLER_UNSET == srv->event_handler) { log_error_write(srv, __FILE__, __LINE__, "s", "sorry, there is no event handler for this system"); return -1; } } else { /* * User override */ for (i = 0; event_handlers[i].name; i++) { if (0 == strcmp(event_handlers[i].name, srv->srvconf.event_handler->ptr)) { srv->event_handler = event_handlers[i].et; break; } } if (FDEVENT_HANDLER_UNSET == srv->event_handler) { log_error_write(srv, __FILE__, __LINE__, "sb", "the selected event-handler in unknown or not supported:", srv->srvconf.event_handler ); return -1; } } if (s->is_ssl) { if (buffer_is_empty(s->ssl_pemfile)) { /* PEM file is require */ log_error_write(srv, __FILE__, __LINE__, "s", "ssl.pemfile has to be set"); return -1; } #ifndef USE_OPENSSL log_error_write(srv, __FILE__, __LINE__, "s", "ssl support is missing, recompile with --with-openssl"); return -1; #endif } return 0; }
static int network_ssl_servername_callback(SSL *ssl, int *al, server *srv) { const char *servername; connection *con = (connection *) SSL_get_app_data(ssl); UNUSED(al); buffer_copy_string(con->uri.scheme, "https"); if (NULL == (servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) { #if 0 /* this "error" just means the client didn't support it */ log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", "failed to get TLS server name"); #endif return SSL_TLSEXT_ERR_NOACK; } buffer_copy_string(con->tlsext_server_name, servername); buffer_to_lower(con->tlsext_server_name); /* Sometimes this is still set, confusing COMP_HTTP_HOST */ buffer_reset(con->uri.authority); config_cond_cache_reset(srv, con); config_setup_connection(srv, con); con->conditional_is_valid[COMP_SERVER_SOCKET] = 1; con->conditional_is_valid[COMP_HTTP_SCHEME] = 1; con->conditional_is_valid[COMP_HTTP_HOST] = 1; config_patch_connection(srv, con); if (NULL == con->conf.ssl_pemfile_x509 || NULL == con->conf.ssl_pemfile_pkey) { /* x509/pkey available <=> pemfile was set <=> pemfile got patched: so this should never happen, unless you nest $SERVER["socket"] */ log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", "no certificate/private key for TLS server name", con->tlsext_server_name); return SSL_TLSEXT_ERR_ALERT_FATAL; } /* first set certificate! setting private key checks whether certificate matches it */ if (!SSL_use_certificate(ssl, con->conf.ssl_pemfile_x509)) { log_error_write(srv, __FILE__, __LINE__, "ssb:s", "SSL:", "failed to set certificate for TLS server name", con->tlsext_server_name, ERR_error_string(ERR_get_error(), NULL)); return SSL_TLSEXT_ERR_ALERT_FATAL; } if (!SSL_use_PrivateKey(ssl, con->conf.ssl_pemfile_pkey)) { log_error_write(srv, __FILE__, __LINE__, "ssb:s", "SSL:", "failed to set private key for TLS server name", con->tlsext_server_name, ERR_error_string(ERR_get_error(), NULL)); return SSL_TLSEXT_ERR_ALERT_FATAL; } if (con->conf.ssl_verifyclient) { if (NULL == con->conf.ssl_ca_file_cert_names) { log_error_write(srv, __FILE__, __LINE__, "ssb:s", "SSL:", "can't verify client without ssl.ca-file for TLS server name", con->tlsext_server_name, ERR_error_string(ERR_get_error(), NULL)); return SSL_TLSEXT_ERR_ALERT_FATAL; } SSL_set_client_CA_list(ssl, SSL_dup_CA_list(con->conf.ssl_ca_file_cert_names)); /* forcing verification here is really not that useful - a client could just connect without SNI */ SSL_set_verify( ssl, SSL_VERIFY_PEER | (con->conf.ssl_verifyclient_enforce ? SSL_VERIFY_FAIL_IF_NO_PEER_CERT : 0), NULL ); SSL_set_verify_depth(ssl, con->conf.ssl_verifyclient_depth); } else { SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); } return SSL_TLSEXT_ERR_OK; }
int config_set_defaults(server *srv) { specific_config *s = &srv->config_storage[0]; #if 0 size_t i; struct stat st1, st2; #endif if (buffer_is_empty(s->document_root)) { log_error_write(srv, __FILE__, __LINE__, "s", "a default document-root has to be set"); return -1; } #if 0 buffer_copy_string_buffer(srv->tmp_buf, s->document_root); buffer_to_lower(srv->tmp_buf); if (0 == stat(srv->tmp_buf->ptr, &st1)) { int is_lower = 0; is_lower = buffer_is_equal(srv->tmp_buf, s->document_root); /* lower-case existed, check upper-case */ buffer_copy_string_buffer(srv->tmp_buf, s->document_root); buffer_to_upper(srv->tmp_buf); /* we have to handle the special case that upper and lower-casing results in the same filename * as in server.document-root = "/" or "/12345/" */ if (is_lower && buffer_is_equal(srv->tmp_buf, s->document_root)) { /* lower-casing and upper-casing didn't result in * an other filename, no need to stat(), * just assume it is case-sensitive. */ s->force_lowercase_filenames = 0; } else if (0 == stat(srv->tmp_buf->ptr, &st2)) { /* upper case exists too, doesn't the FS handle this ? */ /* upper and lower have the same inode -> case-insensitve FS */ if (st1.st_ino == st2.st_ino) { /* upper and lower have the same inode -> case-insensitve FS */ s->force_lowercase_filenames = 1; } } } #endif if (srv->srvconf.port == 0) { srv->srvconf.port = s->is_ssl ? 322 : 554; } if (srv->srvconf.max_conns == 0) srv->srvconf.max_conns = 100; if (srv->srvconf.first_udp_port == 0) srv->srvconf.first_udp_port = RTP_DEFAULT_PORT; if (srv->srvconf.buffered_frames == 0) srv->srvconf.buffered_frames = BUFFERED_FRAMES_DEFAULT; if (s->is_ssl) { if (buffer_is_empty(s->ssl_pemfile)) { /* PEM file is require */ log_error_write(srv, __FILE__, __LINE__, "s", "ssl.pemfile has to be set"); return -1; } #ifndef USE_OPENSSL log_error_write(srv, __FILE__, __LINE__, "s", "ssl support is missing, recompile with --with-openssl"); return -1; #endif } return 0; }
int config_set_defaults(server *srv) { specific_config *s = srv->config_storage[0]; const fdevent_handler_info_t *handler; const network_backend_info_t *backend; struct stat st1, st2; if (buffer_is_empty(s->document_root)) { log_error_write(srv, __FILE__, __LINE__, "s", "a default document-root has to be set"); return -1; } if (buffer_is_empty(srv->srvconf.changeroot)) { pathname_unix2local(s->document_root); if (-1 == stat(s->document_root->ptr, &st1)) { log_error_write(srv, __FILE__, __LINE__, "sbs", "base-docroot doesn't exist:", s->document_root, strerror(errno)); return -1; } } else { buffer_copy_string_buffer(srv->tmp_buf, srv->srvconf.changeroot); buffer_append_string_buffer(srv->tmp_buf, s->document_root); if (-1 == stat(srv->tmp_buf->ptr, &st1)) { log_error_write(srv, __FILE__, __LINE__, "sb", "base-docroot doesn't exist:", srv->tmp_buf); return -1; } } buffer_copy_string_buffer(srv->tmp_buf, s->document_root); buffer_to_lower(srv->tmp_buf); if (0 == stat(srv->tmp_buf->ptr, &st1)) { int is_lower = 0; is_lower = buffer_is_equal(srv->tmp_buf, s->document_root); /* lower-case existed, check upper-case */ buffer_copy_string_buffer(srv->tmp_buf, s->document_root); buffer_to_upper(srv->tmp_buf); /* we have to handle the special case that upper and lower-casing results in the same filename * as in server.document-root = "/" or "/12345/" */ if (is_lower && buffer_is_equal(srv->tmp_buf, s->document_root)) { /* lower-casing and upper-casing didn't result in * an other filename, no need to stat(), * just assume it is case-sensitive. */ s->force_lowercase_filenames = 0; } else if (0 == stat(srv->tmp_buf->ptr, &st2)) { /* upper case exists too, doesn't the FS handle this ? */ /* upper and lower have the same inode -> case-insensitve FS */ if (st1.st_ino == st2.st_ino) { /* upper and lower have the same inode -> case-insensitve FS */ s->force_lowercase_filenames = 1; } } } if (srv->srvconf.port == 0) { srv->srvconf.port = s->is_ssl ? 443 : 80; } if (srv->srvconf.event_handler->used == 0) { /* get a useful default */ handler = fdevent_get_defaulthandler(); if (!handler) { log_error_write(srv, __FILE__, __LINE__, "s", "sorry, there is no event handler for this system"); return -1; } } else { /* * User override */ handler = fdevent_get_handler_info_by_name(srv->srvconf.event_handler->ptr); if (!handler) { log_error_write(srv, __FILE__, __LINE__, "sb", "the selected event-handler is unknown:", srv->srvconf.event_handler ); return -1; } if (!handler->init) { log_error_write(srv, __FILE__, __LINE__, "sb", "the selected event-handler is known but not supported:", srv->srvconf.event_handler ); return -1; } } srv->event_handler = handler->type; if (buffer_is_empty(srv->srvconf.network_backend)) { /* get a useful default */ backend = network_get_defaultbackend(); if (!backend) { log_error_write(srv, __FILE__, __LINE__, "s", "sorry, there is no network backend for this system"); return -1; } } else { /* * User override */ backend = network_get_backend_info_by_name(srv->srvconf.network_backend->ptr); if (!backend) { /* we don't know it */ log_error_write(srv, __FILE__, __LINE__, "sb", "server.network-backend has a unknown value:", srv->srvconf.network_backend); return -1; } if (backend->write_handler == NULL) { /* we know it but not supported */ log_error_write(srv, __FILE__, __LINE__, "sb", "server.network-backend not supported:", srv->srvconf.network_backend); return -1; } } srv->network_backend = backend->type; if (s->is_ssl) { if (buffer_is_empty(s->ssl_pemfile)) { /* PEM file is require */ log_error_write(srv, __FILE__, __LINE__, "s", "ssl.pemfile has to be set"); return -1; } #ifndef USE_OPENSSL log_error_write(srv, __FILE__, __LINE__, "s", "ssl support is missing, recompile with --with-openssl"); return -1; #endif } return 0; }