1 
2 /***************************************************************************
3  * FingerPrintResults.cc -- The FingerPrintResults class the results of OS *
4  * fingerprint matching against a certain host.                            *
5  *                                                                         *
6  ***********************IMPORTANT NMAP LICENSE TERMS************************
7  *                                                                         *
8  * The Nmap Security Scanner is (C) 1996-2020 Insecure.Com LLC ("The Nmap  *
9  * Project"). Nmap is also a registered trademark of the Nmap Project.     *
10  *                                                                         *
11  * This program is distributed under the terms of the Nmap Public Source   *
12  * License (NPSL). The exact license text applying to a particular Nmap    *
13  * release or source code control revision is contained in the LICENSE     *
14  * file distributed with that version of Nmap or source code control       *
15  * revision. More Nmap copyright/legal information is available from       *
16  * https://nmap.org/book/man-legal.html, and further information on the    *
17  * NPSL license itself can be found at https://nmap.org/npsl. This header  *
18  * summarizes some key points from the Nmap license, but is no substitute  *
19  * for the actual license text.                                            *
20  *                                                                         *
21  * Nmap is generally free for end users to download and use themselves,    *
22  * including commercial use. It is available from https://nmap.org.        *
23  *                                                                         *
24  * The Nmap license generally prohibits companies from using and           *
25  * redistributing Nmap in commercial products, but we sell a special Nmap  *
26  * OEM Edition with a more permissive license and special features for     *
27  * this purpose. See https://nmap.org/oem                                  *
28  *                                                                         *
29  * If you have received a written Nmap license agreement or contract       *
30  * stating terms other than these (such as an Nmap OEM license), you may   *
31  * choose to use and redistribute Nmap under those terms instead.          *
32  *                                                                         *
33  * The official Nmap Windows builds include the Npcap software             *
34  * (https://npcap.org) for packet capture and transmission. It is under    *
35  * separate license terms which forbid redistribution without special      *
36  * permission. So the official Nmap Windows builds may not be              *
37  * redistributed without special permission (such as an Nmap OEM           *
38  * license).                                                               *
39  *                                                                         *
40  * Source is provided to this software because we believe users have a     *
41  * right to know exactly what a program is going to do before they run it. *
42  * This also allows you to audit the software for security holes.          *
43  *                                                                         *
44  * Source code also allows you to port Nmap to new platforms, fix bugs,    *
45  * and add new features.  You are highly encouraged to submit your         *
46  * changes as a Github PR or by email to the dev@nmap.org mailing list     *
47  * for possible incorporation into the main distribution. Unless you       *
48  * specify otherwise, it is understood that you are offering us very       *
49  * broad rights to use your submissions as described in the Nmap Public    *
50  * Source License Contributor Agreement. This is important because we      *
51  * fund the project by selling licenses with various terms, and also       *
52  * because the inability to relicense code has caused devastating          *
53  * problems for other Free Software projects (such as KDE and NASM).       *
54  *                                                                         *
55  * The free version of Nmap is distributed in the hope that it will be     *
56  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of  *
57  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,        *
58  * indemnification and commercial support are all available through the    *
59  * Npcap OEM program--see https://nmap.org/oem.                            *
60  *                                                                         *
61  ***************************************************************************/
62 
63 /* $Id: FingerPrintResults.cc 38078 2020-10-02 16:12:22Z dmiller $ */
64 
65 #include "FingerPrintResults.h"
66 #include "osscan.h"
67 #include "NmapOps.h"
68 
69 extern NmapOps o;
70 
FingerPrintResults()71 FingerPrintResults::FingerPrintResults() {
72   num_perfect_matches = num_matches = 0;
73   overall_results = OSSCAN_NOMATCHES;
74   memset(accuracy, 0, sizeof(accuracy));
75   isClassified = false;
76   osscan_opentcpport = osscan_closedtcpport = osscan_closedudpport = -1;
77   distance = -1;
78   distance_guess = -1;
79   distance_calculation_method = DIST_METHOD_NONE;
80   maxTimingRatio = 0;
81   incomplete = false;
82 }
83 
~FingerPrintResults()84 FingerPrintResults::~FingerPrintResults() {
85 }
86 
FingerPrintResultsIPv4()87 FingerPrintResultsIPv4::FingerPrintResultsIPv4() {
88   FPs = (FingerPrint **) safe_zalloc(o.maxOSTries() * sizeof(FingerPrint *));
89   numFPs = 0;
90 }
91 
~FingerPrintResultsIPv4()92 FingerPrintResultsIPv4::~FingerPrintResultsIPv4() {
93   int i;
94 
95   /* Free OS fingerprints of OS scanning was done */
96   for(i=0; i < numFPs; i++) {
97     delete(FPs[i]);
98     FPs[i] = NULL;
99   }
100   numFPs = 0;
101   free(FPs);
102 }
103 
FingerPrintResultsIPv6()104 FingerPrintResultsIPv6::FingerPrintResultsIPv6() {
105   unsigned int i;
106 
107   begin_time.tv_sec = 0;
108   begin_time.tv_usec = 0;
109   for (i = 0; i < sizeof(fp_responses) / sizeof(*fp_responses); i++)
110     fp_responses[i] = NULL;
111   flow_label = 0;
112 }
113 
~FingerPrintResultsIPv6()114 FingerPrintResultsIPv6::~FingerPrintResultsIPv6() {
115   unsigned int i;
116 
117   for (i = 0; i < sizeof(fp_responses) / sizeof(*fp_responses); i++) {
118     if (fp_responses[i])
119       delete fp_responses[i];
120   }
121 }
122 
getOSClassification()123 const struct OS_Classification_Results *FingerPrintResults::getOSClassification() {
124   if (!isClassified) { populateClassification(); isClassified = true; }
125   return &OSR;
126 }
127 
128 /* If the fingerprint is of potentially poor quality, we don't want to
129    print it and ask the user to submit it.  In that case, the reason
130    for skipping the FP is returned as a static string.  If the FP is
131    great and should be printed, NULL is returned. */
OmitSubmissionFP()132 const char *FingerPrintResults::OmitSubmissionFP() {
133   static char reason[128];
134 
135   if (o.scan_delay > 500) { // This can screw up the sequence timing
136     Snprintf(reason, sizeof(reason), "Scan delay (%d) is greater than 500", o.scan_delay);
137     return reason;
138   }
139 
140   if (o.timing_level > 4)
141     return "Timing level 5 (Insane) used";
142 
143   if (osscan_opentcpport <= 0)
144     return "Missing an open TCP port so results incomplete";
145 
146   if (osscan_closedtcpport <= 0)
147     return "Missing a closed TCP port so results incomplete";
148 
149   /* This can happen if the TTL in the response to the UDP probe is somehow
150      greater than the TTL in the probe itself. We exclude -1 because that is
151      used to mean the distance is unknown, though there's a chance it could
152      have come from the distance calculation. */
153   if (distance < -1) {
154     Snprintf(reason, sizeof(reason), "Host distance (%d network hops) appears to be negative", distance);
155     return reason;
156   }
157 
158   if (distance > 5) {
159     Snprintf(reason, sizeof(reason), "Host distance (%d network hops) is greater than five", distance);
160     return reason;
161   }
162 
163   if (maxTimingRatio > 1.4) {
164     Snprintf(reason, sizeof(reason), "maxTimingRatio (%e) is greater than 1.4", maxTimingRatio);
165     return reason;
166   }
167 
168   if (osscan_closedudpport < 0 && !o.udpscan) {
169     /* If we didn't get a U1 response, that might be just
170        because we didn't search for an closed port rather than
171        because this OS doesn't respond to that sort of probe.
172        So we don't print FP if U1 response is lacking AND no UDP
173        scan was performed. */
174     return "Didn't receive UDP response. Please try again with -sSU";
175   }
176 
177   if (incomplete) {
178     return "Some probes failed to send so results incomplete";
179   }
180 
181   return NULL;
182 }
183 
184 /* IPv6 classification is more robust to errors than IPv4, so apply less
185    stringent conditions than the general OmitSubmissionFP. */
OmitSubmissionFP()186 const char *FingerPrintResultsIPv6::OmitSubmissionFP() {
187   static char reason[128];
188 
189   if (o.scan_delay > 500) { // This can screw up the sequence timing
190     Snprintf(reason, sizeof(reason), "Scan delay (%d) is greater than 500", o.scan_delay);
191     return reason;
192   }
193 
194   if (osscan_opentcpport <= 0 && osscan_closedtcpport <= 0) {
195     return "Missing a closed or open TCP port so results incomplete";
196   }
197 
198   if (incomplete) {
199     return "Some probes failed to send so results incomplete";
200   }
201 
202   return NULL;
203 }
204 
205 
206 /* Goes through fingerprinting results to populate OSR */
populateClassification()207 void FingerPrintResults::populateClassification() {
208   std::vector<OS_Classification>::iterator osclass;
209   int printno;
210 
211   OSR.OSC_num_perfect_matches = OSR.OSC_num_matches = 0;
212   OSR.overall_results = OSSCAN_SUCCESS;
213 
214   if (overall_results == OSSCAN_TOOMANYMATCHES) {
215     // The normal classification overflowed so we don't even have all the perfect matches,
216     // I don't see any good reason to do classification.
217     OSR.overall_results = OSSCAN_TOOMANYMATCHES;
218     return;
219   }
220 
221   for(printno = 0; printno < num_matches; printno++) {
222     // a single print may have multiple classifications
223     for (osclass = matches[printno]->OS_class.begin();
224          osclass != matches[printno]->OS_class.end();
225          osclass++) {
226       if (!classAlreadyExistsInResults(&*osclass)) {
227         // Then we have to add it ... first ensure we have room
228         if (OSR.OSC_num_matches == MAX_FP_RESULTS) {
229           // Out of space ... if the accuracy of this one is 100%, we have a problem
230           if (printno < num_perfect_matches)
231             OSR.overall_results = OSSCAN_TOOMANYMATCHES;
232           return;
233         }
234 
235         // We have space, but do we even want this one?  No point
236         // including lesser matches if we have 1 or more perfect
237         // matches.
238         if (OSR.OSC_num_perfect_matches > 0 && printno >= num_perfect_matches) {
239           return;
240         }
241 
242         // OK, we will add the new class
243         OSR.OSC[OSR.OSC_num_matches] = &*osclass;
244         OSR.OSC_Accuracy[OSR.OSC_num_matches] = accuracy[printno];
245         if (printno < num_perfect_matches)
246           OSR.OSC_num_perfect_matches++;
247         OSR.OSC_num_matches++;
248       }
249     }
250   }
251 
252   if (OSR.OSC_num_matches == 0)
253     OSR.overall_results = OSSCAN_NOMATCHES;
254 
255   return;
256 }
257 
258 /* Return true iff s and t are both NULL or both the same string. */
strnulleq(const char * s,const char * t)259 static bool strnulleq(const char *s, const char *t) {
260   if (s == NULL && t == NULL)
261     return true;
262   else if (s == NULL || t == NULL)
263     return false;
264   else
265     return strcmp(s, t) == 0;
266 }
267 
268 // Go through any previously entered classes to see if this is a dupe;
classAlreadyExistsInResults(struct OS_Classification * OSC)269 bool FingerPrintResults::classAlreadyExistsInResults(struct OS_Classification *OSC) {
270   int i;
271 
272   for (i=0; i < OSR.OSC_num_matches; i++) {
273     if (strnulleq(OSC->OS_Vendor, OSR.OSC[i]->OS_Vendor) &&
274         strnulleq(OSC->OS_Family, OSR.OSC[i]->OS_Family) &&
275         strnulleq(OSC->Device_Type, OSR.OSC[i]->Device_Type) &&
276         strnulleq(OSC->OS_Generation, OSR.OSC[i]->OS_Generation)) {
277     // Found a duplicate!
278     return true;
279     }
280   }
281 
282   // Went through all the results -- no duplicates found
283   return false;
284 }
285