1 |
|
|
/* $OpenBSD: pf_osfp.c,v 1.40 2017/04/23 11:37:11 sthen Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2003 Mike Frantzen <frantzen@w4g.org> |
5 |
|
|
* |
6 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
7 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
8 |
|
|
* copyright notice and this permission notice appear in all copies. |
9 |
|
|
* |
10 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 |
|
|
* |
18 |
|
|
*/ |
19 |
|
|
|
20 |
|
|
#include <sys/param.h> |
21 |
|
|
#include <sys/socket.h> |
22 |
|
|
#ifdef _KERNEL |
23 |
|
|
#include <sys/systm.h> |
24 |
|
|
#include <sys/pool.h> |
25 |
|
|
#endif /* _KERNEL */ |
26 |
|
|
#include <sys/queue.h> |
27 |
|
|
#include <sys/mbuf.h> |
28 |
|
|
#include <sys/syslog.h> |
29 |
|
|
|
30 |
|
|
#include <net/if.h> |
31 |
|
|
|
32 |
|
|
#include <netinet/in.h> |
33 |
|
|
#include <netinet/ip.h> |
34 |
|
|
#include <netinet/ip_icmp.h> |
35 |
|
|
#include <netinet/tcp.h> |
36 |
|
|
#include <netinet/udp.h> |
37 |
|
|
|
38 |
|
|
#ifdef INET6 |
39 |
|
|
#include <netinet/ip6.h> |
40 |
|
|
#include <netinet/icmp6.h> |
41 |
|
|
#endif /* INET6 */ |
42 |
|
|
|
43 |
|
|
#include <net/pfvar.h> |
44 |
|
|
#include <net/pfvar_priv.h> |
45 |
|
|
|
46 |
|
|
#ifdef _KERNEL |
47 |
|
|
typedef struct pool pool_t; |
48 |
|
|
|
49 |
|
|
#else /* !_KERNEL */ |
50 |
|
|
/* Userland equivalents so we can lend code to tcpdump et al. */ |
51 |
|
|
|
52 |
|
|
#include <arpa/inet.h> |
53 |
|
|
#include <errno.h> |
54 |
|
|
#include <stdio.h> |
55 |
|
|
#include <stdlib.h> |
56 |
|
|
#include <string.h> |
57 |
|
|
#include <netdb.h> |
58 |
|
|
#define pool_t int |
59 |
|
|
#define pool_get(pool, flags) malloc(*(pool)) |
60 |
|
|
#define pool_put(pool, item) free(item) |
61 |
|
|
#define pool_init(pool, size, a, ao, f, m, p) (*(pool)) = (size) |
62 |
|
|
|
63 |
|
|
#ifdef PFDEBUG |
64 |
|
|
#include <sys/stdarg.h> /* for DPFPRINTF() */ |
65 |
|
|
#endif /* PFDEBUG */ |
66 |
|
|
|
67 |
|
|
#endif /* _KERNEL */ |
68 |
|
|
|
69 |
|
|
SLIST_HEAD(pf_osfp_list, pf_os_fingerprint) pf_osfp_list; |
70 |
|
|
pool_t pf_osfp_entry_pl; |
71 |
|
|
pool_t pf_osfp_pl; |
72 |
|
|
|
73 |
|
|
struct pf_os_fingerprint *pf_osfp_find(struct pf_osfp_list *, |
74 |
|
|
struct pf_os_fingerprint *, u_int8_t); |
75 |
|
|
struct pf_os_fingerprint *pf_osfp_find_exact(struct pf_osfp_list *, |
76 |
|
|
struct pf_os_fingerprint *); |
77 |
|
|
void pf_osfp_insert(struct pf_osfp_list *, |
78 |
|
|
struct pf_os_fingerprint *); |
79 |
|
|
|
80 |
|
|
|
81 |
|
|
#ifdef _KERNEL |
82 |
|
|
/* |
83 |
|
|
* Passively fingerprint the OS of the host (IPv4 TCP SYN packets only) |
84 |
|
|
* Returns the list of possible OSes. |
85 |
|
|
*/ |
86 |
|
|
struct pf_osfp_enlist * |
87 |
|
|
pf_osfp_fingerprint(struct pf_pdesc *pd) |
88 |
|
|
{ |
89 |
|
|
struct tcphdr *th = &pd->hdr.tcp; |
90 |
|
|
struct ip *ip = NULL; |
91 |
|
|
struct ip6_hdr *ip6 = NULL; |
92 |
|
|
char hdr[60]; |
93 |
|
|
|
94 |
|
|
if (pd->proto != IPPROTO_TCP) |
95 |
|
|
return (NULL); |
96 |
|
|
|
97 |
|
|
switch (pd->af) { |
98 |
|
|
case AF_INET: |
99 |
|
|
ip = mtod(pd->m, struct ip *); |
100 |
|
|
break; |
101 |
|
|
case AF_INET6: |
102 |
|
|
ip6 = mtod(pd->m, struct ip6_hdr *); |
103 |
|
|
break; |
104 |
|
|
} |
105 |
|
|
if (!pf_pull_hdr(pd->m, pd->off, hdr, th->th_off << 2, NULL, NULL, |
106 |
|
|
pd->af)) |
107 |
|
|
return (NULL); |
108 |
|
|
|
109 |
|
|
return (pf_osfp_fingerprint_hdr(ip, ip6, (struct tcphdr *)hdr)); |
110 |
|
|
} |
111 |
|
|
#endif /* _KERNEL */ |
112 |
|
|
|
113 |
|
|
struct pf_osfp_enlist * |
114 |
|
|
pf_osfp_fingerprint_hdr(const struct ip *ip, const struct ip6_hdr *ip6, |
115 |
|
|
const struct tcphdr *tcp) |
116 |
|
|
{ |
117 |
|
|
struct pf_os_fingerprint fp, *fpresult; |
118 |
|
|
int cnt, optlen = 0; |
119 |
|
|
const u_int8_t *optp; |
120 |
|
|
#ifdef _KERNEL |
121 |
|
|
char srcname[128]; |
122 |
|
|
#else /* !_KERNEL */ |
123 |
|
|
char srcname[NI_MAXHOST]; |
124 |
|
|
#endif /* _KERNEL */ |
125 |
|
|
|
126 |
|
|
if ((tcp->th_flags & (TH_SYN|TH_ACK)) != TH_SYN) |
127 |
|
|
return (NULL); |
128 |
|
|
if (ip) { |
129 |
|
|
if ((ip->ip_off & htons(IP_OFFMASK)) != 0) |
130 |
|
|
return (NULL); |
131 |
|
|
} |
132 |
|
|
|
133 |
|
|
memset(&fp, 0, sizeof(fp)); |
134 |
|
|
|
135 |
|
|
if (ip) { |
136 |
|
|
#ifndef _KERNEL |
137 |
|
|
struct sockaddr_in sin; |
138 |
|
|
#endif /* _KERNEL */ |
139 |
|
|
|
140 |
|
|
fp.fp_psize = ntohs(ip->ip_len); |
141 |
|
|
fp.fp_ttl = ip->ip_ttl; |
142 |
|
|
if (ip->ip_off & htons(IP_DF)) |
143 |
|
|
fp.fp_flags |= PF_OSFP_DF; |
144 |
|
|
#ifdef _KERNEL |
145 |
|
|
inet_ntop(AF_INET, &ip->ip_src, srcname, sizeof(srcname)); |
146 |
|
|
#else /* !_KERNEL */ |
147 |
|
|
memset(&sin, 0, sizeof(sin)); |
148 |
|
|
sin.sin_family = AF_INET; |
149 |
|
|
sin.sin_len = sizeof(struct sockaddr_in); |
150 |
|
|
sin.sin_addr = ip->ip_src; |
151 |
|
|
(void)getnameinfo((struct sockaddr *)&sin, |
152 |
|
|
sizeof(struct sockaddr_in), srcname, sizeof(srcname), |
153 |
|
|
NULL, 0, NI_NUMERICHOST); |
154 |
|
|
#endif /* _KERNEL */ |
155 |
|
|
} |
156 |
|
|
#ifdef INET6 |
157 |
|
|
else if (ip6) { |
158 |
|
|
#ifndef _KERNEL |
159 |
|
|
struct sockaddr_in6 sin6; |
160 |
|
|
#endif /* !_KERNEL */ |
161 |
|
|
|
162 |
|
|
/* jumbo payload? */ |
163 |
|
|
fp.fp_psize = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen); |
164 |
|
|
fp.fp_ttl = ip6->ip6_hlim; |
165 |
|
|
fp.fp_flags |= PF_OSFP_DF; |
166 |
|
|
fp.fp_flags |= PF_OSFP_INET6; |
167 |
|
|
#ifdef _KERNEL |
168 |
|
|
inet_ntop(AF_INET6, &ip6->ip6_src, srcname, sizeof(srcname)); |
169 |
|
|
#else /* !_KERNEL */ |
170 |
|
|
memset(&sin6, 0, sizeof(sin6)); |
171 |
|
|
sin6.sin6_family = AF_INET6; |
172 |
|
|
sin6.sin6_len = sizeof(struct sockaddr_in6); |
173 |
|
|
sin6.sin6_addr = ip6->ip6_src; |
174 |
|
|
(void)getnameinfo((struct sockaddr *)&sin6, |
175 |
|
|
sizeof(struct sockaddr_in6), srcname, sizeof(srcname), |
176 |
|
|
NULL, 0, NI_NUMERICHOST); |
177 |
|
|
#endif /* !_KERNEL */ |
178 |
|
|
} |
179 |
|
|
#endif /* INET6 */ |
180 |
|
|
else |
181 |
|
|
return (NULL); |
182 |
|
|
fp.fp_wsize = ntohs(tcp->th_win); |
183 |
|
|
|
184 |
|
|
|
185 |
|
|
cnt = (tcp->th_off << 2) - sizeof(*tcp); |
186 |
|
|
optp = (const u_int8_t *)((const char *)tcp + sizeof(*tcp)); |
187 |
|
|
for (; cnt > 0; cnt -= optlen, optp += optlen) { |
188 |
|
|
if (*optp == TCPOPT_EOL) |
189 |
|
|
break; |
190 |
|
|
|
191 |
|
|
fp.fp_optcnt++; |
192 |
|
|
if (*optp == TCPOPT_NOP) { |
193 |
|
|
fp.fp_tcpopts = (fp.fp_tcpopts << PF_OSFP_TCPOPT_BITS) | |
194 |
|
|
PF_OSFP_TCPOPT_NOP; |
195 |
|
|
optlen = 1; |
196 |
|
|
} else { |
197 |
|
|
if (cnt < 2) |
198 |
|
|
return (NULL); |
199 |
|
|
optlen = optp[1]; |
200 |
|
|
if (optlen > cnt || optlen < 2) |
201 |
|
|
return (NULL); |
202 |
|
|
switch (*optp) { |
203 |
|
|
case TCPOPT_MAXSEG: |
204 |
|
|
if (optlen >= TCPOLEN_MAXSEG) |
205 |
|
|
memcpy(&fp.fp_mss, &optp[2], |
206 |
|
|
sizeof(fp.fp_mss)); |
207 |
|
|
fp.fp_tcpopts = (fp.fp_tcpopts << |
208 |
|
|
PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_MSS; |
209 |
|
|
fp.fp_mss = ntohs(fp.fp_mss); |
210 |
|
|
break; |
211 |
|
|
case TCPOPT_WINDOW: |
212 |
|
|
if (optlen >= TCPOLEN_WINDOW) |
213 |
|
|
memcpy(&fp.fp_wscale, &optp[2], |
214 |
|
|
sizeof(fp.fp_wscale)); |
215 |
|
|
fp.fp_tcpopts = (fp.fp_tcpopts << |
216 |
|
|
PF_OSFP_TCPOPT_BITS) | |
217 |
|
|
PF_OSFP_TCPOPT_WSCALE; |
218 |
|
|
break; |
219 |
|
|
case TCPOPT_SACK_PERMITTED: |
220 |
|
|
fp.fp_tcpopts = (fp.fp_tcpopts << |
221 |
|
|
PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_SACK; |
222 |
|
|
break; |
223 |
|
|
case TCPOPT_TIMESTAMP: |
224 |
|
|
if (optlen >= TCPOLEN_TIMESTAMP) { |
225 |
|
|
u_int32_t ts; |
226 |
|
|
memcpy(&ts, &optp[2], sizeof(ts)); |
227 |
|
|
if (ts == 0) |
228 |
|
|
fp.fp_flags |= PF_OSFP_TS0; |
229 |
|
|
|
230 |
|
|
} |
231 |
|
|
fp.fp_tcpopts = (fp.fp_tcpopts << |
232 |
|
|
PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_TS; |
233 |
|
|
break; |
234 |
|
|
default: |
235 |
|
|
return (NULL); |
236 |
|
|
} |
237 |
|
|
} |
238 |
|
|
optlen = MAX(optlen, 1); /* paranoia */ |
239 |
|
|
} |
240 |
|
|
|
241 |
|
|
DPFPRINTF(LOG_INFO, |
242 |
|
|
"fingerprinted %s:%d %d:%d:%d:%d:%llx (%d) " |
243 |
|
|
"(TS=%s,M=%s%d,W=%s%d)", |
244 |
|
|
srcname, ntohs(tcp->th_sport), |
245 |
|
|
fp.fp_wsize, fp.fp_ttl, (fp.fp_flags & PF_OSFP_DF) != 0, |
246 |
|
|
fp.fp_psize, (long long int)fp.fp_tcpopts, fp.fp_optcnt, |
247 |
|
|
(fp.fp_flags & PF_OSFP_TS0) ? "0" : "", |
248 |
|
|
(fp.fp_flags & PF_OSFP_MSS_MOD) ? "%" : |
249 |
|
|
(fp.fp_flags & PF_OSFP_MSS_DC) ? "*" : "", |
250 |
|
|
fp.fp_mss, |
251 |
|
|
(fp.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" : |
252 |
|
|
(fp.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "", |
253 |
|
|
fp.fp_wscale); |
254 |
|
|
|
255 |
|
|
if ((fpresult = pf_osfp_find(&pf_osfp_list, &fp, |
256 |
|
|
PF_OSFP_MAXTTL_OFFSET))) |
257 |
|
|
return (&fpresult->fp_oses); |
258 |
|
|
return (NULL); |
259 |
|
|
} |
260 |
|
|
|
261 |
|
|
/* Match a fingerprint ID against a list of OSes */ |
262 |
|
|
int |
263 |
|
|
pf_osfp_match(struct pf_osfp_enlist *list, pf_osfp_t os) |
264 |
|
|
{ |
265 |
|
|
struct pf_osfp_entry *entry; |
266 |
|
|
int os_class, os_version, os_subtype; |
267 |
|
|
int en_class, en_version, en_subtype; |
268 |
|
|
|
269 |
|
|
if (os == PF_OSFP_ANY) |
270 |
|
|
return (1); |
271 |
|
|
if (list == NULL) { |
272 |
|
|
DPFPRINTF(LOG_INFO, "osfp no match against %x", os); |
273 |
|
|
return (os == PF_OSFP_UNKNOWN); |
274 |
|
|
} |
275 |
|
|
PF_OSFP_UNPACK(os, os_class, os_version, os_subtype); |
276 |
|
|
SLIST_FOREACH(entry, list, fp_entry) { |
277 |
|
|
PF_OSFP_UNPACK(entry->fp_os, en_class, en_version, en_subtype); |
278 |
|
|
if ((os_class == PF_OSFP_ANY || en_class == os_class) && |
279 |
|
|
(os_version == PF_OSFP_ANY || en_version == os_version) && |
280 |
|
|
(os_subtype == PF_OSFP_ANY || en_subtype == os_subtype)) { |
281 |
|
|
DPFPRINTF(LOG_INFO, |
282 |
|
|
"osfp matched %s %s %s %x==%x", |
283 |
|
|
entry->fp_class_nm, entry->fp_version_nm, |
284 |
|
|
entry->fp_subtype_nm, os, entry->fp_os); |
285 |
|
|
return (1); |
286 |
|
|
} |
287 |
|
|
} |
288 |
|
|
DPFPRINTF(LOG_INFO, "fingerprint 0x%x didn't match", os); |
289 |
|
|
return (0); |
290 |
|
|
} |
291 |
|
|
|
292 |
|
|
/* Initialize the OS fingerprint system */ |
293 |
|
|
void |
294 |
|
|
pf_osfp_initialize(void) |
295 |
|
|
{ |
296 |
|
|
pool_init(&pf_osfp_entry_pl, sizeof(struct pf_osfp_entry), 0, |
297 |
|
|
IPL_NONE, PR_WAITOK, "pfosfpen", NULL); |
298 |
|
|
pool_init(&pf_osfp_pl, sizeof(struct pf_os_fingerprint), 0, |
299 |
|
|
IPL_NONE, PR_WAITOK, "pfosfp", NULL); |
300 |
|
|
SLIST_INIT(&pf_osfp_list); |
301 |
|
|
} |
302 |
|
|
|
303 |
|
|
/* Flush the fingerprint list */ |
304 |
|
|
void |
305 |
|
|
pf_osfp_flush(void) |
306 |
|
|
{ |
307 |
|
|
struct pf_os_fingerprint *fp; |
308 |
|
|
struct pf_osfp_entry *entry; |
309 |
|
|
|
310 |
|
|
while ((fp = SLIST_FIRST(&pf_osfp_list))) { |
311 |
|
|
SLIST_REMOVE_HEAD(&pf_osfp_list, fp_next); |
312 |
|
|
while ((entry = SLIST_FIRST(&fp->fp_oses))) { |
313 |
|
|
SLIST_REMOVE_HEAD(&fp->fp_oses, fp_entry); |
314 |
|
|
pool_put(&pf_osfp_entry_pl, entry); |
315 |
|
|
} |
316 |
|
|
pool_put(&pf_osfp_pl, fp); |
317 |
|
|
} |
318 |
|
|
} |
319 |
|
|
|
320 |
|
|
|
321 |
|
|
/* Add a fingerprint */ |
322 |
|
|
int |
323 |
|
|
pf_osfp_add(struct pf_osfp_ioctl *fpioc) |
324 |
|
|
{ |
325 |
|
|
struct pf_os_fingerprint *fp, fpadd; |
326 |
|
|
struct pf_osfp_entry *entry; |
327 |
|
|
|
328 |
|
|
memset(&fpadd, 0, sizeof(fpadd)); |
329 |
|
|
fpadd.fp_tcpopts = fpioc->fp_tcpopts; |
330 |
|
|
fpadd.fp_wsize = fpioc->fp_wsize; |
331 |
|
|
fpadd.fp_psize = fpioc->fp_psize; |
332 |
|
|
fpadd.fp_mss = fpioc->fp_mss; |
333 |
|
|
fpadd.fp_flags = fpioc->fp_flags; |
334 |
|
|
fpadd.fp_optcnt = fpioc->fp_optcnt; |
335 |
|
|
fpadd.fp_wscale = fpioc->fp_wscale; |
336 |
|
|
fpadd.fp_ttl = fpioc->fp_ttl; |
337 |
|
|
|
338 |
|
|
DPFPRINTF(LOG_DEBUG, |
339 |
|
|
"adding osfp %s %s %s = %s%d:%d:%d:%s%d:0x%llx %d " |
340 |
|
|
"(TS=%s,M=%s%d,W=%s%d) %x", |
341 |
|
|
fpioc->fp_os.fp_class_nm, fpioc->fp_os.fp_version_nm, |
342 |
|
|
fpioc->fp_os.fp_subtype_nm, |
343 |
|
|
(fpadd.fp_flags & PF_OSFP_WSIZE_MOD) ? "%" : |
344 |
|
|
(fpadd.fp_flags & PF_OSFP_WSIZE_MSS) ? "S" : |
345 |
|
|
(fpadd.fp_flags & PF_OSFP_WSIZE_MTU) ? "T" : |
346 |
|
|
(fpadd.fp_flags & PF_OSFP_WSIZE_DC) ? "*" : "", |
347 |
|
|
fpadd.fp_wsize, |
348 |
|
|
fpadd.fp_ttl, |
349 |
|
|
(fpadd.fp_flags & PF_OSFP_DF) ? 1 : 0, |
350 |
|
|
(fpadd.fp_flags & PF_OSFP_PSIZE_MOD) ? "%" : |
351 |
|
|
(fpadd.fp_flags & PF_OSFP_PSIZE_DC) ? "*" : "", |
352 |
|
|
fpadd.fp_psize, |
353 |
|
|
(long long int)fpadd.fp_tcpopts, fpadd.fp_optcnt, |
354 |
|
|
(fpadd.fp_flags & PF_OSFP_TS0) ? "0" : "", |
355 |
|
|
(fpadd.fp_flags & PF_OSFP_MSS_MOD) ? "%" : |
356 |
|
|
(fpadd.fp_flags & PF_OSFP_MSS_DC) ? "*" : "", |
357 |
|
|
fpadd.fp_mss, |
358 |
|
|
(fpadd.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" : |
359 |
|
|
(fpadd.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "", |
360 |
|
|
fpadd.fp_wscale, |
361 |
|
|
fpioc->fp_os.fp_os); |
362 |
|
|
|
363 |
|
|
if ((fp = pf_osfp_find_exact(&pf_osfp_list, &fpadd))) { |
364 |
|
|
SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) { |
365 |
|
|
if (PF_OSFP_ENTRY_EQ(entry, &fpioc->fp_os)) |
366 |
|
|
return (EEXIST); |
367 |
|
|
} |
368 |
|
|
if ((entry = pool_get(&pf_osfp_entry_pl, |
369 |
|
|
PR_WAITOK|PR_LIMITFAIL)) == NULL) |
370 |
|
|
return (ENOMEM); |
371 |
|
|
} else { |
372 |
|
|
if ((fp = pool_get(&pf_osfp_pl, |
373 |
|
|
PR_WAITOK|PR_ZERO|PR_LIMITFAIL)) == NULL) |
374 |
|
|
return (ENOMEM); |
375 |
|
|
fp->fp_tcpopts = fpioc->fp_tcpopts; |
376 |
|
|
fp->fp_wsize = fpioc->fp_wsize; |
377 |
|
|
fp->fp_psize = fpioc->fp_psize; |
378 |
|
|
fp->fp_mss = fpioc->fp_mss; |
379 |
|
|
fp->fp_flags = fpioc->fp_flags; |
380 |
|
|
fp->fp_optcnt = fpioc->fp_optcnt; |
381 |
|
|
fp->fp_wscale = fpioc->fp_wscale; |
382 |
|
|
fp->fp_ttl = fpioc->fp_ttl; |
383 |
|
|
SLIST_INIT(&fp->fp_oses); |
384 |
|
|
if ((entry = pool_get(&pf_osfp_entry_pl, |
385 |
|
|
PR_WAITOK|PR_LIMITFAIL)) == NULL) { |
386 |
|
|
pool_put(&pf_osfp_pl, fp); |
387 |
|
|
return (ENOMEM); |
388 |
|
|
} |
389 |
|
|
pf_osfp_insert(&pf_osfp_list, fp); |
390 |
|
|
} |
391 |
|
|
memcpy(entry, &fpioc->fp_os, sizeof(*entry)); |
392 |
|
|
|
393 |
|
|
/* Make sure the strings are NUL terminated */ |
394 |
|
|
entry->fp_class_nm[sizeof(entry->fp_class_nm)-1] = '\0'; |
395 |
|
|
entry->fp_version_nm[sizeof(entry->fp_version_nm)-1] = '\0'; |
396 |
|
|
entry->fp_subtype_nm[sizeof(entry->fp_subtype_nm)-1] = '\0'; |
397 |
|
|
|
398 |
|
|
SLIST_INSERT_HEAD(&fp->fp_oses, entry, fp_entry); |
399 |
|
|
|
400 |
|
|
#ifdef PFDEBUG |
401 |
|
|
if ((fp = pf_osfp_validate())) |
402 |
|
|
DPFPRINTF(LOG_NOTICE, |
403 |
|
|
"Invalid fingerprint list"); |
404 |
|
|
#endif /* PFDEBUG */ |
405 |
|
|
return (0); |
406 |
|
|
} |
407 |
|
|
|
408 |
|
|
|
409 |
|
|
/* Find a fingerprint in the list */ |
410 |
|
|
struct pf_os_fingerprint * |
411 |
|
|
pf_osfp_find(struct pf_osfp_list *list, struct pf_os_fingerprint *find, |
412 |
|
|
u_int8_t ttldiff) |
413 |
|
|
{ |
414 |
|
|
struct pf_os_fingerprint *f; |
415 |
|
|
|
416 |
|
|
#define MATCH_INT(_MOD, _DC, _field) \ |
417 |
|
|
if ((f->fp_flags & _DC) == 0) { \ |
418 |
|
|
if ((f->fp_flags & _MOD) == 0) { \ |
419 |
|
|
if (f->_field != find->_field) \ |
420 |
|
|
continue; \ |
421 |
|
|
} else { \ |
422 |
|
|
if (f->_field == 0 || find->_field % f->_field) \ |
423 |
|
|
continue; \ |
424 |
|
|
} \ |
425 |
|
|
} |
426 |
|
|
|
427 |
|
|
SLIST_FOREACH(f, list, fp_next) { |
428 |
|
|
if (f->fp_tcpopts != find->fp_tcpopts || |
429 |
|
|
f->fp_optcnt != find->fp_optcnt || |
430 |
|
|
f->fp_ttl < find->fp_ttl || |
431 |
|
|
f->fp_ttl - find->fp_ttl > ttldiff || |
432 |
|
|
(f->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)) != |
433 |
|
|
(find->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0))) |
434 |
|
|
continue; |
435 |
|
|
|
436 |
|
|
MATCH_INT(PF_OSFP_PSIZE_MOD, PF_OSFP_PSIZE_DC, fp_psize) |
437 |
|
|
MATCH_INT(PF_OSFP_MSS_MOD, PF_OSFP_MSS_DC, fp_mss) |
438 |
|
|
MATCH_INT(PF_OSFP_WSCALE_MOD, PF_OSFP_WSCALE_DC, fp_wscale) |
439 |
|
|
if ((f->fp_flags & PF_OSFP_WSIZE_DC) == 0) { |
440 |
|
|
if (f->fp_flags & PF_OSFP_WSIZE_MSS) { |
441 |
|
|
if (find->fp_mss == 0) |
442 |
|
|
continue; |
443 |
|
|
|
444 |
|
|
/* Some "smart" NAT devices and DSL routers will tweak the MSS size and |
445 |
|
|
* will set it to whatever is suitable for the link type. |
446 |
|
|
*/ |
447 |
|
|
#define SMART_MSS 1460 |
448 |
|
|
if ((find->fp_wsize % find->fp_mss || |
449 |
|
|
find->fp_wsize / find->fp_mss != |
450 |
|
|
f->fp_wsize) && |
451 |
|
|
(find->fp_wsize % SMART_MSS || |
452 |
|
|
find->fp_wsize / SMART_MSS != |
453 |
|
|
f->fp_wsize)) |
454 |
|
|
continue; |
455 |
|
|
} else if (f->fp_flags & PF_OSFP_WSIZE_MTU) { |
456 |
|
|
if (find->fp_mss == 0) |
457 |
|
|
continue; |
458 |
|
|
|
459 |
|
|
#define MTUOFF (sizeof(struct ip) + sizeof(struct tcphdr)) |
460 |
|
|
#define SMART_MTU (SMART_MSS + MTUOFF) |
461 |
|
|
if ((find->fp_wsize % (find->fp_mss + MTUOFF) || |
462 |
|
|
find->fp_wsize / (find->fp_mss + MTUOFF) != |
463 |
|
|
f->fp_wsize) && |
464 |
|
|
(find->fp_wsize % SMART_MTU || |
465 |
|
|
find->fp_wsize / SMART_MTU != |
466 |
|
|
f->fp_wsize)) |
467 |
|
|
continue; |
468 |
|
|
} else if (f->fp_flags & PF_OSFP_WSIZE_MOD) { |
469 |
|
|
if (f->fp_wsize == 0 || find->fp_wsize % |
470 |
|
|
f->fp_wsize) |
471 |
|
|
continue; |
472 |
|
|
} else { |
473 |
|
|
if (f->fp_wsize != find->fp_wsize) |
474 |
|
|
continue; |
475 |
|
|
} |
476 |
|
|
} |
477 |
|
|
return (f); |
478 |
|
|
} |
479 |
|
|
|
480 |
|
|
return (NULL); |
481 |
|
|
} |
482 |
|
|
|
483 |
|
|
/* Find an exact fingerprint in the list */ |
484 |
|
|
struct pf_os_fingerprint * |
485 |
|
|
pf_osfp_find_exact(struct pf_osfp_list *list, struct pf_os_fingerprint *find) |
486 |
|
|
{ |
487 |
|
|
struct pf_os_fingerprint *f; |
488 |
|
|
|
489 |
|
|
SLIST_FOREACH(f, list, fp_next) { |
490 |
|
|
if (f->fp_tcpopts == find->fp_tcpopts && |
491 |
|
|
f->fp_wsize == find->fp_wsize && |
492 |
|
|
f->fp_psize == find->fp_psize && |
493 |
|
|
f->fp_mss == find->fp_mss && |
494 |
|
|
f->fp_flags == find->fp_flags && |
495 |
|
|
f->fp_optcnt == find->fp_optcnt && |
496 |
|
|
f->fp_wscale == find->fp_wscale && |
497 |
|
|
f->fp_ttl == find->fp_ttl) |
498 |
|
|
return (f); |
499 |
|
|
} |
500 |
|
|
|
501 |
|
|
return (NULL); |
502 |
|
|
} |
503 |
|
|
|
504 |
|
|
/* Insert a fingerprint into the list */ |
505 |
|
|
void |
506 |
|
|
pf_osfp_insert(struct pf_osfp_list *list, struct pf_os_fingerprint *ins) |
507 |
|
|
{ |
508 |
|
|
struct pf_os_fingerprint *f, *prev = NULL; |
509 |
|
|
|
510 |
|
|
/* XXX need to go semi tree based. can key on tcp options */ |
511 |
|
|
|
512 |
|
|
SLIST_FOREACH(f, list, fp_next) |
513 |
|
|
prev = f; |
514 |
|
|
if (prev) |
515 |
|
|
SLIST_INSERT_AFTER(prev, ins, fp_next); |
516 |
|
|
else |
517 |
|
|
SLIST_INSERT_HEAD(list, ins, fp_next); |
518 |
|
|
} |
519 |
|
|
|
520 |
|
|
/* Fill a fingerprint by its number (from an ioctl) */ |
521 |
|
|
int |
522 |
|
|
pf_osfp_get(struct pf_osfp_ioctl *fpioc) |
523 |
|
|
{ |
524 |
|
|
struct pf_os_fingerprint *fp; |
525 |
|
|
struct pf_osfp_entry *entry; |
526 |
|
|
int num = fpioc->fp_getnum; |
527 |
|
|
int i = 0; |
528 |
|
|
|
529 |
|
|
|
530 |
|
|
memset(fpioc, 0, sizeof(*fpioc)); |
531 |
|
|
SLIST_FOREACH(fp, &pf_osfp_list, fp_next) { |
532 |
|
|
SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) { |
533 |
|
|
if (i++ == num) { |
534 |
|
|
fpioc->fp_mss = fp->fp_mss; |
535 |
|
|
fpioc->fp_wsize = fp->fp_wsize; |
536 |
|
|
fpioc->fp_flags = fp->fp_flags; |
537 |
|
|
fpioc->fp_psize = fp->fp_psize; |
538 |
|
|
fpioc->fp_ttl = fp->fp_ttl; |
539 |
|
|
fpioc->fp_wscale = fp->fp_wscale; |
540 |
|
|
fpioc->fp_getnum = num; |
541 |
|
|
memcpy(&fpioc->fp_os, entry, |
542 |
|
|
sizeof(fpioc->fp_os)); |
543 |
|
|
return (0); |
544 |
|
|
} |
545 |
|
|
} |
546 |
|
|
} |
547 |
|
|
|
548 |
|
|
return (EBUSY); |
549 |
|
|
} |
550 |
|
|
|
551 |
|
|
|
552 |
|
|
/* Validate that each signature is reachable */ |
553 |
|
|
struct pf_os_fingerprint * |
554 |
|
|
pf_osfp_validate(void) |
555 |
|
|
{ |
556 |
|
|
struct pf_os_fingerprint *f, *f2, find; |
557 |
|
|
|
558 |
|
|
SLIST_FOREACH(f, &pf_osfp_list, fp_next) { |
559 |
|
|
memcpy(&find, f, sizeof(find)); |
560 |
|
|
|
561 |
|
|
/* We do a few MSS/th_win percolations to make things unique */ |
562 |
|
|
if (find.fp_mss == 0) |
563 |
|
|
find.fp_mss = 128; |
564 |
|
|
if (f->fp_flags & PF_OSFP_WSIZE_MSS) |
565 |
|
|
find.fp_wsize *= find.fp_mss; |
566 |
|
|
else if (f->fp_flags & PF_OSFP_WSIZE_MTU) |
567 |
|
|
find.fp_wsize *= (find.fp_mss + 40); |
568 |
|
|
else if (f->fp_flags & PF_OSFP_WSIZE_MOD) |
569 |
|
|
find.fp_wsize *= 2; |
570 |
|
|
if (f != (f2 = pf_osfp_find(&pf_osfp_list, &find, 0))) { |
571 |
|
|
if (f2) |
572 |
|
|
DPFPRINTF(LOG_NOTICE, |
573 |
|
|
"Found \"%s %s %s\" instead of " |
574 |
|
|
"\"%s %s %s\"\n", |
575 |
|
|
SLIST_FIRST(&f2->fp_oses)->fp_class_nm, |
576 |
|
|
SLIST_FIRST(&f2->fp_oses)->fp_version_nm, |
577 |
|
|
SLIST_FIRST(&f2->fp_oses)->fp_subtype_nm, |
578 |
|
|
SLIST_FIRST(&f->fp_oses)->fp_class_nm, |
579 |
|
|
SLIST_FIRST(&f->fp_oses)->fp_version_nm, |
580 |
|
|
SLIST_FIRST(&f->fp_oses)->fp_subtype_nm); |
581 |
|
|
else |
582 |
|
|
DPFPRINTF(LOG_NOTICE, |
583 |
|
|
"Couldn't find \"%s %s %s\"\n", |
584 |
|
|
SLIST_FIRST(&f->fp_oses)->fp_class_nm, |
585 |
|
|
SLIST_FIRST(&f->fp_oses)->fp_version_nm, |
586 |
|
|
SLIST_FIRST(&f->fp_oses)->fp_subtype_nm); |
587 |
|
|
return (f); |
588 |
|
|
} |
589 |
|
|
} |
590 |
|
|
return (NULL); |
591 |
|
|
} |