diff --git a/CHANGELOG b/CHANGELOG
index 1b16e713a..dd2e2dc9d 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,11 @@
# Nmap Changelog ($Id$); -*-text-*-
+o [NSE] Made gathered CPE codes available to NSE. [Henri]
+
+o [NSE] Fixed a memory leak in PortList::setServiceProbeResults() noticed and
+ reported by David. The leak was triggered by set_port_version calls from NSE.
+ [Henri]
+
o [NSE] Added http-generator.nse by Michael Kohl, which gets version
information for web applications that set the "generator" meta
element.
diff --git a/docs/scripting.xml b/docs/scripting.xml
index 1e9c38dad..e4604739c 100644
--- a/docs/scripting.xml
+++ b/docs/scripting.xml
@@ -1630,6 +1630,12 @@ LUALIB_API int luaopen_openssl(lua_State *L) {
option, then
host.os is nil.
+
+ 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.
+
@@ -1890,6 +1896,15 @@ LUALIB_API int luaopen_openssl(lua_State *L) {
nil if rpc_status is
anything other than good_prog.
+
+
+ cpe
+
+ List of CPE codes for the detected service. As described in the
+ official CPE specification these strings
+ all start with the cpe:/ prefix.
+
+
diff --git a/nse_nmaplib.cc b/nse_nmaplib.cc
index 23baf1c16..11b671f00 100644
--- a/nse_nmaplib.cc
+++ b/nse_nmaplib.cc
@@ -32,6 +32,8 @@ static const int NSE_PROTOCOL[] = {IPPROTO_TCP, IPPROTO_UDP, IPPROTO_SCTP};
void set_version (lua_State *L, const struct serviceDeductions *sd)
{
+ size_t i;
+
setsfield(L, -1, "name", sd->name);
setnfield(L, -1, "name_confidence", sd->name_confidence);
setsfield(L, -1, "product", sd->product);
@@ -61,6 +63,13 @@ void set_version (lua_State *L, const struct serviceDeductions *sd)
setnfield(L, -1, "rpc_lowver", sd->rpc_lowver);
setnfield(L, -1, "rpc_highver", sd->rpc_highver);
}
+
+ lua_newtable(L);
+ for (i = 0; i < sd->cpe.size(); i++) {
+ lua_pushstring(L, sd->cpe[i]);
+ lua_rawseti(L, -2, i+1);
+ }
+ lua_setfield(L, -2, "cpe");
}
/* set some port state information onto the
@@ -179,7 +188,8 @@ 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;
+ int i, classno;
+ 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
@@ -187,6 +197,15 @@ void set_hostinfo(lua_State *L, Target *currenths) {
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);
+ }
+ }
lua_setfield(L, -2, "os");
}
}
@@ -508,6 +527,7 @@ static int l_set_port_version (lua_State *L)
Target *target;
Port *p;
Port port;
+ std::vector cpe;
enum service_tunnel_type tunnel = SERVICE_TUNNEL_NONE;
enum serviceprobestate probestate =
opversion[luaL_checkoption(L, 3, "hardmatched", ops)];
@@ -537,14 +557,23 @@ static int l_set_port_version (lua_State *L)
else
luaL_argerror(L, 2, "invalid value for port.version.service_tunnel");
+ lua_getfield(L, 4, "cpe");
+ if (!lua_istable(L, -1))
+ luaL_error(L, "port.version 'cpe' field must be a table");
+
+ for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
+ cpe.push_back(lua_tostring(L, -1));
+ }
+
if (o.servicescan)
target->ports.setServiceProbeResults(p->portno, p->proto,
probestate, name, tunnel, product,
- version, extrainfo, hostname, ostype, devicetype, NULL, NULL, NULL, NULL);
+ version, extrainfo, hostname, ostype, devicetype,
+ (cpe.size() > 0) ? &cpe : NULL, NULL);
else
target->ports.setServiceProbeResults(p->portno, p->proto,
probestate, name, tunnel, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL, NULL, NULL);
return 0;
}
diff --git a/portlist.cc b/portlist.cc
index 05fb60fc8..4816c3c78 100644
--- a/portlist.cc
+++ b/portlist.cc
@@ -115,7 +115,7 @@ Port::Port() {
state_reason_init(&reason);
}
-void Port::freeService() {
+void Port::freeService(bool del_service) {
if (service != NULL) {
std::vector::iterator it;
@@ -137,7 +137,10 @@ void Port::freeService() {
free(service->service_fp);
for (it = service->cpe.begin(); it != service->cpe.end(); it++)
free(*it);
- delete service;
+ service->cpe.clear();
+
+ if (del_service)
+ delete service;
}
}
@@ -336,8 +339,9 @@ void PortList::setServiceProbeResults(u16 portno, int protocol,
enum serviceprobestate sres, const char *sname,
enum service_tunnel_type tunnel, const char *product, const char *version,
const char *extrainfo, const char *hostname, const char *ostype,
- const char *devicetype, const char *cpe_a, const char *cpe_h, const char *cpe_o,
+ const char *devicetype, const std::vector *cpe,
const char *fingerprint) {
+ std::vector::iterator it;
Port *port;
char *p;
@@ -370,6 +374,8 @@ void PortList::setServiceProbeResults(u16 portno, int protocol,
// port->serviceprobe_results = sres;
port->service->service_tunnel = tunnel;
+ port->freeService(false);
+
if (sname)
port->service->name = strdup(sname);
else
@@ -387,15 +393,15 @@ void PortList::setServiceProbeResults(u16 portno, int protocol,
port->service->ostype = cstringSanityCheck(ostype, 32);
port->service->devicetype = cstringSanityCheck(devicetype, 32);
- p = cstringSanityCheck(cpe_a, 80);
- if (p != NULL)
- port->service->cpe.push_back(p);
- p = cstringSanityCheck(cpe_h, 80);
- if (p != NULL)
- port->service->cpe.push_back(p);
- p = cstringSanityCheck(cpe_o, 80);
- if (p != NULL)
- port->service->cpe.push_back(p);
+ if (cpe) {
+ std::vector::const_iterator cit;
+
+ for (cit = cpe->begin(); cit != cpe->end(); cit++) {
+ p = cstringSanityCheck(*cit, 80);
+ if (p != NULL)
+ port->service->cpe.push_back(p);
+ }
+ }
}
/* Sets the results of an RPC scan. if rpc_status is not
@@ -498,7 +504,7 @@ PortList::~PortList() {
if(port_list[proto]) {
for(i=0; i < port_list_count[proto]; i++) { // free every Port
if(port_list[proto][i]) {
- port_list[proto][i]->freeService();
+ port_list[proto][i]->freeService(true);
delete port_list[proto][i];
}
}
diff --git a/portlist.h b/portlist.h
index 026f111af..317127f22 100644
--- a/portlist.h
+++ b/portlist.h
@@ -178,7 +178,7 @@ class Port {
public:
Port();
- void freeService();
+ void freeService(bool del_service);
void getNmapServiceName(char *namebuf, int buflen, const char *rpcinfo) const;
u16 portno;
@@ -267,7 +267,7 @@ class PortList {
const char *version, const char *hostname,
const char *ostype, const char *devicetype,
const char *extrainfo,
- const char *cpe_a, const char *cpe_h, const char *cpe_o,
+ const std::vector *cpe,
const char *fingerprint);
// pass in an allocated struct serviceDeductions (don't worry about initializing, and
diff --git a/service_scan.cc b/service_scan.cc
index 04dc02be7..514c2fef7 100644
--- a/service_scan.cc
+++ b/service_scan.cc
@@ -2536,6 +2536,15 @@ list::iterator svc;
for(svc = SG->services_finished.begin(); svc != SG->services_finished.end(); svc++) {
if ((*svc)->probe_state != PROBESTATE_FINISHED_NOMATCH) {
+ vector cpe;
+
+ if (*(*svc)->cpe_a_matched)
+ cpe.push_back((*svc)->cpe_a_matched);
+ if (*(*svc)->cpe_h_matched)
+ cpe.push_back((*svc)->cpe_h_matched);
+ if (*(*svc)->cpe_o_matched)
+ cpe.push_back((*svc)->cpe_o_matched);
+
(*svc)->target->ports.setServiceProbeResults((*svc)->portno, (*svc)->proto,
(*svc)->probe_state,
(*svc)->probe_matched,
@@ -2546,15 +2555,13 @@ list::iterator svc;
*(*svc)->hostname_matched? (*svc)->hostname_matched : NULL,
*(*svc)->ostype_matched? (*svc)->ostype_matched : NULL,
*(*svc)->devicetype_matched? (*svc)->devicetype_matched : NULL,
- *(*svc)->cpe_a_matched? (*svc)->cpe_a_matched : NULL,
- *(*svc)->cpe_h_matched? (*svc)->cpe_h_matched : NULL,
- *(*svc)->cpe_o_matched? (*svc)->cpe_o_matched : NULL,
+ (cpe.size() > 0) ? &cpe : NULL,
shouldWePrintFingerprint(*svc) ? (*svc)->getServiceFingerprint(NULL) : NULL);
} else {
(*svc)->target->ports.setServiceProbeResults((*svc)->portno, (*svc)->proto,
(*svc)->probe_state, NULL,
(*svc)->tunnel, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL,
+ NULL,
(*svc)->getServiceFingerprint(NULL));
}
}
@@ -2600,8 +2607,7 @@ static void remove_excluded_ports(AllProbes *AP, ServiceGroup *SG) {
PROBESTATE_EXCLUDED, NULL,
SERVICE_TUNNEL_NONE,
"Excluded from version scan", NULL,
- NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL, NULL, NULL);
SG->services_remaining.erase(i);
SG->services_finished.push_back(svc);