summaryrefslogtreecommitdiffstats
path: root/contrib/tcp_wrappers/fix_options.c
blob: b31c61a0382db15b579654b67bb3988eb38d96c0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
 /*
  * Routine to disable IP-level socket options. This code was taken from 4.4BSD
  * rlogind and kernel source, but all mistakes in it are my fault.
  *
  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
  *
  * $FreeBSD$
  */

#ifndef lint
static char sccsid[] = "@(#) fix_options.c 1.6 97/04/08 02:29:19";
#endif

#include <sys/types.h>
#include <sys/param.h>
#ifdef INET6
#include <sys/socket.h>
#endif
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netdb.h>
#include <stdio.h>
#include <syslog.h>

#ifndef IPOPT_OPTVAL
#define IPOPT_OPTVAL	0
#define IPOPT_OLEN	1
#endif

#include "tcpd.h"

#define BUFFER_SIZE	512		/* Was: BUFSIZ */

/* fix_options - get rid of IP-level socket options */

void
fix_options(request)
struct request_info *request;
{
#ifdef IP_OPTIONS
    unsigned char optbuf[BUFFER_SIZE / 3], *cp;
    char    lbuf[BUFFER_SIZE], *lp;
    int     optsize = sizeof(optbuf), ipproto;
    struct protoent *ip;
    int     fd = request->fd;
    unsigned int opt;
    int     optlen;
    struct in_addr dummy;
#ifdef INET6
    struct sockaddr_storage ss;
    int sslen;

    /*
     * check if this is AF_INET socket
     * XXX IPv6 support?
     */
    sslen = sizeof(ss);
    if (getsockname(fd, (struct sockaddr *)&ss, &sslen) < 0) {
	syslog(LOG_ERR, "getpeername: %m");
	clean_exit(request);
    }
    if (ss.ss_family != AF_INET)
	return;
#endif

    if ((ip = getprotobyname("ip")) != 0)
	ipproto = ip->p_proto;
    else
	ipproto = IPPROTO_IP;

    if (getsockopt(fd, ipproto, IP_OPTIONS, (char *) optbuf, &optsize) == 0
	&& optsize != 0) {

	/*
	 * Horror! 4.[34] BSD getsockopt() prepends the first-hop destination
	 * address to the result IP options list when source routing options
	 * are present (see <netinet/ip_var.h>), but produces no output for
	 * other IP options. Solaris 2.x getsockopt() does produce output for
	 * non-routing IP options, and uses the same format as BSD even when
	 * the space for the destination address is unused. The code below
	 * does the right thing with 4.[34]BSD derivatives and Solaris 2, but
	 * may occasionally miss source routing options on incompatible
	 * systems such as Linux. Their choice.
	 * 
	 * Look for source routing options. Drop the connection when one is
	 * found. Just wiping the IP options is insufficient: we would still
	 * help the attacker by providing a real TCP sequence number, and the
	 * attacker would still be able to send packets (blind spoofing). I
	 * discussed this attack with Niels Provos, half a year before the
	 * attack was described in open mailing lists.
	 * 
	 * It would be cleaner to just return a yes/no reply and let the caller
	 * decide how to deal with it. Resident servers should not terminate.
	 * However I am not prepared to make changes to internal interfaces
	 * on short notice.
	 */
#define ADDR_LEN sizeof(dummy.s_addr)

	for (cp = optbuf + ADDR_LEN; cp < optbuf + optsize; cp += optlen) {
	    opt = cp[IPOPT_OPTVAL];
	    if (opt == IPOPT_LSRR || opt == IPOPT_SSRR) {
		syslog(LOG_WARNING,
		   "refused connect from %s with IP source routing options",
		       eval_client(request));
		shutdown(fd, 2);
		return;
	    }
	    if (opt == IPOPT_EOL)
		break;
	    if (opt == IPOPT_NOP) {
		optlen = 1;
	    } else {
		optlen = cp[IPOPT_OLEN];
		if (optlen <= 0)		/* Do not loop! */
		    break;
	    }
	}
	lp = lbuf;
	for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3)
	    sprintf(lp, " %2.2x", *cp);
	syslog(LOG_NOTICE,
	       "connect from %s with IP options (ignored):%s",
	       eval_client(request), lbuf);
	if (setsockopt(fd, ipproto, IP_OPTIONS, (char *) 0, optsize) != 0) {
	    syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m");
	    shutdown(fd, 2);
	}
    }
#endif
}
OpenPOWER on IntegriCloud