1 /*
2 * Copyright (c) 2001-2002 Secure Software, Inc
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 */
19
20 #include <ctype.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #ifdef _MSC_VER
24 #include <windows.h>
25 #else
26 #include <sys/time.h>
27 #endif
28
29 #include <string.h>
30 #include "report.h"
31
32 int warning_level = 2;
33
34 static input_t * input_head = (input_t *)NULL;
35 static input_t * input_tail = (input_t *)NULL;
36 static ignore_t * ignore_list = (ignore_t *)NULL;
37 static vulnerability_t * list_head = (vulnerability_t *)NULL;
38 static vulnerability_t * list_tail = (vulnerability_t *)NULL;
39
40 static char *context_filename = NULL;
41 static FILE *context_fp = NULL;
42 static int context_line = 0;
43 static int lookup_ignore(char *filename, int lineno, char *token);
44 static int determine_ignorance(vulnerability_t *ptr);
45 static void diff_times(const struct timeval *, const struct timeval *, struct timeval *);
46
47 int total_lines = 0;
48 #ifdef _MSC_VER
49 DWORD time_started;
50 DWORD time_finished;
51 #else
52 struct timeval time_started;
53 struct timeval time_finished;
54 #endif
55
56
57 /* This function EXPECTS a MALLOCED BUFFER to be passed into it, as it will
58 * free it if it needs to be
59 */
60 static char *
xml_escape(char * xstr)61 xml_escape(char *xstr)
62 {
63
64 char *result = NULL;
65 char *cntptr = NULL;
66 char *cpptr = NULL;
67 char *dstptr = NULL;
68 unsigned int newsz = 0;
69
70 newsz = strlen(xstr)+1;
71
72 cntptr = xstr;
73 while((cntptr = strchr(cntptr, '&')))
74 {
75 newsz += 4;
76 cntptr++;
77 }
78
79 cntptr = xstr;
80
81 while((cntptr = strchr(cntptr, '<')))
82 {
83 newsz += 3;
84 cntptr++;
85 }
86
87 cntptr = xstr;
88
89 while((cntptr = strchr(cntptr, '>')))
90 {
91 newsz += 3;
92 cntptr++;
93 }
94
95 if (newsz == strlen(xstr)+1)
96 {
97 return xstr;
98 }
99 result = malloc(newsz);
100 cpptr = xstr;
101 dstptr = result;
102 while(*cpptr && (dstptr < result+newsz))
103 {
104 if (*cpptr == '&')
105 {
106 strncat(dstptr, "&", 5);
107 dstptr += 5;
108 } else if (*cpptr == '<') {
109 strncat(dstptr, "<", 4);
110 dstptr += 4;
111 } else if (*cpptr == '>') {
112 strncat(dstptr, ">", 4);
113 dstptr += 4;
114 } else {
115 *dstptr = *cpptr;
116 *(dstptr+1) = 0;
117 dstptr++;
118 }
119 cpptr++;
120 }
121 free(xstr);
122 return result;
123 }
124
125
126 /* Exclusively for debugging vulnerabilities.
127 * - robbat2@gentoo.org 21/05/2006 */
debug_vuln_dump(vulnerability_t * ptr)128 static void debug_vuln_dump(vulnerability_t *ptr) {
129 fprintf(stderr,"vuln_dump: this=%x f=%s l=%d c=%d d=%x t=%d s=%d u=%x p=(%x,%x)\n",
130 (unsigned int) ptr,
131 ptr->filename,ptr->lineno,ptr->column,
132 (unsigned int) ptr->data,ptr->type,ptr->severity,
133 (unsigned int) ptr->uses, (unsigned int) ptr->next, (unsigned int) ptr->prev);
134 }
135
136
137
138 static void
replace_cfname(char * filename)139 replace_cfname(char *filename)
140 {
141
142 if (context_filename)
143 free(context_filename);
144
145 context_filename = malloc(strlen(filename)+1);
146 strncpy(context_filename, filename, strlen(filename));
147 context_filename[strlen(filename)] = 0;
148 }
149
150 static char *
getctx(char * filename,int lineno)151 getctx(char *filename, int lineno)
152 {
153
154 char *ret = NULL;
155 char buf[4096] = {0};
156
157 if (context_filename && strcmp(context_filename, filename))
158 {
159 replace_cfname(filename);
160 if (context_fp)
161 {
162 fclose(context_fp);
163 context_fp = NULL;
164 }
165 }
166
167 if (!context_filename)
168 {
169 replace_cfname(filename);
170 }
171
172 if (!context_fp)
173 {
174 context_fp = fopen(context_filename, "r");
175 context_line = 0;
176 if (!context_fp)
177 return NULL;
178 }
179
180
181 if (lineno <= context_line)
182 {
183 context_line = 0;
184 fseek(context_fp, 0, SEEK_SET);
185 }
186
187 while(fgets(buf, 4096, context_fp))
188 {
189 if(buf[strlen(buf)-1] == '\n')
190 {
191 context_line++;
192 }
193 if (context_line == lineno)
194 {
195 ret = malloc(strlen(buf)+1);
196 strncpy(ret, buf, strlen(buf));
197 ret[strlen(buf)] = 0;
198 return ret;
199 }
200 }
201 return NULL;
202 }
203
204
205
206 static
insert_vulnerability(vulnerability_t * log)207 void insert_vulnerability(vulnerability_t *log)
208 {
209 int insert = 0;
210 vulnerability_t * ptr;
211
212 for (ptr = list_head; ptr != (vulnerability_t *)NULL; ptr = ptr->next)
213 {
214 if (ptr->severity < log->severity)
215 {
216 insert = 1;
217 ptr = ptr->prev;
218 break;
219 }
220 if (ptr->type == log->type && ptr->data == log->data)
221 {
222 for (ptr = ptr->next; ptr != (vulnerability_t *)NULL; ptr = ptr->next)
223 {
224 if (ptr->type != log->type || ptr->data != log->data)
225 {
226 ptr = ptr->prev;
227 break;
228 }
229 }
230 break;
231 }
232 }
233 if (ptr == (vulnerability_t *)NULL && !insert)
234 ptr = list_tail;
235
236 log->next = (ptr == (vulnerability_t *)NULL ? list_head : ptr->next);
237 log->prev = ptr;
238
239 if (log->next != (vulnerability_t *)NULL)
240 log->next->prev = log;
241 else
242 list_tail = log;
243 if (log->prev != (vulnerability_t *)NULL)
244 log->prev->next = log;
245 else
246 list_head = log;
247 }
248
log_toctou(toctou_t ** table,int first,int last,int check)249 void log_toctou(toctou_t **table, int first, int last, int check)
250 {
251 int i;
252 vulnerability_t * log;
253
254 if (check != -1)
255 {
256 int count = 0, index = 0;
257
258 for (i = first; i <= last; i++)
259 count += (table[i]->use);
260
261 log = (vulnerability_t *)malloc(sizeof(vulnerability_t));
262 log->filename = current_file;
263 log->lineno = table[check]->lineno;
264 log->column = table[check]->column;
265 log->data = table[check]->data;
266 log->type = RaceConditionCheck;
267
268 if (count > 0)
269 {
270 log->severity = Medium;
271 log->uses = (toctou_use_t *)malloc(sizeof(toctou_use_t) * (count + 1));
272
273 for (i = first; i <= last; i++)
274 {
275 if (table[i]->use)
276 {
277 log->uses[index].name = table[i]->data->Name;
278 log->uses[index].lineno = table[i]->lineno;
279 log->uses[index].column = table[i]->column;
280 index++;
281 }
282 }
283 log->uses[index].name = (char *)NULL;
284 log->uses[index].lineno = 0;
285 log->uses[index].column = 0;
286
287 }
288 else
289 {
290 log->severity = Low;
291 log->uses = (toctou_use_t *)NULL;
292 }
293 insert_vulnerability(log);
294 }
295 else
296 {
297 for (i = first; i <= last; i++)
298 {
299 log = (vulnerability_t *)malloc(sizeof(vulnerability_t));
300 log->filename = current_file;
301 log->column = table[i]->column;
302 log->lineno = table[i]->lineno;
303 log->data = table[i]->data;
304 log->type = RaceConditionUse;
305 log->severity = Low;
306 log->uses = (toctou_use_t *)NULL;
307
308 insert_vulnerability(log);
309 }
310 }
311 }
312
log_vulnerability(type_t type,Severity_t severity)313 void log_vulnerability(type_t type, Severity_t severity)
314 {
315 vulnerability_t * log;
316
317 log = (vulnerability_t *)malloc(sizeof(vulnerability_t));
318 log->column = current_frame->column;
319 log->filename = current_file;
320 log->lineno = current_frame->lineno;
321 log->data = current_frame->data;
322 log->type = type;
323 log->severity = severity;
324 log->uses = (toctou_use_t *)NULL;
325
326 insert_vulnerability(log);
327 }
328
329 /* These are special static vulnerabilities because we don't
330 * want NULL data elements in the vulnerability_t->data
331 * field, because the HTML and XML output formats use that
332 * pointer without checking it for being null first.
333 * - robbat2@gentoo.org 21/05/2006 */
334 static struct Vuln_t vuln_PerlBacktick = {
335 .Name = "Perl Backtick"
336 };
337 static struct Vuln_t vuln_PhpBacktick = {
338 .Name = "PHP Backtick"
339 };
340 static struct Vuln_t vuln_PythonBacktick = {
341 .Name = "Python Backtick"
342 };
343 static struct Vuln_t vuln_StaticLocalBuffer = {
344 .Name = "Static Local Buffer"
345 };
346 static struct Vuln_t vuln_StaticGlobalBuffer = {
347 .Name = "Static Global Buffer"
348 };
349
log_perlbacktick(int lineno,int column,Severity_t severity)350 void log_perlbacktick(int lineno, int column, Severity_t severity)
351 {
352 vulnerability_t * log;
353
354 log = (vulnerability_t *)malloc(sizeof(vulnerability_t));
355 log->filename = current_file;
356 log->column = column;
357 log->lineno = lineno;
358 log->data = &vuln_PerlBacktick;
359 log->type = PerlBacktick;
360 log->severity = severity;
361 log->uses = (toctou_use_t *)NULL;
362
363 insert_vulnerability(log);
364 }
365
366
log_phpbacktick(int lineno,int column,Severity_t severity)367 void log_phpbacktick(int lineno, int column, Severity_t severity)
368 {
369 vulnerability_t * log;
370
371 log = (vulnerability_t *)malloc(sizeof(vulnerability_t));
372 log->filename = current_file;
373 log->column = column;
374 log->lineno = lineno;
375 log->data = &vuln_PhpBacktick;
376 log->type = PhpBacktick;
377 log->severity = severity;
378 log->uses = (toctou_use_t *)NULL;
379
380 insert_vulnerability(log);
381 }
382
log_rubybacktick(int lineno,int column,Severity_t severity)383 void log_rubybacktick(int lineno, int column, Severity_t severity)
384 {
385 vulnerability_t * log;
386
387 log = (vulnerability_t *)malloc(sizeof(vulnerability_t));
388 log->filename = current_file;
389 log->column = column;
390 log->lineno = lineno;
391 log->data = (Vuln_t *)NULL;
392 log->type = RubyBacktick;
393 log->severity = severity;
394 log->uses = (toctou_use_t *)NULL;
395
396 insert_vulnerability(log);
397 }
398
log_pythonbacktick(int lineno,int column,Severity_t severity)399 void log_pythonbacktick(int lineno, int column, Severity_t severity)
400 {
401 vulnerability_t * log;
402
403 log = (vulnerability_t *)malloc(sizeof(vulnerability_t));
404 log->filename = current_file;
405 log->column = column;
406 log->lineno = lineno;
407 log->data = &vuln_PythonBacktick;
408 log->type = PythonBacktick;
409 log->severity = severity;
410 log->uses = (toctou_use_t *)NULL;
411
412 insert_vulnerability(log);
413 }
414
log_staticbuffer(type_t type,int lineno,int column,Severity_t severity)415 void log_staticbuffer(type_t type, int lineno, int column, Severity_t severity)
416 {
417 vulnerability_t * log;
418
419 log = (vulnerability_t *)malloc(sizeof(vulnerability_t));
420 log->filename = current_file;
421 log->column = column;
422 log->lineno = lineno;
423 switch(type) {
424 case StaticLocalBuffer:
425 log->data = &vuln_StaticLocalBuffer;
426 break;
427 case StaticGlobalBuffer:
428 log->data = &vuln_StaticGlobalBuffer;
429 break;
430 default:
431 log->data = (Vuln_t *)NULL;
432 }
433 log->type = type;
434 log->severity = severity;
435 log->uses = (toctou_use_t *)NULL;
436
437 insert_vulnerability(log);
438 }
439
record_input(void)440 void record_input(void)
441 {
442 input_t * input;
443
444 input = (input_t *)malloc(sizeof(input_t));
445 input->column = current_frame->column;
446 input->filename = current_file;
447 input->lineno = current_frame->lineno;
448 input->data = current_frame->data;
449 input->next = (input_t *)NULL;
450
451 if (input_tail != (input_t *)NULL)
452 input_tail->next = input;
453 else
454 input_head = input;
455 input_tail = input;
456 }
457
458 static
cleanup_string(char * str)459 void cleanup_string(char *str)
460 {
461 int len;
462 char * c;
463
464 /* strip off leading a trailing whitespace */
465 for (c = str; *c && isspace(*c); c++);
466 for (len = strlen(c); len > 0 && isspace(*(c + len - 1)); len--);
467 *(c + len) = '\0';
468 memmove(str, c, len + 1);
469
470 /* squash occurences of multiple whitespace characters to a single one */
471 /* msg -- this code seems to corrupt memory on occasion */
472 //for (c = str + 1; *c; c++)
473 //{
474 // if (isspace(*c) && isspace(*(c - 1)))
475 // {
476 // char * start;
477
478 // for (start = c++; isspace(*c); c++);
479 // memmove(start, c, (len + 1) - (c - str));
480 // len -= (c - start);
481 // *(start - 1) = ' ';
482 // }
483 //}
484 }
485
486 static char *severities[] = { "Default", "Low", "Medium", "High" };
487
build_xml_vulnerability(vulnerability_t * ptr)488 static void build_xml_vulnerability(vulnerability_t *ptr) {
489 int i;
490
491 /* Debugging - robbat2@gentoo.org 21/05/2006 */
492 if(ptr->data == NULL)
493 debug_vuln_dump(ptr);
494
495 printf("<vulnerability>\n");
496
497 /* Output the severity */
498 printf(" <severity>%s</severity>\n",
499 severities[ptr->severity]);
500
501 switch (ptr->type)
502 {
503 case BOProblem:
504 if (ptr->data->BOProblem->FormatArg > 0)
505 {
506 printf(" <type>%s</type>\n",
507 ptr->data->Name);
508 printf(" <message>\n");
509 printf(" Check to be sure that the format string passed as argument %d to this\n", ptr->data->BOProblem->FormatArg);
510 printf(" function call does not come from an untrusted source that could have added\n");
511 printf(" formatting characters that the code is not prepared to handle.\n");
512 printf(" Additionally, the format string could contain `%%s' without precision that\n");
513 printf(" could result in a buffer overflow.\n");
514 printf(" </message>\n");
515 }
516 if (ptr->data->BOProblem->SrcBufArg > 0)
517 {
518 printf(" <message>\n");
519 printf(" Check to be sure that argument %d passed to this function call will not\n", ptr->data->BOProblem->SrcBufArg);
520 printf(" copy more data than can be handled, resulting in a buffer overflow.\n");
521 printf(" </message>\n");
522 }
523 break;
524
525 case FSProblem:
526 printf(" <type>%s</type>\n",
527 ptr->data->Name);
528 printf(" <message>\n");
529 printf(" Check to be sure that the non-constant format string passed as argument %d \n", ptr->data->FSProblem->Arg);
530 printf(" to this function call does not come from an untrusted source that could\n");
531 printf(" have added formatting characters that the code is not prepared to handle.\n");
532 printf(" </message>\n");
533 break;
534
535 case InputProblem:
536 printf(" <type>%s</type>\n",
537 ptr->data->Name);
538 printf(" <message>\n");
539 printf(" Argument %d to this function call should be checked to ensure that it does\n", ptr->data->InputProblem->Arg);
540 printf(" not come from an untrusted source without first verifying that it contains\n");
541 printf(" nothing dangerous.\n");
542 printf(" </message>\n");
543 break;
544
545 case Info:
546 printf(" <type>%s</type>\n",
547 ptr->data->Name);
548 printf(" <message>\n");
549 if (ptr->data->Info->Description != (char *)NULL) {
550 cleanup_string(ptr->data->Info->Description);
551 printf(" %s\n", ptr->data->Info->Description);
552 }
553 if (ptr->data->Info->URL != (char *)NULL) {
554 cleanup_string(ptr->data->Info->URL);
555 /* This should possibly be made into it's own tag -- Robert */
556 printf(" foSee also:\n %s\n", ptr->data->Info->URL);
557 }
558 printf(" </message>\n");
559 break;
560
561 case RaceConditionCheck:
562 printf(" <type>%s</type>\n",
563 ptr->data->Name);
564 printf(" <message>\n");
565 printf(" A potential TOCTOU (Time Of Check, Time Of Use) vulnerability exists.\n");
566 printf(" This is the first line where a check has occured.");
567 if (ptr->uses != (toctou_use_t *)NULL && ptr->uses[0].lineno != 0)
568 {
569 printf("\n The following line(s) contain uses that may match up with this check:\n");
570 for (i = 0; ptr->uses[i].lineno != 0; i++)
571 printf(" %s%d (%s)", (i == 0 ? "" : ", "), ptr->uses[i].lineno, ptr->uses[i].name);
572 printf("\n");
573 }
574 else
575 {
576 printf(" No matching uses were detected.\n");
577 }
578 printf(" </message>\n");
579 break;
580
581 case RaceConditionUse:
582 printf(" <type>fixed size local buffer</type>\n");
583 printf(" <message>\n");
584 printf(" A potential race condition vulnerability exists here. Normally a call\n");
585 printf(" to this function is vulnerable only when a match check precedes it. No\n");
586 printf(" check was detected, however one could still exist that could not be\n");
587 printf(" detected.\n");
588 printf(" </message>\n");
589 break;
590
591 case StaticLocalBuffer:
592 printf(" <type>fixed size global buffer</type>\n");
593 printf(" <message>\n");
594 printf(" Extra care should be taken to ensure that character arrays that are\n");
595 printf(" allocated on the stack are used safely. They are prime targets for\n");
596 printf(" buffer overflow attacks.\n");
597 printf(" </message>\n");
598 break;
599
600 case StaticGlobalBuffer:
601 printf(" <type>%s</type>\n",
602 ptr->data->Name);
603 printf(" <message>\n");
604 printf(" Extra care should be taken to ensure that character arrays that are\n");
605 printf(" allocated with a static size are used safely. This appears to be a\n");
606 printf(" global allocation and is less dangerous than a similar one on the stack.\n");
607 printf(" Extra caution is still advised, however.\n");
608 printf(" </message>\n");
609 break;
610
611 case Reference:
612 printf(" <type>%s</type>\n",
613 ptr->data->Name);
614 printf(" <message>\n");
615 printf(" A function call is not being made here, but a reference is being made to\n");
616 printf(" a name that is normally a vulnerable function. It could be being\n");
617 printf(" assigned as a pointer to function.\n\n");
618 printf(" </message>\n");
619 break;
620
621 case PythonBacktick:
622 printf(" <type>Backtick</type>\n");
623 printf(" <message>\n");
624 printf(" Do not use a variable that has been derived from untrusted sources\n");
625 printf(" within a backtick. Doing so could allow an attacker to execute\n");
626 printf(" arbitrary python code.\n");
627 printf(" </message>\n");
628 break;
629
630 case PhpBacktick:
631 case PerlBacktick:
632 case RubyBacktick:
633 printf(" <type>Backtick</type>\n");
634 printf(" <message>\n");
635 printf(" The backtick will act just like an call to exec(), so care should be\n");
636 printf(" exercised that the string being backtick evaluated does not come from an\n");
637 printf(" untrusted source.\n");
638 printf(" </message>\n");
639 break;
640
641 case None:
642 printf(" <type>%s</type>\n",
643 ptr->data->Name);
644 printf(" <message>\n");
645 printf(" Unknown!?!?\n\n");
646 printf(" </message>\n");
647 break;
648 }
649 }
650
651 static
report_vulnerability(vulnerability_t * ptr)652 void report_vulnerability(vulnerability_t *ptr)
653 {
654 int i;
655 if(ptr->data == NULL)
656 debug_vuln_dump(ptr);
657 switch (ptr->type)
658 {
659 case BOProblem:
660 if (ptr->data->BOProblem->FormatArg > 0)
661 {
662 printf("Check to be sure that the format string passed as argument %d to this function\n", ptr->data->BOProblem->FormatArg);
663 printf("call does not come from an untrusted source that could have added formatting\n");
664 printf("characters that the code is not prepared to handle. Additionally, the format\n");
665 printf("string could contain `%%s' without precision that could result in a buffer\n");
666 printf("overflow.\n");
667 }
668 if (ptr->data->BOProblem->SrcBufArg > 0)
669 {
670 printf("Check to be sure that argument %d passed to this function call will not copy\n", ptr->data->BOProblem->SrcBufArg);
671 printf("more data than can be handled, resulting in a buffer overflow.\n");
672 }
673 printf("\n");
674 break;
675
676 case FSProblem:
677 printf("Check to be sure that the non-constant format string passed as argument %d to\n", ptr->data->FSProblem->Arg);
678 printf("this function call does not come from an untrusted source that could have added\n");
679 printf("formatting characters that the code is not prepared to handle.\n\n");
680 break;
681
682 case InputProblem:
683 printf("Argument %d to this function call should be checked to ensure that it does not\n", ptr->data->InputProblem->Arg);
684 printf("come from an untrusted source without first verifying that it contains nothing\n");
685 printf("dangerous.\n\n");
686 break;
687
688 case Info:
689 if (ptr->data->Info->Description != (char *)NULL)
690 {
691 cleanup_string(ptr->data->Info->Description);// comment out if causing problems
692 printf("%s\n", ptr->data->Info->Description);
693 }
694 if (ptr->data->Info->URL != (char *)NULL)
695 {
696 cleanup_string(ptr->data->Info->URL);
697 printf("See also: %s\n", ptr->data->Info->URL);
698 }
699 printf("\n");
700 break;
701
702 case RaceConditionCheck:
703 printf("A potential TOCTOU (Time Of Check, Time Of Use) vulnerability exists. This is\n");
704 printf("the first line where a check has occured.");
705 if (ptr->uses != (toctou_use_t *)NULL && ptr->uses[0].lineno != 0)
706 {
707 printf("\nThe following line(s) contain uses that may match up with this check:\n");
708 for (i = 0; ptr->uses[i].lineno != 0; i++)
709 printf("%s%d (%s)", (i == 0 ? "" : ", "), ptr->uses[i].lineno, ptr->uses[i].name);
710 printf("\n");
711 }
712 else
713 {
714 printf(" No matching uses were detected.\n");
715 }
716 printf("\n");
717 break;
718
719 case RaceConditionUse:
720 printf("A potential race condition vulnerability exists here. Normally a call to this\n");
721 printf("function is vulnerable only when a match check precedes it. No check was\n");
722 printf("detected, however one could still exist that could not be detected.\n\n");
723 break;
724
725 case StaticLocalBuffer:
726 printf("Extra care should be taken to ensure that character arrays that are allocated\n");
727 printf("on the stack are used safely. They are prime targets for buffer overflow\n");
728 printf("attacks.\n\n");
729 break;
730
731 case StaticGlobalBuffer:
732 printf("Extra care should be taken to ensure that character arrays that are allocated\n");
733 printf("with a static size are used safely. This appears to be a global allocation\n");
734 printf("and is less dangerous than a similar one on the stack. Extra caution is still\n");
735 printf("advised, however.\n\n");
736 break;
737
738 case Reference:
739 printf("A function call is not being made here, but a reference is being made to a name\n");
740 printf("that is normally a vulnerable function. It could be being assigned as a\n");
741 printf("pointer to function.\n\n");
742 break;
743
744 case PythonBacktick:
745 printf("Do not use a variable that has been derived from untrusted sources within a backtick.\n");
746 printf("Doing so could allow an attacker to execute arbitrary python code\n\n");
747 break;
748
749 case PhpBacktick:
750 case PerlBacktick:
751 case RubyBacktick:
752 printf("The backtick will act just like an call to exec(), so care should be exercised that the\n");
753 printf(" string being backtick evaluated does not come from an untrusted source\n\n");
754 break;
755
756 case None:
757 printf("Unknown!?!?\n\n");
758 break;
759 }
760 }
761
762 static
html_report_inputs(void)763 void html_report_inputs(void)
764 {
765 int count = 0;
766 input_t * next;
767 input_t * ptr;
768
769 if (!(flags & INPUT_MODE))
770 return;
771
772 for (ptr = input_head; ptr != (input_t *)NULL; ptr = next)
773 {
774 next = ptr->next;
775 if (!lookup_ignore(ptr->filename, ptr->lineno, ptr->data->Name))
776 {
777 count++;
778 printf("<b>%s</b>: Line %d: function %s<br>\n", ptr->filename, ptr->lineno, ptr->data->Name);
779 }
780 free(ptr);
781 }
782 input_head = input_tail = (input_t *)NULL;
783
784 if (count > 0)
785 {
786 printf("<br>Double check to be sure that all input accepted from an external data source\n");
787 printf("does not exceed the limits of the variable being used to hold it. Also make\n");
788 printf("sure that the input cannot be used in such a manner as to alter your program's\n");
789 printf("behaviour in an undesirable way.<br>\n");
790 }
791 }
792
793 static
xml_report_inputs(void)794 void xml_report_inputs(void)
795 {
796 int count = 0;
797 input_t * next;
798 input_t * ptr;
799
800 if (!(flags & INPUT_MODE))
801 return;
802
803 for (ptr = input_head; ptr != (input_t *)NULL; ptr = next)
804 {
805 next = ptr->next;
806 if (!lookup_ignore(ptr->filename, ptr->lineno, ptr->data->Name))
807 {
808 count++;
809 printf("<input>\n");
810 printf("<message>");
811 printf("Double check to be sure that all input accepted from an external data source does not exceed the limits of the variable being used to hold it. Also make sure that the input cannot be used in such a manner as to alter your program's behaviour in an undesireable way");
812 printf("</message>\n") ;
813 printf("<function>%s</function>\n", ptr->data->Name);
814 printf("<file><name>%s</name><line>%d</line></file>\n", ptr->filename, ptr->lineno);
815 printf("</input>\n");
816 }
817 free(ptr);
818 }
819 input_head = input_tail = (input_t *)NULL;
820
821 }
822
823
824 static
report_inputs(void)825 void report_inputs(void)
826 {
827 int count = 0;
828 input_t * next;
829 input_t * ptr;
830
831 if (!(flags & INPUT_MODE))
832 return;
833
834 for (ptr = input_head; ptr != (input_t *)NULL; ptr = next)
835 {
836 next = ptr->next;
837 if (!lookup_ignore(ptr->filename, ptr->lineno, ptr->data->Name))
838 {
839 count++;
840 printf("%s: %d: %s\n", ptr->filename, ptr->lineno, ptr->data->Name);
841 }
842 free(ptr);
843 }
844 input_head = input_tail = (input_t *)NULL;
845
846 if (count > 0)
847 {
848 printf("Double check to be sure that all input accepted from an external data source\n");
849 printf("does not exceed the limits of the variable being used to hold it. Also make\n");
850 printf("sure that the input cannot be used in such a manner as to alter your program's\n");
851 printf("behaviour in an undesirable way.\n\n");
852 }
853 }
854
generate_xml()855 void generate_xml() {
856 char vuln_reported=0; /* Have we spewed the vuln message yet? */
857 vulnerability_t * ptr;
858
859 /* Initial necessary cruft */
860 /* Loop iterates through all of the problems found */
861 for (ptr = list_head; ptr != (vulnerability_t *)NULL; ptr = ptr->next) {
862
863 /* Check the severity of the vuln. If it's below our level, skip
864 * and go to the next one */
865 if (ptr->severity != Default && ptr->severity < warning_level) {
866 continue;
867 }
868
869 if (determine_ignorance(ptr))
870 {
871 continue;
872 }
873
874 /* If we haven't reported the vuln message yet for this type, do so. */
875 if(!vuln_reported) {
876 build_xml_vulnerability(ptr);
877 vuln_reported++;
878 }
879
880 /* If the filename of this vuln is different from the filename of the
881 * previous vuln, report the filename, or if the vuln type is different*/
882 if(ptr->prev==(vulnerability_t *)NULL||
883 strcmp(ptr->filename,ptr->prev->filename)|| ptr->type == RaceConditionCheck ||
884 ptr->prev->type != ptr->type || ptr->prev->data != ptr->data) {
885 printf(" <file>\n <name>%s</name>\n",
886 ptr->filename);
887 }
888
889 /* report the line number of the infraction */
890 printf(" <line>%d</line>\n",
891 ptr->lineno);
892 if (flags & SHOW_COLUMNS)
893 printf(" <column>%d</column>\n", ptr->column);
894 if (flags & SHOW_CONTEXT)
895 {
896 char *ctx = NULL;
897 ctx = getctx(ptr->filename, ptr->lineno);
898 if (ctx)
899 {
900 ctx = xml_escape(ctx);
901 printf("<context>%s</context>\n", ctx);
902 free(ctx);
903 }
904 }
905
906 /* If the next file or vuln type is different close the file tag */
907 if(ptr->next==(vulnerability_t *)NULL||
908 strcmp(ptr->filename,ptr->next->filename)|| ptr->type == RaceConditionCheck ||
909 ptr->next->type != ptr->type || ptr->next->data != ptr->data) {
910 printf(" </file>\n");
911 }
912
913 /* If the next vuln is different reset the vuln_reported variable to 0 so
914 * we know to report next time */
915 if (ptr->next == (vulnerability_t *)NULL || ptr->next->type != ptr->type ||
916 ptr->type == RaceConditionCheck || ptr->next->data != ptr->data) {
917 printf("</vulnerability>\n");
918 vuln_reported=0;
919 }
920
921 }
922 xml_report_inputs();
923
924 if (!(flags & NO_FOOTER))
925 {
926 #ifdef _MSC_VER
927 DWORD ttime;
928 #else
929 struct timeval ttime;
930 #endif
931
932 float fsecs;
933
934 #ifdef _MSC_VER
935 ttime = time_finished - time_started;
936 fsecs = ttime/1000 + (float)((ttime%1000)/1000);
937 #else
938 diff_times(&time_finished, &time_started, &ttime);
939 fsecs = ttime.tv_sec+ (ttime.tv_usec/(double)1000000);
940 #endif
941 printf("<timing>\n");
942 printf("<total_lines>%d</total_lines>\n", total_lines);
943 printf("<total_time>%f</total_time>\n", fsecs);
944 printf("<lines_per_second>%d</lines_per_second>\n", (int)(total_lines/fsecs));
945 printf("</timing>\n");
946 }
947
948 printf("</rats_output>\n");
949
950 }
951
build_html_vulnerability(vulnerability_t * ptr)952 static void build_html_vulnerability(vulnerability_t *ptr) {
953 int i;
954
955 /* Debugging - robbat2@gentoo.org 21/05/2006 */
956 if(ptr->data == NULL)
957 debug_vuln_dump(ptr);
958
959 /* Output the severity */
960 printf(" <b>Severity: %s</b><br/>\n",
961 severities[ptr->severity]);
962
963 switch (ptr->type)
964 {
965 case BOProblem:
966 if (ptr->data->BOProblem->FormatArg > 0)
967 {
968 printf(" Issue: %s<br/>\n",
969 ptr->data->Name);
970 printf(" Check to be sure that the format string passed as argument %d to this\n", ptr->data->BOProblem->FormatArg);
971 printf(" function call does not come from an untrusted source that could have added\n");
972 printf(" formatting characters that the code is not prepared to handle.\n");
973 printf(" Additionally, the format string could contain `%%s' without precision that\n");
974 printf(" could result in a buffer overflow.\n");
975 printf(" <br/>\n");
976 }
977 if (ptr->data->BOProblem->SrcBufArg > 0)
978 {
979 printf(" Issue: %s<br/>\n",
980 ptr->data->Name);
981 printf(" Check to be sure that argument %d passed to this function call will not\n", ptr->data->BOProblem->SrcBufArg);
982 printf(" copy more data than can be handled, resulting in a buffer overflow.\n");
983 printf(" <br/>\n");
984 }
985 break;
986
987 case FSProblem:
988 printf(" Issue: %s<br/>\n",
989 ptr->data->Name);
990 printf(" Check to be sure that the non-constant format string passed as argument %d \n", ptr->data->FSProblem->Arg);
991 printf(" to this function call does not come from an untrusted source that could\n");
992 printf(" have added formatting characters that the code is not prepared to handle.\n");
993 printf(" <br/>\n");
994 break;
995
996 case InputProblem:
997 printf(" Issue: %s<br/>\n",
998 ptr->data->Name);
999 printf(" Argument %d to this function call should be checked to ensure that it does\n", ptr->data->InputProblem->Arg);
1000 printf(" not come from an untrusted source without first verifying that it contains\n");
1001 printf(" nothing dangerous.\n");
1002 printf(" <br/>\n");
1003 break;
1004
1005 case Info:
1006 printf(" Issue: %s<br/>\n",
1007 ptr->data->Name);
1008 if (ptr->data->Info->Description != (char *)NULL) {
1009 cleanup_string(ptr->data->Info->Description);
1010 printf(" %s\n", ptr->data->Info->Description);
1011 }
1012 if (ptr->data->Info->URL != (char *)NULL) {
1013 cleanup_string(ptr->data->Info->URL);
1014 /* This should possibly be made into it's own tag -- Robert */
1015 printf(" See also:\n %s\n", ptr->data->Info->URL);
1016 }
1017 printf(" <br/>\n");
1018 break;
1019
1020 case RaceConditionCheck:
1021 printf(" Issue: %s<br/>\n",
1022 ptr->data->Name);
1023 printf(" A potential TOCTOU (Time Of Check, Time Of Use) vulnerability exists.\n");
1024 printf(" This is the first line where a check has occured.");
1025 if (ptr->uses != (toctou_use_t *)NULL && ptr->uses[0].lineno != 0)
1026 {
1027 printf("\n The following line(s) contain uses that may match up with this check:\n");
1028 for (i = 0; ptr->uses[i].lineno != 0; i++)
1029 printf(" %s%d (%s)", (i == 0 ? "" : ", "), ptr->uses[i].lineno, ptr->uses[i].name);
1030 printf("\n");
1031 }
1032 else
1033 {
1034 printf(" No matching uses were detected.\n");
1035 }
1036 printf(" <br/>\n");
1037 break;
1038
1039 case RaceConditionUse:
1040 printf(" Issue: fixed size local buffer<br/>\n");
1041 printf(" A potential race condition vulnerability exists here. Normally a call\n");
1042 printf(" to this function is vulnerable only when a match check precedes it. No\n");
1043 printf(" check was detected, however one could still exist that could not be\n");
1044 printf(" detected.\n");
1045 printf(" <br/>\n");
1046 break;
1047
1048 case StaticLocalBuffer:
1049 printf(" Issue: fixed size global buffer<br/>\n");
1050 printf(" Extra care should be taken to ensure that character arrays that are\n");
1051 printf(" allocated on the stack are used safely. They are prime targets for\n");
1052 printf(" buffer overflow attacks.\n");
1053 printf(" <br/>\n");
1054 break;
1055
1056 case StaticGlobalBuffer:
1057 printf(" Issue: %s<br/>\n",
1058 ptr->data->Name);
1059 printf(" Extra care should be taken to ensure that character arrays that are\n");
1060 printf(" allocated with a static size are used safely. This appears to be a\n");
1061 printf(" global allocation and is less dangerous than a similar one on the stack.\n");
1062 printf(" Extra caution is still advised, however.\n");
1063 printf(" <br/>\n");
1064 break;
1065
1066 case Reference:
1067 printf(" Issue: %s<br/>\n",
1068 ptr->data->Name);
1069 printf(" A function call is not being made here, but a reference is being made to\n");
1070 printf(" a name that is normally a vulnerable function. It could be being\n");
1071 printf(" assigned as a pointer to function.\n\n");
1072 printf(" <br/>\n");
1073 break;
1074
1075 case PythonBacktick:
1076 printf(" Issue: backtick<br/>\n");
1077 printf(" Do not use a variable that has been derived from untrusted sources\n");
1078 printf(" within a backtick. Doing so could allow an attacker to execute\n");
1079 printf(" arbitrary python code.\n");
1080 printf(" <br/>\n");
1081 break;
1082
1083 case PhpBacktick:
1084 case PerlBacktick:
1085 case RubyBacktick:
1086 printf(" Issue: backtick<br/>\n");
1087 printf(" The backtick will act just like an call to exec(), so care should be\n");
1088 printf(" exercised that the string being backtick evaluated does not come from an\n");
1089 printf(" untrusted source.\n");
1090 printf(" <br/>\n");
1091 break;
1092
1093 case None:
1094 printf(" Issue: %s<br/>\n",
1095 ptr->data->Name);
1096 printf(" Unknown!?!?\n\n");
1097 printf(" <br/>\n");
1098 break;
1099 }
1100 }
1101
generate_html()1102 void generate_html() {
1103 char vuln_reported=0; /* Have we spewed the vuln message yet? */
1104 vulnerability_t * ptr;
1105
1106 /* Initial necessary cruft */
1107 printf("<h2>RATS results.\n</h2><br>\n");
1108
1109 /* Loop iterates through all of the problems found */
1110 for (ptr = list_head; ptr != (vulnerability_t *)NULL; ptr = ptr->next) {
1111
1112 /* Check the severity of the vuln. If it's below our level, skip
1113 * and go to the next one */
1114 if (ptr->severity != Default && ptr->severity < warning_level) {
1115 continue;
1116 }
1117
1118 if (determine_ignorance(ptr))
1119 {
1120 continue;
1121 }
1122 /* If we haven't reported the vuln message yet for this type, do so. */
1123 if(!vuln_reported) {
1124 build_html_vulnerability(ptr);
1125 vuln_reported++;
1126 }
1127
1128 /* If the filename of this vuln is different from the filename of the
1129 * previous vuln, report the filename, or if the vuln type is different*/
1130 if(ptr->prev==(vulnerability_t *)NULL||
1131 strcmp(ptr->filename,ptr->prev->filename)|| ptr->type == RaceConditionCheck ||
1132 ptr->prev->type != ptr->type || ptr->prev->data != ptr->data) {
1133 printf("<ul>\n");
1134 if (!(flags & SHOW_CONTEXT))
1135 {
1136 printf("File: <b>%s</b><br/>Lines: \n",
1137 ptr->filename);
1138 }
1139 }
1140
1141 /* report the line number of the infraction */
1142 if (!(flags & SHOW_CONTEXT))
1143 {
1144 printf("%d",
1145 ptr->lineno);
1146 if (flags & SHOW_COLUMNS)
1147 printf("[%d]", ptr->column);
1148 printf(" ");
1149 } else {
1150 char *ctx = NULL;
1151 printf("File: <b>%s</b> Line:<b>%d", ptr->filename, ptr->lineno);
1152 if (flags & SHOW_COLUMNS)
1153 printf("[%d]", ptr->column);
1154 printf("</b><br>\n");
1155 ctx = getctx(ptr->filename, ptr->lineno);
1156 if(ctx)
1157 {
1158 printf("%s<br>\n", ctx);
1159 free(ctx);
1160 }
1161 }
1162
1163
1164
1165
1166 /* If the next file or vuln type is different close the file tag */
1167 if(ptr->next==(vulnerability_t *)NULL||
1168 strcmp(ptr->filename,ptr->next->filename)|| ptr->type == RaceConditionCheck ||
1169 ptr->next->type != ptr->type || ptr->next->data != ptr->data) {
1170 printf(" </ul>\n");
1171 }
1172
1173 /* If the next vuln is different reset the vuln_reported variable to 0 so
1174 * we know to report next time */
1175 if (ptr->next == (vulnerability_t *)NULL || ptr->next->type != ptr->type ||
1176 ptr->type == RaceConditionCheck || ptr->next->data != ptr->data) {
1177 vuln_reported=0;
1178 }
1179
1180 }
1181
1182
1183 printf("<h3>Inputs detected at the following points</h3>\n");
1184
1185 printf("<ul>\n");
1186 html_report_inputs();
1187 printf("</ul>\n");
1188
1189 printf("<br><br>\n");
1190
1191
1192 if (!(flags & NO_FOOTER))
1193 {
1194 #ifdef _MSC_VER
1195 DWORD ttime;
1196 #else
1197 struct timeval ttime;
1198 #endif
1199
1200 float fsecs;
1201
1202 #ifdef _MSC_VER
1203 ttime = time_finished - time_started;
1204 fsecs = ttime/1000 + (float)((ttime%1000)/1000);
1205 #else
1206 diff_times(&time_finished, &time_started, &ttime);
1207 fsecs = ttime.tv_sec+(ttime.tv_usec/(double)1000000);
1208 #endif
1209
1210
1211 printf("Total lines analyzed: <b>%d</b><br>\n", total_lines);
1212 printf("Total time <b>%f</b> seconds<br>\n", fsecs);
1213 printf("<b>%d</b> lines per second<br>\n", (int)(total_lines/fsecs));
1214 }
1215
1216 printf("</body></html>\n");
1217 }
1218
1219
1220 static int
time_greater(const struct timeval * a,const struct timeval * b)1221 time_greater(const struct timeval *a, const struct timeval *b)
1222 {
1223 if(a->tv_sec > b->tv_sec || (a->tv_sec == b->tv_sec &&
1224 a->tv_usec > b->tv_usec))
1225 {
1226 return 1;
1227 }
1228 return 0;
1229 }
1230
1231 static void
diff_times(const struct timeval * a,const struct timeval * b,struct timeval * result)1232 diff_times(const struct timeval *a, const struct timeval *b,
1233 struct timeval *result)
1234 {
1235 const struct timeval *bigger, *lesser;
1236
1237 if(time_greater(a,b))
1238 {
1239 bigger = a; lesser = b;
1240 } else
1241 {
1242 bigger = b; lesser = a;
1243 }
1244 if(bigger->tv_usec < lesser->tv_usec)
1245 {
1246 result->tv_usec = 1000000 - lesser->tv_usec + bigger->tv_usec;
1247 result->tv_sec = bigger->tv_sec - lesser->tv_sec - 1;
1248 }
1249 else
1250 {
1251 result->tv_usec = bigger->tv_usec - lesser->tv_usec;
1252 result->tv_sec = bigger->tv_sec - lesser->tv_sec;
1253 }
1254 }
1255
1256
1257 /* returns 1 if you should ignore this, 0 if not */
1258 static int
determine_ignorance(vulnerability_t * ptr)1259 determine_ignorance(vulnerability_t *ptr)
1260 {
1261
1262 char *lookup = NULL;
1263
1264 switch (ptr->type)
1265 {
1266 case BOProblem:
1267 case FSProblem:
1268 case Info:
1269 case InputProblem:
1270 case RaceConditionCheck:
1271 case RaceConditionUse:
1272 lookup = ptr->data->Name;
1273 break;
1274
1275 case StaticLocalBuffer:
1276 lookup = "$fixed_buffer$";
1277 break;
1278
1279 case StaticGlobalBuffer:
1280 lookup = "$global_buffer$";
1281 break;
1282
1283 case Reference:
1284 lookup = ptr->data->Name;
1285 break;
1286
1287 case PythonBacktick:
1288 lookup = "$python_backtick$";
1289 break;
1290
1291 case PhpBacktick:
1292 lookup = "$php_backtick$";
1293 break;
1294
1295 case PerlBacktick:
1296 lookup = "$perl_backtick$";
1297 break;
1298 case RubyBacktick:
1299 lookup = "$ruby_backtick$";
1300 break;
1301
1302 case None:
1303 default:
1304 lookup = (char *)NULL;
1305 break;
1306 }
1307 if (lookup != NULL)
1308 {
1309 if (lookup_ignore(ptr->filename, ptr->lineno, lookup))
1310 {
1311 return 1;
1312 }
1313 }
1314 return 0;
1315 }
1316
1317
generate_report()1318 void generate_report()
1319 {
1320 char * name;
1321 char * name2 = (char *)NULL;
1322 int doprint = 0;
1323 int reported = 0;
1324 ignore_t * iptr;
1325 ignore_t * inext;
1326 vulnerability_t * ptr;
1327 vulnerability_t * next;
1328
1329
1330 for (ptr = list_head; ptr != (vulnerability_t *)NULL; ptr = ptr->next)
1331 {
1332 if (ptr->severity == Default || ptr->severity >= warning_level)
1333 {
1334 switch (ptr->type)
1335 {
1336 case BOProblem:
1337 case FSProblem:
1338 case Info:
1339 case InputProblem:
1340 case RaceConditionCheck:
1341 case RaceConditionUse:
1342 name = ptr->data->Name;
1343 break;
1344
1345 case StaticLocalBuffer:
1346 name = "fixed size local buffer";
1347 break;
1348
1349 case StaticGlobalBuffer:
1350 name = "fixed size global buffer";
1351 break;
1352
1353 case Reference:
1354 name = "non-function call reference";
1355
1356 name2 = ptr->data->Name;
1357 break;
1358
1359 case PythonBacktick:
1360 case PhpBacktick:
1361 case PerlBacktick:
1362 case RubyBacktick:
1363 name = "backtick";
1364 break;
1365
1366 case None:
1367 default:
1368 name = "Unknown / Database Error";
1369 break;
1370 }
1371 doprint = 1;
1372 if (determine_ignorance(ptr))
1373 {
1374 doprint = 0;
1375 }
1376
1377 if (doprint)
1378 {
1379 if (name2 == (char *)NULL) {
1380 printf("%s:%d", ptr->filename, ptr->lineno);
1381 if (flags & SHOW_COLUMNS)
1382 printf("[%d]", ptr->column);
1383 printf(": %s: %s\n", severities[ptr->severity], name);
1384 }
1385 else {
1386 printf("%s:%d", ptr->filename, ptr->lineno);
1387 if (flags & SHOW_COLUMNS)
1388 printf("[%d]", ptr->column);
1389 printf(": %s: %s: %s\n", severities[ptr->severity], name, name2);
1390
1391 }
1392
1393 if (flags & SHOW_CONTEXT)
1394 {
1395 char *ctx = NULL;
1396 ctx = getctx(ptr->filename, ptr->lineno);
1397 if (ctx)
1398 {
1399 printf("%s", ctx);
1400 free(ctx);
1401 }
1402 }
1403 reported++;
1404 }
1405
1406 if (ptr->next == (vulnerability_t *)NULL || ptr->next->type != ptr->type ||
1407 ptr->type == RaceConditionCheck || ptr->next->data != ptr->data)
1408 {
1409 if (reported)
1410 report_vulnerability(ptr);
1411 }
1412 if (ptr->next && (ptr->next->type != ptr->type))
1413 reported = 0;
1414
1415 }
1416 }
1417
1418 report_inputs();
1419
1420 for (iptr = ignore_list; iptr != (ignore_t *)NULL; iptr = inext)
1421 {
1422 inext = iptr->next;
1423 if (iptr->token != (char *)NULL)
1424 free(iptr->token);
1425 free(iptr);
1426 }
1427 ignore_list = (ignore_t *)NULL;
1428
1429 for (ptr = list_head; ptr != (vulnerability_t *)NULL; ptr = next)
1430 {
1431 next = ptr->next;
1432 free(ptr);
1433 }
1434 list_head = list_tail = (vulnerability_t *)NULL;
1435 if (!(flags & NO_FOOTER))
1436 {
1437 #ifdef _MSC_VER
1438 DWORD ttime;
1439 #else
1440 struct timeval ttime;
1441 #endif
1442
1443 float fsecs;
1444
1445 #ifdef _MSC_VER
1446 ttime = time_finished - time_started;
1447 fsecs = ttime/1000 + (float)(ttime%1000)/1000;
1448 #else
1449 diff_times(&time_finished, &time_started, &ttime);
1450 fsecs = ttime.tv_sec+(ttime.tv_usec/(double)1000000);
1451 #endif
1452
1453 printf("Total lines analyzed: %d\n", total_lines);
1454 printf("Total time %f seconds\n", fsecs);
1455 printf("%d lines per second\n", (int)(total_lines/fsecs));
1456 }
1457 }
1458
new_ignore(int lineno,char * token)1459 ignore_t *new_ignore(int lineno, char *token)
1460 {
1461 ignore_t * ign;
1462
1463 if ((ign = (ignore_t *)malloc(sizeof(ignore_t))) == (ignore_t *)NULL)
1464 return (ignore_t *)NULL;
1465 ign->filename = current_file;
1466 ign->lineno = lineno;
1467 ign->token = (token == (char *)NULL ? token : strdup(token));
1468 ign->next = ignore_list;
1469 ignore_list = ign;
1470
1471 return ign;
1472 }
1473
1474 static
lookup_ignore(char * filename,int lineno,char * token)1475 int lookup_ignore(char *filename, int lineno, char *token)
1476 {
1477 ignore_t * ptr;
1478
1479 for (ptr = ignore_list; ptr != (ignore_t *)NULL; ptr = ptr->next)
1480 {
1481 if (ptr->filename != filename) /* yes, this is safe and will work */
1482 continue;
1483 if (ptr->lineno != lineno)
1484 continue;
1485 if (ptr->token == (char *)NULL || !strcmp(ptr->token, token))
1486 return 1;
1487 }
1488
1489 return 0;
1490 }
1491