Update from Debian version 2.7-1.2.
[pound.git] / http.c
diff --git a/http.c b/http.c
old mode 100755 (executable)
new mode 100644 (file)
index d81f7dc..749b279
--- a/http.c
+++ b/http.c
@@ -46,24 +46,14 @@ err_reply(BIO *const c, const char *head, const char *txt)
     return;
 }
 
-
-static char *redir_pre = "<html><head><title>Redirect</title></head><body><h1>Redirect</h1><p>You should go to <a href=\"";
-static char *redir_post = "\">here</a></p></body></html>";
-
-static char hexchar(char a) {
-  a = a & 0xF;
-  if (a > 9) return (a+'a'-10);
-  return a+'0';
-}
-
 /*
  * Reply with a redirect
  */
 static void
 redirect_reply(BIO *const c, const char *url, const int code)
 {
-    char    rep[MAXBUF], urlbuf[MAXBUF], ch, *code_msg;
-    int     i,j;
+    char    rep[MAXBUF], cont[MAXBUF], safe_url[MAXBUF], *code_msg;
+    int     i, j;
 
     switch(code) {
     case 301:
@@ -76,30 +66,26 @@ redirect_reply(BIO *const c, const char *url, const int code)
         code_msg = "Found";
         break;
     }
-    for(i=0,j=0; url[i] && j<MAXBUF-1; i++) {
-       ch = url[i];
-       if (
-           (ch>= 'A' && ch <='Z') ||
-           (ch>= 'a' && ch <='z') ||
-           (ch>= '0' && ch <='9') ||
-            ch == '-' || ch == '_' || ch == '.' || ch == ':' || ch == '/' || ch == '?' || ch == '&' || ch == ';' || ch == '=') {
-
-           urlbuf[j++] = ch;
-           continue;
-       }
-       if (j>MAXBUF-4) break;
-       urlbuf[j++] = '%';
-       urlbuf[j++] = hexchar(ch>>4);
-       urlbuf[j++] = hexchar(ch);
-    }
-    urlbuf[j++] = 0;
+    /*
+     * Make sure to return a safe version of the URL (otherwise CSRF becomes a possibility)
+     */
+    memset(safe_url, 0, MAXBUF);
+    for(i = j = 0; i < MAXBUF && j < MAXBUF && url[i]; i++)
+        if(isalnum(url[i]) || url[i] == '_' || url[i] == '.' || url[i] == ':' || url[i] == '/'
+        || url[i] == '?' || url[i] == '&' || url[i] == ';' || url[i] == '-' || url[i] == '=')
+            safe_url[j++] = url[i];
+        else {
+            sprintf(safe_url + j, "%%%02x", url[i]);
+            j += 3;
+        }
+    snprintf(cont, sizeof(cont),
+        "<html><head><title>Redirect</title></head><body><h1>Redirect</h1><p>You should go to <a href=\"%s\">%s</a></p></body></html>",
+        safe_url, safe_url);
     snprintf(rep, sizeof(rep),
-        "HTTP/1.0 %d %s\r\nLocation: %s\r\nContent-Type: text/html\r\nContent-Length: %u\r\n\r\n",
-        code, code_msg, urlbuf, (unsigned int)(strlen(redir_pre)+strlen(redir_post)+strlen(urlbuf)));
+        "HTTP/1.0 %d %s\r\nLocation: %s\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n",
+        code, code_msg, safe_url, strlen(cont));
     BIO_write(c, rep, strlen(rep));
-    BIO_write(c, redir_pre, strlen(redir_pre));
-    BIO_write(c, urlbuf, strlen(urlbuf));
-    BIO_write(c, redir_post, strlen(redir_post));
+    BIO_write(c, cont, strlen(cont));
     BIO_flush(c);
     return;
 }
