1 /* $OpenBSD: pfctl_osfp.c,v 1.14 2006/04/08 02:13:14 ray Exp $ */
2
3 /*
4 * Copyright (c) 2003 Mike Frantzen <frantzen@openbsd.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 #include <sys/types.h>
20 #include <sys/ioctl.h>
21 #include <sys/socket.h>
22
23 #include <net/if.h>
24 #include <net/pfvar.h>
25
26 #include <netinet/in_systm.h>
27 #include <netinet/ip.h>
28 #include <netinet/ip6.h>
29
30 #include <ctype.h>
31 #include <err.h>
32 #include <errno.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include "pfctl_parser.h"
38 #include "pfctl.h"
39
40 #ifndef MIN
41 # define MIN(a,b) (((a) < (b)) ? (a) : (b))
42 #endif /* MIN */
43 #ifndef MAX
44 # define MAX(a,b) (((a) > (b)) ? (a) : (b))
45 #endif /* MAX */
46
47
48 #if 0
49 # define DEBUG(fp, str, v...) \
50 fprintf(stderr, "%s:%s:%s " str "\n", (fp)->fp_os.fp_class_nm, \
51 (fp)->fp_os.fp_version_nm, (fp)->fp_os.fp_subtype_nm , ## v);
52 #else
53 # define DEBUG(fp, str, v...) ((void)0)
54 #endif
55
56
57 struct name_entry;
58 LIST_HEAD(name_list, name_entry);
59 struct name_entry {
60 LIST_ENTRY(name_entry) nm_entry;
61 int nm_num;
62 char nm_name[PF_OSFP_LEN];
63
64 struct name_list nm_sublist;
65 int nm_sublist_num;
66 };
67 static struct name_list classes = LIST_HEAD_INITIALIZER(&classes);
68 static int class_count;
69 static int fingerprint_count;
70
71 void add_fingerprint(int, int, struct pf_osfp_ioctl *);
72 struct name_entry *fingerprint_name_entry(struct name_list *, char *);
73 void pfctl_flush_my_fingerprints(struct name_list *);
74 char *get_field(char **, size_t *, int *);
75 int get_int(char **, size_t *, int *, int *, const char *,
76 int, int, const char *, int);
77 int get_str(char **, size_t *, char **, const char *, int,
78 const char *, int);
79 int get_tcpopts(const char *, int, const char *,
80 pf_tcpopts_t *, int *, int *, int *, int *, int *,
81 int *);
82 void import_fingerprint(struct pf_osfp_ioctl *);
83 const char *print_ioctl(struct pf_osfp_ioctl *);
84 void print_name_list(int, struct name_list *, const char *);
85 void sort_name_list(int, struct name_list *);
86 struct name_entry *lookup_name_list(struct name_list *, const char *);
87
88 /* Load fingerprints from a file */
89 int
pfctl_file_fingerprints(int dev,int opts,const char * fp_filename)90 pfctl_file_fingerprints(int dev, int opts, const char *fp_filename)
91 {
92 FILE *in;
93 char *line;
94 size_t len;
95 int i, lineno = 0;
96 int window, w_mod, ttl, df, psize, p_mod, mss, mss_mod, wscale,
97 wscale_mod, optcnt, ts0;
98 pf_tcpopts_t packed_tcpopts;
99 char *class, *version, *subtype, *desc, *tcpopts;
100 struct pf_osfp_ioctl fp;
101
102 pfctl_flush_my_fingerprints(&classes);
103
104 if ((in = pfctl_fopen(fp_filename, "r")) == NULL) {
105 warn("%s", fp_filename);
106 return (1);
107 }
108 class = version = subtype = desc = tcpopts = NULL;
109
110 if ((opts & PF_OPT_NOACTION) == 0)
111 pfctl_clear_fingerprints(dev, opts);
112
113 while ((line = fgetln(in, &len)) != NULL) {
114 lineno++;
115 free(class);
116 free(version);
117 free(subtype);
118 free(desc);
119 free(tcpopts);
120 class = version = subtype = desc = tcpopts = NULL;
121 memset(&fp, 0, sizeof(fp));
122
123 /* Chop off comment */
124 for (i = 0; i < len; i++)
125 if (line[i] == '#') {
126 len = i;
127 break;
128 }
129 /* Chop off whitespace */
130 while (len > 0 && isspace(line[len - 1]))
131 len--;
132 while (len > 0 && isspace(line[0])) {
133 len--;
134 line++;
135 }
136 if (len == 0)
137 continue;
138
139 #define T_DC 0x01 /* Allow don't care */
140 #define T_MSS 0x02 /* Allow MSS multiple */
141 #define T_MTU 0x04 /* Allow MTU multiple */
142 #define T_MOD 0x08 /* Allow modulus */
143
144 #define GET_INT(v, mod, n, ty, mx) \
145 get_int(&line, &len, &v, mod, n, ty, mx, fp_filename, lineno)
146 #define GET_STR(v, n, mn) \
147 get_str(&line, &len, &v, n, mn, fp_filename, lineno)
148
149 if (GET_INT(window, &w_mod, "window size", T_DC|T_MSS|T_MTU|
150 T_MOD, 0xffff) ||
151 GET_INT(ttl, NULL, "ttl", 0, 0xff) ||
152 GET_INT(df, NULL, "don't fragment frag", 0, 1) ||
153 GET_INT(psize, &p_mod, "overall packet size", T_MOD|T_DC,
154 8192) ||
155 GET_STR(tcpopts, "TCP Options", 1) ||
156 GET_STR(class, "OS class", 1) ||
157 GET_STR(version, "OS version", 0) ||
158 GET_STR(subtype, "OS subtype", 0) ||
159 GET_STR(desc, "OS description", 2))
160 continue;
161 if (get_tcpopts(fp_filename, lineno, tcpopts, &packed_tcpopts,
162 &optcnt, &mss, &mss_mod, &wscale, &wscale_mod, &ts0))
163 continue;
164 if (len != 0) {
165 fprintf(stderr, "%s:%d excess field\n", fp_filename,
166 lineno);
167 continue;
168 }
169
170 fp.fp_ttl = ttl;
171 if (df)
172 fp.fp_flags |= PF_OSFP_DF;
173 switch (w_mod) {
174 case 0:
175 break;
176 case T_DC:
177 fp.fp_flags |= PF_OSFP_WSIZE_DC;
178 break;
179 case T_MSS:
180 fp.fp_flags |= PF_OSFP_WSIZE_MSS;
181 break;
182 case T_MTU:
183 fp.fp_flags |= PF_OSFP_WSIZE_MTU;
184 break;
185 case T_MOD:
186 fp.fp_flags |= PF_OSFP_WSIZE_MOD;
187 break;
188 }
189 fp.fp_wsize = window;
190
191 switch (p_mod) {
192 case T_DC:
193 fp.fp_flags |= PF_OSFP_PSIZE_DC;
194 break;
195 case T_MOD:
196 fp.fp_flags |= PF_OSFP_PSIZE_MOD;
197 }
198 fp.fp_psize = psize;
199
200
201 switch (wscale_mod) {
202 case T_DC:
203 fp.fp_flags |= PF_OSFP_WSCALE_DC;
204 break;
205 case T_MOD:
206 fp.fp_flags |= PF_OSFP_WSCALE_MOD;
207 }
208 fp.fp_wscale = wscale;
209
210 switch (mss_mod) {
211 case T_DC:
212 fp.fp_flags |= PF_OSFP_MSS_DC;
213 break;
214 case T_MOD:
215 fp.fp_flags |= PF_OSFP_MSS_MOD;
216 break;
217 }
218 fp.fp_mss = mss;
219
220 fp.fp_tcpopts = packed_tcpopts;
221 fp.fp_optcnt = optcnt;
222 if (ts0)
223 fp.fp_flags |= PF_OSFP_TS0;
224
225 if (class[0] == '@')
226 fp.fp_os.fp_enflags |= PF_OSFP_GENERIC;
227 if (class[0] == '*')
228 fp.fp_os.fp_enflags |= PF_OSFP_NODETAIL;
229
230 if (class[0] == '@' || class[0] == '*')
231 strlcpy(fp.fp_os.fp_class_nm, class + 1,
232 sizeof(fp.fp_os.fp_class_nm));
233 else
234 strlcpy(fp.fp_os.fp_class_nm, class,
235 sizeof(fp.fp_os.fp_class_nm));
236 strlcpy(fp.fp_os.fp_version_nm, version,
237 sizeof(fp.fp_os.fp_version_nm));
238 strlcpy(fp.fp_os.fp_subtype_nm, subtype,
239 sizeof(fp.fp_os.fp_subtype_nm));
240
241 add_fingerprint(dev, opts, &fp);
242
243 fp.fp_flags |= (PF_OSFP_DF | PF_OSFP_INET6);
244 fp.fp_psize += sizeof(struct ip6_hdr) - sizeof(struct ip);
245 add_fingerprint(dev, opts, &fp);
246 }
247
248 free(class);
249 free(version);
250 free(subtype);
251 free(desc);
252 free(tcpopts);
253
254 fclose(in);
255
256 if (opts & PF_OPT_VERBOSE2)
257 printf("Loaded %d passive OS fingerprints\n",
258 fingerprint_count);
259 return (0);
260 }
261
262 /* flush the kernel's fingerprints */
263 void
pfctl_clear_fingerprints(int dev,int opts)264 pfctl_clear_fingerprints(int dev, int opts)
265 {
266 if (ioctl(dev, DIOCOSFPFLUSH))
267 err(1, "DIOCOSFPFLUSH");
268 }
269
270 /* flush pfctl's view of the fingerprints */
271 void
pfctl_flush_my_fingerprints(struct name_list * list)272 pfctl_flush_my_fingerprints(struct name_list *list)
273 {
274 struct name_entry *nm;
275
276 while ((nm = LIST_FIRST(list)) != NULL) {
277 LIST_REMOVE(nm, nm_entry);
278 pfctl_flush_my_fingerprints(&nm->nm_sublist);
279 free(nm);
280 }
281 fingerprint_count = 0;
282 class_count = 0;
283 }
284
285 /* Fetch the active fingerprints from the kernel */
286 int
pfctl_load_fingerprints(int dev,int opts)287 pfctl_load_fingerprints(int dev, int opts)
288 {
289 struct pf_osfp_ioctl io;
290 int i;
291
292 pfctl_flush_my_fingerprints(&classes);
293
294 for (i = 0; i >= 0; i++) {
295 memset(&io, 0, sizeof(io));
296 io.fp_getnum = i;
297 if (ioctl(dev, DIOCOSFPGET, &io)) {
298 if (errno == EBUSY)
299 break;
300 warn("DIOCOSFPGET");
301 return (1);
302 }
303 import_fingerprint(&io);
304 }
305 return (0);
306 }
307
308 /* List the fingerprints */
309 void
pfctl_show_fingerprints(int opts)310 pfctl_show_fingerprints(int opts)
311 {
312 if (LIST_FIRST(&classes) != NULL) {
313 if (opts & PF_OPT_SHOWALL) {
314 pfctl_print_title("OS FINGERPRINTS:");
315 printf("%u fingerprints loaded\n", fingerprint_count);
316 } else {
317 printf("Class\tVersion\tSubtype(subversion)\n");
318 printf("-----\t-------\t-------------------\n");
319 sort_name_list(opts, &classes);
320 print_name_list(opts, &classes, "");
321 }
322 }
323 }
324
325 /* Lookup a fingerprint */
326 pf_osfp_t
pfctl_get_fingerprint(const char * name)327 pfctl_get_fingerprint(const char *name)
328 {
329 struct name_entry *nm, *class_nm, *version_nm, *subtype_nm;
330 pf_osfp_t ret = PF_OSFP_NOMATCH;
331 int class, version, subtype;
332 int unp_class, unp_version, unp_subtype;
333 int wr_len, version_len, subtype_len;
334 char *ptr, *wr_name;
335
336 if (strcasecmp(name, "unknown") == 0)
337 return (PF_OSFP_UNKNOWN);
338
339 /* Try most likely no version and no subtype */
340 if ((nm = lookup_name_list(&classes, name))) {
341 class = nm->nm_num;
342 version = PF_OSFP_ANY;
343 subtype = PF_OSFP_ANY;
344 goto found;
345 } else {
346
347 /* Chop it up into class/version/subtype */
348
349 if ((wr_name = strdup(name)) == NULL)
350 err(1, "malloc");
351 if ((ptr = strchr(wr_name, ' ')) == NULL) {
352 free(wr_name);
353 return (PF_OSFP_NOMATCH);
354 }
355 *ptr++ = '\0';
356
357 /* The class is easy to find since it is delimited by a space */
358 if ((class_nm = lookup_name_list(&classes, wr_name)) == NULL) {
359 free(wr_name);
360 return (PF_OSFP_NOMATCH);
361 }
362 class = class_nm->nm_num;
363
364 /* Try no subtype */
365 if ((version_nm = lookup_name_list(&class_nm->nm_sublist, ptr)))
366 {
367 version = version_nm->nm_num;
368 subtype = PF_OSFP_ANY;
369 free(wr_name);
370 goto found;
371 }
372
373
374 /*
375 * There must be a version and a subtype.
376 * We'll do some fuzzy matching to pick up things like:
377 * Linux 2.2.14 (version=2.2 subtype=14)
378 * FreeBSD 4.0-STABLE (version=4.0 subtype=STABLE)
379 * Windows 2000 SP2 (version=2000 subtype=SP2)
380 */
381 #define CONNECTOR(x) ((x) == '.' || (x) == ' ' || (x) == '\t' || (x) == '-')
382 wr_len = strlen(ptr);
383 LIST_FOREACH(version_nm, &class_nm->nm_sublist, nm_entry) {
384 version_len = strlen(version_nm->nm_name);
385 if (wr_len < version_len + 2 ||
386 !CONNECTOR(ptr[version_len]))
387 continue;
388 /* first part of the string must be version */
389 if (strncasecmp(ptr, version_nm->nm_name,
390 version_len))
391 continue;
392
393 LIST_FOREACH(subtype_nm, &version_nm->nm_sublist,
394 nm_entry) {
395 subtype_len = strlen(subtype_nm->nm_name);
396 if (wr_len != version_len + subtype_len + 1)
397 continue;
398
399 /* last part of the string must be subtype */
400 if (strcasecmp(&ptr[version_len+1],
401 subtype_nm->nm_name) != 0)
402 continue;
403
404 /* Found it!! */
405 version = version_nm->nm_num;
406 subtype = subtype_nm->nm_num;
407 free(wr_name);
408 goto found;
409 }
410 }
411
412 free(wr_name);
413 return (PF_OSFP_NOMATCH);
414 }
415
416 found:
417 PF_OSFP_PACK(ret, class, version, subtype);
418 if (ret != PF_OSFP_NOMATCH) {
419 PF_OSFP_UNPACK(ret, unp_class, unp_version, unp_subtype);
420 if (class != unp_class) {
421 fprintf(stderr, "warning: fingerprint table overflowed "
422 "classes\n");
423 return (PF_OSFP_NOMATCH);
424 }
425 if (version != unp_version) {
426 fprintf(stderr, "warning: fingerprint table overflowed "
427 "versions\n");
428 return (PF_OSFP_NOMATCH);
429 }
430 if (subtype != unp_subtype) {
431 fprintf(stderr, "warning: fingerprint table overflowed "
432 "subtypes\n");
433 return (PF_OSFP_NOMATCH);
434 }
435 }
436 if (ret == PF_OSFP_ANY) {
437 /* should never happen */
438 fprintf(stderr, "warning: fingerprint packed to 'any'\n");
439 return (PF_OSFP_NOMATCH);
440 }
441
442 return (ret);
443 }
444
445 /* Lookup a fingerprint name by ID */
446 char *
pfctl_lookup_fingerprint(pf_osfp_t fp,char * buf,size_t len)447 pfctl_lookup_fingerprint(pf_osfp_t fp, char *buf, size_t len)
448 {
449 int class, version, subtype;
450 struct name_list *list;
451 struct name_entry *nm;
452
453 char *class_name, *version_name, *subtype_name;
454 class_name = version_name = subtype_name = NULL;
455
456 if (fp == PF_OSFP_UNKNOWN) {
457 strlcpy(buf, "unknown", len);
458 return (buf);
459 }
460 if (fp == PF_OSFP_ANY) {
461 strlcpy(buf, "any", len);
462 return (buf);
463 }
464
465 PF_OSFP_UNPACK(fp, class, version, subtype);
466 if (class >= (1 << _FP_CLASS_BITS) ||
467 version >= (1 << _FP_VERSION_BITS) ||
468 subtype >= (1 << _FP_SUBTYPE_BITS)) {
469 warnx("PF_OSFP_UNPACK(0x%x) failed!!", fp);
470 strlcpy(buf, "nomatch", len);
471 return (buf);
472 }
473
474 LIST_FOREACH(nm, &classes, nm_entry) {
475 if (nm->nm_num == class) {
476 class_name = nm->nm_name;
477 if (version == PF_OSFP_ANY)
478 goto found;
479 list = &nm->nm_sublist;
480 LIST_FOREACH(nm, list, nm_entry) {
481 if (nm->nm_num == version) {
482 version_name = nm->nm_name;
483 if (subtype == PF_OSFP_ANY)
484 goto found;
485 list = &nm->nm_sublist;
486 LIST_FOREACH(nm, list, nm_entry) {
487 if (nm->nm_num == subtype) {
488 subtype_name =
489 nm->nm_name;
490 goto found;
491 }
492 } /* foreach subtype */
493 strlcpy(buf, "nomatch", len);
494 return (buf);
495 }
496 } /* foreach version */
497 strlcpy(buf, "nomatch", len);
498 return (buf);
499 }
500 } /* foreach class */
501
502 strlcpy(buf, "nomatch", len);
503 return (buf);
504
505 found:
506 snprintf(buf, len, "%s", class_name);
507 if (version_name) {
508 strlcat(buf, " ", len);
509 strlcat(buf, version_name, len);
510 if (subtype_name) {
511 if (strchr(version_name, ' '))
512 strlcat(buf, " ", len);
513 else if (strchr(version_name, '.') &&
514 isdigit(*subtype_name))
515 strlcat(buf, ".", len);
516 else
517 strlcat(buf, " ", len);
518 strlcat(buf, subtype_name, len);
519 }
520 }
521 return (buf);
522 }
523
524 /* lookup a name in a list */
525 struct name_entry *
lookup_name_list(struct name_list * list,const char * name)526 lookup_name_list(struct name_list *list, const char *name)
527 {
528 struct name_entry *nm;
529 LIST_FOREACH(nm, list, nm_entry)
530 if (strcasecmp(name, nm->nm_name) == 0)
531 return (nm);
532
533 return (NULL);
534 }
535
536
537 void
add_fingerprint(int dev,int opts,struct pf_osfp_ioctl * fp)538 add_fingerprint(int dev, int opts, struct pf_osfp_ioctl *fp)
539 {
540 struct pf_osfp_ioctl fptmp;
541 struct name_entry *nm_class, *nm_version, *nm_subtype;
542 int class, version, subtype;
543
544 /* We expand #-# or #.#-#.# version/subtypes into multiple fingerprints */
545 #define EXPAND(field) do { \
546 int _dot = -1, _start = -1, _end = -1, _i = 0; \
547 /* pick major version out of #.# */ \
548 if (isdigit(fp->field[_i]) && fp->field[_i+1] == '.') { \
549 _dot = fp->field[_i] - '0'; \
550 _i += 2; \
551 } \
552 if (isdigit(fp->field[_i])) \
553 _start = fp->field[_i++] - '0'; \
554 else \
555 break; \
556 if (isdigit(fp->field[_i])) \
557 _start = (_start * 10) + fp->field[_i++] - '0'; \
558 if (fp->field[_i++] != '-') \
559 break; \
560 if (isdigit(fp->field[_i]) && fp->field[_i+1] == '.' && \
561 fp->field[_i] - '0' == _dot) \
562 _i += 2; \
563 else if (_dot != -1) \
564 break; \
565 if (isdigit(fp->field[_i])) \
566 _end = fp->field[_i++] - '0'; \
567 else \
568 break; \
569 if (isdigit(fp->field[_i])) \
570 _end = (_end * 10) + fp->field[_i++] - '0'; \
571 if (isdigit(fp->field[_i])) \
572 _end = (_end * 10) + fp->field[_i++] - '0'; \
573 if (fp->field[_i] != '\0') \
574 break; \
575 memcpy(&fptmp, fp, sizeof(fptmp)); \
576 for (;_start <= _end; _start++) { \
577 memset(fptmp.field, 0, sizeof(fptmp.field)); \
578 fptmp.fp_os.fp_enflags |= PF_OSFP_EXPANDED; \
579 if (_dot == -1) \
580 snprintf(fptmp.field, sizeof(fptmp.field), \
581 "%d", _start); \
582 else \
583 snprintf(fptmp.field, sizeof(fptmp.field), \
584 "%d.%d", _dot, _start); \
585 add_fingerprint(dev, opts, &fptmp); \
586 } \
587 } while(0)
588
589 /* We allow "#-#" as a version or subtype and we'll expand it */
590 EXPAND(fp_os.fp_version_nm);
591 EXPAND(fp_os.fp_subtype_nm);
592
593 if (strcasecmp(fp->fp_os.fp_class_nm, "nomatch") == 0)
594 errx(1, "fingerprint class \"nomatch\" is reserved");
595
596 version = PF_OSFP_ANY;
597 subtype = PF_OSFP_ANY;
598
599 nm_class = fingerprint_name_entry(&classes, fp->fp_os.fp_class_nm);
600 if (nm_class->nm_num == 0)
601 nm_class->nm_num = ++class_count;
602 class = nm_class->nm_num;
603
604 nm_version = fingerprint_name_entry(&nm_class->nm_sublist,
605 fp->fp_os.fp_version_nm);
606 if (nm_version) {
607 if (nm_version->nm_num == 0)
608 nm_version->nm_num = ++nm_class->nm_sublist_num;
609 version = nm_version->nm_num;
610 nm_subtype = fingerprint_name_entry(&nm_version->nm_sublist,
611 fp->fp_os.fp_subtype_nm);
612 if (nm_subtype) {
613 if (nm_subtype->nm_num == 0)
614 nm_subtype->nm_num =
615 ++nm_version->nm_sublist_num;
616 subtype = nm_subtype->nm_num;
617 }
618 }
619
620
621 DEBUG(fp, "\tsignature %d:%d:%d %s", class, version, subtype,
622 print_ioctl(fp));
623
624 PF_OSFP_PACK(fp->fp_os.fp_os, class, version, subtype);
625 fingerprint_count++;
626
627 #ifdef FAKE_PF_KERNEL
628 /* Linked to the sys/net/pf_osfp.c. Call pf_osfp_add() */
629 if ((errno = pf_osfp_add(fp)))
630 #else
631 if ((opts & PF_OPT_NOACTION) == 0 && ioctl(dev, DIOCOSFPADD, fp))
632 #endif /* FAKE_PF_KERNEL */
633 {
634 if (errno == EEXIST) {
635 warn("Duplicate signature for %s %s %s",
636 fp->fp_os.fp_class_nm,
637 fp->fp_os.fp_version_nm,
638 fp->fp_os.fp_subtype_nm);
639
640 } else {
641 err(1, "DIOCOSFPADD");
642 }
643 }
644 }
645
646 /* import a fingerprint from the kernel */
647 void
import_fingerprint(struct pf_osfp_ioctl * fp)648 import_fingerprint(struct pf_osfp_ioctl *fp)
649 {
650 struct name_entry *nm_class, *nm_version, *nm_subtype;
651 int class, version, subtype;
652
653 PF_OSFP_UNPACK(fp->fp_os.fp_os, class, version, subtype);
654
655 nm_class = fingerprint_name_entry(&classes, fp->fp_os.fp_class_nm);
656 if (nm_class->nm_num == 0) {
657 nm_class->nm_num = class;
658 class_count = MAX(class_count, class);
659 }
660
661 nm_version = fingerprint_name_entry(&nm_class->nm_sublist,
662 fp->fp_os.fp_version_nm);
663 if (nm_version) {
664 if (nm_version->nm_num == 0) {
665 nm_version->nm_num = version;
666 nm_class->nm_sublist_num = MAX(nm_class->nm_sublist_num,
667 version);
668 }
669 nm_subtype = fingerprint_name_entry(&nm_version->nm_sublist,
670 fp->fp_os.fp_subtype_nm);
671 if (nm_subtype) {
672 if (nm_subtype->nm_num == 0) {
673 nm_subtype->nm_num = subtype;
674 nm_version->nm_sublist_num =
675 MAX(nm_version->nm_sublist_num, subtype);
676 }
677 }
678 }
679
680
681 fingerprint_count++;
682 DEBUG(fp, "import signature %d:%d:%d", class, version, subtype);
683 }
684
685 /* Find an entry for a fingerprints class/version/subtype */
686 struct name_entry *
fingerprint_name_entry(struct name_list * list,char * name)687 fingerprint_name_entry(struct name_list *list, char *name)
688 {
689 struct name_entry *nm_entry;
690
691 if (name == NULL || strlen(name) == 0)
692 return (NULL);
693
694 LIST_FOREACH(nm_entry, list, nm_entry) {
695 if (strcasecmp(nm_entry->nm_name, name) == 0) {
696 /* We'll move this to the front of the list later */
697 LIST_REMOVE(nm_entry, nm_entry);
698 break;
699 }
700 }
701 if (nm_entry == NULL) {
702 nm_entry = calloc(1, sizeof(*nm_entry));
703 if (nm_entry == NULL)
704 err(1, "calloc");
705 LIST_INIT(&nm_entry->nm_sublist);
706 strlcpy(nm_entry->nm_name, name, sizeof(nm_entry->nm_name));
707 }
708 LIST_INSERT_HEAD(list, nm_entry, nm_entry);
709 return (nm_entry);
710 }
711
712
713 void
print_name_list(int opts,struct name_list * nml,const char * prefix)714 print_name_list(int opts, struct name_list *nml, const char *prefix)
715 {
716 char newprefix[32];
717 struct name_entry *nm;
718
719 LIST_FOREACH(nm, nml, nm_entry) {
720 snprintf(newprefix, sizeof(newprefix), "%s%s\t", prefix,
721 nm->nm_name);
722 printf("%s\n", newprefix);
723 print_name_list(opts, &nm->nm_sublist, newprefix);
724 }
725 }
726
727 void
sort_name_list(int opts,struct name_list * nml)728 sort_name_list(int opts, struct name_list *nml)
729 {
730 struct name_list new;
731 struct name_entry *nm, *nmsearch, *nmlast;
732
733 /* yes yes, it's a very slow sort. so sue me */
734
735 LIST_INIT(&new);
736
737 while ((nm = LIST_FIRST(nml)) != NULL) {
738 LIST_REMOVE(nm, nm_entry);
739 nmlast = NULL;
740 LIST_FOREACH(nmsearch, &new, nm_entry) {
741 if (strcasecmp(nmsearch->nm_name, nm->nm_name) > 0) {
742 LIST_INSERT_BEFORE(nmsearch, nm, nm_entry);
743 break;
744 }
745 nmlast = nmsearch;
746 }
747 if (nmsearch == NULL) {
748 if (nmlast)
749 LIST_INSERT_AFTER(nmlast, nm, nm_entry);
750 else
751 LIST_INSERT_HEAD(&new, nm, nm_entry);
752 }
753
754 sort_name_list(opts, &nm->nm_sublist);
755 }
756 nmlast = NULL;
757 while ((nm = LIST_FIRST(&new)) != NULL) {
758 LIST_REMOVE(nm, nm_entry);
759 if (nmlast == NULL)
760 LIST_INSERT_HEAD(nml, nm, nm_entry);
761 else
762 LIST_INSERT_AFTER(nmlast, nm, nm_entry);
763 nmlast = nm;
764 }
765 }
766
767 /* parse the next integer in a formatted config file line */
768 int
get_int(char ** line,size_t * len,int * var,int * mod,const char * name,int flags,int max,const char * filename,int lineno)769 get_int(char **line, size_t *len, int *var, int *mod,
770 const char *name, int flags, int max, const char *filename, int lineno)
771 {
772 int fieldlen, i;
773 char *field;
774 long val = 0;
775
776 if (mod)
777 *mod = 0;
778 *var = 0;
779
780 field = get_field(line, len, &fieldlen);
781 if (field == NULL)
782 return (1);
783 if (fieldlen == 0) {
784 fprintf(stderr, "%s:%d empty %s\n", filename, lineno, name);
785 return (1);
786 }
787
788 i = 0;
789 if ((*field == '%' || *field == 'S' || *field == 'T' || *field == '*')
790 && fieldlen >= 1) {
791 switch (*field) {
792 case 'S':
793 if (mod && (flags & T_MSS))
794 *mod = T_MSS;
795 if (fieldlen == 1)
796 return (0);
797 break;
798 case 'T':
799 if (mod && (flags & T_MTU))
800 *mod = T_MTU;
801 if (fieldlen == 1)
802 return (0);
803 break;
804 case '*':
805 if (fieldlen != 1) {
806 fprintf(stderr, "%s:%d long '%c' %s\n",
807 filename, lineno, *field, name);
808 return (1);
809 }
810 if (mod && (flags & T_DC)) {
811 *mod = T_DC;
812 return (0);
813 }
814 case '%':
815 if (mod && (flags & T_MOD))
816 *mod = T_MOD;
817 if (fieldlen == 1) {
818 fprintf(stderr, "%s:%d modulus %s must have a "
819 "value\n", filename, lineno, name);
820 return (1);
821 }
822 break;
823 }
824 if (mod == NULL || *mod == 0) {
825 fprintf(stderr, "%s:%d does not allow %c' %s\n",
826 filename, lineno, *field, name);
827 return (1);
828 }
829 i++;
830 }
831
832 for (; i < fieldlen; i++) {
833 if (field[i] < '0' || field[i] > '9') {
834 fprintf(stderr, "%s:%d non-digit character in %s\n",
835 filename, lineno, name);
836 return (1);
837 }
838 val = val * 10 + field[i] - '0';
839 if (val < 0) {
840 fprintf(stderr, "%s:%d %s overflowed\n", filename,
841 lineno, name);
842 return (1);
843 }
844 }
845
846 if (val > max) {
847 fprintf(stderr, "%s:%d %s value %ld > %d\n", filename, lineno,
848 name, val, max);
849 return (1);
850 }
851 *var = (int)val;
852
853 return (0);
854 }
855
856 /* parse the next string in a formatted config file line */
857 int
get_str(char ** line,size_t * len,char ** v,const char * name,int minlen,const char * filename,int lineno)858 get_str(char **line, size_t *len, char **v, const char *name, int minlen,
859 const char *filename, int lineno)
860 {
861 int fieldlen;
862 char *ptr;
863
864 ptr = get_field(line, len, &fieldlen);
865 if (ptr == NULL)
866 return (1);
867 if (fieldlen < minlen) {
868 fprintf(stderr, "%s:%d too short %s\n", filename, lineno, name);
869 return (1);
870 }
871 if ((*v = malloc(fieldlen + 1)) == NULL) {
872 perror("malloc()");
873 return (1);
874 }
875 memcpy(*v, ptr, fieldlen);
876 (*v)[fieldlen] = '\0';
877
878 return (0);
879 }
880
881 /* Parse out the TCP opts */
882 int
get_tcpopts(const char * filename,int lineno,const char * tcpopts,pf_tcpopts_t * packed,int * optcnt,int * mss,int * mss_mod,int * wscale,int * wscale_mod,int * ts0)883 get_tcpopts(const char *filename, int lineno, const char *tcpopts,
884 pf_tcpopts_t *packed, int *optcnt, int *mss, int *mss_mod, int *wscale,
885 int *wscale_mod, int *ts0)
886 {
887 int i, opt;
888
889 *packed = 0;
890 *optcnt = 0;
891 *wscale = 0;
892 *wscale_mod = T_DC;
893 *mss = 0;
894 *mss_mod = T_DC;
895 *ts0 = 0;
896 if (strcmp(tcpopts, ".") == 0)
897 return (0);
898
899 for (i = 0; tcpopts[i] && *optcnt < PF_OSFP_MAX_OPTS;) {
900 switch ((opt = toupper(tcpopts[i++]))) {
901 case 'N': /* FALLTHROUGH */
902 case 'S':
903 *packed = (*packed << PF_OSFP_TCPOPT_BITS) |
904 (opt == 'N' ? PF_OSFP_TCPOPT_NOP :
905 PF_OSFP_TCPOPT_SACK);
906 break;
907 case 'W': /* FALLTHROUGH */
908 case 'M': {
909 int *this_mod, *this;
910
911 if (opt == 'W') {
912 this = wscale;
913 this_mod = wscale_mod;
914 } else {
915 this = mss;
916 this_mod = mss_mod;
917 }
918 *this = 0;
919 *this_mod = 0;
920
921 *packed = (*packed << PF_OSFP_TCPOPT_BITS) |
922 (opt == 'W' ? PF_OSFP_TCPOPT_WSCALE :
923 PF_OSFP_TCPOPT_MSS);
924 if (tcpopts[i] == '*' && (tcpopts[i + 1] == '\0' ||
925 tcpopts[i + 1] == ',')) {
926 *this_mod = T_DC;
927 i++;
928 break;
929 }
930
931 if (tcpopts[i] == '%') {
932 *this_mod = T_MOD;
933 i++;
934 }
935 do {
936 if (!isdigit(tcpopts[i])) {
937 fprintf(stderr, "%s:%d unknown "
938 "character '%c' in %c TCP opt\n",
939 filename, lineno, tcpopts[i], opt);
940 return (1);
941 }
942 *this = (*this * 10) + tcpopts[i++] - '0';
943 } while(tcpopts[i] != ',' && tcpopts[i] != '\0');
944 break;
945 }
946 case 'T':
947 if (tcpopts[i] == '0') {
948 *ts0 = 1;
949 i++;
950 }
951 *packed = (*packed << PF_OSFP_TCPOPT_BITS) |
952 PF_OSFP_TCPOPT_TS;
953 break;
954 }
955 (*optcnt) ++;
956 if (tcpopts[i] == '\0')
957 break;
958 if (tcpopts[i] != ',') {
959 fprintf(stderr, "%s:%d unknown option to %c TCP opt\n",
960 filename, lineno, opt);
961 return (1);
962 }
963 i++;
964 }
965
966 return (0);
967 }
968
969 /* rip the next field ouf of a formatted config file line */
970 char *
get_field(char ** line,size_t * len,int * fieldlen)971 get_field(char **line, size_t *len, int *fieldlen)
972 {
973 char *ret, *ptr = *line;
974 size_t plen = *len;
975
976
977 while (plen && isspace(*ptr)) {
978 plen--;
979 ptr++;
980 }
981 ret = ptr;
982 *fieldlen = 0;
983
984 for (; plen > 0 && *ptr != ':'; plen--, ptr++)
985 (*fieldlen)++;
986 if (plen) {
987 *line = ptr + 1;
988 *len = plen - 1;
989 } else {
990 *len = 0;
991 }
992 while (*fieldlen && isspace(ret[*fieldlen - 1]))
993 (*fieldlen)--;
994 return (ret);
995 }
996
997
998 const char *
print_ioctl(struct pf_osfp_ioctl * fp)999 print_ioctl(struct pf_osfp_ioctl *fp)
1000 {
1001 static char buf[1024];
1002 char tmp[32];
1003 int i, opt;
1004
1005 *buf = '\0';
1006 if (fp->fp_flags & PF_OSFP_WSIZE_DC)
1007 strlcat(buf, "*", sizeof(buf));
1008 else if (fp->fp_flags & PF_OSFP_WSIZE_MSS)
1009 strlcat(buf, "S", sizeof(buf));
1010 else if (fp->fp_flags & PF_OSFP_WSIZE_MTU)
1011 strlcat(buf, "T", sizeof(buf));
1012 else {
1013 if (fp->fp_flags & PF_OSFP_WSIZE_MOD)
1014 strlcat(buf, "%", sizeof(buf));
1015 snprintf(tmp, sizeof(tmp), "%d", fp->fp_wsize);
1016 strlcat(buf, tmp, sizeof(buf));
1017 }
1018 strlcat(buf, ":", sizeof(buf));
1019
1020 snprintf(tmp, sizeof(tmp), "%d", fp->fp_ttl);
1021 strlcat(buf, tmp, sizeof(buf));
1022 strlcat(buf, ":", sizeof(buf));
1023
1024 if (fp->fp_flags & PF_OSFP_DF)
1025 strlcat(buf, "1", sizeof(buf));
1026 else
1027 strlcat(buf, "0", sizeof(buf));
1028 strlcat(buf, ":", sizeof(buf));
1029
1030 if (fp->fp_flags & PF_OSFP_PSIZE_DC)
1031 strlcat(buf, "*", sizeof(buf));
1032 else {
1033 if (fp->fp_flags & PF_OSFP_PSIZE_MOD)
1034 strlcat(buf, "%", sizeof(buf));
1035 snprintf(tmp, sizeof(tmp), "%d", fp->fp_psize);
1036 strlcat(buf, tmp, sizeof(buf));
1037 }
1038 strlcat(buf, ":", sizeof(buf));
1039
1040 if (fp->fp_optcnt == 0)
1041 strlcat(buf, ".", sizeof(buf));
1042 for (i = fp->fp_optcnt - 1; i >= 0; i--) {
1043 opt = fp->fp_tcpopts >> (i * PF_OSFP_TCPOPT_BITS);
1044 opt &= (1 << PF_OSFP_TCPOPT_BITS) - 1;
1045 switch (opt) {
1046 case PF_OSFP_TCPOPT_NOP:
1047 strlcat(buf, "N", sizeof(buf));
1048 break;
1049 case PF_OSFP_TCPOPT_SACK:
1050 strlcat(buf, "S", sizeof(buf));
1051 break;
1052 case PF_OSFP_TCPOPT_TS:
1053 strlcat(buf, "T", sizeof(buf));
1054 if (fp->fp_flags & PF_OSFP_TS0)
1055 strlcat(buf, "0", sizeof(buf));
1056 break;
1057 case PF_OSFP_TCPOPT_MSS:
1058 strlcat(buf, "M", sizeof(buf));
1059 if (fp->fp_flags & PF_OSFP_MSS_DC)
1060 strlcat(buf, "*", sizeof(buf));
1061 else {
1062 if (fp->fp_flags & PF_OSFP_MSS_MOD)
1063 strlcat(buf, "%", sizeof(buf));
1064 snprintf(tmp, sizeof(tmp), "%d", fp->fp_mss);
1065 strlcat(buf, tmp, sizeof(buf));
1066 }
1067 break;
1068 case PF_OSFP_TCPOPT_WSCALE:
1069 strlcat(buf, "W", sizeof(buf));
1070 if (fp->fp_flags & PF_OSFP_WSCALE_DC)
1071 strlcat(buf, "*", sizeof(buf));
1072 else {
1073 if (fp->fp_flags & PF_OSFP_WSCALE_MOD)
1074 strlcat(buf, "%", sizeof(buf));
1075 snprintf(tmp, sizeof(tmp), "%d", fp->fp_wscale);
1076 strlcat(buf, tmp, sizeof(buf));
1077 }
1078 break;
1079 }
1080
1081 if (i != 0)
1082 strlcat(buf, ",", sizeof(buf));
1083 }
1084 strlcat(buf, ":", sizeof(buf));
1085
1086 strlcat(buf, fp->fp_os.fp_class_nm, sizeof(buf));
1087 strlcat(buf, ":", sizeof(buf));
1088 strlcat(buf, fp->fp_os.fp_version_nm, sizeof(buf));
1089 strlcat(buf, ":", sizeof(buf));
1090 strlcat(buf, fp->fp_os.fp_subtype_nm, sizeof(buf));
1091 strlcat(buf, ":", sizeof(buf));
1092
1093 snprintf(tmp, sizeof(tmp), "TcpOpts %d 0x%llx", fp->fp_optcnt,
1094 (long long int)fp->fp_tcpopts);
1095 strlcat(buf, tmp, sizeof(buf));
1096
1097 return (buf);
1098 }
1099