CVE-2020-7670 (C, published 2020-06-10T16:15Z)
Description: value: agoo prior to 2.14.0 allows request smuggling attacks where agoo is used as a backend and a frontend proxy also being vulnerable. HTTP pipelining issues and request smuggling attacks might be possible due to incorrect Content-Length and Transfer encoding header parsing. It is possible to conduct HTTP request smuggling attacks where `agoo` is used as part of a chain of backend servers due to insufficient `Content-Length` and `Transfer Encoding` parsing.
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-444 \u2014 Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling')
The product acts as an intermediary HTTP agent
         (such as a proxy or firewall) in the data flow between two
         entities such as a client and server, but it does not
         interpret malformed HTTP requests or responses in ways that
         are consistent with how the messages will be processed by
         those entities that are at the ultimate destination.
Repository: https://github.com/ohler55/agoo
Files changed:
CHANGELOG.md
ext/agoo/con.c
ext/agoo/con.h
ext/agoo/http.c
ext/agoo/req.h
ext/agoo/request.c
ext/agoo/server.c
ext/agoo/websocket.c
lib/agoo/version.rb
test/base_handler_test.rb
test/rack_handler_test.rb
Commit message: Remote addr (#99)

* REMOTE_ADDR added

* Ready for merge

Vulnerable code:
// clearly is not consisten with RFC7230 so stick with the RFC.
    "Accept-Language",
    "Archived-At",
    "Comments",
    "Content-Base",
    "Content-Disposition",
    "Content-ID",
    "Content-Language",
    "Content-Location",
    "Content-MD5",
    "Content-Transfer-Encoding",
    "Content-Type",
    "Date",
    "Date",
    "EDIINT-Features",
    "Expires",
    "Expires",
    "From",
    "From",
    "Jabber-ID",
    "Keywords",
    "MIME-Version",
    "Message-ID",
    "Message-ID",
    "Organization",
    "PICS-Label",
    "References",
    "Reply-To",
    "Subject",
    "Supersedes",
    "User-Agent",
    "X-Archived-At",
    
    
    
	rb_hash_aset(hh, content_type_val, rb_str_new(val, vlen));
	rb_hash_aset(hh, content_length_val, rb_str_new(val, vlen));
	rb_hash_aset(hh, rb_str_new(hkey, klen + 5), sval);
	rb_hash_aset(env, server_name_val, req_server_name(req));
		    agoo_log_cat(&agoo_con_cat, "Server with pid %d accepted connection %llu on %s [%d]",
				 getpid(), (unsigned long long)cnt, b->id, con->sock);
  VERSION = '2.13.0'
  
    
  
    

Fixed code:
## [2.14.0] - 2020-11-07

### Added

- REMOTE_ADDR element added to requests/env argument to `call()`.

- Added check for multiple Content-Length headers.

- Multiple occurrances of a header are now passed to the Rack `call()` method as an array.

	// It would be better to get this information in server.c after
	// accept() but that does not work on macOS so instead a call to
	// getpeername() is used instead.
	struct sockaddr_storage	addr;
	socklen_t		len = sizeof(addr);

	getpeername(sock, (struct sockaddr*)&addr, &len);
	if (addr.ss_family == AF_INET) {
	    struct sockaddr_in	*s = (struct sockaddr_in*)&addr;

	    inet_ntop(AF_INET, &s->sin_addr, c->remote, sizeof(c->remote));
	} else {
	    struct sockaddr_in6	*s = (struct sockaddr_in6*)&addr;

	    inet_ntop(AF_INET6, &s->sin6_addr, c->remote, sizeof(c->remote));
	}
    memcpy(c->req->remote, c->remote, sizeof(c->remote));
#include <arpa/inet.h>
    char			remote[INET6_ADDRSTRLEN];
// clearly is not consistent with RFC7230 so stick with the RFC.
    "DNT",
    "Front-End-Https",
    "Proxy-Connection",
    "Save-Data",
    "Upgrade-Insecure-Requests",
    "X-ATT-DeviceId",
    "X-Correlation-ID",
    "X-Csrf-Token",
    "X-Forwarded-For",
    "X-Forwarded-Host",
    "X-Forwarded-Proto",
    "X-Http-Method-Override",
    "X-Request-ID",
    "X-Requested-With",
    "X-UIDH",
    "X-Wap-Profile",



#include <arpa/inet.h>
    char			remote[INET6_ADDRSTRLEN];
static VALUE	remote_addr_val = Qundef;
static VALUE
req_remote_addr(agooReq r) {

    if (NULL == r) {
	rb_raise(rb_eArgError, "Request is no longer valid.");
    }
    return rb_str_new(r->remote, strlen(r->remote));
}

/* Document-method: remote_addr
 *
 * call-seq: remote_addr()
 *
 * Returns the remote address.
 */
static VALUE
remote_addr(VALUE self) {
    return req_remote_addr((agooReq)DATA_PTR(self));
}


    VALUE	v;

	if (Qnil == (v = rb_hash_lookup2(hh, content_type_val, Qnil))) {
	    rb_hash_aset(hh, content_type_val, rb_str_new(val, vlen));
	} else {
	    volatile VALUE	a = rb_ary_new();

	    rb_ary_push(a, v);
	    rb_ary_push(a, rb_str_new(val, vlen));
	    rb_hash_aset(hh, content_type_val, a);
	}
	if (Qnil == (v = rb_hash_lookup2(hh, content_length_val, Qnil))) {
	    rb_hash_aset(hh, content_length_val, rb_str_new(val, vlen));
	} else {
	    rb_raise(rb_eArgError, "Multiple Content-Length headers.");
	}
	volatile VALUE	kval;
	kval = rb_str_new(hkey, klen + 5);
	if (Qnil == (v = rb_hash_lookup2(hh, kval, Qnil))) {
	    rb_hash_aset(hh, kval, sval);
	} else {
	    volatile VALUE	a = rb_ary_new();

	    rb_ary_push(a, v);
	    rb_ary_push(a, sval);
	    rb_hash_aset(hh, kval, a);
	}
	rb_hash_aset(env, remote_addr_val, req_remote_addr(req));
	rb_hash_aset(env, server_name_val, req_server_name(req));
    rb_define_method(req_class, "remote_addr", remote_addr, 0);
    remote_addr_val = rb_str_new_cstr("REMOTE_ADDR");		rb_gc_register_address(&remote_addr_val);
		    agoo_log_cat(&agoo_con_cat, "Server with pid %d accepted connection %llu on %s [%d] from %s",
				 getpid(), (unsigned long long)cnt, b->id, con->sock, con->remote);
    memcpy(c->req->remote, c->remote, sizeof(c->remote));
  VERSION = '2.14.0'




      "REMOTE_ADDR" => "127.0.0.1",