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