diff --git a/CHANGELOG b/CHANGELOG index b87f4dc..4817390 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,15 @@ +------------------------------------------------------------------------ +r83 | roseg | 2018-05-11 12:16:05 +0200 (Fri, 11 May 2018) | 9 lines + +Release 2.8 + +Enhancements: + - removed DynScale flag and support + - removed support for multi-line headers (both input and output) + +Bug fixes: + - fixed potential request smuggling via fudged headers + ------------------------------------------------------------------------ r82 | roseg | 2016-10-23 16:59:47 +0200 (Sun, 23 Oct 2016) | 8 lines diff --git a/FAQ b/FAQ old mode 100755 new mode 100644 diff --git a/GPL.txt b/GPL.txt old mode 100755 new mode 100644 diff --git a/Makefile.in b/Makefile.in index a5af21c..7b12f3b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -27,7 +27,7 @@ CC=@PTHREAD_CC@ CFLAGS=-DF_CONF=\"@sysconfdir@/pound.cfg\" -DVERSION=\"@PACKAGE_VERSION@\" -DC_SSL=\"@C_SSL@\" -DC_T_RSA=\"@C_T_RSA@\" \ -DC_DH_LEN=\"@C_DH_LEN@\" -DC_MAXBUF=\"@C_MAXBUF@\" -DC_OWNER=\"@C_OWNER@\" -DC_GROUP=\"@C_GROUP@\" \ - -DC_SUPER=\"@C_SUPER@\" -DC_CERT1L=\"@C_CERT1L@\" @CFLAGS@ @PTHREAD_CFLAGS@ @CPPFLAGS@ + -DC_SUPER=\"@C_SUPER@\" @CFLAGS@ @PTHREAD_CFLAGS@ @CPPFLAGS@ LIBS=@LIBS@ @PTHREAD_LIBS@ prefix=@prefix@ @@ -37,7 +37,7 @@ exec_prefix=@exec_prefix@ OBJS=pound.o http.o config.o svc.o -all: pound poundctl pound.8 +all: pound poundctl pound.8 dh2048.pem pound: $(OBJS) ${CC} @LDFLAGS@ -o pound $(OBJS) $(LIBS) @@ -54,6 +54,9 @@ dh@C_DH_LEN@.h: svc.o: svc.c dh512.h dh@C_DH_LEN@.h ${CC} ${CFLAGS} -c -o svc.o svc.c +dh2048.pem: + openssl dhparam -5 -out dh2048.pem 2048 + $(OBJS) poundctl.o: pound.h config.h install: all diff --git a/README b/README old mode 100755 new mode 100644 diff --git a/config.c b/config.c index d41a3ee..608d9ea 100644 --- a/config.c +++ b/config.c @@ -74,22 +74,33 @@ static CODE facilitynames[] = { #endif static regex_t Empty, Comment, User, Group, RootJail, Daemon, LogFacility, LogLevel, Alive, SSLEngine, Control; -static regex_t ListenHTTP, ListenHTTPS, End, Address, Port, Cert, xHTTP, Client, CheckURL; -static regex_t Err414, Err500, Err501, Err503, MaxRequest, HeadRemove, RewriteLocation, RewriteDestination; -static regex_t Service, ServiceName, URL, HeadRequire, HeadDeny, BackEnd, Emergency, Priority, HAport, HAportAddr; -static regex_t Redirect, RedirectN, TimeOut, Session, Type, TTL, ID; +static regex_t ListenHTTP, ListenHTTPS, End, Address, Port, Cert, CertDir, xHTTP, Client, CheckURL; +static regex_t Err414, Err500, Err501, Err503, ErrNoSsl, NoSslRedirect, MaxRequest, HeadRemove, RewriteLocation, RewriteDestination; +static regex_t Service, ServiceName, URL, OrURLs, HeadRequire, HeadDeny, BackEnd, Emergency, Priority, HAport, HAportAddr; +static regex_t Redirect, TimeOut, Session, Type, TTL, ID; static regex_t ClientCert, AddHeader, DisableProto, SSLAllowClientRenegotiation, SSLHonorCipherOrder, Ciphers; static regex_t CAlist, VerifyList, CRLlist, NoHTTPS11, Grace, Include, ConnTO, IgnoreCase, HTTPS; -static regex_t Disabled, Threads, CNName, Anonymise, ECDHCurve; +static regex_t Disabled, Threads, CNName, Anonymise, DHParams, ECDHCurve; + +static regex_t ControlGroup, ControlUser, ControlMode; +static regex_t ThreadModel; + +static regex_t IncludeDir; + +static regex_t ForceHTTP10, SSLUncleanShutdown; + +static regex_t BackendKey, BackendCookie; static regmatch_t matches[5]; +static DH *DHCustom_params; + static char *xhttp[] = { "^(GET|POST|HEAD) ([^ ]+) HTTP/1.[01]$", "^(GET|POST|HEAD|PUT|PATCH|DELETE) ([^ ]+) HTTP/1.[01]$", - "^(GET|POST|HEAD|PUT|PATCH|DELETE|LOCK|UNLOCK|PROPFIND|PROPPATCH|SEARCH|MKCOL|MOVE|COPY|OPTIONS|TRACE|MKACTIVITY|CHECKOUT|MERGE|REPORT) ([^ ]+) HTTP/1.[01]$", - "^(GET|POST|HEAD|PUT|PATCH|DELETE|LOCK|UNLOCK|PROPFIND|PROPPATCH|SEARCH|MKCOL|MOVE|COPY|OPTIONS|TRACE|MKACTIVITY|CHECKOUT|MERGE|REPORT|SUBSCRIBE|UNSUBSCRIBE|BPROPPATCH|POLL|BMOVE|BCOPY|BDELETE|BPROPFIND|NOTIFY|CONNECT) ([^ ]+) HTTP/1.[01]$", - "^(GET|POST|HEAD|PUT|PATCH|DELETE|LOCK|UNLOCK|PROPFIND|PROPPATCH|SEARCH|MKCOL|MOVE|COPY|OPTIONS|TRACE|MKACTIVITY|CHECKOUT|MERGE|REPORT|SUBSCRIBE|UNSUBSCRIBE|BPROPPATCH|POLL|BMOVE|BCOPY|BDELETE|BPROPFIND|NOTIFY|CONNECT|RPC_IN_DATA|RPC_OUT_DATA) ([^ ]+) HTTP/1.[01]$", + "^(GET|POST|HEAD|PUT|PATCH|DELETE|LOCK|UNLOCK|PROPFIND|PROPPATCH|SEARCH|MKCOL|MKCALENDAR|MOVE|COPY|OPTIONS|TRACE|MKACTIVITY|CHECKOUT|MERGE|REPORT) ([^ ]+) HTTP/1.[01]$", + "^(GET|POST|HEAD|PUT|PATCH|DELETE|LOCK|UNLOCK|PROPFIND|PROPPATCH|SEARCH|MKCOL|MKCALENDAR|MOVE|COPY|OPTIONS|TRACE|MKACTIVITY|CHECKOUT|MERGE|REPORT|SUBSCRIBE|UNSUBSCRIBE|BPROPPATCH|POLL|BMOVE|BCOPY|BDELETE|BPROPFIND|NOTIFY|CONNECT) ([^ ]+) HTTP/1.[01]$", + "^(GET|POST|HEAD|PUT|PATCH|DELETE|LOCK|UNLOCK|PROPFIND|PROPPATCH|SEARCH|MKCOL|MKCALENDAR|MOVE|COPY|OPTIONS|TRACE|MKACTIVITY|CHECKOUT|MERGE|REPORT|SUBSCRIBE|UNSUBSCRIBE|BPROPPATCH|POLL|BMOVE|BCOPY|BDELETE|BPROPFIND|NOTIFY|CONNECT|RPC_IN_DATA|RPC_OUT_DATA) ([^ ]+) HTTP/1.[01]$", }; static int log_level = 1; @@ -104,13 +115,16 @@ static int EC_nid = NID_X9_62_prime256v1; #endif #endif -#define MAX_FIN 8 +#define MAX_FIN 100 static FILE *f_in[MAX_FIN]; static char *f_name[MAX_FIN]; static int n_lin[MAX_FIN]; static int cur_fin; +static void load_cert(int has_other, LISTENER *res, char *filename); +static void load_certdir(int has_other, LISTENER *res, const char *dir_path); + static int conf_init(const char *name) { @@ -134,6 +148,63 @@ conf_err(const char *msg) exit(1); } +static void include_dir(const char *conf_path) { + DIR * dp; + struct dirent *de; + + char buf[512]; + char *files[200], *cp; + int filecnt = 0; + int idx,use; + + logmsg(LOG_DEBUG, "Including Dir %s", conf_path); + + if((dp = opendir(conf_path)) == NULL) { + conf_err("can't open IncludeDir directory"); + exit(1); + } + + while((de = readdir(dp))!=NULL) { + if (de->d_name[0] == '.') continue; + if ( (strlen(de->d_name) >= 5 && !strncmp(de->d_name + strlen(de->d_name) - 4, ".cfg", 4)) || + (strlen(de->d_name) >= 6 && !strncmp(de->d_name + strlen(de->d_name) - 5, ".conf", 5)) + ){ + snprintf(buf, sizeof(buf), "%s%s%s", conf_path, (conf_path[strlen(conf_path)-1]=='/')?"":"/", de->d_name); + buf[sizeof(buf)-1] = 0; + if (filecnt == sizeof(files)/sizeof(*files)) { + conf_err("Max config files per directory reached"); + } + if ((files[filecnt++] = strdup(buf)) == NULL) { + conf_err("IncludeDir out of memory"); + } + continue; + } + } + /* We order the list, and include in reverse order, because include_file adds to the top of the list */ + while(filecnt) { + use = 0; + for(idx = 1; idx %s", files[use]); + + // Copied from Include logic + if(cur_fin == (MAX_FIN - 1)) + conf_err("Include nesting too deep"); + cur_fin++; + f_name[cur_fin] = files[use]; + if((f_in[cur_fin] = fopen(files[use], "rt")) == NULL) { + logmsg(LOG_ERR, "%s line %d: Can't open included file %s", f_name[cur_fin], n_lin[cur_fin], files[use]); + exit(1); + } + n_lin[cur_fin] = 0; + files[use] = files[--filecnt]; + } + + closedir(dp); +} + static char * conf_fgets(char *buf, const int max) { @@ -170,6 +241,11 @@ conf_fgets(char *buf, const int max) n_lin[cur_fin] = 0; continue; } + if(!regexec(&IncludeDir, buf, 4, matches, 0)) { + buf[matches[1].rm_eo] = '\0'; + include_dir(buf + matches[1].rm_so); + continue; + } return buf; } } @@ -229,6 +305,7 @@ static BACKEND * parse_be(const int is_emergency) { char lin[MAXBUF]; + char *cp; BACKEND *res; int has_addr, has_port; struct hostent *host; @@ -247,6 +324,7 @@ parse_be(const int is_emergency) res->priority = 5; memset(&res->ha_addr, 0, sizeof(res->ha_addr)); res->url = NULL; + res->bekey = NULL; res->next = NULL; has_addr = has_port = 0; pthread_mutex_init(&res->mut, NULL); @@ -286,6 +364,10 @@ parse_be(const int is_emergency) conf_err("Port is supported only for INET/INET6 back-ends"); } has_port = 1; + } else if(!regexec(&BackendKey, lin, 4, matches, 0)) { + lin[matches[1].rm_eo] = '\0'; + if ((res->bekey = strdup(lin + matches[1].rm_so))==NULL) + conf_err("out of memory"); } else if(!regexec(&Priority, lin, 4, matches, 0)) { if(is_emergency) conf_err("Priority is not supported for Emergency back-ends"); @@ -359,7 +441,11 @@ parse_be(const int is_emergency) sprintf(lin, "%d-Pound-%ld", getpid(), random()); SSL_CTX_set_session_id_context(res->ctx, (unsigned char *)lin, strlen(lin)); SSL_CTX_set_tmp_rsa_callback(res->ctx, RSA_tmp_callback); - SSL_CTX_set_tmp_dh_callback(res->ctx, DH_tmp_callback); + if (NULL == DHCustom_params) + SSL_CTX_set_tmp_dh_callback(res->ctx, DH_tmp_callback); + else + SSL_CTX_set_tmp_dh(res->ctx, DHCustom_params); + #if OPENSSL_VERSION_NUMBER >= 0x0090800fL #ifndef OPENSSL_NO_ECDH /* This generates a EC_KEY structure with no key, but a group defined */ @@ -413,6 +499,18 @@ parse_be(const int is_emergency) conf_err("BackEnd missing Address - aborted"); if((res->addr.ai_family == AF_INET || res->addr.ai_family == AF_INET6) && !has_port) conf_err("BackEnd missing Port - aborted"); + if(!res->bekey) { + if (res->addr.ai_family == AF_INET) + snprintf(lin, MAXBUF-1, "4-%08x-%x",htonl(((struct sockaddr_in *)(res->addr.ai_addr))->sin_addr.s_addr), htons(((struct sockaddr_in *)(res->addr.ai_addr))->sin_port)); + else if (res->addr.ai_family == AF_INET6) { + cp = (char*) &(((struct sockaddr_in6 *)(res->addr.ai_addr))->sin6_addr); + snprintf(lin, MAXBUF-1, "6-%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x-%x",cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7],cp[8],cp[9],cp[10],cp[11],cp[12],cp[13],cp[14],cp[15],htons(((struct sockaddr_in6 *)(res->addr.ai_addr))->sin6_port)); + } else + conf_err("cannot autogenerate backendkey, please specify one"); + + if ((res->bekey = strdup(lin))==NULL) + conf_err("out of memory autogenerating backendkey"); + } return res; } else { conf_err("unknown directive"); @@ -545,6 +643,56 @@ static IMPLEMENT_LHASH_COMP_FN(t, TABNODE) static IMPLEMENT_LHASH_COMP_FN(t_cmp, const TABNODE *) #endif +/* + * parse an OrURLs block + * + * Forms a composite pattern of all URLs within + * of the form ((url1)|(url2)|(url3)) (and so on) + */ +static char * +parse_orurls(void) +{ + char lin[MAXBUF]; + char *pattern; + regex_t comp; + + pattern = NULL; + while(conf_fgets(lin, MAXBUF)) { + if(strlen(lin) > 0 && lin[strlen(lin) - 1] == '\n') + lin[strlen(lin) - 1] = '\0'; + if(!regexec(&URL, lin, 4, matches, 0)) { + lin[matches[1].rm_eo] = '\0'; + /* Verify the pattern is valid */ + if(regcomp(&comp, lin + matches[1].rm_so, REG_NEWLINE | REG_EXTENDED )) + conf_err("URL bad pattern - aborted"); + regfree(&comp); + if (pattern==NULL) { + if((pattern = (char *)malloc(strlen(lin + matches[1].rm_so)+5)) == NULL) + conf_err("OrURLs config: out of memory - aborted"); + *pattern = 0; + strcat(pattern, "(("); + strcat(pattern, lin + matches[1].rm_so); + strcat(pattern, "))"); + } else { + if((pattern = (char *)realloc(pattern, strlen(pattern) + strlen(lin + matches[1].rm_so) + 4)) == NULL) + conf_err("OrURLs config: out of memory - aborted"); + pattern[strlen(pattern)-1]=0; + strcat(pattern, "|("); + strcat(pattern, lin + matches[1].rm_so); + strcat(pattern, "))"); + } + } else if(!regexec(&End, lin, 4, matches, 0)) { + if (!pattern) + conf_err("No URL directives specified within OrURLs block"); + return pattern; + } else { + conf_err("unknown directive"); + } + } + + conf_err("OrURLs premature EOF"); + return NULL; +} /* * parse a service @@ -553,6 +701,8 @@ static SERVICE * parse_service(const char *svc_name) { char lin[MAXBUF]; + char pat[MAXBUF]; + char *ptr; SERVICE *res; BACKEND *be; MATCHER *m; @@ -591,6 +741,23 @@ parse_service(const char *svc_name) lin[matches[1].rm_eo] = '\0'; if(regcomp(&m->pat, lin + matches[1].rm_so, REG_NEWLINE | REG_EXTENDED | (ign_case? REG_ICASE: 0))) conf_err("URL bad pattern - aborted"); + } else if(!regexec(&OrURLs, lin, 4, matches, 0)) { + if(res->url) { + for(m = res->url; m->next; m = m->next) + ; + if((m->next = (MATCHER *)malloc(sizeof(MATCHER))) == NULL) + conf_err("URL config: out of memory - aborted"); + m = m->next; + } else { + if((res->url = (MATCHER *)malloc(sizeof(MATCHER))) == NULL) + conf_err("URL config: out of memory - aborted"); + m = res->url; + } + memset(m, 0, sizeof(MATCHER)); + ptr = parse_orurls(); + if(regcomp(&m->pat, ptr, REG_NEWLINE | REG_EXTENDED | (ign_case? REG_ICASE: 0))) + conf_err("OrURLs bad pattern - aborted"); + free(ptr); } else if(!regexec(&HeadRequire, lin, 4, matches, 0)) { if(res->req_head) { for(m = res->req_head; m->next; m = m->next) @@ -635,44 +802,30 @@ parse_service(const char *svc_name) conf_err("Redirect config: out of memory - aborted"); be = res->backends; } + // 1 - Dynamic or not, 2 - Request Redirect #, 3 - Destination URL memset(be, 0, sizeof(BACKEND)); be->be_type = 302; - be->priority = 1; - be->alive = 1; - pthread_mutex_init(& be->mut, NULL); - lin[matches[1].rm_eo] = '\0'; - if((be->url = strdup(lin + matches[1].rm_so)) == NULL) - conf_err("Redirector config: out of memory - aborted"); - /* split the URL into its fields */ - if(regexec(&LOCATION, be->url, 4, matches, 0)) - conf_err("Redirect bad URL - aborted"); - if((be->redir_req = matches[3].rm_eo - matches[3].rm_so) == 1) - /* the path is a single '/', so remove it */ - be->url[matches[3].rm_so] = '\0'; - } else if(!regexec(&RedirectN, lin, 4, matches, 0)) { - if(res->backends) { - for(be = res->backends; be->next; be = be->next) - ; - if((be->next = (BACKEND *)malloc(sizeof(BACKEND))) == NULL) - conf_err("Redirect config: out of memory - aborted"); - be = be->next; - } else { - if((res->backends = (BACKEND *)malloc(sizeof(BACKEND))) == NULL) - conf_err("Redirect config: out of memory - aborted"); - be = res->backends; + be->redir_req = 0; + if (matches[1].rm_eo != matches[1].rm_so) { + if((lin[matches[1].rm_so] & ~0x20)=='D') { + be->redir_req = 2; + if(!res->url || res->url->next) + conf_err("Dynamic Redirect must be preceeded by a URL line"); + } else if((lin[matches[1].rm_so] & ~0x20)=='A') + be->redir_req = 1; } - memset(be, 0, sizeof(BACKEND)); - be->be_type = atoi(lin + matches[1].rm_so); + if (matches[2].rm_eo != matches[2].rm_so) + be->be_type = atoi(lin + matches[2].rm_so); be->priority = 1; be->alive = 1; - pthread_mutex_init(& be->mut, NULL); - lin[matches[2].rm_eo] = '\0'; - if((be->url = strdup(lin + matches[2].rm_so)) == NULL) + pthread_mutex_init(&be->mut, NULL); + lin[matches[3].rm_eo] = '\0'; + if((be->url = strdup(lin + matches[3].rm_so)) == NULL) conf_err("Redirector config: out of memory - aborted"); /* split the URL into its fields */ if(regexec(&LOCATION, be->url, 4, matches, 0)) conf_err("Redirect bad URL - aborted"); - if((be->redir_req = matches[3].rm_eo - matches[3].rm_so) == 1) + if((matches[3].rm_eo - matches[3].rm_so) == 1) /* the path is a single '/', so remove it */ be->url[matches[3].rm_so] = '\0'; } else if(!regexec(&BackEnd, lin, 4, matches, 0)) { @@ -684,6 +837,27 @@ parse_service(const char *svc_name) res->backends = parse_be(0); } else if(!regexec(&Emergency, lin, 4, matches, 0)) { res->emergency = parse_be(1); + } else if(!regexec(&BackendCookie, lin, 5, matches, 0)) { + lin[matches[1].rm_eo] = '\0'; + lin[matches[2].rm_eo] = '\0'; + lin[matches[3].rm_eo] = '\0'; + lin[matches[4].rm_eo] = '\0'; + snprintf(pat, MAXBUF - 1, "Cookie[^:]*:.*[; \t]%s=\"?([^\";]*)\"?", lin + matches[1].rm_so); + if(matches[1].rm_so==matches[1].rm_eo) + conf_err("Backend cookie must have a name"); + if((res->becookie=strdup(lin+matches[1].rm_so))==NULL) + conf_err("out of memory"); + if(regcomp(&res->becookie_re, pat, REG_ICASE | REG_NEWLINE | REG_EXTENDED)) + conf_err("Backend Cookie pattern failed - aborted"); + if(matches[2].rm_so!=matches[2].rm_eo && (res->becdomain=strdup(lin+matches[2].rm_so))==NULL) + conf_err("out of memory"); + if(matches[3].rm_so!=matches[3].rm_eo && (res->becpath=strdup(lin+matches[3].rm_so))==NULL) + conf_err("out of memory"); + res->becage = atoi(lin+matches[4].rm_so); + if ((lin[matches[4].rm_so]&~0x20)=='S') + res->becage = -1; + else + res->becage = atoi(lin+matches[4].rm_so); } else if(!regexec(&Session, lin, 4, matches, 0)) { parse_sess(res); } else if(!regexec(&IgnoreCase, lin, 4, matches, 0)) { @@ -752,6 +926,9 @@ parse_HTTP(void) res->err500 = "An internal server error occurred. Please try again later."; res->err501 = "This method may not be used."; res->err503 = "The service is not available. Please try again later."; + res->errnossl= "Please use HTTPS."; + res->nossl_url = NULL; + res->nossl_redir = 0; res->log_level = log_level; if(regcomp(&res->verb, xhttp[0], REG_ICASE | REG_NEWLINE | REG_EXTENDED)) conf_err("xHTTP bad default pattern - aborted"); @@ -846,6 +1023,15 @@ parse_HTTP(void) res->rewr_dest = atoi(lin + matches[1].rm_so); } else if(!regexec(&LogLevel, lin, 4, matches, 0)) { res->log_level = atoi(lin + matches[1].rm_so); + } else if(!regexec(&ForceHTTP10, lin, 4, matches, 0)) { + if((m = (MATCHER *)malloc(sizeof(MATCHER))) == NULL) + conf_err("out of memory"); + memset(m, 0, sizeof(MATCHER)); + m->next = res->forcehttp10; + res->forcehttp10 = m; + lin[matches[1].rm_eo] = '\0'; + if(regcomp(&m->pat, lin + matches[1].rm_so, REG_ICASE | REG_NEWLINE | REG_EXTENDED)) + conf_err("ForceHTTP10 bad pattern"); } else if(!regexec(&Service, lin, 4, matches, 0)) { if(res->services == NULL) res->services = parse_service(NULL); @@ -956,6 +1142,9 @@ parse_HTTPS(void) res->err501 = "This method may not be used."; res->err503 = "The service is not available. Please try again later."; res->allow_client_reneg = 0; + res->errnossl = "Please use HTTPS."; + res->nossl_url = NULL; + res->nossl_redir = 0; res->log_level = log_level; if(regcomp(&res->verb, xhttp[0], REG_ICASE | REG_NEWLINE | REG_EXTENDED)) conf_err("xHTTP bad default pattern - aborted"); @@ -1009,6 +1198,21 @@ parse_HTTPS(void) } else if(!regexec(&Err503, lin, 4, matches, 0)) { lin[matches[1].rm_eo] = '\0'; res->err503 = file2str(lin + matches[1].rm_so); + } else if(!regexec(&ErrNoSsl, lin, 4, matches, 0)) { + lin[matches[1].rm_eo] = '\0'; + res->errnossl = file2str(lin + matches[1].rm_so); + } else if(!regexec(&NoSslRedirect, lin, 4, matches, 0)) { + res->nossl_redir = 302; + if (matches[1].rm_eo != matches[1].rm_so) + res->nossl_redir = atoi(lin + matches[1].rm_so); + lin[matches[2].rm_eo] = '\0'; + if((res->nossl_url = strdup(lin + matches[2].rm_so))==NULL) + conf_err("NoSslRedirect out of memory"); + if(regexec(&LOCATION, res->nossl_url, 4, matches, 0)) + conf_err("Redirect bad URL - aborted"); + if((matches[3].rm_eo - matches[3].rm_so) == 1) + /* the path is a single '/', so remove it */ + res->nossl_url[matches[3].rm_so] = '\0'; } else if(!regexec(&MaxRequest, lin, 4, matches, 0)) { res->max_req = ATOL(lin + matches[1].rm_so); } else if(!regexec(&HeadRemove, lin, 4, matches, 0)) { @@ -1034,73 +1238,11 @@ parse_HTTPS(void) } else if(!regexec(&LogLevel, lin, 4, matches, 0)) { res->log_level = atoi(lin + matches[1].rm_so); } else if(!regexec(&Cert, lin, 4, matches, 0)) { -#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB - /* we have support for SNI */ - FILE *fcert; - char server_name[MAXBUF], *cp; - X509 *x509; - - if(has_other) - conf_err("Cert directives MUST precede other SSL-specific directives - aborted"); - if(res->ctx) { - for(pc = res->ctx; pc->next; pc = pc->next) - ; - if((pc->next = malloc(sizeof(POUND_CTX))) == NULL) - conf_err("ListenHTTPS new POUND_CTX: out of memory - aborted"); - pc = pc->next; - } else { - if((res->ctx = malloc(sizeof(POUND_CTX))) == NULL) - conf_err("ListenHTTPS new POUND_CTX: out of memory - aborted"); - pc = res->ctx; - } - if((pc->ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) - conf_err("SSL_CTX_new failed - aborted"); - pc->server_name = NULL; - pc->next = NULL; lin[matches[1].rm_eo] = '\0'; - if(SSL_CTX_use_certificate_chain_file(pc->ctx, lin + matches[1].rm_so) != 1) - conf_err("SSL_CTX_use_certificate_chain_file failed - aborted"); - if(SSL_CTX_use_PrivateKey_file(pc->ctx, lin + matches[1].rm_so, SSL_FILETYPE_PEM) != 1) - conf_err("SSL_CTX_use_PrivateKey_file failed - aborted"); - if(SSL_CTX_check_private_key(pc->ctx) != 1) - conf_err("SSL_CTX_check_private_key failed - aborted"); - if((fcert = fopen(lin + matches[1].rm_so, "r")) == NULL) - conf_err("ListenHTTPS: could not open certificate file"); - if((x509 = PEM_read_X509(fcert, NULL, NULL, NULL)) == NULL) - conf_err("ListenHTTPS: could not get certificate subject"); - fclose(fcert); - memset(server_name, '\0', MAXBUF); - X509_NAME_oneline(X509_get_subject_name(x509), server_name, MAXBUF - 1); - pc->subjectAltNameCount = 0; - pc->subjectAltNames = NULL; - pc->subjectAltNames = get_subjectaltnames(x509, &(pc->subjectAltNameCount)); - X509_free(x509); - if(!regexec(&CNName, server_name, 4, matches, 0)) { - server_name[matches[1].rm_eo] = '\0'; - if((pc->server_name = strdup(server_name + matches[1].rm_so)) == NULL) - conf_err("ListenHTTPS: could not set certificate subject"); - } else - conf_err("ListenHTTPS: could not get certificate CN"); -#else - /* no SNI support */ - if(has_other) - conf_err("Cert directives MUST precede other SSL-specific directives - aborted"); - if(res->ctx) - conf_err("ListenHTTPS: multiple certificates not supported - aborted"); - if((res->ctx = malloc(sizeof(POUND_CTX))) == NULL) - conf_err("ListenHTTPS new POUND_CTX: out of memory - aborted"); - res->ctx->server_name = NULL; - res->ctx->next = NULL; - if((res->ctx->ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) - conf_err("SSL_CTX_new failed - aborted"); + load_cert(has_other, res, lin+matches[1].rm_so); + } else if(!regexec(&CertDir, lin, 4, matches, 0)) { lin[matches[1].rm_eo] = '\0'; - if(SSL_CTX_use_certificate_chain_file(res->ctx->ctx, lin + matches[1].rm_so) != 1) - conf_err("SSL_CTX_use_certificate_chain_file failed - aborted"); - if(SSL_CTX_use_PrivateKey_file(res->ctx->ctx, lin + matches[1].rm_so, SSL_FILETYPE_PEM) != 1) - conf_err("SSL_CTX_use_PrivateKey_file failed - aborted"); - if(SSL_CTX_check_private_key(res->ctx->ctx) != 1) - conf_err("SSL_CTX_check_private_key failed - aborted"); -#endif + load_certdir(has_other, res, lin+matches[1].rm_so); } else if(!regexec(&ClientCert, lin, 4, matches, 0)) { has_other = 1; if(res->ctx == NULL) @@ -1227,6 +1369,24 @@ parse_HTTPS(void) #endif } else if(!regexec(&NoHTTPS11, lin, 4, matches, 0)) { res->noHTTPS11 = atoi(lin + matches[1].rm_so); + } else if(!regexec(&ForceHTTP10, lin, 4, matches, 0)) { + if((m = (MATCHER *)malloc(sizeof(MATCHER))) == NULL) + conf_err("out of memory"); + memset(m, 0, sizeof(MATCHER)); + m->next = res->forcehttp10; + res->forcehttp10 = m; + lin[matches[1].rm_eo] = '\0'; + if(regcomp(&m->pat, lin + matches[1].rm_so, REG_ICASE | REG_NEWLINE | REG_EXTENDED)) + conf_err("bad pattern"); + } else if(!regexec(&SSLUncleanShutdown, lin, 4, matches, 0)) { + if((m = (MATCHER *)malloc(sizeof(MATCHER))) == NULL) + conf_err("out of memory"); + memset(m, 0, sizeof(MATCHER)); + m->next = res->ssl_uncln_shutdn; + res->ssl_uncln_shutdn = m; + lin[matches[1].rm_eo] = '\0'; + if(regcomp(&m->pat, lin + matches[1].rm_so, REG_ICASE | REG_NEWLINE | REG_EXTENDED)) + conf_err("bad pattern"); } else if(!regexec(&Service, lin, 4, matches, 0)) { if(res->services == NULL) res->services = parse_service(NULL); @@ -1263,8 +1423,12 @@ parse_HTTPS(void) sprintf(lin, "%d-Pound-%ld", getpid(), random()); SSL_CTX_set_session_id_context(pc->ctx, (unsigned char *)lin, strlen(lin)); SSL_CTX_set_tmp_rsa_callback(pc->ctx, RSA_tmp_callback); - SSL_CTX_set_tmp_dh_callback(pc->ctx, DH_tmp_callback); SSL_CTX_set_info_callback(pc->ctx, SSLINFO_callback); + if (NULL == DHCustom_params) + SSL_CTX_set_tmp_dh_callback(pc->ctx, DH_tmp_callback); + else + SSL_CTX_set_tmp_dh(pc->ctx, DHCustom_params); + #if OPENSSL_VERSION_NUMBER >= 0x0090800fL #ifndef OPENSSL_NO_ECDH /* This generates a EC_KEY structure with no key, but a group defined */ @@ -1287,6 +1451,130 @@ parse_HTTPS(void) return NULL; } +static void load_certdir(int has_other, LISTENER *res, const char *dir_path) { + DIR * dp; + struct dirent *de; + + char buf[512]; + char *files[200], *cp; + char *pattern; + int filecnt = 0; + int idx,use; + + logmsg(LOG_DEBUG, "Including Certs from Dir %s", dir_path); + + pattern = strrchr(dir_path, '/'); + if (pattern) { + *pattern++ = 0; + if (!*pattern) pattern = NULL; + } + + if((dp = opendir(dir_path)) == NULL) { + conf_err("can't open IncludeDir directory"); + exit(1); + } + + while((de = readdir(dp))!=NULL) { + if (de->d_name[0] == '.') continue; + if (!pattern || fnmatch(pattern, de->d_name, 0) == 0 ) { + snprintf(buf, sizeof(buf), "%s%s%s", dir_path, (dir_path[strlen(dir_path)-1]=='/')?"":"/", de->d_name); + buf[sizeof(buf)-1] = 0; + if (filecnt == sizeof(files)/sizeof(*files)) { + conf_err("Max certificate files per directory reached"); + } + if ((files[filecnt++] = strdup(buf)) == NULL) { + conf_err("CertDir out of memory"); + } + continue; + } + } + /* We order the list, and load in ascending order */ + while(filecnt) { + use = 0; + for(idx = 1; idx0) + use=idx; + + logmsg(LOG_DEBUG, " I Cert ==> %s", files[use]); + + load_cert(has_other, res, files[use]); + files[use] = files[--filecnt]; + } + + closedir(dp); +} + +static void +load_cert(int has_other, LISTENER *res, char *filename) +{ + POUND_CTX *pc; +#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB + /* we have support for SNI */ + FILE *fcert; + char server_name[MAXBUF], *cp; + X509 *x509; + + if(has_other) + conf_err("Cert directives MUST precede other SSL-specific directives - aborted"); + if(res->ctx) { + for(pc = res->ctx; pc->next; pc = pc->next) + ; + if((pc->next = malloc(sizeof(POUND_CTX))) == NULL) + conf_err("ListenHTTPS new POUND_CTX: out of memory - aborted"); + pc = pc->next; + } else { + if((res->ctx = malloc(sizeof(POUND_CTX))) == NULL) + conf_err("ListenHTTPS new POUND_CTX: out of memory - aborted"); + pc = res->ctx; + } + if((pc->ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) + conf_err("SSL_CTX_new failed - aborted"); + pc->server_name = NULL; + pc->next = NULL; + if(SSL_CTX_use_certificate_chain_file(pc->ctx, filename) != 1) + conf_err("SSL_CTX_use_certificate_chain_file failed - aborted"); + if(SSL_CTX_use_PrivateKey_file(pc->ctx, filename, SSL_FILETYPE_PEM) != 1) + conf_err("SSL_CTX_use_PrivateKey_file failed - aborted"); + if(SSL_CTX_check_private_key(pc->ctx) != 1) + conf_err("SSL_CTX_check_private_key failed - aborted"); + if((fcert = fopen(filename, "r")) == NULL) + conf_err("ListenHTTPS: could not open certificate file"); + if((x509 = PEM_read_X509(fcert, NULL, NULL, NULL)) == NULL) + conf_err("ListenHTTPS: could not get certificate subject"); + fclose(fcert); + memset(server_name, '\0', MAXBUF); + X509_NAME_oneline(X509_get_subject_name(x509), server_name, MAXBUF - 1); + pc->subjectAltNameCount = 0; + pc->subjectAltNames = NULL; + pc->subjectAltNames = get_subjectaltnames(x509, &(pc->subjectAltNameCount)); + X509_free(x509); + if(!regexec(&CNName, server_name, 4, matches, 0)) { + server_name[matches[1].rm_eo] = '\0'; + if((pc->server_name = strdup(server_name + matches[1].rm_so)) == NULL) + conf_err("ListenHTTPS: could not set certificate subject"); + } else + conf_err("ListenHTTPS: could not get certificate CN"); +#else + /* no SNI support */ + if(has_other) + conf_err("Cert directives MUST precede other SSL-specific directives - aborted"); + if(res->ctx) + conf_err("ListenHTTPS: multiple certificates not supported - aborted"); + if((res->ctx = malloc(sizeof(POUND_CTX))) == NULL) + conf_err("ListenHTTPS new POUND_CTX: out of memory - aborted"); + res->ctx->server_name = NULL; + res->ctx->next = NULL; + if((res->ctx->ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) + conf_err("SSL_CTX_new failed - aborted"); + if(SSL_CTX_use_certificate_chain_file(res->ctx->ctx, filename) != 1) + conf_err("SSL_CTX_use_certificate_chain_file failed - aborted"); + if(SSL_CTX_use_PrivateKey_file(res->ctx->ctx, filename, SSL_FILETYPE_PEM) != 1) + conf_err("SSL_CTX_use_PrivateKey_file failed - aborted"); + if(SSL_CTX_check_private_key(res->ctx->ctx) != 1) + conf_err("SSL_CTX_check_private_key failed - aborted"); +#endif +} + /* * parse the config file */ @@ -1316,10 +1604,18 @@ parse_file(void) lin[matches[1].rm_eo] = '\0'; if((root_jail = strdup(lin + matches[1].rm_so)) == NULL) conf_err("RootJail config: out of memory - aborted"); + } else if(!regexec(&DHParams, lin, 4, matches, 0)) { + lin[matches[1].rm_eo] = '\0'; + DH *dh = load_dh_params(lin + matches[1].rm_so); + if (!dh) + conf_err("DHParams config: could not load file"); + DHCustom_params = dh; } else if(!regexec(&Daemon, lin, 4, matches, 0)) { daemonize = atoi(lin + matches[1].rm_so); } else if(!regexec(&Threads, lin, 4, matches, 0)) { numthreads = atoi(lin + matches[1].rm_so); + } else if(!regexec(&ThreadModel, lin, 4, matches, 0)) { + threadpool = ((lin[matches[1].rm_so]|0x20) == 'p'); /* 'pool' */ } else if(!regexec(&LogFacility, lin, 4, matches, 0)) { lin[matches[1].rm_eo] = '\0'; if(lin[matches[1].rm_so] == '-') @@ -1376,6 +1672,25 @@ parse_file(void) conf_err("Control multiply defined - aborted"); lin[matches[1].rm_eo] = '\0'; ctrl_name = strdup(lin + matches[1].rm_so); + } else if(!regexec(&ControlUser, lin, 4, matches, 0)) { + lin[matches[1].rm_eo] = '\0'; + if((ctrl_user = strdup(lin + matches[1].rm_so)) == NULL) { + logmsg(LOG_ERR, "line %d: ControlUser config: out of memory - aborted", n_lin); + exit(1); + } + } else if(!regexec(&ControlGroup, lin, 4, matches, 0)) { + lin[matches[1].rm_eo] = '\0'; + if((ctrl_group = strdup(lin + matches[1].rm_so)) == NULL) { + logmsg(LOG_ERR, "line %d: ControlGroup config: out of memory - aborted", n_lin); + exit(1); + } + } else if(!regexec(&ControlMode, lin, 4, matches, 0)) { + lin[matches[1].rm_eo] = '\0'; + ctrl_mode = strtol(lin+matches[1].rm_so, NULL, 8); + if(errno==ERANGE || errno==EINVAL) { + logmsg(LOG_ERR, "line %d: ControlMode config: %s - aborted", n_lin, strerror(errno)); + exit(1); + } } else if(!regexec(&ListenHTTP, lin, 4, matches, 0)) { if(listeners == NULL) listeners = parse_HTTP(); @@ -1435,18 +1750,24 @@ config_parse(const int argc, char **const argv) || regcomp(&RootJail, "^[ \t]*RootJail[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Daemon, "^[ \t]*Daemon[ \t]+([01])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Threads, "^[ \t]*Threads[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&ThreadModel, "^[ \t]*ThreadModel[ \t]+(pool|dynamic)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&LogFacility, "^[ \t]*LogFacility[ \t]+([a-z0-9-]+)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&LogLevel, "^[ \t]*LogLevel[ \t]+([0-5])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Grace, "^[ \t]*Grace[ \t]+([0-9]+)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Alive, "^[ \t]*Alive[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&SSLEngine, "^[ \t]*SSLEngine[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Control, "^[ \t]*Control[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&ControlUser, "^[ \t]*ControlUser[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&ControlGroup, "^[ \t]*ControlGroup[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&ControlMode, "^[ \t]*ControlMode[ \t]+([0-7]+)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&ListenHTTP, "^[ \t]*ListenHTTP[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&ListenHTTPS, "^[ \t]*ListenHTTPS[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&End, "^[ \t]*End[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&BackendKey, "^[ \t]*Key[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Address, "^[ \t]*Address[ \t]+([^ \t]+)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Port, "^[ \t]*Port[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Cert, "^[ \t]*Cert[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&CertDir, "^[ \t]*CertDir[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&xHTTP, "^[ \t]*xHTTP[ \t]+([01234])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Client, "^[ \t]*Client[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&CheckURL, "^[ \t]*CheckURL[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) @@ -1454,6 +1775,8 @@ config_parse(const int argc, char **const argv) || regcomp(&Err500, "^[ \t]*Err500[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Err501, "^[ \t]*Err501[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Err503, "^[ \t]*Err503[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&ErrNoSsl, "^[ \t]*ErrNoSsl[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&NoSslRedirect, "^[ \t]*NoSslRedirect[ \t]+(30[127][ \t]+)?\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&MaxRequest, "^[ \t]*MaxRequest[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&HeadRemove, "^[ \t]*HeadRemove[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&RewriteLocation, "^[ \t]*RewriteLocation[ \t]+([012])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) @@ -1461,6 +1784,8 @@ config_parse(const int argc, char **const argv) || regcomp(&Service, "^[ \t]*Service[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&ServiceName, "^[ \t]*Service[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&URL, "^[ \t]*URL[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&OrURLs, "^[ \t]*OrURLS[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&BackendCookie, "^[ \t]*BackendCookie[ \t]+\"(.+)\"[ \t]+\"(.*)\"[ \t]+\"(.*)\"[ \t]+([0-9]+|Session)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&HeadRequire, "^[ \t]*HeadRequire[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&HeadDeny, "^[ \t]*HeadDeny[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&BackEnd, "^[ \t]*BackEnd[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) @@ -1469,8 +1794,7 @@ config_parse(const int argc, char **const argv) || regcomp(&TimeOut, "^[ \t]*TimeOut[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&HAport, "^[ \t]*HAport[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&HAportAddr, "^[ \t]*HAport[ \t]+([^ \t]+)[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) - || regcomp(&Redirect, "^[ \t]*Redirect[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) - || regcomp(&RedirectN, "^[ \t]*Redirect[ \t]+(30[127])[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&Redirect, "^[ \t]*Redirect(Append|Dynamic|)[ \t]+(30[127][ \t]+|)\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Session, "^[ \t]*Session[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Type, "^[ \t]*Type[ \t]+([^ \t]+)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&TTL, "^[ \t]*TTL[ \t]+([1-9-][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) @@ -1485,11 +1809,15 @@ config_parse(const int argc, char **const argv) || regcomp(&VerifyList, "^[ \t]*VerifyList[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&CRLlist, "^[ \t]*CRLlist[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&NoHTTPS11, "^[ \t]*NoHTTPS11[ \t]+([0-2])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&ForceHTTP10, "^[ \t]*ForceHTTP10[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&SSLUncleanShutdown, "^[ \t]*SSLUncleanShutdown[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Include, "^[ \t]*Include[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&IncludeDir, "^[ \t]*IncludeDir[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&ConnTO, "^[ \t]*ConnTO[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&IgnoreCase, "^[ \t]*IgnoreCase[ \t]+([01])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&HTTPS, "^[ \t]*HTTPS[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Disabled, "^[ \t]*Disabled[ \t]+([01])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) + || regcomp(&DHParams, "^[ \t]*DHParams[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&CNName, ".*[Cc][Nn]=([-*.A-Za-z0-9]+).*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) || regcomp(&Anonymise, "^[ \t]*Anonymise[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED) #if OPENSSL_VERSION_NUMBER >= 0x0090800fL @@ -1524,15 +1852,14 @@ config_parse(const int argc, char **const argv) case 'V': print_log = 1; logmsg(LOG_DEBUG, "Version %s", VERSION); +#ifdef SSLEAY_VERSION + logmsg(LOG_DEBUG, "OpenSSL version %s", SSLeay_version(SSLEAY_VERSION)); +#endif logmsg(LOG_DEBUG, " Configuration switches:"); #ifdef C_SUPER if(strcmp(C_SUPER, "0")) logmsg(LOG_DEBUG, " --disable-super"); #endif -#ifdef C_CERT1L - if(strcmp(C_CERT1L, "1")) - logmsg(LOG_DEBUG, " --enable-cert1l"); -#endif #ifdef C_SSL if(strcmp(C_SSL, "")) logmsg(LOG_DEBUG, " --with-ssl=%s", C_SSL); @@ -1576,8 +1903,10 @@ config_parse(const int argc, char **const argv) group = NULL; root_jail = NULL; ctrl_name = NULL; + DHCustom_params = NULL; numthreads = 128; + threadpool = 1; alive_to = 30; daemonize = 1; grace = 30; @@ -1604,18 +1933,24 @@ config_parse(const int argc, char **const argv) regfree(&RootJail); regfree(&Daemon); regfree(&Threads); + regfree(&ThreadModel); regfree(&LogFacility); regfree(&LogLevel); regfree(&Grace); regfree(&Alive); regfree(&SSLEngine); regfree(&Control); + regfree(&ControlUser); + regfree(&ControlGroup); + regfree(&ControlMode); regfree(&ListenHTTP); regfree(&ListenHTTPS); regfree(&End); + regfree(&BackendKey); regfree(&Address); regfree(&Port); regfree(&Cert); + regfree(&CertDir); regfree(&xHTTP); regfree(&Client); regfree(&CheckURL); @@ -1623,6 +1958,8 @@ config_parse(const int argc, char **const argv) regfree(&Err500); regfree(&Err501); regfree(&Err503); + regfree(&ErrNoSsl); + regfree(&NoSslRedirect); regfree(&MaxRequest); regfree(&HeadRemove); regfree(&RewriteLocation); @@ -1630,6 +1967,8 @@ config_parse(const int argc, char **const argv) regfree(&Service); regfree(&ServiceName); regfree(&URL); + regfree(&OrURLs); + regfree(&BackendCookie); regfree(&HeadRequire); regfree(&HeadDeny); regfree(&BackEnd); @@ -1639,7 +1978,6 @@ config_parse(const int argc, char **const argv) regfree(&HAport); regfree(&HAportAddr); regfree(&Redirect); - regfree(&RedirectN); regfree(&Session); regfree(&Type); regfree(&TTL); @@ -1654,7 +1992,10 @@ config_parse(const int argc, char **const argv) regfree(&VerifyList); regfree(&CRLlist); regfree(&NoHTTPS11); + regfree(&ForceHTTP10); + regfree(&SSLUncleanShutdown); regfree(&Include); + regfree(&IncludeDir); regfree(&ConnTO); regfree(&IgnoreCase); regfree(&HTTPS); @@ -1666,6 +2007,8 @@ config_parse(const int argc, char **const argv) regfree(&ECDHCurve); #endif #endif + regfree(&DHParams); + if (DHCustom_params) DH_free(DHCustom_params); /* set the facility only here to ensure the syslog gets opened if necessary */ log_facility = def_facility; diff --git a/config.h.in b/config.h.in old mode 100755 new mode 100644 diff --git a/configure b/configure index 1e82480..0ba56e5 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for pound 2.8a. +# Generated by GNU Autoconf 2.69 for pound 2.8+github. # # Report bugs to . # @@ -580,8 +580,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='pound' PACKAGE_TARNAME='pound' -PACKAGE_VERSION='2.8a' -PACKAGE_STRING='pound 2.8a' +PACKAGE_VERSION='2.8+github' +PACKAGE_STRING='pound 2.8+github' PACKAGE_BUGREPORT='roseg@apsis.ch' PACKAGE_URL='' @@ -630,7 +630,6 @@ LIBOBJS EGREP GREP CPP -C_CERT1L C_SUPER C_GROUP C_OWNER @@ -713,7 +712,6 @@ with_maxbuf with_owner with_group enable_super -enable_cert1l enable_pcreposix enable_tcmalloc enable_hoard @@ -1277,7 +1275,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures pound 2.8a to adapt to many kinds of systems. +\`configure' configures pound 2.8+github to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1344,7 +1342,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of pound 2.8a:";; + short | recursive ) echo "Configuration of pound 2.8+github:";; esac cat <<\_ACEOF @@ -1354,8 +1352,6 @@ Optional Features: --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-super enable or disable running with supervisor process (default: enabled) - --enable-cert1l enable or disable single-line certificate (default: - disabled) --enable-pcreposix enable or disable using the pcreposix library (default: enabled if available) --enable-tcmalloc enable or disable using the tcmalloc library @@ -1451,7 +1447,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -pound configure 2.8a +pound configure 2.8+github generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1874,7 +1870,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by pound $as_me 2.8a, which was +It was created by pound $as_me 2.8+github, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3829,13 +3825,6 @@ fi -# Check whether --enable-cert1l was given. -if test "${enable_cert1l+set}" = set; then : - enableval=$enable_cert1l; test ${enableval} = "yes" && CFLAGS="${CFLAGS} -DCERT1L"; C_CERT1L="1" -fi - - - # Check whether --enable-pcreposix was given. if test "${enable_pcreposix+set}" = set; then : enableval=$enable_pcreposix; C_PCREPOSIX=${enableval} @@ -3862,51 +3851,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: *** Checking for libraries ***" >&5 $as_echo "$as_me: *** Checking for libraries ***" >&6;} -LIBS="${LIBS} -lm" -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 -$as_echo_n "checking for dlopen in -ldl... " >&6; } -if ${ac_cv_lib_dl_dlopen+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldl $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char dlopen (); -int -main () -{ -return dlopen (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_dl_dlopen=yes -else - ac_cv_lib_dl_dlopen=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 -$as_echo "$ac_cv_lib_dl_dlopen" >&6; } -if test "x$ac_cv_lib_dl_dlopen" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBDL 1 -_ACEOF - - LIBS="-ldl $LIBS" -fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for socket in -lsocket" >&5 $as_echo_n "checking for socket in -lsocket... " >&6; } @@ -3948,46 +3893,6 @@ if test "x$ac_cv_lib_socket_socket" = xyes; then : LIBS="-lsocket -lnsl ${LIBS}" fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for hstrerror in -lresolv" >&5 -$as_echo_n "checking for hstrerror in -lresolv... " >&6; } -if ${ac_cv_lib_resolv_hstrerror+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lresolv $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char hstrerror (); -int -main () -{ -return hstrerror (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_resolv_hstrerror=yes -else - ac_cv_lib_resolv_hstrerror=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_resolv_hstrerror" >&5 -$as_echo "$ac_cv_lib_resolv_hstrerror" >&6; } -if test "x$ac_cv_lib_resolv_hstrerror" = xyes; then : - LIBS="-lresolv ${LIBS}" -fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for BIO_new in -lcrypto" >&5 $as_echo_n "checking for BIO_new in -lcrypto... " >&6; } if ${ac_cv_lib_crypto_BIO_new+:} false; then : @@ -6183,7 +6088,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by pound $as_me 2.8a, which was +This file was extended by pound $as_me 2.8+github, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -6245,7 +6150,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -pound config.status 2.8a +pound config.status 2.8+github configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/http.c b/http.c index dd211e4..ff4feac 100644 --- a/http.c +++ b/http.c @@ -32,7 +32,7 @@ static char *h500 = "500 Internal Server Error", *h501 = "501 Not Implemented", *h503 = "503 Service Unavailable", *h414 = "414 Request URI too long", - *h400 = "Bad Request"; + *h400 = "400 Bad Request"; static char *err_response = "HTTP/1.0 %s\r\nContent-Type: text/html\r\nContent-Length: %d\r\nExpires: now\r\nPragma: no-cache\r\nCache-control: no-cache,no-store\r\n\r\n%s"; @@ -547,7 +547,7 @@ do_http(thr_arg *arg) BACKEND *backend, *cur_backend, *old_backend; struct addrinfo from_host, z_addr; struct sockaddr_storage from_host_addr; - BIO *cl, *be, *bb, *b64; + BIO *oldcl, *cl, *be, *bb, *b64; X509 *x509; char request[MAXBUF], response[MAXBUF], buf[MAXBUF], url[MAXBUF], loc_path[MAXBUF], **headers, headers_ok[MAXHEADERS], v_host[MAXBUF], referer[MAXBUF], u_agent[MAXBUF], u_name[MAXBUF], @@ -556,6 +556,8 @@ do_http(thr_arg *arg) LONG cont, res_bytes; regmatch_t matches[4]; struct linger l; + struct tm expires; + time_t exptime; double start_req, end_req; RENEG_STATE reneg_state; BIO_ARG ba1, ba2; @@ -620,14 +622,21 @@ do_http(thr_arg *arg) } BIO_set_ssl(bb, ssl, BIO_CLOSE); BIO_set_ssl_mode(bb, 0); + + oldcl = cl; cl = bb; if(BIO_do_handshake(cl) <= 0) { - /* no need to log every client without a certificate... - addr2str(caddr, MAXBUF - 1, &from_host, 1); - logmsg(LOG_NOTICE, "BIO_do_handshake with %s failed: %s", caddr, - ERR_error_string(ERR_get_error(), NULL)); - x509 = NULL; - */ + if ((ERR_GET_REASON(ERR_peek_error()) == SSL_R_HTTP_REQUEST) + && (ERR_GET_LIB(ERR_peek_error()) == ERR_LIB_SSL)) { + addr2str(caddr, MAXBUF - 1, &from_host, 1); + if (lstn->nossl_redir) { + logmsg(LOG_NOTICE, "(%lx) errNoSsl from %s redirecting to \"%s\"", pthread_self(), caddr, lstn->nossl_url); + redirect_reply(oldcl, lstn->nossl_url, lstn->nossl_redir); + } else { + logmsg(LOG_NOTICE, "(%lx) errNoSsl from %s sending error", pthread_self(), caddr); + err_reply(oldcl, h400, lstn->errnossl); + } + } BIO_reset(cl); BIO_free_all(cl); return; @@ -1136,7 +1145,6 @@ do_http(thr_arg *arg) clean_all(); return; } -#ifdef CERT1L PEM_write_bio_X509(bb, x509); get_line(bb, buf, MAXBUF); if(BIO_printf(be, "X-SSL-certificate: %s", buf) <= 0) { @@ -1161,20 +1169,7 @@ do_http(thr_arg *arg) return; } } - if(BIO_printf(be, "\r\n", buf) <= 0) { - str_be(buf, MAXBUF - 1, cur_backend); - end_req = cur_time(); - logmsg(LOG_WARNING, "(%lx) e500 error write X-SSL-certificate to %s: %s (%.3f sec)", - pthread_self(), buf, strerror(errno), (end_req - start_req) / 1000000.0); - err_reply(cl, h500, lstn->err500); - BIO_free_all(bb); - clean_all(); - return; - } -#else - PEM_write_bio_X509(bb, x509); - get_line(bb, buf, MAXBUF); - if(BIO_printf(be, "X-SSL-certificate: %s\r\n", buf) <= 0) { + if(BIO_printf(be, "\r\n") <= 0) { str_be(buf, MAXBUF - 1, cur_backend); end_req = cur_time(); logmsg(LOG_WARNING, "(%lx) e500 error write X-SSL-certificate to %s: %s (%.3f sec)", @@ -1184,19 +1179,6 @@ do_http(thr_arg *arg) clean_all(); return; } - while(get_line(bb, buf, MAXBUF) == 0) { - if(BIO_printf(be, "\t%s\r\n", buf) <= 0) { - str_be(buf, MAXBUF - 1, cur_backend); - end_req = cur_time(); - logmsg(LOG_WARNING, "(%lx) e500 error write X-SSL-certificate to %s: %s (%.3f sec)", - pthread_self(), buf, strerror(errno), (end_req - start_req) / 1000000.0); - err_reply(cl, h500, lstn->err500); - BIO_free_all(bb); - clean_all(); - return; - } - } -#endif BIO_free_all(bb); } } @@ -1326,6 +1308,15 @@ do_http(thr_arg *arg) break; default: force_10 = 0; + if (lstn->forcehttp10) { + MATCHER *m; + + for(m = lstn->forcehttp10; m; m = m->next) + if(!regexec(&m->pat, u_agent, 0, NULL, 0)) { + force_10 = 1; + break; + } + } break; } @@ -1333,9 +1324,38 @@ do_http(thr_arg *arg) if(cur_backend->be_type) { memset(buf, 0, sizeof(buf)); if(!cur_backend->redir_req) - snprintf(buf, sizeof(buf) - 1, "%s%s", cur_backend->url, url); - else strncpy(buf, cur_backend->url, sizeof(buf) - 1); + else if (cur_backend->redir_req==1) + snprintf(buf, sizeof(buf) - 1, "%s%s", cur_backend->url, url); + else { + regmatch_t umtch[10]; + char *chptr, *enptr, *srcptr; + + // Redirect Dynamic + //fprintf(stderr, "redir dynamic url %s replace %s\n", url, cur_backend->url); + + if(regexec(&svc->url->pat, url, 10, umtch, 0)) + logmsg(LOG_WARNING, "URL pattern didn't match in redirdynamic... shouldn't happen %s", url); + chptr = buf; + enptr = buf + sizeof(buf) - 1; + *enptr = '\0'; + srcptr = cur_backend->url; + for(; *srcptr && chptr < enptr-1; ) { + if (srcptr[0] == '$' && srcptr[1] == '$') { + *chptr++ = *srcptr++; + srcptr++; + } + if (srcptr[0] == '$' && isdigit(srcptr[1])) { + if (chptr + umtch[srcptr[1]-0x30].rm_eo - umtch[srcptr[1]-0x30].rm_so > enptr-1) break; + memcpy(chptr, url + umtch[srcptr[1]-0x30].rm_so, umtch[srcptr[1]-0x30].rm_eo - umtch[srcptr[1]-0x30].rm_so); + chptr += umtch[srcptr[1]-0x30].rm_eo - umtch[srcptr[1]-0x30].rm_so; + srcptr += 2; + continue; + } + *chptr++ = *srcptr++; + } + *chptr++='\0'; + } redirect_reply(cl, buf, cur_backend->be_type); addr2str(caddr, MAXBUF - 1, &from_host, 1); switch(lstn->log_level) { @@ -1358,6 +1378,10 @@ do_http(thr_arg *arg) logmsg(LOG_INFO, "%s - %s [%s] \"%s\" %d 0 \"%s\" \"%s\"", caddr, u_name[0]? u_name: "-", req_time, request, cur_backend->be_type, referer, u_agent); break; + case 6: + logmsg(LOG_INFO, "%s - %s [%s] \"%s\" %d 0 \"%s\" \"%s\" \"%s\"", caddr, + u_name[0]? u_name: "-", req_time, request, cur_backend->be_type, buf, referer, u_agent); + break; } if(!cl_11 || conn_closed || force_10) break; @@ -1500,6 +1524,39 @@ do_http(thr_arg *arg) } } free_headers(headers); + if(!skip && cur_backend->be_type == 0 && svc->becookie && cur_backend->bekey) { + char *cp = buf; + char *ep = buf + sizeof(buf) - 1; + buf[0] = '\0'; + snprintf(cp, (ep-cp-1), "Set-Cookie: %s=%s", svc->becookie, cur_backend->bekey); + cp += strlen(cp); + if(svc->becage!=0) { + strncat(cp, "; expires=", ep-cp-1); + cp += strlen(cp); + exptime = time(NULL); + /* Explicit age? Or match session timer? (-1) */ + if (svc->becage>0) + exptime += svc->becage; + else + exptime += svc->sess_ttl; + strftime(cp, ep-cp-1, "%a, %e-%b-%Y %H:%M:%S GMT", gmtime(&exptime)); + cp += strlen(cp); + } + if(svc->becpath) { + strncat(cp, "; path=", ep-cp-1); + cp += strlen(cp); + strncat(cp, svc->becpath, ep-cp-1); + cp += strlen(cp); + } + if(svc->becdomain) { + strncat(cp, "; domain=", ep-cp-1); + cp += strlen(cp); + strncat(cp, svc->becdomain, ep-cp-1); + cp += strlen(cp); + } + /*logmsg(LOG_DEBUG, "%s", buf);*/ + BIO_printf(cl, "%s\r\n", buf); + } /* final CRLF */ if(!skip) @@ -1669,7 +1726,21 @@ do_http(thr_arg *arg) /* * This may help with some versions of IE with a broken channel shutdown */ - if(ssl != NULL) + if(lstn && lstn->ssl_uncln_shutdn && ssl != NULL) { + MATCHER *m; + int found = 0; + + for(m = lstn->ssl_uncln_shutdn; m; m = m->next) + if(!regexec(&m->pat, u_agent, 0, NULL, 0)) { + found = 1; + break; + } + + if(found) + SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); + else + SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); + } else if(ssl != NULL) SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); clean_all(); @@ -1677,7 +1748,19 @@ do_http(thr_arg *arg) } void * -thr_http(void *dummy) +thr_http_single(void *dummy) +{ + thr_arg *arg; + + arg = (thr_arg *)dummy; + if (arg == NULL) + logmsg(LOG_NOTICE, "NULL get_thr_arg"); + else + do_http(arg); +} + +void * +thr_http_pool(void *dummy) { thr_arg *arg; diff --git a/pound.8 b/pound.8 index c98b5cd..15994eb 100644 --- a/pound.8 +++ b/pound.8 @@ -222,6 +222,19 @@ puts itself in the background). By specifying this option you can force to work like a regular process. Useful for debugging or if you want to use something like \fIdaemontools\fR. .TP +\fBThreadModel\fR [pool|dynamic] +If set to pool, +.B Pound +will spawn +\fBThreads\fR +number of threads when it starts and reuse those threads throughout its +lifetime. This sets a hard limit on the number of concurrent requests +that can be processed. +If set to dynamic, a new thread is spawned for each and every request. +This means you have a penalty for thread creation on every request, and +possibility of DoS if too many requests come in, but it also means your +thread count will scale up and down as necessary. +.TP \fBThreads\fR nnn How many worker threads .B Pound @@ -241,6 +254,11 @@ the facility name causes .B Pound to log to stdout/stderr. .TP +\fBDHParams\fR "path/to/dhparams.pem" +Use the supplied dhparams pem file for DH key exchange for non-export-controlled +negotiations. Generate such a file with \fBopenssl dhparam\fR. +This can be used to do 2048bit DHE. +.TP \fBLogLevel\fR value Specify the logging level: 0 for no logging, 1 (default) for regular logging, 2 for extended logging (show chosen backend server as well), @@ -314,12 +332,26 @@ the .I poundctl(8) program. .TP +\fBControlUser\fR "user" +The username to chown the Control socket to. +.TP +\fBControlGroup\fR "group" +The groupname to chgrp the Control socket to. +.TP +\fBControlMode\fR 0660 +The mode the Control socket should use, in octal. +.TP \fBInclude\fR "/path/to/file" Include the file as though it were part of the configuration file. .TP \fBAnonymise\fR Replace the last byte of the client address with 0 for logging purposes. Default: log the client address in full. +\fBIncludeDir\fR "/path/" +Looks for files with .conf or .cfg extensions in "path", and includes all files, in sorted +order, inline in the configuration as if it were part of the configuration file. +This directive can be used in any block... but the result must be syntactically correct. +.TP .SH "HTTP Listener" An HTTP listener defines an address and port that .B Pound @@ -349,6 +381,10 @@ will listen on. This is a .B mandatory parameter. .TP +\fBKey\fR "key" +The key associated to this backend, if using BackendCookie in the service. +If left blank, it'll be autogenerated from the backend address. +.TP \fBxHTTP\fR value Defines which HTTP verbs are accepted. The possible values are: .IP @@ -361,7 +397,7 @@ additionally allow extended HTTP requests (PUT, PATCH, DELETE). .I 2 additionally allow standard WebDAV verbs (LOCK, UNLOCK, PROPFIND, PROPPATCH, SEARCH, MKCOL, MOVE, COPY, OPTIONS, TRACE, MKACTIVITY, -CHECKOUT, MERGE, REPORT). +CHECKOUT, MERGE, REPORT, MKCALENDAR). .IP .I 3 additionally allow MS extensions WebDAV verbs (SUBSCRIBE, UNSUBSCRIBE, @@ -402,6 +438,24 @@ Default: "This method may not be used.". A file with the text to be displayed if an Error 503 occurs. Default: "The service is not available. Please try again later.". .TP +\fBErrNoSsl\fR "filename" +A file with the text to be displayed if a user connects to a HTTPS listener with HTTP. +Default: "Please use HTTPS.". + +Only valid for HTTPS listeners. +.TP +\fBNoSslRedirect\fR [code] "url" +A url that the user will be redirected to if the user connects to a HTTPS listener with HTTP. +.br +The code here is just like the code in Redirect blocks. It defaults to 302, but could be 301 or 307. +.br +Only valid for HTTPS listeners. +.br +Example: +.IP +.br +NoSslRedirect "https://thishost:port" +.TP \fBMaxRequest\fR nnn Request maximal size. All requests will be limited to these many bytes. If a request contains more data than allowed an error 414 is returned. Default: @@ -442,6 +496,12 @@ Override the global .I LogLevel value. .TP +\fBForceHTTP10\fR "user agent pattern" +Force connections for browser user agents matching this pattern to use +HTTP/1.0 even if the browser has requested HTTP/1.1. Some MSIE browsers have +problems with HTTP/1.1 over SSL connections. Other browsers, like Java/1.0 or +JDK connections might require this feature as well. +.TP \fBService\fR [ "name" ] This defines a private service (see below for service definition syntax). This service will be used only by this listener. The service may be optionally @@ -465,12 +525,16 @@ following additional directives are also available: Specify the server certificate. The .I certificate file is the file containing the certificate, possibly a certificate chain and the signature -for this server. This directive is +for this server. This directive or the +.I CertDir +directive is .B mandatory for HTTPS listeners. .IP Please note that multiple .I Cert +or +.I CertDir directives are allowed if your OpenSSL version supports SNI. In such cases, the first directive is the default certificate, with additional certificates used if the client requests them. @@ -482,6 +546,43 @@ most-specific-to-least specific order (i.e. wildcard certificates host-specific certificates). .IP .I Cert +and +.I CertDir +directives +.B must +precede all other SSL-specific directives. +.TP +\fBCertDir\fR "certificate directory" +Specify the server certificate or certificates. The +.I certificate directory +is a directory path containing one or more certificates, possibly a certificate chain and the signature +for this server. This directive or +.I Cert +is +.B mandatory +for HTTPS listeners. +.IP +If a wildcard is specified, it will be honored. Otherwise all files will be loaded from that directory. +For example, "/etc/certs/*.pem" will load all files from that directory that match the file extension given. +.IP +Please note that multiple +.I Cert +or +.I CertDir +directives are allowed if your OpenSSL version supports SNI. In such cases, +the first directive is the default certificate, with additional certificates +used if the client requests them. +.IP +The filenames in the directory will be sorted before being loaded. The order of files +is important: the first certificate where the CN +matches the client request will be used, so sort your files in the +most-specific-to-least specific order (i.e. wildcard certificates +.B after +host-specific certificates). +.IP +.I Cert +and +.I CertDir directives .B must precede all other SSL-specific directives. @@ -547,6 +648,15 @@ Behave like an HTTP/1.0 server for HTTPS clients. If this value is requests on SSL connections. If the value is 2 (default) disable multiple requests on SSL connections only for MSIE clients. Required work-around for a bug in certain versions of IE. +.TP +\fBSSLUncleanShutdown\fR "UserAgent Match Pattern" +Implement a workaround for MSIE's ssl shutdown code... basically don't +send a shutdown message because MSIE can't handle it. Pound, by default, +would do this on all connections, and will do that if this directive is +never specified. If this directive is specified, one or many times, any +matching useragent pattern will cause the ssl unclean shutdown behavior. +Other user agents will follow the mod_ssl compliant and safe code. +You likely want to set this for all MSIE browsers. .SH "Service" A service is a definition of which back-end servers .B Pound @@ -577,6 +687,26 @@ was defined then all requests match. The matching is by default case-sensitive, but this can be overridden by specifying .B IgnoreCase 1 .TP +\fBOrURLs\fR +Defines a block of +.I URL +directives that should be merged into a single pattern, all OR'd together. +This creates a pattern like +.B ((url1)|(url2)|(url3)) + for as many +.I URL +directives as are specified within the block. End the block with an +.I End +directive. +.TP +\fBBackendCookie\fR "cookiename" "domain" "path" age|Session +If defined, Pound will inject a cookie in each response with the appropriate backend's key, so that +even if the session table is flushed or sessions are disabled, the proper backend can be chosen. +This allows for session databases to be offloaded to the client side via browser cookies. +See \fBKey\fR in the backend definition. The given age will be how many seconds the cookie will +persist for. If set to 0, it will be a so-called "memory" cookie which will expire when the browser +closes. If set to "Session", it will mimick the session TTL behavior. +.TP \fBIgnoreCase\fR 0|1 Override the global .B IgnoreCase @@ -622,19 +752,25 @@ multiple back-ends per service, in which case .B Pound will attempt to load-balance between them. .TP -\fBRedirect\fR [code] "url" +\fB[Redirect | RedirectAppend | RedirectDynamic]\fR [code] "url" This is a special type of back-end. Instead of sending the request to a back-end .B Pound replies immediately with a redirection to the given URL. You may define multiple redirectors in a service, as well as mixing them with regular back-ends. .IP -The address the client is redirected to is determined by the actual -.I url -you specify: if it is a "pure" host (i.e. with no path) then the client will be -redirected to the host you specified, with the original request path appended. If -your +The address the client is redirected to is determined by the command you specify. +If you specify \fBRedirect\fR, the url is taken as an absolute host and path +to redirect to. If you use \fBRedirectAppend\fR, the original request path +will be appended to the host and path you specified. If you use \fBRedirectDynamic\fR, +then .I url -does contain a path then the request path is ignored. +can contain RegEx replacements in the form +.I $1 +through +.I $9 +which indicate expression captured from the original request path. You must have a +\fBURL\fR directive, and the first \fBURL\fR directive for the service is the one +used for capturing expressions. .IP Examples: if you specified .br @@ -647,17 +783,28 @@ Examples: if you specified and the client requested .I http://xyz/a/b/c then it will be redirected to -.IR "http://abc.example/a/b/c", +.IR "http://abc.example", but if you specified .br .br - Redirect "http://abc.example/index.html" + RedirectAppend "http://abc.example" +.br + +.br +it will be sent to +.IR "http://abc.example/a/b/c. +.IP +If you specified +.br + URL "^/a(/([^/]*)(/[^/]*)" +.br + RedirectDynamic "http://abc.example$2$1/index.html" .br .br it will be sent to -.IR "http://abc.example/index.html". +.IR "http://abc.example/c/b/index.html. .IP .IR "Technical note": in an ideal world diff --git a/pound.c b/pound.c index 3643f70..921feda 100644 --- a/pound.c +++ b/pound.c @@ -32,7 +32,11 @@ char *user, /* user to run as */ *group, /* group to run as */ *root_jail, /* directory to chroot to */ *pid_name, /* file to record pid in */ - *ctrl_name; /* control socket name */ + *ctrl_name, /* control socket name */ + *ctrl_user, /* control socket username */ + *ctrl_group; /* control socket group name */ + +long ctrl_mode; /* octal mode of the control socket */ int alive_to, /* check interval for resurrection */ anonymise, /* anonymise client address */ @@ -111,7 +115,7 @@ l_id(void) static thr_arg *first = NULL, *last = NULL; static pthread_cond_t arg_cond; static pthread_mutex_t arg_mut; -int numthreads; +int numthreads, threadpool; static void init_thr_arg(void) @@ -121,20 +125,29 @@ init_thr_arg(void) return; } -/* - * add a request to the queue - */ -int -put_thr_arg(thr_arg *arg) +thr_arg * +get_dyn_thr_arg(thr_arg *arg) { thr_arg *res; if((res = malloc(sizeof(thr_arg))) == NULL) { logmsg(LOG_WARNING, "thr_arg malloc"); - return -1; + return NULL; } memcpy(res, arg, sizeof(thr_arg)); res->next = NULL; + return res; +} + +/* + * add a request to the queue + */ +int +put_thr_arg(thr_arg *arg) +{ + thr_arg *res; + if ((res = get_dyn_thr_arg(arg))==NULL) + return -1; (void)pthread_mutex_lock(&arg_mut); if(last == NULL) first = last = res; @@ -143,7 +156,7 @@ put_thr_arg(thr_arg *arg) last = last->next; } (void)pthread_mutex_unlock(&arg_mut); - pthread_cond_signal(&arg_cond); + if (threadpool) pthread_cond_signal(&arg_cond); return 0; } @@ -156,13 +169,12 @@ get_thr_arg(void) thr_arg *res; (void)pthread_mutex_lock(&arg_mut); - if(first == NULL) + while((res = first) == NULL) (void)pthread_cond_wait(&arg_cond, &arg_mut); - if((res = first) != NULL) - if((first = first->next) == NULL) - last = NULL; + if((first = first->next) == NULL) + last = NULL; (void)pthread_mutex_unlock(&arg_mut); - if(first != NULL) + if(res->next != NULL) pthread_cond_signal(&arg_cond); return res; } @@ -170,6 +182,7 @@ get_thr_arg(void) /* * get the current queue length */ +int get_thr_qlen(void) { int res; @@ -246,6 +259,8 @@ main(const int argc, char **argv) print_log = 0; (void)umask(077); control_sock = -1; + ctrl_user=ctrl_group=NULL; + ctrl_mode = -1; log_facility = -1; logmsg(LOG_NOTICE, "starting..."); @@ -325,6 +340,35 @@ main(const int argc, char **argv) logmsg(LOG_ERR, "Control \"%s\" bind: %s", ctrl.sun_path, strerror(errno)); exit(1); } + if (ctrl_user) { + struct passwd *pw; + + if((pw = getpwnam(ctrl_user)) == NULL) { + logmsg(LOG_ERR, "no such user %s - aborted", ctrl_user); + exit(1); + } + if (chown(ctrl_name, pw->pw_uid, -1)) { + logmsg(LOG_ERR, "chown error on control socket - aborted (%s)", strerror(errno)); + exit(1); + } + } + if (ctrl_group) { + struct group *gr; + if((gr = getgrnam(ctrl_group)) == NULL) { + logmsg(LOG_ERR, "no such group %s - aborted", ctrl_group); + exit(1); + } + if (chown(ctrl_name, -1, gr->gr_gid)) { + logmsg(LOG_ERR, "chown error on control socket - aborted (%s)", strerror(errno)); + exit(1); + } + } + if (ctrl_mode>0) { + if (chmod(ctrl_name, ctrl_mode)) { + logmsg(LOG_ERR, "chmod error on control socket - aborted (%s)", strerror(errno)); + exit(1); + } + } listen(control_sock, 512); } @@ -474,12 +518,14 @@ main(const int argc, char **argv) /* pause to make sure the service threads were started */ sleep(1); - /* create the worker threads */ - for(i = 0; i < numthreads; i++) - if(pthread_create(&thr, &attr, thr_http, NULL)) { - logmsg(LOG_ERR, "create thr_http: %s - aborted", strerror(errno)); - exit(1); - } + if (threadpool) { + /* create the worker threads */ + for(i = 0; i < numthreads; i++) + if(pthread_create(&thr, &attr, thr_http_pool, NULL)) { + logmsg(LOG_ERR, "create thr_http: %s - aborted", strerror(errno)); + exit(1); + } + } /* pause to make sure at least some of the worker threads were started */ sleep(1); @@ -514,7 +560,7 @@ main(const int argc, char **argv) logmsg(LOG_WARNING, "HTTP accept: %s", strerror(errno)); } else if(((struct sockaddr_in *)&clnt_addr)->sin_family == AF_INET || ((struct sockaddr_in *)&clnt_addr)->sin_family == AF_INET6) { - thr_arg arg; + thr_arg arg, *argp; if(lstn->disabled) { /* @@ -536,8 +582,19 @@ main(const int argc, char **argv) arg.from_host.ai_family = AF_INET; else arg.from_host.ai_family = AF_INET6; - if(put_thr_arg(&arg)) - close(clnt); + if(threadpool) { + if(put_thr_arg(&arg)) + close(clnt); + } else { + if((argp = get_dyn_thr_arg(&arg)) == NULL) + close(clnt); + else if(pthread_create(&thr, &attr, thr_http_single, (void*)argp)) { + logmsg(LOG_ERR, "create thr_http: %s - aborted", strerror(errno)); + free(argp->from_host.ai_addr); + free(argp); + close(clnt); + } + } } else { /* may happen on FreeBSD, I am told */ logmsg(LOG_WARNING, "HTTP connection prematurely closed by peer"); diff --git a/pound.h b/pound.h index fa22c36..ae490c4 100644 --- a/pound.h +++ b/pound.h @@ -29,6 +29,7 @@ #include #include +#include #if HAVE_STDLIB_H #include #else @@ -264,10 +265,16 @@ extern char *user, /* user to run as */ *group, /* group to run as */ *root_jail, /* directory to chroot to */ *pid_name, /* file to record pid in */ - *ctrl_name; /* control socket name */ + *ctrl_name, /* control socket name */ + *ctrl_user, /* control socket username */ + *ctrl_group; /* control socket group name */ + +extern long ctrl_mode; /* octal mode of the control socket */ extern int numthreads, /* number of worker threads */ anonymise, /* anonymise client address */ + threadpool, /* 1 to use a threadpool (i.e. 2.6 behavior) + 0 to use new thread per request (2.5 behavior) */ alive_to, /* check interval for resurrection */ daemonize, /* run as daemon */ log_facility, /* log facility to use */ @@ -321,7 +328,8 @@ typedef struct _backend { int conn_to; /* connection time-out */ struct addrinfo ha_addr; /* HA address/port */ char *url; /* for redirectors */ - int redir_req; /* the redirect should include the request path */ + int redir_req; /* 0 - redirect is absolute, 1 - the redirect should include the request path, or 2 if it should use perl dynamic replacement */ + char *bekey; /* Backend Key for Cookie */ SSL_CTX *ctx; /* CTX for SSL connections */ pthread_mutex_t mut; /* mutex for this back-end */ int n_requests; /* number of requests seen */ @@ -368,6 +376,11 @@ typedef struct _service { #else LHASH *sessions; /* currently active sessions */ #endif + regex_t becookie_re;/* Regexs to find backend cookies */ + char *becookie, /* Backend Cookie Name */ + *becdomain, /* Backend Cookie domain */ + *becpath; /* Backend cookie path */ + int becage; /* Backend cookie age */ int disabled; /* true if the service is disabled */ struct _service *next; } SERVICE; @@ -391,6 +404,8 @@ typedef struct _listener { POUND_CTX *ctx; /* CTX for SSL connections */ int clnt_check; /* client verification mode */ int noHTTPS11; /* HTTP 1.1 mode for SSL */ + MATCHER *forcehttp10; /* User Agent Patterns to force HTTP 1.0 mode */ + MATCHER *ssl_uncln_shutdn; /* User Agent Patterns to enable ssl unclean shutdown */ char *add_head; /* extra SSL header */ regex_t verb; /* pattern to match the request verb against */ int to; /* client time-out */ @@ -399,7 +414,10 @@ typedef struct _listener { char *err414, /* error messages */ *err500, *err501, - *err503; + *err503, + *errnossl; + char *nossl_url; /* If a user goes to a https port with a http: url, redirect them to this url */ + int nossl_redir; /* Code to use for redirect (301 302 307)*/ LONG max_req; /* max. request size */ MATCHER *head_off; /* headers to remove */ int rewr_loc; /* rewrite location response */ @@ -447,7 +465,7 @@ typedef enum { CTRL_EN_LSTN, CTRL_DE_LSTN, CTRL_EN_SVC, CTRL_DE_SVC, CTRL_EN_BE, CTRL_DE_BE, - CTRL_ADD_SESS, CTRL_DEL_SESS + CTRL_ADD_SESS, CTRL_DEL_SESS, CTRL_FLUSH_SESS } CTRL_CODE; typedef struct { @@ -486,12 +504,13 @@ extern thr_arg *get_thr_arg(void); /* * get the current queue length */ -extern get_thr_qlen(void); +extern int get_thr_qlen(void); /* * handle an HTTP request */ -extern void *thr_http(void *); +extern void *thr_http_single(void *); +extern void *thr_http_pool(void *); /* * Log an error to the syslog or to stderr @@ -583,6 +602,11 @@ extern void config_parse(const int, char **const); */ extern RSA *RSA_tmp_callback(SSL *, int, int); +/* + * handle custom DH keys + */ +extern DH *load_dh_params(char *); + /* * return a pre-generated RSA key */ diff --git a/poundctl.8 b/poundctl.8 old mode 100755 new mode 100644 diff --git a/poundctl.c b/poundctl.c old mode 100755 new mode 100644 index ed6d2a7..3989cf5 --- a/poundctl.c +++ b/poundctl.c @@ -37,10 +37,11 @@ usage(const char *arg0) fprintf(stderr, "\twhere cmd is one of:\n"); fprintf(stderr, "\t-L n - enable listener n\n"); fprintf(stderr, "\t-l n - disable listener n\n"); - fprintf(stderr, "\t-S n m - enable service m in service n (use -1 for global services)\n"); - fprintf(stderr, "\t-s n m - disable service m in service n (use -1 for global services)\n"); + fprintf(stderr, "\t-S n m - enable service m in listener n (use -1 for global services)\n"); + fprintf(stderr, "\t-s n m - disable service m in listener n (use -1 for global services)\n"); fprintf(stderr, "\t-B n m r - enable back-end r in service m in listener n\n"); fprintf(stderr, "\t-b n m r - disable back-end r in service m in listener n\n"); + fprintf(stderr, "\t-f n m r - flush all sessions for back-end r in service m in listener n\n"); fprintf(stderr, "\t-N n m k r - add a session with key k and back-end r in service m in listener n\n"); fprintf(stderr, "\t-n n m k - remove a session with key k r in service m in listener n\n"); fprintf(stderr, "\n"); @@ -101,12 +102,19 @@ be_prt(const int sock) { BACKEND be; struct sockaddr_storage a, h; - int n_be; + char bekey[MAXBUF+1]; + int n_be,sz; n_be = 0; while(read(sock, (void *)&be, sizeof(BACKEND)) == sizeof(BACKEND)) { if(be.disabled < 0) break; + + read(sock, &sz, sizeof(sz)); + if(sz) read(sock, bekey, sz); + bekey[sz]='\0'; + be.bekey=bekey; + read(sock, &a, be.addr.ai_addrlen); be.addr.ai_addr = (struct sockaddr *)&a; if(be.ha_addr.ai_addrlen > 0) { @@ -114,8 +122,8 @@ be_prt(const int sock) be.ha_addr.ai_addr = (struct sockaddr *)&h; } if(xml_out) - printf("\n", - n_be++, + printf("\n", + n_be++, be.bekey, prt_addr(&be.addr), be.t_average / 1000000, be.priority, be.alive? "yes": "DEAD", be.disabled? "DISABLED": "active"); else @@ -216,7 +224,7 @@ main(const int argc, char **argv) CTRL_CMD cmd; int sock, n_lstn, n_svc, n_be, n_sess, i; char *arg0, *sock_name, buf[KEY_SIZE + 1]; - int c_opt, en_lst, de_lst, en_svc, de_svc, en_be, de_be, a_sess, d_sess, is_set; + int c_opt, en_lst, de_lst, en_svc, de_svc, en_be, de_be, a_sess, d_sess, f_sess, is_set; LISTENER lstn; SERVICE svc; BACKEND be; @@ -225,7 +233,7 @@ main(const int argc, char **argv) arg0 = *argv; sock_name = NULL; - en_lst = de_lst = en_svc = de_svc = en_be = de_be = is_set = a_sess = d_sess = 0; + en_lst = de_lst = en_svc = de_svc = en_be = de_be = is_set = a_sess = d_sess = f_sess = 0; memset(&cmd, 0, sizeof(cmd)); opterr = 0; i = 0; @@ -277,6 +285,11 @@ main(const int argc, char **argv) usage(arg0); d_sess = is_set = 1; break; + case 'f': + if(is_set) + usage(arg0); + f_sess = is_set = 1; + break; case 'H': host_names = 1; break; @@ -332,6 +345,14 @@ main(const int argc, char **argv) cmd.service = atoi(argv[optind++]); strncpy(cmd.key, argv[optind++], KEY_SIZE); } + if(f_sess) { + if(optind != (argc - 3)) + usage(arg0); + cmd.cmd = CTRL_FLUSH_SESS; + cmd.listener = atoi(argv[optind++]); + cmd.service = atoi(argv[optind++]); + cmd.backend = atoi(argv[optind++]); + } if(!is_set) { if(optind != argc) usage(arg0); diff --git a/svc.c b/svc.c index 60ba488..7756019 100644 --- a/svc.c +++ b/svc.c @@ -286,8 +286,14 @@ addr2str(char *const res, const int res_len, const struct addrinfo *addr, const case AF_INET6: src = (void *)&((struct sockaddr_in6 *)addr->ai_addr)->sin6_addr.s6_addr; port = ntohs(((struct sockaddr_in6 *)addr->ai_addr)->sin6_port); - if(inet_ntop(AF_INET6, src, buf, MAXBUF - 1) == NULL) - strncpy(buf, "(UNKNOWN)", MAXBUF - 1); + if( IN6_IS_ADDR_V4MAPPED( &(((struct sockaddr_in6 *)addr->ai_addr)->sin6_addr) )) { + src = (void *)&((struct sockaddr_in6 *)addr->ai_addr)->sin6_addr.s6_addr[12]; + if(inet_ntop(AF_INET, src, buf, MAXBUF - 1) == NULL) + strncpy(buf, "(UNKNOWN)", MAXBUF - 1); + } else { + if(inet_ntop(AF_INET6, src, buf, MAXBUF - 1) == NULL) + strncpy(buf, "(UNKNOWN)", MAXBUF - 1); + } break; case AF_UNIX: strncpy(buf, (char *)addr->ai_addr, MAXBUF - 1); @@ -409,9 +415,6 @@ check_header(const char *header, char *const content) return hd_types[i].val; } return HEADER_OTHER; - } else if(header[0] == ' ' || header[0] == '\t') { - *content = '\0'; - return HEADER_OTHER; } else return HEADER_ILLEGAL; } @@ -524,6 +527,27 @@ get_HEADERS(char *res, const SERVICE *svc, char **const headers) return res[0] != '\0'; } +static int +get_bekey_from_HEADERS(char *res, const SERVICE *svc, char **const headers) +{ + int i, n, s; + regmatch_t matches[4]; + + res[0] = '\0'; + if (!svc->becookie) return res[0] != '\0'; + for(i = 0; i < (MAXHEADERS - 1); i++) { + if(headers[i] == NULL) + continue; + if(regexec(&svc->becookie_re, headers[i], 4, matches, 0)) + continue; + if((n = matches[1].rm_eo - matches[1].rm_so) > KEY_SIZE) + n = KEY_SIZE; + strncpy(res, headers[i] + matches[1].rm_so, n); + res[n] = '\0'; + } + return res[0] != '\0'; +} + /* * Pick a random back-end from a candidate list */ @@ -542,6 +566,17 @@ rand_backend(BACKEND *be, int pri) return be; } +static BACKEND * +get_backend_by_key(BACKEND *be, const char *bekey) { + if (!bekey || !*bekey) return NULL; + while(be) { + if(be->bekey && strcmp(be->bekey, bekey)==0) return be; + be = be->next; + } + return NULL; +} + + /* * return a back-end based on a fixed hash value * this is used for session_ttl < 0 @@ -585,6 +620,7 @@ get_backend(SERVICE *const svc, const struct addrinfo *from_host, const char *re { BACKEND *res; char key[KEY_SIZE + 1]; + char bekey[KEY_SIZE + 1]; int ret_val, no_be; void *vp; @@ -593,21 +629,32 @@ get_backend(SERVICE *const svc, const struct addrinfo *from_host, const char *re no_be = (svc->tot_pri <= 0); + key[0]='\0'; + bekey[0]='\0'; switch(svc->sess_type) { case SESS_NONE: /* choose one back-end randomly */ - res = no_be? svc->emergency: rand_backend(svc->backends, random() % svc->tot_pri); + if (no_be) res = svc->emergency; + else if (get_bekey_from_HEADERS(bekey, svc, headers)) { + logmsg(LOG_DEBUG, "Found BEKEY %s in headers",bekey); + res = get_backend_by_key(svc->backends, bekey); + if (res==NULL || !res->alive ) res = rand_backend(svc->backends, random() % svc->tot_pri); else logmsg(LOG_DEBUG, "found matching backend by bekey"); + } else res = rand_backend(svc->backends, random() % svc->tot_pri); break; case SESS_IP: addr2str(key, KEY_SIZE, from_host, 1); - if(svc->sess_ttl < 0) + if(svc->sess_ttl < 0) { res = no_be? svc->emergency: hash_backend(svc->backends, svc->abs_pri, key); - else if((vp = t_find(svc->sessions, key)) == NULL) { + } else if((vp = t_find(svc->sessions, key)) == NULL) { if(no_be) res = svc->emergency; else { /* no session yet - create one */ - res = rand_backend(svc->backends, random() % svc->tot_pri); + if (get_bekey_from_HEADERS(bekey, svc, headers)) { + logmsg(LOG_DEBUG, "Found BEKEY %s in headers",bekey); + res = get_backend_by_key(svc->backends, bekey); + if (res==NULL || !res->alive ) res = rand_backend(svc->backends, random() % svc->tot_pri); else logmsg(LOG_DEBUG, "found matching backend by bekey"); + } else res = rand_backend(svc->backends, random() % svc->tot_pri); t_add(svc->sessions, key, &res, sizeof(res)); } } else @@ -623,7 +670,11 @@ get_backend(SERVICE *const svc, const struct addrinfo *from_host, const char *re res = svc->emergency; else { /* no session yet - create one */ - res = rand_backend(svc->backends, random() % svc->tot_pri); + if (get_bekey_from_HEADERS(bekey, svc, headers)) { + logmsg(LOG_DEBUG,"Found BEKEY %s in headers",bekey); + res = get_backend_by_key(svc->backends, bekey); + if (res==NULL || !res->alive ) res = rand_backend(svc->backends, random() % svc->tot_pri); else logmsg(LOG_DEBUG, "found matching backend by bekey"); + } else res = rand_backend(svc->backends, random() % svc->tot_pri); t_add(svc->sessions, key, &res, sizeof(res)); } } else @@ -642,13 +693,22 @@ get_backend(SERVICE *const svc, const struct addrinfo *from_host, const char *re res = svc->emergency; else { /* no session yet - create one */ - res = rand_backend(svc->backends, random() % svc->tot_pri); + if (get_bekey_from_HEADERS(bekey, svc, headers)) { + logmsg(LOG_DEBUG, "Found BEKEY %s in headers",bekey); + res = get_backend_by_key(svc->backends, bekey); + if (res==NULL || !res->alive ) res = rand_backend(svc->backends, random() % svc->tot_pri); else printf("found matching backend by bekey"); + } else res = rand_backend(svc->backends, random() % svc->tot_pri); t_add(svc->sessions, key, &res, sizeof(res)); } } else memcpy(&res, vp, sizeof(res)); } else { - res = no_be? svc->emergency: rand_backend(svc->backends, random() % svc->tot_pri); + if (no_be) res = svc->emergency; + else if (get_bekey_from_HEADERS(bekey, svc, headers)) { + logmsg(LOG_DEBUG, "Found BEKEY %s in headers",bekey); + res = get_backend_by_key(svc->backends, bekey); + if (res==NULL || !res->alive ) res = rand_backend(svc->backends, random() % svc->tot_pri); else logmsg(LOG_DEBUG, "found matching backend by bekey"); + } else res = rand_backend(svc->backends, random() % svc->tot_pri); } break; } @@ -1502,7 +1562,7 @@ thr_control(void *arg) { CTRL_CMD cmd; struct sockaddr sa; - int ctl, dummy, n, ret_val; + int ctl, dummy, n, ret_val, sz; LISTENER *lstn, dummy_lstn; SERVICE *svc, dummy_svc; BACKEND *be, dummy_be; @@ -1549,6 +1609,9 @@ thr_control(void *arg) (void)write(ctl, (void *)svc, sizeof(SERVICE)); for(be = svc->backends; be; be = be->next) { (void)write(ctl, (void *)be, sizeof(BACKEND)); + sz = be->bekey?strlen(be->bekey):0; + (void)write(ctl, (void *)&sz, sizeof(sz)); + if(sz>0) (void)write(ctl, (void *)be->bekey, sz); (void)write(ctl, be->addr.ai_addr, be->addr.ai_addrlen); if(be->ha_addr.ai_addrlen > 0) (void)write(ctl, be->ha_addr.ai_addr, be->ha_addr.ai_addrlen); @@ -1570,6 +1633,9 @@ thr_control(void *arg) (void)write(ctl, (void *)svc, sizeof(SERVICE)); for(be = svc->backends; be; be = be->next) { (void)write(ctl, (void *)be, sizeof(BACKEND)); + sz = be->bekey?strlen(be->bekey):0; + (void)write(ctl, (void *)&sz, sizeof(sz)); + if(sz>0) (void)write(ctl, (void *)be->bekey, sz); (void)write(ctl, be->addr.ai_addr, be->addr.ai_addrlen); if(be->ha_addr.ai_addrlen > 0) (void)write(ctl, be->ha_addr.ai_addr, be->ha_addr.ai_addrlen); @@ -1656,6 +1722,21 @@ thr_control(void *arg) if(ret_val = pthread_mutex_unlock(&svc->mut)) logmsg(LOG_WARNING, "thr_control() del session unlock: %s", strerror(ret_val)); break; + case CTRL_FLUSH_SESS: + if((svc = sel_svc(&cmd)) == NULL) { + logmsg(LOG_INFO, "thr_control() bad service %d/%d", cmd.listener, cmd.service); + break; + } + if((be = sel_be(&cmd)) == NULL) { + logmsg(LOG_INFO, "thr_control() bad backend %d/%d/%d", cmd.listener, cmd.service, cmd.backend); + break; + } + if(ret_val = pthread_mutex_lock(&svc->mut)) + logmsg(LOG_WARNING, "thr_control() del session lock: %s", strerror(ret_val)); + t_clean(svc->sessions, &be, sizeof(be)); + if(ret_val = pthread_mutex_unlock(&svc->mut)) + logmsg(LOG_WARNING, "thr_control() del session unlock: %s", strerror(ret_val)); + break; default: logmsg(LOG_WARNING, "thr_control() unknown command"); break; @@ -1687,3 +1768,19 @@ SSLINFO_callback(const SSL *ssl, int where, int rc) *reneg_state = RENEG_REJECT; } } + +DH *load_dh_params(char *file) +{ + DH *dh = NULL; + BIO *bio; + + if ((bio = BIO_new_file(file, "r")) == NULL) { + logmsg(LOG_WARNING, "Unable to open DH file - %s" ,file); + return NULL; + } + + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); + return dh; +} +