"Fossies" - the Fresh Open Source Software Archive 
Member "portsentry-2.0b1/portsentry.c" (8 Apr 2002, 30426 Bytes) of package /linux/privat/old/portsentry-2.0b1.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "portsentry.c" see the
Fossies "Dox" file reference documentation.
1 /************************************************************************/
2 /* */
3 /* Psionic PortSentry */
4 /* */
5 /* Created: 10-12-1997 */
6 /* Modified: 03-27-2002 */
7 /* */
8 /* Send all changes/modifications/bugfixes to sentrysupport@psionic.com */
9 /* */
10 /* */
11 /* This software is Copyright(c) 1997-2002 Psionic Technologies, Inc. */
12 /* */
13 /* Disclaimer: */
14 /* */
15 /* All software distributed by Psionic Technologies is distributed */
16 /* AS IS and carries NO WARRANTY or GUARANTEE OF ANY KIND. End users of */
17 /* the software acknowledge that they will not hold Psionic Technologies*/
18 /* liable for failure or non-function of the software product. YOU ARE */
19 /* USING THIS PRODUCT AT YOUR OWN RISK. */
20 /* */
21 /* Licensing restrictions apply. Commercial re-sell is prohibited under */
22 /* certain conditions. See the license that came with this package or */
23 /* visit http://www.psionic.com for more information. */
24 /* */
25 /* $Id: portsentry.c,v 1.53 2002/04/08 16:48:15 crowland Exp crowland $ */
26 /************************************************************************/
27
28 #include "portsentry.h"
29 #include "portsentry_io.h"
30 #include "portsentry_util.h"
31
32 /* Globals */
33 pcap_t *gblPcapSocketPtr;
34 pcap_t *gblHandlePtr;
35 bpf_u_int32 gblAddressPcapPtr;
36 bpf_u_int32 gblNetmaskPcapPtr;
37 int gblDataLink;
38 int gblLinkSize;
39 struct in_addr gblSrc, gblDst;
40 char gblAttackerIP[IPMAXBUF], gblTargetIP[IPMAXBUF];
41
42 struct {
43 char gblScanDetectHost[MAXSTATE][IPMAXBUF];
44 char gblKillRoute[MAXBUF];
45 char gblKillHostsDeny[MAXBUF];
46 char gblKillRunCmd[MAXBUF];
47 char gblBlockedFile[MAXBUF];
48 char gblHistoryFile[MAXBUF];
49 char gblIgnoreFile[MAXBUF];
50 char gblInterface[MAXBUF];
51 char gblInterfaceAddr[MAXBUF];
52 int gblScanDetectCount;
53 int gblTriggerCount;
54 } gblConfig;
55
56
57 struct {
58 int gblBlockTCP;
59 int gblBlockUDP;
60 int gblRunCmdFirst;
61 int gblResolveHost;
62 int gblIPChecks;
63 int gblTCPChecks;
64 int gblUDPChecks;
65 int gblICMPChecks;
66 } gblFlags;
67
68
69 /* Here we go */
70 int main (int argc, char **argv)
71 {
72
73
74 if (argc != 1)
75 {
76 Usage ();
77 ExitNow (ERROR);
78 }
79 else if ((geteuid ()) && (getuid ()) != 0)
80 {
81 printf ("You need to be root to run this.\n");
82 ExitNow (ERROR);
83 }
84 else if (CheckConfig () != TRUE)
85 {
86 Log ("adminalert: ERROR: Configuration files are missing/corrupted. Shutting down.\n");
87 printf ("ERROR: Configuration files are missing/corrupted.\n");
88 printf ("ERROR: Check your syslog for a more detailed error message.\n");
89 printf ("ERROR: PortSentry is shutting down!\n");
90 ExitNow (ERROR);
91 }
92 else if (InitConfig () != TRUE)
93 {
94 Log ("adminalert: ERROR: Your config file is corrupted/missing mandatory option! Shutting down.\n");
95 printf ("ERROR: Your config file is corrupted/missing mandatory option!\n");
96 printf ("ERROR: Check your syslog for a more detailed error message.\n");
97 printf ("ERROR: PortSentry is shutting down!\n");
98 ExitNow (ERROR);
99 }
100 else if (InitStats () != TRUE)
101 {
102 Log ("adminalert: ERROR: Couldn't initialize statistics. Shutting down.\n");
103 printf ("ERROR: Couldn't initialize statistics. Shutting down.\n");
104 ExitNow (ERROR);
105 }
106 #ifndef NODAEMON
107 else if (DaemonSeed () == ERROR)
108 {
109 Log ("adminalert: ERROR: could not go into daemon mode. Shutting down.\n");
110 printf ("ERROR: could not go into daemon mode. Shutting down.\n");
111 ExitNow (ERROR);
112 }
113 #endif
114 else if (InitInterface (gblConfig.gblInterface) != TRUE)
115 {
116 Log ("adminalert: ERROR: Couldn't initialize interface. Shutting down.\n");
117 printf ("ERROR: Couldn't initialize interface. Shutting down.\n");
118 ExitNow (ERROR);
119 }
120 else if(InitFilter() != TRUE)
121 {
122 Log ("adminalert: ERROR: Couldn't initialize BPF filter. Shutting down.\n");
123 printf ("ERROR: Couldn't initialize BPF filter. Shutting down.\n");
124 ExitNow (ERROR);
125 }
126
127 Log("adminalert: PortSentry is initialized and monitoring.");
128
129
130 /* Grab multiple packets */
131 pcap_loop(gblHandlePtr, -1, PktEngine, NULL);
132
133 /* Close interface */
134 pcap_close(gblHandlePtr);
135
136 exit(0);
137 }
138
139
140 /****************************************************************/
141 /* Reads generic config options into global variables */
142 /****************************************************************/
143 int
144 InitConfig (void)
145 {
146 FILE *input;
147 char configToken[MAXBUF];
148
149 gblFlags.gblBlockTCP = CheckFlag ("BLOCK_TCP");
150 gblFlags.gblBlockUDP = CheckFlag ("BLOCK_UDP");
151 gblFlags.gblResolveHost = CheckFlag ("RESOLVE_HOST");
152 gblFlags.gblIPChecks = CheckFlag("CHECK_IP");
153 gblFlags.gblTCPChecks = CheckFlag("CHECK_TCP");
154 gblFlags.gblUDPChecks = CheckFlag("CHECK_UDP");
155 gblFlags.gblICMPChecks = CheckFlag("CHECK_ICMP");
156
157 memset (gblConfig.gblKillRoute, '\0', MAXBUF);
158 memset (gblConfig.gblKillHostsDeny, '\0', MAXBUF);
159 memset (gblConfig.gblKillRunCmd, '\0', MAXBUF);
160
161 if ((ConfigTokenRetrieve ("SCAN_TRIGGER", configToken)) == FALSE)
162 {
163 Log ("adminalert: ERROR: Could not read SCAN_TRIGGER option from config file. Disabling SCAN DETECTION TRIGGER");
164 gblConfig.gblTriggerCount = 0;
165 }
166 else
167 {
168 #ifdef DEBUG
169 Log ("debug: InitConfig: retrieved SCAN_TRIGGER option: %s \n",
170 configToken);
171 #endif
172 gblConfig.gblTriggerCount = atoi (configToken);
173 }
174
175 if ((ConfigTokenRetrieve ("KILL_ROUTE", gblConfig.gblKillRoute)) == TRUE)
176 {
177 #ifdef DEBUG
178 Log ("debug: InitConfig: retrieved KILL_ROUTE option: %s \n",
179 gblConfig.gblKillRoute);
180 #endif
181 }
182 else
183 {
184 #ifdef DEBUG
185 Log ("debug: InitConfig: KILL_ROUTE option NOT FOUND.\n");
186 #endif
187 }
188
189 if ((ConfigTokenRetrieve ("KILL_HOSTS_DENY", gblConfig.gblKillHostsDeny)) == TRUE)
190 {
191 #ifdef DEBUG
192 Log ("debug: InitConfig: retrieved KILL_HOSTS_DENY option: %s \n",
193 gblConfig.gblKillHostsDeny);
194 #endif
195 }
196 else
197 {
198 #ifdef DEBUG
199 Log ("debug: InitConfig: KILL_HOSTS_DENY option NOT FOUND.\n");
200 #endif
201 }
202
203 if ((ConfigTokenRetrieve ("KILL_RUN_CMD", gblConfig.gblKillRunCmd)) == TRUE)
204 {
205 #ifdef DEBUG
206 Log ("debug: InitConfig: retrieved KILL_RUN_CMD option: %s \n",
207 gblConfig.gblKillRunCmd);
208 #endif
209 /* Check the order we should run the KILL_RUN_CMD */
210 /* Default is to run the command after blocking */
211 gblFlags.gblRunCmdFirst = CheckFlag ("KILL_RUN_CMD_FIRST");
212 }
213 else
214 {
215 #ifdef DEBUG
216 Log ("debug: InitConfig: KILL_RUN_CMD option NOT FOUND.\n");
217 #endif
218 }
219
220 if ((ConfigTokenRetrieve ("BLOCKED_FILE", gblConfig.gblBlockedFile)) == TRUE)
221 {
222 #ifdef DEBUG
223 Log ("debug: InitConfig: retrieved BLOCKED_FILE option: %s \n",
224 gblConfig.gblBlockedFile);
225 Log ("debug: CheckConfig: Removing old block file: %s \n",
226 gblConfig.gblBlockedFile);
227 #endif
228
229 if ((input = fopen (gblConfig.gblBlockedFile, "w")) == NULL)
230 {
231 Log ("adminalert: ERROR: Cannot delete blocked file on startup: %s.\n",
232 gblConfig.gblBlockedFile);
233 return (FALSE);
234 }
235 else
236 fclose(input);
237 }
238 else
239 {
240 Log ("adminalert: ERROR: Cannot retrieve BLOCKED_FILE option! Aborting\n");
241 return (FALSE);
242 }
243
244
245 if ((ConfigTokenRetrieve ("HISTORY_FILE", gblConfig.gblHistoryFile)) == TRUE)
246 {
247 #ifdef DEBUG
248 Log ("debug: InitConfig: retrieved HISTORY_FILE option: %s \n",
249 gblConfig.gblHistoryFile);
250 #endif
251 }
252 else
253 {
254 Log ("adminalert: ERROR: Cannot retrieve HISTORY_FILE option! Aborting\n");
255 return (FALSE);
256 }
257
258
259 if ((ConfigTokenRetrieve ("IGNORE_FILE", gblConfig.gblIgnoreFile)) == TRUE)
260 {
261 #ifdef DEBUG
262 Log ("debug: InitConfig: retrieved IGNORE_FILE option: %s \n",
263 gblConfig.gblIgnoreFile);
264 #endif
265 }
266 else
267 {
268 Log ("adminalert: ERROR: Cannot retrieve IGNORE_FILE option! Aborting\n");
269 return (FALSE);
270 }
271
272 if ((ConfigTokenRetrieve ("INTERFACE", gblConfig.gblInterface)) == TRUE)
273 {
274 #ifdef DEBUG
275 Log ("debug: InitConfig: retrieved INTERFACE option: %s \n",
276 gblConfig.gblInterface);
277 #endif
278 }
279 else
280 {
281 Log ("adminalert: ERROR: Cannot retrieve INTERFACE option! Aborting\n");
282 return (FALSE);
283 }
284
285 if ((ConfigTokenRetrieve ("INTERFACE_ADDRESS", gblConfig.gblInterfaceAddr)) == TRUE)
286 {
287 #ifdef DEBUG
288 Log ("debug: InitConfig: retrieved INTERFACE_ADDRESS option: %s \n",
289 gblConfig.gblInterface);
290 #endif
291 }
292 else
293 {
294 Log ("adminalert: ERROR: Cannot retrieve INTERFACE_ADDRESS option! Aborting\n");
295 return (FALSE);
296 }
297
298 return (TRUE);
299 }
300
301
302 /* Initialize statistic counters */
303 int InitStats(void)
304 {
305 gblStats.gblFrameCount=0;
306 gblStats.gblIPPackCount=0;
307 gblStats.gblIcmpPackCount=0;
308 gblStats.gblTcpPackCount=0;
309 gblStats.gblUdpPackCount=0;
310
311 return(TRUE);
312 }
313
314
315 /* Initialize the interface */
316 int InitInterface(char *interface)
317 {
318
319 char *interfacePtr;
320 char pcapError[PCAP_ERRBUF_SIZE];
321 /* XXX lookupnet vars
322 struct in_addr addr;
323 char *addressPtr, *netmaskPtr;
324 */
325
326 /* Initialize Interface */
327 if(strcmp(interface,"auto") == 0)
328 interfacePtr = pcap_lookupdev(pcapError);
329 else
330 interfacePtr = interface;
331
332 if(interfacePtr == NULL)
333 {
334 Log("adminalert: ERROR: Error looking up interface: %s\n", pcapError);
335 return(ERROR);
336 }
337
338 #ifdef DEBUG
339 Log ("debug: InitInterface: Opening interface: %s \n",interfacePtr);
340 #endif
341
342 /* Initialize address/netmask */
343 if(pcap_lookupnet(interfacePtr, &gblAddressPcapPtr, &gblNetmaskPcapPtr, pcapError) == ERROR)
344 {
345 Log("adminalert: ERROR: Error looking up network: %s\n", pcapError);
346 return(ERROR);
347 }
348
349 /* XXX Broken. Can't determine Interface IP */
350 /*addr.s_addr = gblAddressPcapPtr;
351 addressPtr = (char *)inet_ntoa(addr);
352 addr.s_addr = gblNetmaskPcapPtr;
353 netmaskPtr = (char *)inet_ntoa(addr);
354
355 if(addressPtr == NULL)
356 {
357 Log("adminalert: ERROR: Can't translate network address for parsing.\n");
358 return(ERROR);
359 }
360 */
361
362 Log("adminalert: Monitoring interface %s and address: %s\n", interfacePtr, gblConfig.gblInterfaceAddr);
363
364 /* Open device */
365 if((gblHandlePtr = pcap_open_live(interfacePtr, MAXBUF, 0, 0, pcapError)) == NULL)
366 {
367 Log("adminalert: ERROR: Can't open device %s for monitoring: %s\n", interfacePtr, pcapError);
368 return(ERROR);
369 }
370
371 #ifdef DEBUG
372 Log ("debug: InitInterface: Interface %s is now live. \n",interfacePtr);
373 #endif
374
375
376 /* Check media type function here */
377 gblDataLink = pcap_datalink(gblHandlePtr);
378 switch(gblDataLink)
379 {
380 case DLT_EN10MB:
381 gblLinkSize = LINK_ETHERSIZE;
382 break;
383 default:
384 Log("adminalert: ERROR: Datalink type is not supported yet.");
385 return(ERROR);
386 }
387
388 return(TRUE);
389 }
390
391
392
393 /* Initialize BPF */
394 int InitFilter(void)
395 {
396 char tcpPorts[MAXBUF], udpPorts[MAXBUF], filterString[MAXBUF * 2], finalFilter[MAXBUF * 2];
397 struct bpf_program BPF;
398
399
400 Log ("adminalert: Initializing PortSentry BPF filters.\n");
401
402 /* XXX */
403 /* Need to do:
404 Bypass ports in use already
405 Add unused ports to list with 'or' separator
406 add ICMP
407 put in the config retrieve in the main init function.
408 */
409
410 if ((ConfigTokenRetrieve ("TCP_PORTS", tcpPorts)) == FALSE)
411 Log ("adminalert: No TCP_PORTS defined in config file. Continuing.");
412 else
413 Log ("adminalert: Monitoring TCP ports: %s\n", tcpPorts);
414
415 if ((ConfigTokenRetrieve ("UDP_PORTS", udpPorts)) == FALSE)
416 Log ("adminalert: No UDP_PORTS defined in config file. Continuing.");
417 else
418 Log ("adminalert: Monitoring UDP ports: %s\n", udpPorts);
419
420
421 if ((strlen(tcpPorts) == 0) && (strlen(udpPorts) == 0))
422 {
423 Log("adminalert: No TCP or UDP ports defined in config file. Aborting.");
424 return(ERROR);
425 }
426 else
427 {
428 SafeStrncpy(finalFilter, "not src host ", 14);
429 strncat(finalFilter, gblConfig.gblInterfaceAddr, IPMAXBUF);
430 if(strlen(tcpPorts) != 0)
431 {
432 strncat(finalFilter, " and tcp dst port ", MAXBUF);
433 SubstString(" or ", ",", tcpPorts, filterString);
434 strncat(finalFilter, filterString, ((MAXBUF * 2)- strlen(finalFilter)));
435 }
436 if(strlen(udpPorts) != 0)
437 {
438 /* Straigtens out filter ordering here */
439 if(strlen(tcpPorts) != 0)
440 strncat(finalFilter, " or udp dst port ", ((MAXBUF * 2) - strlen(finalFilter)));
441 else
442 strncat(finalFilter, " and udp dst port ", ((MAXBUF * 2) - strlen(finalFilter)));
443
444 SubstString(" or ", ",", udpPorts, filterString);
445 strncat(finalFilter, filterString, ((MAXBUF * 2) - strlen(finalFilter)));
446 }
447 }
448
449
450 #ifdef DEBUG
451 Log ("debug: InitFilter: Applying filter: \"%s\" \n", finalFilter);
452 #endif
453
454 /* Set Filters function here */
455 if (pcap_compile(gblHandlePtr, &BPF, finalFilter, 0, gblNetmaskPcapPtr) == -1)
456 {
457 Log ("adminalert: ERROR: Could not apply BPF filter: %s", pcap_geterr(gblHandlePtr));
458 return(ERROR);
459 }
460 else if(pcap_setfilter(gblHandlePtr, &BPF) == -1)
461 {
462 Log ("adminalert: ERROR: Could not apply BPF filter: %s", pcap_geterr(gblHandlePtr));
463 return(ERROR);
464 }
465
466
467 #ifdef DEBUG
468 Log ("debug: InitFilter: Filter set.\n", finalFilter);
469 #endif
470
471 return(TRUE);
472 }
473
474
475 void PktEngine(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
476 {
477
478 const struct ether_header *ethernet;
479 const struct ip *ip;
480 const struct tcphdr *tcp;
481 const struct udphdr *udp;
482 const struct icmp *icmp;
483 int dataoffset = 0;
484 int result = FALSE;
485
486
487 gblStats.gblFrameCount++;
488
489 #ifdef DEBUG
490 Log ("debug: PktEngine: Frame counter: %d\n",gblStats.gblFrameCount);
491 Log ("debug: PktEngine: IP counter: %d\n",gblStats.gblIPPackCount);
492 Log ("debug: PktEngine: TCP counter: %d\n",gblStats.gblTcpPackCount);
493 Log ("debug: PktEngine: UDP counter: %d\n",gblStats.gblUdpPackCount);
494 Log ("debug: PktEngine: ICMP counter: %d\n",gblStats.gblIcmpPackCount);
495 #endif
496
497 /* shuts up compiler about uninitialized ip struct warning in switch() */
498 ip = NULL;
499
500
501 /* XXX Re-evaluate this switch statement if you apply BPF first */
502 /* the ethertype check is probably not necessary with ip,tcp,udp,icmp options with BPF */
503 switch(gblDataLink)
504 {
505 case DLT_EN10MB:
506 ethernet = (struct ether_header *) (packet);
507 if (ntohs(ethernet -> ether_type) == ETHERTYPE_IP)
508 {
509 #ifdef DEBUG
510 Log ("debug: PktEngine: Found ETHERTYPE_IP\n");
511 #endif
512 gblStats.gblIPPackCount++;
513 ip = (struct ip *) (packet + gblLinkSize);
514 gblSrc.s_addr = ip->ip_src.s_addr;
515 gblDst.s_addr = ip->ip_dst.s_addr;
516 if((dataoffset = CheckIP(ip)))
517 {
518 SafeStrncpy (gblAttackerIP, (char *) inet_ntoa (gblSrc), IPMAXBUF);
519 SafeStrncpy (gblTargetIP, (char *) inet_ntoa (gblDst), IPMAXBUF);
520 break;
521 }
522 else
523 {
524 Log("attackalert: An illegal IP packet type was detected and discarded.\n");
525 break;
526 }
527
528 }
529 /* Found other ETHERTYPE (probably an ARP) */
530 else
531 break;
532 }
533
534 if(dataoffset)
535 {
536 /* check if we should ignore this IP */
537 /* XXX This should read in at Init and placed into a list */
538 result = NeverBlock (gblAttackerIP, gblConfig.gblIgnoreFile);
539 if (result == ERROR)
540 Log ("attackalert: ERROR: cannot open ignore file. Analyzing attack anyway.\n");
541 else if(result == TRUE) /* Ignore this IP */
542 return;
543
544 /* check if this target is already blocked */
545 if (IsBlocked (gblAttackerIP, gblConfig.gblBlockedFile) == TRUE)
546 {
547 Log ("attackalert: Host: %s is already blocked - Ignoring", gblAttackerIP);
548 return;
549 }
550
551 /* check if they've visited before */
552 if (!CheckStateEngine (gblAttackerIP))
553 return;
554
555 switch(ip->ip_p)
556 {
557 case IPPROTO_TCP:
558 tcp = (struct tcphdr *) (packet + gblLinkSize + dataoffset);
559 gblStats.gblTcpPackCount++;
560 if (!CheckTCP(ip, tcp))
561 Log("adminalert: ERROR: Error checking TCP packet.\n");
562 break;
563 case IPPROTO_UDP:
564 gblStats.gblUdpPackCount++;
565 udp = (struct udphdr *) (packet + gblLinkSize + dataoffset);
566 if (!CheckUDP(ip, udp))
567 Log("adminalert: ERROR: Error checking UDP packet.\n");
568 break;
569 case IPPROTO_ICMP:
570 gblStats.gblIcmpPackCount++;
571 icmp = (struct icmp *) (packet + gblLinkSize + dataoffset);
572 if (!CheckICMP(ip, icmp))
573 Log("adminalert: ERROR: Error checking ICMP packet.\n");
574 break;
575 }
576 }
577
578 }
579
580 int CheckTCP(const struct ip *ip, const struct tcphdr *tcp)
581 {
582 char resolvedHost[DNSMAXBUF];
583 char *scanType;
584 int dstPort = 0, srcPort = 0;
585
586
587 /* Set destination/src port */
588 dstPort = ntohs(tcp->th_dport);
589 srcPort = ntohs(tcp->th_sport);
590
591 if(SmartVerify(dstPort, "TCP") == TRUE)
592 {
593 #ifdef DEBUG
594 Log("debug: CheckTCP: SmartVerify indicates port is in use. Ignoring connection\n");
595 #endif DEBUG
596 return(TRUE);
597 }
598
599 /* By-Bye */
600 if (Dispose(gblAttackerIP, dstPort, "TCP") != TRUE)
601 Log ("attackalert: ERROR: Could not block host %s!", gblAttackerIP);
602
603 /* Ok we've already blocked this guy or ignored the actions */
604 /* Since those are the time critical areas we'll now do the optional */
605 /* resolution for logging */
606 if (gblFlags.gblResolveHost)
607 {
608 if(CleanAndResolve(resolvedHost, gblAttackerIP) != TRUE)
609 {
610 Log ("attackalert: ERROR: Error resolving host. \
611 resolving disabled for this host.\n");
612 snprintf (resolvedHost, DNSMAXBUF, "%s", gblAttackerIP);
613 }
614 }
615 else
616 snprintf (resolvedHost, DNSMAXBUF, "%s", gblAttackerIP);
617
618 /* XXX Put in flags to null out for unused bit attack vulnerability or simply report?? */
619 /* Also this false alarms with Explicit Congestion Notification aware kernels */
620 /*
621 if (tcp->th_flags & (TCP_UNUSED1|TCP_UNUSED2))
622 Log("attackalert: TCP Unused bits set. Detection evasion technique in use!\n");
623 */
624
625 if(tcp->th_flags == 0)
626 {
627 Log("attackalert: TCP NULL scan from host %s/%s to TCP port: %d from TCP port: %d", resolvedHost, \
628 gblAttackerIP, dstPort, srcPort);
629 scanType="NULL";
630 }
631 else if (tcp->th_flags == (TH_FIN|TH_URG|TH_PUSH|TH_ACK|TH_SYN|TH_RST))
632 {
633 Log("attackalert: TCP XMAS-FULL scan from host %s/%s to TCP port: %d from TCP port: %d", resolvedHost, \
634 gblAttackerIP, dstPort, srcPort);
635 scanType="XMAS-FULL";
636 }
637 else if (tcp->th_flags == (TH_FIN|TH_URG|TH_PUSH))
638 {
639 Log("attackalert: TCP XMAS scan from host %s/%s to TCP port: %d from TCP port: %d", resolvedHost, \
640 gblAttackerIP, dstPort, srcPort);
641 scanType="XMAS";
642 }
643 else if (tcp->th_flags == TH_SYN)
644 {
645 Log("attackalert: TCP SYN scan from host %s/%s to TCP port: %d from TCP port: %d", resolvedHost, \
646 gblAttackerIP, dstPort, srcPort);
647 scanType="SYN";
648 }
649 else if (tcp->th_flags == TH_FIN)
650 {
651 Log("attackalert: TCP FIN scan from host %s/%s to TCP port: %d from TCP port: %d", resolvedHost, \
652 gblAttackerIP, dstPort, srcPort);
653 scanType="FIN";
654 }
655 else
656 {
657 Log("attackalert: Unknown/Illegal scan type: TCP Packet Flags: FIN %d SYN: %d RST: %d PUSH: %d ACK: %d URG: %d \
658 UNUSED1: %d UNUSED2: %d scan from host %s/%s to TCP port: %d from TCP port: %d", \
659 ((tcp->th_flags & TH_FIN) >> 0), \
660 ((tcp->th_flags & TH_SYN) >> 1), \
661 ((tcp->th_flags & TH_RST) >> 2), \
662 ((tcp->th_flags & TH_PUSH) >> 3), \
663 ((tcp->th_flags & TH_ACK) >> 4), \
664 ((tcp->th_flags & TH_URG) >> 5), \
665 ((tcp->th_flags & TCP_UNUSED1) >> 6), \
666 ((tcp->th_flags & TCP_UNUSED2) >> 7), \
667 resolvedHost, gblAttackerIP, dstPort, srcPort);
668 scanType="UNKNOWN";
669 }
670
671 if (WriteBlocked (gblAttackerIP, resolvedHost, "TCP", scanType, dstPort, srcPort, \
672 gblConfig.gblBlockedFile, gblConfig.gblHistoryFile) != TRUE)
673 Log("attackalert: ERROR: Could not write out to history/blocked file.");
674
675
676 #ifdef DEBUG
677 Log("debug: CheckTCP: src: %s\n", gblAttackerIP);
678 Log("debug: CheckTCP: dst: %s\n", gblTargetIP);
679 Log("debug: CheckTCP: src port: %d\n", ntohs(tcp->th_sport));
680 Log("debug: CheckTCP: dst port: %d\n", ntohs(tcp->th_dport));
681 Log("debug: CheckTCP: FIN: %d\n", (tcp->th_flags & TH_FIN) >> 0);
682 Log("debug: CheckTCP: SYN: %d\n", (tcp->th_flags & TH_SYN) >> 1);
683 Log("debug: CheckTCP: RST: %d\n", (tcp->th_flags & TH_RST) >> 2);
684 Log("debug: CheckTCP: PSH: %d\n", (tcp->th_flags & TH_PUSH) >> 3);
685 Log("debug: CheckTCP: ACK: %d\n", (tcp->th_flags & TH_ACK) >> 4);
686 Log("debug: CheckTCP: URG: %d\n", (tcp->th_flags & TH_URG) >> 5);
687 Log("debug: CheckTCP: UNUSED1: %d\n", (tcp->th_flags & TCP_UNUSED1) >> 6);
688 Log("debug: CheckTCP: UNUSED2: %d\n", (tcp->th_flags & TCP_UNUSED2) >> 7);
689 Log("debug: CheckTCP: total flags: %d\n", tcp->th_flags);
690 #endif
691
692 return(TRUE);
693 }
694
695
696 int CheckUDP(const struct ip *ip, const struct udphdr *udp)
697 {
698 char resolvedHost[DNSMAXBUF];
699 int dstPort = 0, srcPort = 0;
700
701
702 /* Set destination/src port */
703 dstPort = ntohs(udp->uh_dport);
704 srcPort = ntohs(udp->uh_sport);
705
706 if((SmartVerify(dstPort, "UDP")) == TRUE)
707 {
708 #ifdef DEBUG
709 Log("debug: CheckUDP: SmartVerify indicates port is in use. Ignoring connection\n");
710 #endif DEBUG
711 return(TRUE);
712 }
713
714 /* By-Bye */
715 if (Dispose(gblAttackerIP, dstPort, "UDP") != TRUE)
716 Log ("attackalert: ERROR: Could not block host %s!", gblAttackerIP);
717
718 /* Ok we've already blocked this guy or ignored the actions */
719 /* Since those are the time critical areas we'll now do the optional */
720 /* resolution for logging */
721 if (gblFlags.gblResolveHost)
722 {
723 if(CleanAndResolve(resolvedHost, gblAttackerIP) != TRUE)
724 {
725 Log ("attackalert: ERROR: Error resolving host. \
726 resolving disabled for this host.\n");
727 snprintf (resolvedHost, DNSMAXBUF, "%s", gblAttackerIP);
728 }
729 }
730 else
731 snprintf (resolvedHost, DNSMAXBUF, "%s", gblAttackerIP);
732
733 Log("attackalert: UDP scan from host %s/%s to UDP port: %d from UDP port: %d", resolvedHost, \
734 gblAttackerIP, dstPort, srcPort);
735 if (WriteBlocked (gblAttackerIP, resolvedHost, "UDP", "UDP", dstPort, srcPort, \
736 gblConfig.gblBlockedFile, gblConfig.gblHistoryFile) != TRUE)
737 Log("attackalert: ERROR: Could not write out to history/blocked file.");
738
739 #ifdef DEBUG
740 Log("debug: CheckUDP: src: %s\n", gblAttackerIP);
741 Log("debug: CheckUDP: dst: %s\n", gblTargetIP);
742 Log("debug: CheckUDP: src port: %d\n", ntohs(udp->uh_sport));
743 Log("debug: CheckUDP: dst port: %d\n", ntohs(udp->uh_dport));
744 #endif
745
746 return(TRUE);
747 }
748
749
750
751 /* XXX Not used yet */
752 int CheckICMP(const struct ip *ip, const struct icmp *icmp)
753 {
754 struct in_addr saddr;
755
756 saddr.s_addr = ip->ip_src.s_addr;
757
758 Log("attackalert: ICMP scan from host %s\n", gblAttackerIP);
759 printf("ICMP Scan from host %s\n", gblAttackerIP);
760
761 return(TRUE);
762 }
763
764
765
766 int CheckIP(const struct ip *ip)
767 {
768 int dataoffset = 0;
769
770 /* This should never be less than 5 */
771 if (ip->ip_hl < 5)
772 {
773 Log ("attackalert: Illegal IP header length detected in IP packet - length: %d from (possible) host: %s\n",
774 ip->ip_hl, inet_ntoa (gblSrc));
775 Log("attackalert: This could be an attempt to bypass detection or attack the system.\n");
776 }
777 else
778 {
779 #ifdef DEBUG
780 Log("debug: PktEngine: Found a good IP packet. Type: %d hlen: %d\n", ip->ip_p, ip->ip_hl);
781 #endif
782 if((dataoffset = (ip->ip_hl * 4)) > 20)
783 {
784 Log ("attackalert: An IP packet with options set was found - length: %d from host: %s\n",
785 ip->ip_hl, inet_ntoa (gblSrc));
786 Log("attackalert: This could be an attempt to bypass detection or attack the system.\n");
787 }
788 }
789
790 /* XXX Put additional checks in here if anomaly flag is set */
791 return(dataoffset);
792 }
793
794
795 int
796 SmartVerify (int port, char *mode)
797 {
798 int testSockfd;
799
800 /* Ok here is where we "Smart-Verify" the socket. If the port was previously */
801 /* unbound, but now appears to have someone there, then we will skip responding */
802 /* to this inbound packet. This a basic "stateful" inspection of the */
803 /* the connection */
804
805 if (mode == "TCP")
806 {
807 if ((testSockfd = OpenTCPSocket ()) == ERROR)
808 {
809 Log ("adminalert: ERROR: could not open TCP socket to SmartVerify.\n");
810 return (FALSE);
811 }
812 }
813 else if (mode == "UDP")
814 {
815 if ((testSockfd = OpenUDPSocket ()) == ERROR)
816 {
817 Log ("adminalert: ERROR: could not open UDP socket to SmartVerify.\n");
818 return (FALSE);
819 }
820 }
821 else
822 {
823 Log ("adminalert: ERROR: Illegal socket mode passed to SmartVerify.\n");
824 return (FALSE);
825 }
826
827
828 if (BindSocket (testSockfd, port) == ERROR)
829 {
830 #ifdef DEBUG
831 Log ("debug: SmartVerify: SmartVerify Port In Use: %d", port);
832 #endif
833 close (testSockfd);
834 return (TRUE);
835 }
836
837 close (testSockfd);
838 return (FALSE);
839 }
840
841
842 void
843 Usage (void)
844 {
845 printf ("Psionic PortSentry - Port Scan Detector.\n");
846 printf ("Copyright 1997-2002 Psionic Technologies, Inc. http://www.psionic.com\n");
847 printf ("Licensing restrictions apply. COMMERCIAL RESALE PROHIBITED WITHOUT LICENSING.\n");
848 printf ("See documentation for more information. Questions and comments: <sentrysupport@psionic.com>\n");
849 printf ("Version: %s\n\n", VERSION);
850 printf ("usage: portsentry\n\n");
851 printf ("*** PLEASE READ THE DOCS BEFORE USING *** \n\n");
852 }
853
854 /* our cheesy state engine to monitor who has connected here before */
855 int
856 CheckStateEngine (char *target)
857 {
858 int count = 0, scanDetectTrigger = TRUE;
859 int gotOne = 0;
860
861 /* This is the rather basic scan state engine. It maintains */
862 /* an array of past hosts who triggered a connection on a port */
863 /* when a new host arrives it is compared against the array */
864 /* if it is found in the array it increments a state counter by */
865 /* one and checks the remainder of the array. It does this until */
866 /* the end is reached or the trigger value has been exceeded */
867 /* This would probably be better as a linked list/hash table, */
868 /* but for the number of hosts we are tracking this is just as good. */
869 /* This will probably change in the future */
870
871 gotOne = 1; /* our flag counter if we get a match */
872 scanDetectTrigger = TRUE; /* set to TRUE until set otherwise */
873
874 if (gblConfig.gblTriggerCount > 0)
875 {
876 for (count = 0; count < MAXSTATE; count++)
877 {
878 /* if the array has the IP address then increment the gotOne counter and */
879 /* check the trigger value. If it is exceeded break out of the loop and */
880 /* set the detecttrigger to TRUE */
881 if (strcmp (gblConfig.gblScanDetectHost[count], target) == 0 )
882 {
883 /* compare the number of matches to the configured trigger value */
884 /* if we've exceeded we can stop this noise */
885 if (++gotOne >= gblConfig.gblTriggerCount)
886 {
887 scanDetectTrigger = TRUE;
888 #ifdef DEBUG
889 Log ("debug: CheckStateEngine: host: %s has exceeded trigger value: %d\n",
890 gblConfig.gblScanDetectHost[count], gblConfig.gblTriggerCount);
891 #endif
892 break;
893 }
894 }
895 else
896 scanDetectTrigger = FALSE;
897 }
898
899 /* now add the fresh meat into the state engine */
900 /* if our array is still less than MAXSTATE large add it to the end */
901 if (gblConfig.gblScanDetectCount < MAXSTATE)
902 {
903 SafeStrncpy (gblConfig.gblScanDetectHost[gblConfig.gblScanDetectCount], target,
904 IPMAXBUF);
905 gblConfig.gblScanDetectCount++;
906 }
907 else
908 {
909 /* otherwise tack it to the beginning and start overwriting older ones */
910 gblConfig.gblScanDetectCount = 0;
911 SafeStrncpy (gblConfig.gblScanDetectHost[gblConfig.gblScanDetectCount], target,
912 IPMAXBUF);
913 gblConfig.gblScanDetectCount++;
914 }
915
916 #ifdef DEBUG
917 for (count = 0; count < MAXSTATE; count++)
918 Log ("debug: CheckStateEngine: state engine host: %s -> position: %d Detected: %d\n",
919 gblConfig.gblScanDetectHost[count], count, scanDetectTrigger);
920 #endif
921 /* end catch to set state if gblConfigTriggerCount == 0 */
922 if (gotOne >= gblConfig.gblTriggerCount)
923 scanDetectTrigger = TRUE;
924 }
925
926
927 if (gblConfig.gblTriggerCount > MAXSTATE)
928 {
929 Log ("securityalert: WARNING: Trigger value %d is larger than state engine capacity of %d.\n",
930 gblConfig.gblTriggerCount);
931 Log ("Adjust the value lower or recompile with a larger state engine value.\n",
932 MAXSTATE);
933 Log ("securityalert: Blocking host anyway because of invalid trigger value");
934 scanDetectTrigger = TRUE;
935 }
936 return (scanDetectTrigger);
937 }
938
939
940
941
942 /* kill the connection depending on config option */
943 int
944 Dispose (char *target, int port, char *mode)
945 {
946 int status = TRUE;
947 int responseFlags = 0;
948
949 if (mode == "TCP")
950 responseFlags = gblFlags.gblBlockTCP;
951 else if (mode == "UDP")
952 responseFlags = gblFlags.gblBlockUDP;
953 else
954 {
955 Log ("attackalert: An unknown response type was passed to the Dispose function.\n");
956 return(ERROR);
957 }
958
959 #ifdef DEBUG
960 Log ("debug: Dispose: disposing of host %s on port %d with mode %s and option: %d",
961 target, port, mode, responseFlags);
962 Log ("debug: Dispose: killRunCmd: %s", gblConfig.gblKillRunCmd);
963 Log ("debug: Dispose: gblRunCmdFirst: %d", gblFlags.gblRunCmdFirst);
964 Log ("debug: Dispose: killHostsDeny: %s", gblConfig.gblKillHostsDeny);
965 Log ("debug: Dispose: killRoute: %s %d", gblConfig.gblKillRoute,
966 strlen (gblConfig.gblKillRoute));
967 #endif
968
969 /* Should we ignore active response? */
970 if (responseFlags == 1)
971 {
972 /* run external command first, hosts.deny second, dead route last */
973 if (gblFlags.gblRunCmdFirst)
974 {
975 if (strlen (gblConfig.gblKillRunCmd) > 0)
976 if (KillRunCmd (target, port, gblConfig.gblKillRunCmd, mode) != TRUE)
977 status = FALSE;
978 if (strlen (gblConfig.gblKillHostsDeny) > 0)
979 if (KillHostsDeny (target, port, gblConfig.gblKillHostsDeny, mode) != TRUE)
980 status = FALSE;
981 if (strlen (gblConfig.gblKillRoute) > 0)
982 if (KillRoute (target, port, gblConfig.gblKillRoute, mode) != TRUE)
983 status = FALSE;
984 }
985 /* run hosts.deny first, dead route second, external command last */
986 else
987 {
988 if (strlen (gblConfig.gblKillHostsDeny) > 0)
989 if (KillHostsDeny (target, port, gblConfig.gblKillHostsDeny, mode) != TRUE)
990 status = FALSE;
991 if (strlen (gblConfig.gblKillRoute) > 0)
992 if (KillRoute (target, port, gblConfig.gblKillRoute, mode) != TRUE)
993 status = FALSE;
994 if (strlen (gblConfig.gblKillRunCmd) > 0)
995 if (KillRunCmd (target, port, gblConfig.gblKillRunCmd, mode) != TRUE)
996 status = FALSE;
997 }
998 }
999 else if (responseFlags == 2)
1000 {
1001 /* run external command only */
1002 if (strlen (gblConfig.gblKillRunCmd) > 0)
1003 if (KillRunCmd (target, port, gblConfig.gblKillRunCmd, mode) != TRUE)
1004 status = FALSE;
1005 }
1006 else
1007 Log ("attackalert: Ignoring response per configuration file setting.");
1008
1009 return (status);
1010 }
1011
1012