@@ -274,7 +260,7 @@ copy_chunks(BIO *const cl, BIO *const be, LONG *res_bytes, const int no_write, c
 static int  err_to = -1;
 
 typedef struct {
-    int timeout;
+    int         timeout;
     RENEG_STATE *reneg_state;
 } BIO_ARG;
 
@@ -293,21 +279,23 @@ bio_callback(BIO *const bio, const int cmd, const char *argp, int argi, long arg
         return ret;
 
     /* a time-out already occured */
-    if((bio_arg = (BIO_ARG*)BIO_get_callback_arg(bio))==NULL) return ret;
+    if((bio_arg = (BIO_ARG*)BIO_get_callback_arg(bio))==NULL)
+        return ret;
     if((to = bio_arg->timeout * 1000) < 0) {
         errno = ETIMEDOUT;
         return -1;
     }
 
     /* Renegotiations */
+    /* logmsg(LOG_NOTICE, "RENEG STATE %d", bio_arg->reneg_state==NULL?-1:*bio_arg->reneg_state); */
     if (bio_arg->reneg_state != NULL && *bio_arg->reneg_state == RENEG_ABORT) {
         logmsg(LOG_NOTICE, "REJECTING renegotiated session");
         errno = ECONNABORTED;
         return -1;
     }
 
-    //logmsg(LOG_NOTICE, "TO %d", to);
-    if (to == 0) return ret;
+    if (to == 0)
+        return ret;
 
     for(;;) {
         memset(&p, 0, sizeof(p));
@@ -547,7 +535,6 @@ do_http(thr_arg *arg)
     regmatch_t          matches[4];
     struct linger       l;
     double              start_req, end_req;
-
     RENEG_STATE         reneg_state;
     BIO_ARG             ba1, ba2;
 
@@ -556,7 +543,6 @@ do_http(thr_arg *arg)
     ba2.reneg_state = &reneg_state;
     ba1.timeout = 0;
     ba2.timeout = 0;
-
     from_host = ((thr_arg *)arg)->from_host;
     memcpy(&from_host_addr, from_host.ai_addr, from_host.ai_addrlen);
     from_host.ai_addr = (struct sockaddr *)&from_host_addr;
@@ -565,7 +551,8 @@ do_http(thr_arg *arg)
     free(((thr_arg *)arg)->from_host.ai_addr);
     free(arg);
 
-    if(lstn->allow_client_reneg) reneg_state = RENEG_ALLOW;
+    if(lstn->allow_client_reneg)
+        reneg_state = RENEG_ALLOW;
 
     n = 1;
     setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&n, sizeof(n));
@@ -590,7 +577,6 @@ do_http(thr_arg *arg)
         close(sock);
         return;
     }
-
     ba1.timeout = lstn->to;
     BIO_set_callback_arg(cl, (char *)&ba1);
     BIO_set_callback(cl, bio_callback);
@@ -628,6 +614,7 @@ do_http(thr_arg *arg)
             && SSL_get_verify_result(ssl) != X509_V_OK) {
                 addr2str(caddr, MAXBUF - 1, &from_host, 1);
                 logmsg(LOG_NOTICE, "Bad certificate from %s", caddr);
+                X509_free(x509);
                 BIO_reset(cl);
                 BIO_free_all(cl);
                 return;
@@ -640,6 +627,8 @@ do_http(thr_arg *arg)
 
     if((bb = BIO_new(BIO_f_buffer())) == NULL) {
         logmsg(LOG_WARNING, "(%lx) BIO_new(buffer) failed", pthread_self());
+        if(x509 != NULL)
+            X509_free(x509);
         BIO_reset(cl);
         BIO_free_all(cl);
         return;
@@ -735,9 +724,21 @@ do_http(thr_arg *arg)
             case HEADER_CONTENT_LENGTH:
                 if(chunked || cont >= 0L)
                     headers_ok[n] = 0;
-                else
-                    if((cont = ATOL(buf)) < 0L)
-                        headers_ok[n] = 0;
+                else {
+                     if((cont = ATOL(buf)) < 0L)
+                         headers_ok[n] = 0;
+                    if(is_rpc == 1 && (cont < 0x20000L || cont > 0x80000000L))
+                        is_rpc = -1;
+                }
+                break;
+            case HEADER_EXPECT:
+                /*
+                 * we do NOT support the "Expect: 100-continue" headers
+                 * support may involve severe performance penalties (non-responding back-end, etc)
+                 * as a stop-gap measure we just skip these headers
+                 */
+                if(!strcasecmp("100-continue", buf))
+                    headers_ok[n] = 0;
                 break;
             case HEADER_ILLEGAL:
                 if(lstn->log_level > 0) {
@@ -1009,12 +1010,12 @@ do_http(thr_arg *arg)
 
         /* if SSL put additional headers for client certificate */
         if(cur_backend->be_type == 0 && ssl != NULL) {
-            SSL_CIPHER  *cipher;
+            const SSL_CIPHER  *cipher;
 
             if((cipher = SSL_get_current_cipher(ssl)) != NULL) {
                 SSL_CIPHER_description(cipher, buf, MAXBUF - 1);
                 strip_eol(buf);
-                if(BIO_printf(be, "X-SSL-cipher: %s\r\n", buf) <= 0) {
+                if(BIO_printf(be, "X-SSL-cipher: %s/%s\r\n", SSL_get_version(ssl), buf) <= 0) {
                     str_be(buf, MAXBUF - 1, cur_backend);
                     end_req = cur_time();
                     logmsg(LOG_WARNING, "(%lx) e500 error write X-SSL-cipher to %s: %s (%.3f sec)",
@@ -1396,8 +1397,12 @@ do_http(thr_arg *arg)
                 case HEADER_CONTENT_LENGTH:
                     cont = ATOL(buf);
                     /* treat RPC_OUT_DATA like reply without content-length */
-                    if(is_rpc == 0 && cont == 0x40000000L)
-                        cont = -1L;
+                    if(is_rpc == 0) {
+                        if(cont >= 0x20000L && cont <= 0x80000000L)
+                            cont = -1L;
+                        else
+                            is_rpc = -1;
+                    }
                     break;
                 case HEADER_LOCATION:
                     if(v_host[0] && need_rewrite(lstn->rewr_loc, buf, loc_path, v_host, lstn, cur_backend)) {
@@ -1554,6 +1559,12 @@ do_http(thr_arg *arg)
         memset(s_res_bytes, 0, LOG_BYTES_SIZE);
         log_bytes(s_res_bytes, res_bytes);
         addr2str(caddr, MAXBUF - 1, &from_host, 1);
+        if(anonymise) {
+            char    *last;
+
+            if((last = strrchr(caddr, '.')) != NULL || (last = strrchr(caddr, ':')) != NULL)
+                strcpy(++last, "0");
+        }
         str_be(buf, MAXBUF - 1, cur_backend);
         switch(lstn->log_level) {
         case 0:
@@ -1625,7 +1636,7 @@ thr_http(void *dummy)
 
     for(;;) {
         while((arg = get_thr_arg()) == NULL)
-            logmsg(LOG_WARNING, "NULL get_thr_arg");
+            logmsg(LOG_NOTICE, "NULL get_thr_arg");
         do_http(arg);
     }
 }