mirror of
				https://github.com/telekom-security/tpotce.git
				synced 2025-10-30 20:12:53 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			450 lines
		
	
	
	
		
			9.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			450 lines
		
	
	
	
		
			9.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|    p0f - p0f.fp file parser
 | |
|    ------------------------
 | |
| 
 | |
|    Every project has this one really ugly C file. This is ours.
 | |
| 
 | |
|    Copyright (C) 2012 by Michal Zalewski <lcamtuf@coredump.cx>
 | |
| 
 | |
|    Distributed under the terms and conditions of GNU LGPL.
 | |
| 
 | |
|  */
 | |
| 
 | |
| #define _GNU_SOURCE
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <unistd.h>
 | |
| #include <ctype.h>
 | |
| 
 | |
| #include <sys/fcntl.h>
 | |
| #include <netinet/in.h>
 | |
| #include <sys/types.h>
 | |
| #include <sys/stat.h>
 | |
| 
 | |
| #include "types.h"
 | |
| #include "config.h"
 | |
| #include "debug.h"
 | |
| #include "alloc-inl.h"
 | |
| #include "fp_tcp.h"
 | |
| #include "fp_mtu.h"
 | |
| #include "fp_http.h"
 | |
| #include "readfp.h"
 | |
| 
 | |
| static u32 sig_cnt;                     /* Total number of p0f.fp sigs        */
 | |
| 
 | |
| static u8 state = CF_NEED_SECT,         /* Parser state (CF_NEED_*)           */
 | |
|           mod_type,                     /* Current module (CF_MOD_*)          */
 | |
|           mod_to_srv,                   /* Traffic direction                  */
 | |
|           generic;                      /* Generic signature?                 */
 | |
| 
 | |
| static s32 sig_class;                   /* Signature class ID (-1 = userland) */
 | |
| static u32 sig_name;                    /* Signature name                     */
 | |
| static u8* sig_flavor;                  /* Signature flavor                   */
 | |
| 
 | |
| static u32* cur_sys;                    /* Current 'sys' values               */
 | |
| static u32  cur_sys_cnt;                /* Number of 'sys' entries            */
 | |
| 
 | |
| u8 **fp_os_classes,                     /* Map of OS classes                  */
 | |
|    **fp_os_names;                       /* Map of OS names                    */
 | |
| 
 | |
| static u32 class_cnt,                   /* Sizes for maps                     */
 | |
|            name_cnt,
 | |
|            label_id,                    /* Current label ID                   */
 | |
|            line_no;                     /* Current line number                */
 | |
| 
 | |
| 
 | |
| /* Parse 'classes' parameter by populating fp_os_classes. */
 | |
| 
 | |
