dgaudet     98/02/03 01:53:51

  Modified:    src      CHANGES http_protocol.c
  Log:
  Ed's patch has been running on one of my 1.2 servers w/name and ip vhosts
  for 5 days now without a hassle.
  
  Ed says:
  
  > I posted a patch much like this some months ago, but Dean pointed out that
  > it still had a few problems.  So far as I can see, this covers all of
  > those; check_fulluri and reduce_uri (in mod_rewrite) can mess with
  > r->filename (removing http[s]://{hostname}[:{port}]), but they don't
  > change r->server, which is where the security hole lies.
  >
  > It also fixes check_fulluri to work for virtual hosts w/ a wildcard port
  > and/or multiple ports.
  >
  > It also fixes check_serverpath, which should make sure the server in
  > question can possibly be listening for this request (note that one of
  > either check_hostalias and check_serverpath will run).
  >
  > It's not needed for 1.3, since Dean's rewrite of the vhost code fixed it
  > in that.
  
  Submitted by: Ed Korthof <[EMAIL PROTECTED]>
  
  Revision  Changes    Path
  1.291     +7 -0      apache-1.2/src/CHANGES
  
  Index: CHANGES
  ===================================================================
  RCS file: /export/home/cvs/apache-1.2/src/CHANGES,v
  retrieving revision 1.290
  retrieving revision 1.291
  diff -u -r1.290 -r1.291
  --- CHANGES   1998/02/01 02:47:37     1.290
  +++ CHANGES   1998/02/03 09:53:48     1.291
  @@ -1,4 +1,11 @@
   Changes with Apache 1.2.6
  +  
  +  *) SECURITY: When a client connects to a particular port/addr, and
  +     gives a Host: header ensure that the virtual host requested can
  +     actually be reached via that port/addr.  [Ed Korthof <[EMAIL 
PROTECTED]>]
  +
  +  *) Support virtual hosts with wildcard port and/or multiple ports
  +     properly.  [Ed Korthof <[EMAIL PROTECTED]>]
   
     *) Fixed some case-sensitivity issues according to RFC2068.
        [Dean Gaudet]
  
  
  
  1.131     +112 -31   apache-1.2/src/http_protocol.c
  
  Index: http_protocol.c
  ===================================================================
  RCS file: /export/home/cvs/apache-1.2/src/http_protocol.c,v
  retrieving revision 1.130
  retrieving revision 1.131
  diff -u -r1.130 -r1.131
  --- http_protocol.c   1998/02/01 02:47:39     1.130
  +++ http_protocol.c   1998/02/03 09:53:49     1.131
  @@ -548,13 +548,15 @@
       }
   }
   
  -const char *check_fulluri (request_rec *r, const char *uri) {
  +const char *check_fulluri (request_rec *r, const char *uri)
  +{
     char *name, *host;
     int i;
     unsigned port;
  +  server_addr_rec * sar;
   
     /* This routine parses full URLs, if they match the server */
  -  if (strncmp(uri, "http://";, 7)) return uri;
  +  if (strncasecmp(uri, "http://";, 7)) return uri;
     name = pstrdup(r->pool, uri + 7);
     
     /* Find the hostname, assuming a valid request */
  @@ -567,7 +569,13 @@
     else port = 80;
   
     /* Make sure ports patch */
  -  if (port != r->server->port) return uri;
  +  if (port != r->server->port) {
  +    for (sar = r->server->addrs; sar; sar = sar->next) {
  +      if( (sar->host_port == 0) || (port == sar->host_port) )
  +        break;
  +    }
  +    if (!sar) return uri;
  +  }
   
     /* Save it for later use */
     r->hostname = pstrdup(r->pool, host);
  @@ -678,15 +686,29 @@
       }
   }
   
  -static void check_hostalias (request_rec *r) {
  +#define ADDR_MATCHES(addr1,addr2) \
  +   (addr1.s_addr == addr2.s_addr) || (addr1.s_addr == htonl(INADDR_ANY)) \
  +        || (addr1.s_addr == DEFAULT_VHOST_ADDR)
  +
  +static void check_hostalias (request_rec *r)
  +{
     const char *hostname=r->hostname;
     char *host = getword(r->pool, &hostname, ':');     /* Get rid of port */
     unsigned port = (*hostname) ? atoi(hostname) : 80;
  -  server_rec *s;
  +  server_rec *s = r->server;
  +  server_addr_rec * sar;
     int l;
   
  -  if (port && (port != r->server->port))
  -    return;
  +/* make sure the client can't spoof the port;
  + * have to check all possiblities to see if the server
  + * should be listening. */
  +  if (port != r->server->port) {
  +    for (sar = s->addrs; sar; sar = sar->next) {
  +      if ( (port == sar->host_port) || (sar->host_port == 0) )
  +        break;
  +    }
  +    if (!sar) return;
  +  }
   
     l = strlen(host)-1;
     if ((host[l]) == '.') {
  @@ -704,37 +726,68 @@
            configuration */
        continue;
       }
  -
  -    if ((!strcasecmp(host, s->server_hostname)) && (port == s->port)) {
  -      r->server = r->connection->server = s;
  -      if (r->hostlen && !strncmp(r->uri, "http://";, 7)) {
  -     r->uri += r->hostlen;
  -     parse_uri(r, r->uri);
  +/* ok, now there are several possibilities, and we're matching the
  + * hostname, the port, and r->connection->local_addr.  The last is
  + * required so as to only respond on an address to which this vhost
  + * should actually be listening.
  + *
  + * Either we can match s->server_name and s->port while matching
  + * against the ip address in a record in the s->addrs list *or* we
  + * can match s->server_name and a complete record in the s->addrs
  + * list *or* we can match the virtual host name and the address and
  + * the port of a record in the s->addrs list.
  + */
  +    if (!strcasecmp(host,s->server_hostname)) {   /* ServerName matches 
hostname */
  +      if (port == s->port) {                  /* possibly configured by Port 
*/
  +        for (sar = s->addrs; sar; sar = sar->next) {
  +          if 
(ADDR_MATCHES(sar->host_addr,r->connection->local_addr.sin_addr))
  +            break; /* SN matches, Port matches, and one IP addr matches */
  +        }
  +      } else {                             /* check to see if an addr 
matches */
  +        for (sar = s->addrs; sar; sar = sar->next) {
  +          if (((port == sar->host_port) || (sar->host_port == 0))
  +              && (ADDR_MATCHES(sar->host_addr,
  +                               r->connection->local_addr.sin_addr)))
  +            break; /* SN matches, and a addr matches IP & port */
  +        }
         }
  -    }
  +      if (sar) { /* we got a match */
  +        r->server = r->connection->server = s;
  +        if (r->hostlen && !strncasecmp(r->uri, "http://";, 7)) {
  +          r->uri += r->hostlen;
  +          parse_uri(r, r->uri);
  +          /* we still might want to do something below (ie. set r->proxyreq) 
*/
  +        }
  +      }
  +    } /* ServerName doesn't match */
   
  -    /* search all the names from <VirtualHost> directive */
  -    for( sar = s->addrs; sar; sar = sar->next ) {
  -      if( !strcasecmp( sar->virthost, host ) &&
  -       ( (sar->host_port == 0) || (port == sar->host_port) )) {
  -     r->server = r->connection->server = s;
  -     if( r->hostlen && !strncmp( r->uri, "http://";, 7) ) {
  -       r->uri += r->hostlen;
  -       r->proxyreq = 0;
  -     }
  +    /* now s->addrs list, include the names, from the VirtualHost directive 
*/
  +    for (sar = s->addrs; sar; sar = sar->next) {
  +      if (((sar->host_port==0) || (port==sar->host_port))
  +          && 
(ADDR_MATCHES(sar->host_addr,r->connection->local_addr.sin_addr))
  +          && !strcasecmp(host,sar->virthost)) {
  +        /* ok, an element in the addrs list matched all three items */
  +        r->server = r->connection->server = s;
  +        if (r->hostlen && !strncasecmp(r->uri, "http://";, 7)) {
  +          r->uri += r->hostlen;
  +          r->proxyreq = 0;
  +        }
         }
       }
   
  -    /* search all the aliases from ServerAlias directive */
  +    /* search all the aliases from ServerAlias directive
  +     * ServerAlias acts like a wildcard, so as to help deal with the
  +     * transition when the DNS for a given host changes.
  +     */
       names = s->names;
  -    if( names ) {
  +    if (names) {
         while (*names) {
        char *name = getword_conf (r->pool, &names);
   
        if ((is_matchexp(name) && !strcasecmp_match(host, name)) ||
            (!strcasecmp(host, name))) {
          r->server = r->connection->server = s;
  -       if (r->hostlen && !strncmp(r->uri, "http://";, 7)) {
  +       if (r->hostlen && !strncasecmp(r->uri, "http://";, 7)) {
            r->uri += r->hostlen;
            r->proxyreq = 0;
          }
  @@ -744,8 +797,11 @@
     }
   }
   
  -void check_serverpath (request_rec *r) {
  +void check_serverpath (request_rec *r)
  +{
     server_rec *s;
  +  server_addr_rec * sar = NULL;
  +  int port = r->connection->local_addr.sin_port;
   
     /* This is in conjunction with the ServerPath code in
      * http_core, so we get the right host attached to a non-
  @@ -753,10 +809,35 @@
      */
   
     for (s = r->server->next; s; s = s->next) {
  -    if (s->addrs && s->path && !strncmp(r->uri, s->path, s->pathlen) &&
  -     (s->path[s->pathlen - 1] == '/' ||
  -      r->uri[s->pathlen] == '/' ||
  -      r->uri[s->pathlen] == '\0'))
  +    /* we should check to make sure that this server should be listening
  +     * at all.
  +     *
  +     * this code is duplicated in check_hostalias, but only one of these
  +     * two functions runs for a given request.
  +     */
  +    if (!s->addrs) continue;
  +
  +    if( (port == s->port) ) {
  +        for(sar = s->addrs; sar; sar = sar->next) {
  +          if(ADDR_MATCHES(sar->host_addr, 
r->connection->local_addr.sin_addr))
  +            break;
  +        }
  +    }
  +    else {
  +        for(sar = s->addrs; sar; sar = sar->next) {
  +          if( ( (port == sar->host_port) || (sar->host_port == 0) )
  +              && ( ADDR_MATCHES(sar->host_addr,
  +                                r->connection->local_addr.sin_addr) ) )
  +            break;
  +        }
  +    }
  +
  +    if (!sar) continue; /* no match */
  +
  +    if (s->path && !strncmp(r->uri, s->path, s->pathlen)
  +     && (s->path[s->pathlen - 1] == '/'
  +         || r->uri[s->pathlen] == '/'
  +         || r->uri[s->pathlen] == '\0'))
         r->server = r->connection->server = s;
     }
   }
  
  
  

Reply via email to