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