From b5a9bca6dc3011d0e32adce566bd8f59588ede01 Mon Sep 17 00:00:00 2001 From: david Date: Fri, 9 Mar 2012 02:32:30 +0000 Subject: [PATCH] Change the structure of host.os tables. Previously they were a flat list intermixing human-readable names and CPE strings. Now they reflect the structure that we use to represent them. In brief: host.os = { { name = "Microsoft Windows XP", classes = { { vendor = "Microsoft", osfamily = "Windows", osgen = "XP", type = "general purpose", cpe = { "cpe:/o:microsoft:windows_xp" } }, ... more classes ... }, }, ... more OS matches ... } --- docs/scripting.xml | 91 ++++++++++++++++++++++++++++++++++++++++------ nse_nmaplib.cc | 61 ++++++++++++++++++++++++------- 2 files changed, 126 insertions(+), 26 deletions(-) diff --git a/docs/scripting.xml b/docs/scripting.xml index 1d45dd592..956142bb7 100644 --- a/docs/scripting.xml +++ b/docs/scripting.xml @@ -1643,20 +1643,87 @@ LUALIB_API int luaopen_openssl(lua_State *L) { - The os entry in the host table is - an array of strings. The strings (as many as eight) are the - names of the operating systems the target is possibly - running. Strings are only entered in this array if the - target machine is a perfect match for one or more OS - database entries. If Nmap was run without the - option, then - host.os is nil. + An array of OS match tables. An OS match consists of a + human-readable name and an array of OS classes. Each OS + class consists of a vendor, OS family, OS generation, + device type, and an array of + CPECPE entries + for the class. (See + for a description of OS match fields.) Fields may be + nil if they are not defined. The + host.os table has this overall + structure: + + +host.os = { + { + name = string, + classes = { + { + vendor = string, + osfamily = string, + osgen = string, + type = string, + cpe = { + "cpe:/...", + More CPE + } + }, + More classes + }, + }, + More OS matches +} + + - Additionally the table can contain CPE codes for the detected - operating system. These codes, as described in - the official CPE specification - all start with the cpe:/ prefix. + For example, an OS match on this + nmap-os-dbnmap-os-db + entry: + + + +Fingerprint Linux 2.6.32 - 3.2 +Class Linux | Linux | 2.6.X | general purpose +CPE cpe:/o:linux:kernel:2.6 +Class Linux | Linux | 3.X | general purpose +CPE cpe:/o:linux:kernel:3 + + + + will result in this host.os table: + + + +host.os = { + { + name = "Linux 2.6.32 - 3.2", + classes = { + { + vendor = "Linux", + osfamily = "Linux", + osgen = "2.6.X", + type = "general purpose", + cpe = { "cpe:/o:linux:kernel:2.6" } + }, + { + vendor = "Linux", + osfamily = "Linux", + osgen = "3.X", + type = "general purpose", + cpe = { "cpe:/o:linux:kernel:3" } + } + }, + } +} + + + + Only entries corresponding to perfect OS matches are put + in the host.os table. If Nmap was run + without the option, then + host.os is nil. diff --git a/nse_nmaplib.cc b/nse_nmaplib.cc index 949c60a32..766cf8a82 100644 --- a/nse_nmaplib.cc +++ b/nse_nmaplib.cc @@ -110,6 +110,49 @@ static void push_bin_ip(lua_State *L, const struct sockaddr_storage *ss) } } +static void set_string_or_nil(lua_State *L, const char *fieldname, const char *value) { + if (value != NULL) { + lua_pushstring(L, value); + lua_setfield(L, -2, fieldname); + } +} + +static void push_osclass_table(lua_State *L, + const struct OS_Classification *osclass) { + unsigned int i; + + lua_newtable(L); + + set_string_or_nil(L, "vendor", osclass->OS_Vendor); + set_string_or_nil(L, "osfamily", osclass->OS_Family); + set_string_or_nil(L, "osgen", osclass->OS_Generation); + set_string_or_nil(L, "type", osclass->Device_Type); + + lua_newtable(L); + for (i = 0; i < osclass->cpe.size(); i++) { + lua_pushstring(L, osclass->cpe[i]); + lua_rawseti(L, -2, i + 1); + } + lua_setfield(L, -2, "cpe"); +} + +static void push_osmatch_table(lua_State *L, const FingerMatch *match, + const OS_Classification_Results *OSR) { + int i; + + lua_newtable(L); + + lua_pushstring(L, match->OS_name); + lua_setfield(L, -2, "name"); + + lua_newtable(L); + for (i = 0; i < OSR->OSC_num_matches; i++) { + push_osclass_table(L, OSR->OSC[i]); + lua_rawseti(L, -2, i + 1); + } + lua_setfield(L, -2, "classes"); +} + /* set host ip, host name and target name onto the * table which is currently on the stack * set name of the os run by the host onto the @@ -191,23 +234,13 @@ void set_hostinfo(lua_State *L, Target *currenths) { FPR->overall_results == OSSCAN_SUCCESS && FPR->num_perfect_matches > 0 && FPR->num_perfect_matches <= 8 ) { - int i, classno; + int i; const OS_Classification_Results *OSR = FPR->getOSClassification(); lua_newtable(L); - // this will run at least one time and at most 8 times, see if condition - for(i = 0; FPR->accuracy[i] == 1; i++) { - lua_pushstring(L, FPR->matches[i]->OS_name); - lua_rawseti(L, -2, i+1); - } - - for (classno = 0; classno < OSR->OSC_num_matches; classno++) { - size_t j; - - for (j = 0; j < OSR->OSC[classno]->cpe.size(); j++) { - lua_pushstring(L, OSR->OSC[classno]->cpe[j]); - lua_rawseti(L, -2, ++i); - } + for (i = 0; i < FPR->num_perfect_matches; i++) { + push_osmatch_table(L, FPR->matches[i], OSR); + lua_rawseti(L, -2, i + 1); } lua_setfield(L, -2, "os"); }