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