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