1 /* $OpenBSD: pfctl_osfp.c,v 1.5 2008/06/16 03:40:34 david 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 <ctype.h> 27 #include <err.h> 28 #include <errno.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 33 #include <netinet/in_systm.h> 34 #include <netinet/ip.h> 35 #include <netinet/ip6.h> 36 37 #include "privsep.h" 38 #include "pfctl_parser.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 struct name_list classes = LIST_HEAD_INITIALIZER(&classes); 68 int class_count; 69 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 /* XXX arbitrary */ 89 #define MAX_FP_LINE 1024 90 91 /* Load fingerprints from a file */ 92 int 93 pfctl_file_fingerprints(int dev, int opts, const char *fp_filename) 94 { 95 char buf[MAX_FP_LINE]; 96 char *line; 97 size_t len; 98 int i, lineno = 0; 99 int window, w_mod, ttl, df, psize, p_mod, mss, mss_mod, wscale, 100 wscale_mod, optcnt, ts0; 101 pf_tcpopts_t packed_tcpopts; 102 char *class, *version, *subtype, *desc, *tcpopts; 103 struct pf_osfp_ioctl fp; 104 105 pfctl_flush_my_fingerprints(&classes); 106 class = version = subtype = desc = tcpopts = NULL; 107 108 if ((opts & PF_OPT_NOACTION) == 0) 109 pfctl_clear_fingerprints(dev, opts); 110 111 priv_getlines(FTAB_PFOSFP); 112 while ((len = priv_getline(buf, sizeof(buf))) > 0) { 113 buf[len -1] = '\n'; 114 line = buf; 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 263 if (opts & PF_OPT_VERBOSE2) 264 printf("Loaded %d passive OS fingerprints\n", 265 fingerprint_count); 266 return (0); 267 } 268 269 /* flush the kernel's fingerprints */ 270 void 271 pfctl_clear_fingerprints(int dev, int opts) 272 { 273 if (ioctl(dev, DIOCOSFPFLUSH)) 274 err(1, "DIOCOSFPFLUSH"); 275 } 276 277 /* flush pfctl's view of the fingerprints */ 278 void 279 pfctl_flush_my_fingerprints(struct name_list *list) 280 { 281 struct name_entry *nm; 282 283 while ((nm = LIST_FIRST(list)) != NULL) { 284 LIST_REMOVE(nm, nm_entry); 285 pfctl_flush_my_fingerprints(&nm->nm_sublist); 286 fingerprint_count--; 287 free(nm); 288 } 289 class_count = 0; 290 } 291 292 /* Fetch the active fingerprints from the kernel */ 293 int 294 pfctl_load_fingerprints(int dev, int opts) 295 { 296 struct pf_osfp_ioctl io; 297 int i; 298 299 pfctl_flush_my_fingerprints(&classes); 300 301 for (i = 0; i >= 0; i++) { 302 memset(&io, 0, sizeof(io)); 303 io.fp_getnum = i; 304 if (ioctl(dev, DIOCOSFPGET, &io)) { 305 if (errno == EBUSY) 306 break; 307 warn("DIOCOSFPGET"); 308 return (1); 309 } 310 import_fingerprint(&io); 311 } 312 return (0); 313 } 314 315 /* List the fingerprints */ 316 void 317 pfctl_show_fingerprints(int opts) 318 { 319 printf("Passive OS Fingerprints:\n"); 320 printf("\tClass\tVersion\tSubtype(subversion)\n"); 321 printf("\t-----\t-------\t-------------------\n"); 322 sort_name_list(opts, &classes); 323 print_name_list(opts, &classes, "\t"); 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 = strchr(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 (strchr(version_name, ' ')) 513 strlcat(buf, " ", len); 514 else if (strchr(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_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, sizeof(nm_entry->nm_name)); 708 } 709 LIST_INSERT_HEAD(list, nm_entry, nm_entry); 710 return (nm_entry); 711 } 712 713 714 void 715 print_name_list(int opts, struct name_list *nml, const char *prefix) 716 { 717 char newprefix[32]; 718 struct name_entry *nm; 719 720 LIST_FOREACH(nm, nml, nm_entry) { 721 snprintf(newprefix, sizeof(newprefix), "%s%s\t", prefix, 722 nm->nm_name); 723 printf("%s\n", newprefix); 724 print_name_list(opts, &nm->nm_sublist, newprefix); 725 } 726 } 727 728 void 729 sort_name_list(int opts, struct name_list *nml) 730 { 731 struct name_list new; 732 struct name_entry *nm, *nmsearch, *nmlast; 733 734 /* yes yes, it's a very slow sort. so sue me */ 735 736 LIST_INIT(&new); 737 738 while ((nm = LIST_FIRST(nml)) != NULL) { 739 LIST_REMOVE(nm, nm_entry); 740 nmlast = NULL; 741 LIST_FOREACH(nmsearch, &new, nm_entry) { 742 if (strcasecmp(nmsearch->nm_name, nm->nm_name) > 0) { 743 LIST_INSERT_BEFORE(nmsearch, nm, nm_entry); 744 break; 745 } 746 nmlast = nmsearch; 747 } 748 if (nmsearch == NULL) { 749 if (nmlast) 750 LIST_INSERT_AFTER(nmlast, nm, nm_entry); 751 else 752 LIST_INSERT_HEAD(&new, nm, nm_entry); 753 } 754 755 sort_name_list(opts, &nm->nm_sublist); 756 } 757 nmlast = NULL; 758 while ((nm = LIST_FIRST(&new)) != NULL) { 759 LIST_REMOVE(nm, nm_entry); 760 if (nmlast == NULL) 761 LIST_INSERT_HEAD(nml, nm, nm_entry); 762 else 763 LIST_INSERT_AFTER(nmlast, nm, nm_entry); 764 nmlast = nm; 765 } 766 return; 767 } 768 769 /* parse the next integer in a formatted config file line */ 770 int 771 get_int(char **line, size_t *len, int *var, int *mod, 772 const char *name, int flags, int max, const char *filename, int lineno) 773 { 774 int fieldlen, i; 775 char *field; 776 long val = 0; 777 778 if (mod) 779 *mod = 0; 780 *var = 0; 781 782 field = get_field(line, len, &fieldlen); 783 if (field == NULL) 784 return (1); 785 if (fieldlen == 0) { 786 fprintf(stderr, "%s:%d empty %s\n", filename, lineno, name); 787 return (1); 788 } 789 790 i = 0; 791 if ((*field == '%' || *field == 'S' || *field == 'T' || *field == '*') 792 && fieldlen >= 1) { 793 switch (*field) { 794 case 'S': 795 if (mod && (flags & T_MSS)) 796 *mod = T_MSS; 797 if (fieldlen == 1) 798 return (0); 799 break; 800 case 'T': 801 if (mod && (flags & T_MTU)) 802 *mod = T_MTU; 803 if (fieldlen == 1) 804 return (0); 805 break; 806 case '*': 807 if (fieldlen != 1) { 808 fprintf(stderr, "%s:%d long '%c' %s\n", 809 filename, lineno, *field, name); 810 return (1); 811 } 812 if (mod && (flags & T_DC)) { 813 *mod = T_DC; 814 return (0); 815 } 816 case '%': 817 if (mod && (flags & T_MOD)) 818 *mod = T_MOD; 819 if (fieldlen == 1) { 820 fprintf(stderr, "%s:%d modulus %s must have a " 821 "value\n", filename, lineno, name); 822 return (1); 823 } 824 break; 825 } 826 if (mod == NULL || *mod == 0) { 827 fprintf(stderr, "%s:%d does not allow %c' %s\n", 828 filename, lineno, *field, name); 829 return (1); 830 } 831 i++; 832 } 833 834 for (; i < fieldlen; i++) { 835 if (field[i] < '0' || field[i] > '9') { 836 fprintf(stderr, "%s:%d non-digit character in %s\n", 837 filename, lineno, name); 838 return (1); 839 } 840 val = val * 10 + field[i] - '0'; 841 if (val < 0) { 842 fprintf(stderr, "%s:%d %s overflowed\n", filename, 843 lineno, name); 844 return (1); 845 } 846 } 847 848 if (val > max) { 849 fprintf(stderr, "%s:%d %s value %ld > %d\n", filename, lineno, 850 name, val, max); 851 return (1); 852 } 853 *var = (int)val; 854 855 return (0); 856 } 857 858 /* parse the next string in a formatted config file line */ 859 int 860 get_str(char **line, size_t *len, char **v, const char *name, int minlen, 861 const char *filename, int lineno) 862 { 863 int fieldlen; 864 char *ptr; 865 866 ptr = get_field(line, len, &fieldlen); 867 if (ptr == NULL) 868 return (1); 869 if (fieldlen < minlen) { 870 fprintf(stderr, "%s:%d too short %s\n", filename, lineno, name); 871 return (1); 872 } 873 if ((*v = malloc(fieldlen + 1)) == NULL) { 874 perror("malloc()"); 875 return (1); 876 } 877 memcpy(*v, ptr, fieldlen); 878 (*v)[fieldlen] = '\0'; 879 880 return (0); 881 } 882 883 /* Parse out the TCP opts */ 884 int 885 get_tcpopts(const char *filename, int lineno, const char *tcpopts, 886 pf_tcpopts_t *packed, int *optcnt, int *mss, int *mss_mod, int *wscale, 887 int *wscale_mod, int *ts0) 888 { 889 int i, opt; 890 891 *packed = 0; 892 *optcnt = 0; 893 *wscale = 0; 894 *wscale_mod = T_DC; 895 *mss = 0; 896 *mss_mod = T_DC; 897 *ts0 = 0; 898 if (strcmp(tcpopts, ".") == 0) 899 return (0); 900 901 for (i = 0; tcpopts[i] && *optcnt < PF_OSFP_MAX_OPTS;) { 902 switch ((opt = toupper(tcpopts[i++]))) { 903 case 'N': /* FALLTHROUGH */ 904 case 'S': 905 *packed = (*packed << PF_OSFP_TCPOPT_BITS) | 906 (opt == 'N' ? PF_OSFP_TCPOPT_NOP : 907 PF_OSFP_TCPOPT_SACK); 908 break; 909 case 'W': /* FALLTHROUGH */ 910 case 'M': { 911 int *this_mod, *this; 912 913 if (opt == 'W') { 914 this = wscale; 915 this_mod = wscale_mod; 916 } else { 917 this = mss; 918 this_mod = mss_mod; 919 } 920 *this = 0; 921 *this_mod = 0; 922 923 *packed = (*packed << PF_OSFP_TCPOPT_BITS) | 924 (opt == 'W' ? PF_OSFP_TCPOPT_WSCALE : 925 PF_OSFP_TCPOPT_MSS); 926 if (tcpopts[i] == '*' && (tcpopts[i + 1] == '\0' || 927 tcpopts[i + 1] == ',')) { 928 *this_mod = T_DC; 929 i++; 930 break; 931 } 932 933 if (tcpopts[i] == '%') { 934 *this_mod = T_MOD; 935 i++; 936 } 937 do { 938 if (!isdigit(tcpopts[i])) { 939 fprintf(stderr, "%s:%d unknown " 940 "character '%c' in %c TCP opt\n", 941 filename, lineno, tcpopts[i], opt); 942 return (1); 943 } 944 *this = (*this * 10) + tcpopts[i++] - '0'; 945 } while(tcpopts[i] != ',' && tcpopts[i] != '\0'); 946 break; 947 } 948 case 'T': 949 if (tcpopts[i] == '0') { 950 *ts0 = 1; 951 i++; 952 } 953 *packed = (*packed << PF_OSFP_TCPOPT_BITS) | 954 PF_OSFP_TCPOPT_TS; 955 break; 956 } 957 (*optcnt) ++; 958 if (tcpopts[i] == '\0') 959 break; 960 if (tcpopts[i] != ',') { 961 fprintf(stderr, "%s:%d unknown option to %c TCP opt\n", 962 filename, lineno, opt); 963 return (1); 964 } 965 i++; 966 } 967 968 return (0); 969 } 970 971 /* rip the next field ouf of a formatted config file line */ 972 char * 973 get_field(char **line, size_t *len, int *fieldlen) 974 { 975 char *ret, *ptr = *line; 976 size_t plen = *len; 977 978 979 while (plen && isspace(*ptr)) { 980 plen--; 981 ptr++; 982 } 983 ret = ptr; 984 *fieldlen = 0; 985 986 for (; plen > 0 && *ptr != ':'; plen--, ptr++) 987 (*fieldlen)++; 988 if (plen) { 989 *line = ptr + 1; 990 *len = plen - 1; 991 } else { 992 *len = 0; 993 } 994 while (*fieldlen && isspace(ret[*fieldlen - 1])) 995 (*fieldlen)--; 996 return (ret); 997 } 998 999 1000 const char * 1001 print_ioctl(struct pf_osfp_ioctl *fp) 1002 { 1003 static char buf[1024]; 1004 char tmp[32]; 1005 int i, opt; 1006 1007 *buf = '\0'; 1008 if (fp->fp_flags & PF_OSFP_WSIZE_DC) 1009 strlcat(buf, "*", sizeof(buf)); 1010 else if (fp->fp_flags & PF_OSFP_WSIZE_MSS) 1011 strlcat(buf, "S", sizeof(buf)); 1012 else if (fp->fp_flags & PF_OSFP_WSIZE_MTU) 1013 strlcat(buf, "T", sizeof(buf)); 1014 else { 1015 if (fp->fp_flags & PF_OSFP_WSIZE_MOD) 1016 strlcat(buf, "%", sizeof(buf)); 1017 snprintf(tmp, sizeof(tmp), "%d", fp->fp_wsize); 1018 strlcat(buf, tmp, sizeof(buf)); 1019 } 1020 strlcat(buf, ":", sizeof(buf)); 1021 1022 snprintf(tmp, sizeof(tmp), "%d", fp->fp_ttl); 1023 strlcat(buf, tmp, sizeof(buf)); 1024 strlcat(buf, ":", sizeof(buf)); 1025 1026 if (fp->fp_flags & PF_OSFP_DF) 1027 strlcat(buf, "1", sizeof(buf)); 1028 else 1029 strlcat(buf, "0", sizeof(buf)); 1030 strlcat(buf, ":", sizeof(buf)); 1031 1032 if (fp->fp_flags & PF_OSFP_PSIZE_DC) 1033 strlcat(buf, "*", sizeof(buf)); 1034 else { 1035 if (fp->fp_flags & PF_OSFP_PSIZE_MOD) 1036 strlcat(buf, "%", sizeof(buf)); 1037 snprintf(tmp, sizeof(tmp), "%d", fp->fp_psize); 1038 strlcat(buf, tmp, sizeof(buf)); 1039 } 1040 strlcat(buf, ":", sizeof(buf)); 1041 1042 if (fp->fp_optcnt == 0) 1043 strlcat(buf, ".", sizeof(buf)); 1044 for (i = fp->fp_optcnt - 1; i >= 0; i--) { 1045 opt = fp->fp_tcpopts >> (i * PF_OSFP_TCPOPT_BITS); 1046 opt &= (1 << PF_OSFP_TCPOPT_BITS) - 1; 1047 switch (opt) { 1048 case PF_OSFP_TCPOPT_NOP: 1049 strlcat(buf, "N", sizeof(buf)); 1050 break; 1051 case PF_OSFP_TCPOPT_SACK: 1052 strlcat(buf, "S", sizeof(buf)); 1053 break; 1054 case PF_OSFP_TCPOPT_TS: 1055 strlcat(buf, "T", sizeof(buf)); 1056 if (fp->fp_flags & PF_OSFP_TS0) 1057 strlcat(buf, "0", sizeof(buf)); 1058 break; 1059 case PF_OSFP_TCPOPT_MSS: 1060 strlcat(buf, "M", sizeof(buf)); 1061 if (fp->fp_flags & PF_OSFP_MSS_DC) 1062 strlcat(buf, "*", sizeof(buf)); 1063 else { 1064 if (fp->fp_flags & PF_OSFP_MSS_MOD) 1065 strlcat(buf, "%", sizeof(buf)); 1066 snprintf(tmp, sizeof(tmp), "%d", fp->fp_mss); 1067 strlcat(buf, tmp, sizeof(buf)); 1068 } 1069 break; 1070 case PF_OSFP_TCPOPT_WSCALE: 1071 strlcat(buf, "W", sizeof(buf)); 1072 if (fp->fp_flags & PF_OSFP_WSCALE_DC) 1073 strlcat(buf, "*", sizeof(buf)); 1074 else { 1075 if (fp->fp_flags & PF_OSFP_WSCALE_MOD) 1076 strlcat(buf, "%", sizeof(buf)); 1077 snprintf(tmp, sizeof(tmp), "%d", fp->fp_wscale); 1078 strlcat(buf, tmp, sizeof(buf)); 1079 } 1080 break; 1081 } 1082 1083 if (i != 0) 1084 strlcat(buf, ",", sizeof(buf)); 1085 } 1086 strlcat(buf, ":", sizeof(buf)); 1087 1088 strlcat(buf, fp->fp_os.fp_class_nm, sizeof(buf)); 1089 strlcat(buf, ":", sizeof(buf)); 1090 strlcat(buf, fp->fp_os.fp_version_nm, sizeof(buf)); 1091 strlcat(buf, ":", sizeof(buf)); 1092 strlcat(buf, fp->fp_os.fp_subtype_nm, sizeof(buf)); 1093 strlcat(buf, ":", sizeof(buf)); 1094 1095 snprintf(tmp, sizeof(tmp), "TcpOpts %d 0x%llx", fp->fp_optcnt, 1096 (long long int)fp->fp_tcpopts); 1097 strlcat(buf, tmp, sizeof(buf)); 1098 1099 return (buf); 1100 } 1101