/*************************************************************************** * osscan.h -- Routines used for OS detection via TCP/IP fingerprinting. * * For more information on how this works in Nmap, see my paper at * * https://nmap.org/osdetect/ * * * ***********************IMPORTANT NMAP LICENSE TERMS************************ * * The Nmap Security Scanner is (C) 1996-2024 Nmap Software LLC ("The Nmap * Project"). Nmap is also a registered trademark of the Nmap Project. * * This program is distributed under the terms of the Nmap Public Source * License (NPSL). The exact license text applying to a particular Nmap * release or source code control revision is contained in the LICENSE * file distributed with that version of Nmap or source code control * revision. More Nmap copyright/legal information is available from * https://nmap.org/book/man-legal.html, and further information on the * NPSL license itself can be found at https://nmap.org/npsl/ . This * header summarizes some key points from the Nmap license, but is no * substitute for the actual license text. * * Nmap is generally free for end users to download and use themselves, * including commercial use. It is available from https://nmap.org. * * The Nmap license generally prohibits companies from using and * redistributing Nmap in commercial products, but we sell a special Nmap * OEM Edition with a more permissive license and special features for * this purpose. See https://nmap.org/oem/ * * If you have received a written Nmap license agreement or contract * stating terms other than these (such as an Nmap OEM license), you may * choose to use and redistribute Nmap under those terms instead. * * The official Nmap Windows builds include the Npcap software * (https://npcap.com) for packet capture and transmission. It is under * separate license terms which forbid redistribution without special * permission. So the official Nmap Windows builds may not be redistributed * without special permission (such as an Nmap OEM license). * * Source is provided to this software because we believe users have a * right to know exactly what a program is going to do before they run it. * This also allows you to audit the software for security holes. * * Source code also allows you to port Nmap to new platforms, fix bugs, and * add new features. You are highly encouraged to submit your changes as a * Github PR or by email to the dev@nmap.org mailing list for possible * incorporation into the main distribution. Unless you specify otherwise, it * is understood that you are offering us very broad rights to use your * submissions as described in the Nmap Public Source License Contributor * Agreement. This is important because we fund the project by selling licenses * with various terms, and also because the inability to relicense code has * caused devastating problems for other Free Software projects (such as KDE * and NASM). * * The free version of Nmap is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, * indemnification and commercial support are all available through the * Npcap OEM program--see https://nmap.org/oem/ * ***************************************************************************/ /* $Id$ */ #ifndef OSSCAN_H #define OSSCAN_H #include #include #include class Target; class FingerPrintResultsIPv4; #define OSSCAN_SUCCESS 0 #define OSSCAN_NOMATCHES -1 #define OSSCAN_TOOMANYMATCHES -2 /* We won't even consider matches with a lower accuracy than this */ #define OSSCAN_GUESS_THRESHOLD 0.85 /* The method used to calculate the Target::distance, included in OS fingerprints. */ enum dist_calc_method { DIST_METHOD_NONE, DIST_METHOD_LOCALHOST, DIST_METHOD_DIRECT, DIST_METHOD_ICMP, DIST_METHOD_TRACEROUTE }; /********************** STRUCTURES ***********************************/ #define NUM_FPTESTS 13 // T2-T7 and U1 have 11 attributes each #define FP_MAX_TEST_ATTRS 11 // RIPCK #define FP_MAX_NAME_LEN 5 // Short alphanumeric strings. template struct ShortStr { char str[_MaxStrLen+1]; bool trunc; ShortStr() : trunc(false) {memset(str, 0, sizeof(str));} ShortStr(const char *s) { setStr(s); } ShortStr(const char *s, const char *e) { setStr(s, e); } void setStr(const char *in); void setStr(const char *in, const char *end); // Helpers for type conversion operator const char *() const {return this->str;} operator char *() {return this->str;} bool operator==(const char *other) const { return (!trunc && strncmp(str, other, _MaxStrLen) == 0); } bool operator==(const ShortStr &other) const { return (!trunc && !other.trunc && strncmp(str, other.str, _MaxStrLen) == 0); } bool operator!=(const ShortStr &other) const { return (trunc || other.trunc || strncmp(str, other.str, _MaxStrLen) != 0); } bool operator<(const ShortStr &other) const { return (trunc < other.trunc || strncmp(str, other.str, _MaxStrLen) < 0); } }; typedef ShortStr FPstr; struct Attr { FPstr name; int points; Attr() : name(), points(0) {} Attr(const char *n) : name(n), points(0) {} }; struct FingerTestDef { FPstr name; u8 numAttrs; bool hasR; std::map AttrIdx; std::vector Attrs; FingerTestDef() : name(), numAttrs(0), hasR(false) {} FingerTestDef(const FPstr &n, const char *a[]); }; #define ID2INT(_i) static_cast(_i) #define INT2ID(_i) static_cast(_i) class FingerPrintDef { public: enum TestID { SEQ, OPS, WIN, ECN, T1, T2, T3, T4, T5, T6, T7, U1, IE, INVALID }; static const char *test_attrs[NUM_FPTESTS][FP_MAX_TEST_ATTRS]; FingerPrintDef(); bool parseTestStr(const char *str, const char *end); FingerTestDef &getTestDef(TestID id) { return TestDefs[ID2INT(id)]; } const FingerTestDef &getTestDef(TestID id) const { return TestDefs[ID2INT(id)]; } int getTestIndex(const FPstr testname) const { return ID2INT(TestIdx.at(testname)); } TestID str2TestID(const FPstr testname) const { return TestIdx.at(testname); } private: std::map TestIdx; std::vector TestDefs; }; struct OS_Classification { const char *OS_Vendor; const char *OS_Family; const char *OS_Generation; /* Can be NULL if unclassified */ const char *Device_Type; std::vector cpe; }; /* A description of an operating system: a human-readable name and a list of classifications. */ struct FingerMatch { int line; /* For reference prints, the line # in nmap-os-db */ /* For IPv6 matches, the number of fingerprints that contributed to this * classification group */ /* For IPv4 fingerprints, the number of points possible */ unsigned short numprints; const char *OS_name; std::vector OS_class; FingerMatch() : line(-1), numprints(0), OS_name(NULL) {} }; struct FingerPrintDB; struct FingerTest { FingerPrintDef::TestID id; const FingerTestDef *def; std::vector *results; FingerTest() : id(FingerPrintDef::INVALID), def(NULL), results(NULL) {} FingerTest(const FPstr &testname, const FingerPrintDef &Defs) { id = Defs.str2TestID(testname); def = &Defs.getTestDef(id); results = new std::vector(def->numAttrs, NULL); } FingerTest(FingerPrintDef::TestID testid, const FingerPrintDef &Defs) : id(testid), results(NULL) { def = &Defs.getTestDef(id); results = new std::vector(def->numAttrs, NULL); } FingerTest(const FingerTest &other) : id(other.id), def(other.def), results(other.results) {} ~FingerTest() { // results must be freed manually } void erase(); bool str2AVal(const char *str, const char *end); void setAVal(const char *attr, const char *value); const char *getAVal(const char *attr) const; const char *getAValName(u8 index) const; const char *getTestName() const { return def->name.str; } int getMaxPoints() const; }; /* Same struct used for reference prints (DB) and observations */ struct FingerPrint { FingerMatch match; FingerTest tests[NUM_FPTESTS]; void erase(); void setTest(const FingerTest &test) { tests[ID2INT(test.id)] = test; } }; /* These structs are used in fingerprint-processing code outside of Nmap itself * { */ /* SCAN pseudo-test */ struct FingerPrintScan { enum Attribute { V, E, D, OT, CT, CU, PV, DS, DC, G, M, TM, P, MAX_ATTR }; static const char *attr_names[static_cast(MAX_ATTR)]; const char *values[static_cast(MAX_ATTR)]; bool present; FingerPrintScan() : present(false) {memset(values, 0, sizeof(values));} bool parse(const char *str, const char *end); const char *scan2str() const; }; /* An observation parsed from string representation */ struct ObservationPrint { FingerPrint fp; FingerPrintScan scan_info; std::vector extra_tests; const char *getInfo(FingerPrintScan::Attribute attr) const { if (attr >= FingerPrintScan::MAX_ATTR) return NULL; return scan_info.values[static_cast(attr)]; } void mergeTest(const FingerTest &test) { FingerTest &ours = fp.tests[ID2INT(test.id)]; if (ours.id == FingerPrintDef::INVALID) ours = test; else { extra_tests.push_back(test); } } }; /* } */ /* This structure contains the important data from the fingerprint database (nmap-os-db) */ struct FingerPrintDB { FingerPrintDef *MatchPoints; std::vector prints; FingerPrintDB(); ~FingerPrintDB(); }; /********************** PROTOTYPES ***********************************/ const char *fp2ascii(const FingerPrint *FP); /* Parses a single fingerprint from the memory region given. If a non-null fingerprint is returned, the user is in charge of freeing it when done. This function does not require the fingerprint to be 100% complete since it is used by scripts such as scripts/fingerwatch for which some partial fingerprints are OK. */ ObservationPrint *parse_single_fingerprint(const FingerPrintDB *DB, const char *fprint); /* These functions take a file/db name and open+parse it, returning an (allocated) FingerPrintDB containing the results. They exit with an error message in the case of error. */ FingerPrintDB *parse_fingerprint_file(const char *fname, bool points_only); FingerPrintDB *parse_fingerprint_reference_file(const char *dbname); void free_fingerprint_file(FingerPrintDB *DB); /* Compares 2 fingerprints -- a referenceFP (can have expression attributes) with an observed fingerprint (no expressions). If verbose is nonzero, differences will be printed. The comparison accuracy (between 0 and 1) is returned). MatchPoints is a special "fingerprints" which tells how many points each test is worth. */ double compare_fingerprints(const FingerPrint *referenceFP, const FingerPrint *observedFP, const FingerPrintDef *MatchPoints, int verbose, double threshold); /* Takes a fingerprint and looks for matches inside the passed in reference fingerprint DB. The results are stored in in FPR (which must point to an instantiated FingerPrintResultsIPv4 class) -- results will be reverse-sorted by accuracy. No results below accuracy_threshold will be included. The max matches returned is the maximum that fits in a FingerPrintResultsIPv4 class. */ void match_fingerprint(const FingerPrint *FP, FingerPrintResultsIPv4 *FPR, const FingerPrintDB *DB, double accuracy_threshold); /* Returns true if perfect match -- if num_subtests & num_subtests_succeeded are non_null it updates them. if shortcircuit is zero, it does all the tests, otherwise it returns when the first one fails */ void WriteSInfo(char *ostr, int ostrlen, bool isGoodFP, const char *engine_id, const struct sockaddr_storage *addr, int distance, enum dist_calc_method distance_calculation_method, const u8 *mac, int openTcpPort, int closedTcpPort, int closedUdpPort); const char *mergeFPs(FingerPrint *FPs[], int numFPs, bool isGoodFP, const struct sockaddr_storage *addr, int distance, enum dist_calc_method distance_calculation_method, const u8 *mac, int openTcpPort, int closedTcpPort, int closedUdpPort, bool wrapit); // Internal matching function. Defined here for test purposes. bool expr_match(const char *val, size_t vlen, const char *expr, size_t explen, bool do_nested=false); #endif /*OSSCAN_H*/