| static void config_parse_classes(u8* val) {
 | |
| 
 | |
|   while (*val) {
 | |
| 
 | |
|     u8* nxt;
 | |
| 
 | |
|     while (isblank(*val) || *val == ',') val++;
 | |
| 
 | |
|     nxt = val;
 | |
| 
 | |
|     while (isalnum(*nxt)) nxt++;
 | |
| 
 | |
|     if (nxt == val || (*nxt && *nxt != ','))
 | |
|       FATAL("Malformed class entry in line %u.", line_no);
 | |
| 
 | |
|     fp_os_classes = DFL_ck_realloc(fp_os_classes, (class_cnt + 1) * sizeof(u8*));
 | |
| 
 | |
|     fp_os_classes[class_cnt++] = DFL_ck_memdup_str(val, nxt - val);
 | |
| 
 | |
|     val = nxt;
 | |
| 
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Look up or create OS or application id. */
 | |
| 
 | |
| u32 lookup_name_id(u8* name, u8 len) {
 | |
| 
 | |
|   u32 i;
 | |
| 
 | |
|   for (i = 0; i < name_cnt; i++)
 | |
|     if (!strncasecmp((char*)name, (char*)fp_os_names[i], len)
 | |
|         && !fp_os_names[i][len]) break;
 | |
| 
 | |
|   if (i == name_cnt) {
 | |
| 
 | |
|     sig_name = name_cnt;
 | |
| 
 | |
|     fp_os_names = DFL_ck_realloc(fp_os_names, (name_cnt + 1) * sizeof(u8*));
 | |
|     fp_os_names[name_cnt++] = DFL_ck_memdup_str(name, len);
 | |
| 
 | |
|   }
 | |
| 
 | |
|   return i;
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Parse 'label' parameter by looking up ID and recording name / flavor. */
 | |
| 
 | |
| static void config_parse_label(u8* val) {
 | |
| 
 | |
|   u8* nxt;
 | |
|   u32 i;
 | |
| 
 | |
|   /* Simplified handling for [mtu] signatures. */
 | |
| 
 | |
|   if (mod_type == CF_MOD_MTU) {
 | |
| 
 | |
|     if (!*val) FATAL("Empty MTU label in line %u.\n", line_no);
 | |
| 
 | |
|     sig_flavor  = DFL_ck_strdup(val);
 | |
|     return;
 | |
| 
 | |
|   }
 | |
|  
 | |
|   if (*val == 'g') generic = 1;
 | |
|   else if (*val == 's') generic = 0;
 | |
|   else FATAL("Malformed class entry in line %u.", line_no);
 | |
| 
 | |
|   if (val[1] != ':') FATAL("Malformed class entry in line %u.", line_no);
 | |
| 
 | |
|   val += 2;
 | |
| 
 | |
|   nxt = val;
 | |
|   while (isalnum(*nxt) || *nxt == '!') nxt++;
 | |
| 
 | |
|   if (nxt == val || *nxt != ':') FATAL("Malformed class entry in line %u.", line_no);
 | |
| 
 | |
|   if (*val == '!' && val[1] == ':') {
 | |
| 
 | |
|     sig_class = -1;
 | |
| 
 | |
|   } else {
 | |
| 
 | |
|     *nxt = 0;
 | |
| 
 | |
|     for (i = 0; i < class_cnt; i++)
 | |
|       if (!strcasecmp((char*)val, (char*)fp_os_classes[i])) break;
 | |
| 
 | |
|     if (i == class_cnt) FATAL("Unknown class '%s' in line %u.", val, line_no);
 | |
| 
 | |
|     sig_class = i;
 | |
| 
 | |
|   }
 | |
| 
 | |
|   nxt++;
 | |
|   val = nxt;
 | |
|   while (isalnum(*nxt) || (*nxt && strchr(NAME_CHARS, *nxt))) nxt++;
 | |
| 
 | |
|   if (nxt == val || *nxt != ':') FATAL("Malformed name in line %u.", line_no);
 | |
| 
 | |
|   sig_name = lookup_name_id(val, nxt - val);
 | |
| 
 | |
|   if (nxt[1]) sig_flavor = DFL_ck_strdup(nxt + 1);
 | |
|     else sig_flavor = NULL;
 | |
| 
 | |
|   label_id++;
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Parse 'sys' parameter into cur_sys[]. */
 | |
| 
 | |
| static void config_parse_sys(u8* val) {
 | |
| 
 | |
|   if (cur_sys) {
 | |
|     cur_sys = NULL;
 | |
|     cur_sys_cnt = 0;
 | |
|   }
 | |
| 
 | |
|   while (*val) {
 | |
| 
 | |
|     u8* nxt;
 | |
|     u8  is_cl = 0, orig;
 | |
|     u32 i;
 | |
| 
 | |
|     while (isblank(*val) || *val == ',') val++;
 | |
| 
 | |
|     if (*val == '@') { is_cl = 1; val++; }
 | |
| 
 | |
|     nxt = val;
 | |
| 
 | |
|     while (isalnum(*nxt) || (*nxt && strchr(NAME_CHARS, *nxt))) nxt++;
 | |
| 
 | |
|     if (nxt == val || (*nxt && *nxt != ','))
 | |
|       FATAL("Malformed sys entry in line %u.", line_no);
 | |
| 
 | |
|     orig = *nxt;
 | |
|     *nxt = 0;
 | |
| 
 | |
|     if (is_cl) {
 | |
| 
 | |
|       for (i = 0; i < class_cnt; i++)
 | |
|         if (!strcasecmp((char*)val, (char*)fp_os_classes[i])) break;
 | |
| 
 | |
|       if (i == class_cnt)
 | |
|         FATAL("Unknown class '%s' in line %u.", val, line_no);
 | |
| 
 | |
|       i |= SYS_CLASS_FLAG;
 | |
| 
 | |
|     } else {
 | |
| 
 | |
|       for (i = 0; i < name_cnt; i++)
 | |
|         if (!strcasecmp((char*)val, (char*)fp_os_names[i])) break;
 | |
| 
 | |
|       if (i == name_cnt) {
 | |
|         fp_os_names = DFL_ck_realloc(fp_os_names, (name_cnt + 1) * sizeof(u8*));
 | |
|         fp_os_names[name_cnt++] = DFL_ck_memdup_str(val, nxt - val);
 | |
|       }
 | |
| 
 | |
|     }
 | |
| 
 | |
|     cur_sys = DFL_ck_realloc(cur_sys, (cur_sys_cnt + 1) * 4);
 | |
|     cur_sys[cur_sys_cnt++] = i;
 | |
| 
 | |
|     *nxt = orig;
 | |
|     val = nxt;
 | |
| 
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Read p0f.fp line, dispatching it to fingerprinting modules as necessary. */
 | |
| 
 | |
| static void config_parse_line(u8* line) {
 | |
| 
 | |
|   u8 *val,*eon;
 | |
| 
 | |
|   /* Special handling for [module:direction]... */
 | |
| 
 | |
|   if (*line == '[') {
 | |
| 
 | |
|     u8* dir;
 | |
| 
 | |
|     line++;
 | |
| 
 | |
|     /* Simplified case for [mtu]. */
 | |
| 
 | |
|     if (!strcmp((char*)line, "mtu]")) {
 | |
| 
 | |
|       mod_type = CF_MOD_MTU;
 | |
|       state = CF_NEED_LABEL;
 | |
|       return;
 | |
| 
 | |
|     }
 | |
| 
 | |
|     dir = (u8*)strchr((char*)line, ':');
 | |
| 
 | |
|     if (!dir) FATAL("Malformed section identifier in line %u.", line_no);
 | |
| 
 | |
|     *dir = 0; dir++;
 | |
| 
 | |
|     if (!strcmp((char*)line, "tcp")) {
 | |
| 
 | |
|       mod_type = CF_MOD_TCP;
 | |
| 
 | |
|     } else if (!strcmp((char*)line, "http")) {
 | |
| 
 | |
|       mod_type = CF_MOD_HTTP;
 | |
| 
 | |
|     } else {
 | |
| 
 | |
|       FATAL("Unrecognized fingerprinting module '%s' in line %u.", line, line_no);
 | |
| 
 | |
|     }
 | |
| 
 | |
|     if (!strcmp((char*)dir, "request]")) {
 | |
| 
 | |
|       mod_to_srv = 1;
 | |
| 
 | |
|     } else if (!strcmp((char*)dir, "response]")) {
 | |
| 
 | |
|       mod_to_srv = 0;
 | |
| 
 | |
|     } else {
 | |
| 
 | |
|       FATAL("Unrecognized traffic direction in line %u.", line_no);
 | |
| 
 | |
|     }
 | |
| 
 | |
|     state = CF_NEED_LABEL;
 | |
|     return;
 | |
| 
 | |
|   }
 | |
| 
 | |
|   /* Everything else follows the 'name = value' approach. */
 | |
| 
 | |
|   val = line;
 | |
| 
 | |
|   while (isalpha(*val) || *val == '_') val++;
 | |
|   eon = val;
 | |
| 
 | |
|   while (isblank(*val)) val++;
 | |
| 
 | |
|   if (line == val || *val != '=')
 | |
|     FATAL("Unexpected statement in line %u.", line_no);
 | |
| 
 | |
|   while (isblank(*++val));
 | |
| 
 | |
|   *eon = 0;
 | |
| 
 | |
|   if (!strcmp((char*)line, "classes")) {
 | |
| 
 | |
|     if (state != CF_NEED_SECT) 
 | |
|       FATAL("misplaced 'classes' in line %u.", line_no);
 | |
| 
 | |
|     config_parse_classes(val);
 | |
| 
 | |
|   } else if (!strcmp((char*)line, "ua_os")) {
 | |
| 
 | |
|     if (state != CF_NEED_LABEL || mod_to_srv != 1 || mod_type != CF_MOD_HTTP) 
 | |
|       FATAL("misplaced 'us_os' in line %u.", line_no);
 | |
| 
 | |
|     http_parse_ua(val, line_no);
 | |
| 
 | |
|   } else if (!strcmp((char*)line, "label")) {
 | |
| 
 | |
|     /* We will drop sig_sys / sig_flavor on the floor if no signatures
 | |
|        actually created, but it's not worth tracking that. */
 | |
| 
 | |
|     if (state != CF_NEED_LABEL && state != CF_NEED_SIG)
 | |
|       FATAL("misplaced 'label' in line %u.", line_no);
 | |
| 
 | |
|     config_parse_label(val);
 | |
| 
 | |
|     if (mod_type != CF_MOD_MTU && sig_class < 0) state = CF_NEED_SYS;
 | |
|     else state = CF_NEED_SIG;
 | |
| 
 | |
|   } else if (!strcmp((char*)line, "sys")) {
 | |
| 
 | |
|     if (state != CF_NEED_SYS)
 | |
|       FATAL("Misplaced 'sys' in line %u.", line_no);
 | |
| 
 | |
|     config_parse_sys(val);
 | |
| 
 | |
|     state = CF_NEED_SIG;
 | |
| 
 | |
|   } else if (!strcmp((char*)line, "sig")) {
 | |
| 
 | |
|     if (state != CF_NEED_SIG) FATAL("Misplaced 'sig' in line %u.", line_no);
 | |
| 
 | |
|     switch (mod_type) {
 | |
| 
 | |
|       case CF_MOD_TCP:
 | |
|         tcp_register_sig(mod_to_srv, generic, sig_class, sig_name, sig_flavor,
 | |
|                          label_id, cur_sys, cur_sys_cnt, val, line_no);
 | |
|         break;
 | |
| 
 | |
|       case CF_MOD_MTU:
 | |
|         mtu_register_sig(sig_flavor, val, line_no);
 | |
|         break;
 | |
| 
 | |
|       case CF_MOD_HTTP:
 | |
|         http_register_sig(mod_to_srv, generic, sig_class, sig_name, sig_flavor,
 | |
|                           label_id, cur_sys, cur_sys_cnt, val, line_no);
 | |
|         break;
 | |
| 
 | |
|     }
 | |
| 
 | |
|     sig_cnt++;
 | |
| 
 | |
|   } else {
 | |
| 
 | |
|     FATAL("Unrecognized field '%s' in line %u.", line, line_no);
 | |
| 
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Top-level file parsing. */
 | |
| 
 | |
| void read_config(u8* fname) {
 | |
| 
 | |
|   s32 f;
 | |
|   struct stat st;
 | |
|   u8  *data, *cur;
 | |
| 
 | |
|   f = open((char*)fname, O_RDONLY);
 | |
|   if (f < 0) PFATAL("Cannot open '%s' for reading.", fname);
 | |
| 
 | |
|   if (fstat(f, &st)) PFATAL("fstat() on '%s' failed.", fname);
 | |
| 
 | |
|   if (!st.st_size) { 
 | |
|     close(f);
 | |
|     goto end_fp_read;
 | |
|   }
 | |
| 
 | |
|   cur = data = ck_alloc(st.st_size + 1);
 | |
| 
 | |
|   if (read(f, data, st.st_size) != st.st_size)
 | |
|     FATAL("Short read from '%s'.", fname);
 | |
| 
 | |
|   data[st.st_size] = 0;
 | |
| 
 | |
|   close(f);
 | |
| 
 | |
|   /* If you put NUL in your p0f.fp... Well, sucks to be you. */
 | |
| 
 | |
|   while (1) {
 | |
| 
 | |
|     u8 *eol;
 | |
| 
 | |
|     line_no++;
 | |
| 
 | |
|     while (isblank(*cur)) cur++;
 | |
| 
 | |
|     eol = cur;
 | |
|     while (*eol && *eol != '\n') eol++;
 | |
| 
 | |
|     if (*cur != ';' && cur != eol) {
 | |
| 
 | |
|       u8* line = ck_memdup_str(cur, eol - cur);
 | |
| 
 | |
|       config_parse_line(line);
 | |
| 
 | |
|       ck_free(line);
 | |
| 
 | |
|     }
 | |
| 
 | |
|     if (!*eol) break;
 | |
| 
 | |
|     cur = eol + 1;
 | |
| 
 | |
|   }
 | |
| 
 | |
|   ck_free(data);
 | |
| 
 | |
| end_fp_read:  
 | |
| 
 | |
|   if (!sig_cnt)
 | |
|     SAYF("[!] No signatures found in '%s'.\n", fname);
 | |
|   else 
 | |
|     SAYF("[+] Loaded %u signature%s from '%s'.\n", sig_cnt,
 | |
|          sig_cnt == 1 ? "" : "s", fname);
 | |
| 
 | |
| }
 | |
| 
 | 
