check.c - randomcrap - random crap programs of varying quality
(HTM) git clone git://git.codemadness.org/randomcrap
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
check.c (8548B)
---
1 #include <ctype.h>
2 #include <err.h>
3 #include <errno.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <unistd.h>
8
9 #ifndef MIN
10 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
11 #endif
12
13 #ifndef MAX
14 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
15 #endif
16
17 enum {
18 DEWEY_LT,
19 DEWEY_LE,
20 DEWEY_EQ,
21 DEWEY_GE,
22 DEWEY_GT,
23 DEWEY_NE
24 };
25
26 /* do not modify these values, or things will NOT work */
27 enum {
28 Alpha = -3,
29 Beta = -2,
30 RC = -1,
31 Dot = 0,
32 Patch = 1
33 };
34
35 /* this struct defines a version number */
36 typedef struct arr_t {
37 unsigned c; /* # of version numbers */
38 unsigned size; /* size of array */
39 int *v; /* array of decimal numbers */
40 int pkg; /* any package suffix */
41 } arr_t;
42
43 /* this struct describes a test */
44 typedef struct test_t {
45 const char *s; /* string representation */
46 unsigned len; /* length of string */
47 int t; /* enumerated type of test */
48 } test_t;
49
50 /* the tests that are recognised. */
51 const test_t tests[] = {
52 { "<=", 2, DEWEY_LE },
53 { "<", 1, DEWEY_LT },
54 { ">=", 2, DEWEY_GE },
55 { ">", 1, DEWEY_GT },
56 { "==", 2, DEWEY_EQ },
57 { "!=", 2, DEWEY_NE },
58 { NULL, 0, 0 }
59 };
60
61 const test_t modifiers[] = {
62 { "alpha", 5, Alpha },
63 { "beta", 4, Beta },
64 { "pre", 3, RC },
65 { "rc", 2, RC },
66 { "pl", 2, Dot },
67 { ".", 1, Dot },
68 { NULL, 0, 0 }
69 };
70
71 struct pkgname {
72 char *name;
73 struct pkgname *next;
74 };
75
76 static struct pkgname *pkghead;
77 static struct pkgname *pkgcur;
78
79 /* locate the test in the tests array */
80 int
81 dewey_mktest(int *op, const char *test)
82 {
83 const test_t *tp;
84
85 for (tp = tests ; tp->s ; tp++) {
86 if (strncasecmp(test, tp->s, tp->len) == 0) {
87 *op = tp->t;
88 return tp->len;
89 }
90 }
91 return -1;
92 }
93
94 /*
95 * make a component of a version number.
96 * '.' encodes as Dot which is '0'
97 * 'pl' encodes as 'patch level', or 'Dot', which is 0.
98 * 'alpha' encodes as 'alpha version', or Alpha, which is -3.
99 * 'beta' encodes as 'beta version', or Beta, which is -2.
100 * 'rc' encodes as 'release candidate', or RC, which is -1.
101 * 'nb' encodes as 'netbsd version', which is used after all other tests
102 */
103 static int
104 mkcomponent(arr_t *ap, const char *num)
105 {
106 static const char alphas[] = "abcdefghijklmnopqrstuvwxyz";
107 const test_t *modp;
108 int n;
109 const char *cp;
110
111 if (ap->c == ap->size) {
112 if (ap->size == 0) {
113 ap->size = 62;
114 if ((ap->v = malloc(ap->size * sizeof(int))) == NULL)
115 err(EXIT_FAILURE, "mkver malloc failed");
116 } else {
117 ap->size *= 2;
118 if ((ap->v = realloc(ap->v, ap->size * sizeof(int)))
119 == NULL)
120 err(EXIT_FAILURE, "mkver realloc failed");
121 }
122 }
123 if (isdigit((unsigned char)*num)) {
124 for (cp = num, n = 0 ; isdigit((unsigned char)*num) ; num++) {
125 n = (n * 10) + (*num - '0');
126 }
127 ap->v[ap->c++] = n;
128 return (int)(num - cp);
129 }
130 for (modp = modifiers ; modp->s ; modp++) {
131 if (strncasecmp(num, modp->s, modp->len) == 0) {
132 ap->v[ap->c++] = modp->t;
133 return modp->len;
134 }
135 }
136
137 /* OpenBSD package version: p0
138 FreeBSD package version: ,1 or _1
139 NetBSD package version: "nb" */
140 if (((*num == 'p' || *num == ',' || *num == '_') && isdigit((unsigned char)*(num + 1))) ||
141 (strncasecmp(num, "nb", 2) == 0)) {
142 cp = num;
143 num += strcspn(num, "0123456789");
144 for (n = 0; isdigit((unsigned char)*num) ; num++) {
145 n = (n * 10) + (*num - '0');
146 }
147 /* XXX: ignore package version for now.
148 ap->pkg = n; */
149
150 ap->pkg = 0; /* hardcoded package version to 0 for now */
151 return (int)(num - cp);
152 }
153
154 if (isalpha((unsigned char)*num)) {
155 ap->v[ap->c++] = Dot;
156 cp = strchr(alphas, tolower((unsigned char)*num));
157 if (ap->c == ap->size) {
158 ap->size *= 2;
159 if ((ap->v = realloc(ap->v, ap->size * sizeof(int))) == NULL)
160 err(EXIT_FAILURE, "mkver realloc failed");
161 }
162 ap->v[ap->c++] = (int)(cp - alphas) + 1;
163 return 1;
164 }
165 return 1;
166 }
167
168 /* make a version number string into an array of comparable ints */
169 static int
170 mkversion(arr_t *ap, const char *num)
171 {
172 ap->c = 0;
173 ap->size = 0;
174 ap->v = NULL;
175 ap->pkg = 0;
176
177 while (*num) {
178 num += mkcomponent(ap, num);
179 }
180 return 1;
181 }
182
183 #define DIGIT(v, c, n) (((n) < (c)) ? v[n] : 0)
184
185 /* compare the result against the test we were expecting */
186 static int
187 result(int cmp, int tst)
188 {
189 switch(tst) {
190 case DEWEY_LT:
191 return cmp < 0;
192 case DEWEY_LE:
193 return cmp <= 0;
194 case DEWEY_GT:
195 return cmp > 0;
196 case DEWEY_GE:
197 return cmp >= 0;
198 case DEWEY_EQ:
199 return cmp == 0;
200 case DEWEY_NE:
201 return cmp != 0;
202 default:
203 return 0;
204 }
205 }
206
207 /* do the test on the 2 vectors */
208 static int
209 vtest(arr_t *lhs, int tst, arr_t *rhs)
210 {
211 int cmp;
212 unsigned int c, i;
213
214 for (i = 0, c = MAX(lhs->c, rhs->c) ; i < c ; i++) {
215 if ((cmp = DIGIT(lhs->v, lhs->c, i) - DIGIT(rhs->v, rhs->c, i)) != 0) {
216 return result(cmp, tst);
217 }
218 }
219 return result(lhs->pkg - rhs->pkg, tst);
220 }
221
222 /* TODO */
223 int
224 checkversion(const char *ranges, const char *version)
225 {
226 char tmp[1024];
227 const char *s, *e;
228 int op, r, match;
229 arr_t ap, apr;
230
231 if (mkversion(&ap, version) != 1)
232 return -1;
233
234 match = 1;
235 for (s = ranges; *s; ) {
236 if (*s == ' ') {
237 /* new condition */
238 match = 1;
239 s++;
240 }
241
242 // printf("DEBUG: try: %s\n", s);
243 op = 0;
244 r = dewey_mktest(&op, s);
245 if (r == -1)
246 break; /* TODO: use EQ? */
247 s += r;
248 // printf("DEBUG: %d %s\n", op, s);
249
250 /* seek until next version or end of condition */
251 for (e = s; *e && !strchr("><!= ", *e); e++)
252 ;
253
254 if (e - s + 1 < sizeof(tmp)) {
255 /* TODO: don't copy (that floppy) */
256 memcpy(tmp, s, e - s);
257 tmp[e - s] = '\0';
258
259 // printf("DEBUG: tmp: |%s|\n", tmp);
260
261 if (mkversion(&apr, tmp) != 1)
262 return -1;
263 s = e;
264
265 r = vtest(&ap, op, &apr);
266 if (match && r == 1 && (*s == ' ' || *s == '\0'))
267 return 1;
268 if (r == 0)
269 match = 0;
270 } else {
271 return -1;
272 }
273 }
274
275 return match;
276 }
277
278 void
279 testversion(const char *ranges, const char *version, int expect)
280 {
281 int r;
282
283 r = checkversion(ranges, version);
284 printf("%s in [%s]? %d (expect) == %d (result)\n", version, ranges, expect, r);
285 if (r != expect) {
286 printf("test failed\n");
287 exit(1);
288 }
289 }
290
291 struct pkgname *
292 readpkgs(FILE *fp)
293 {
294 char *line = NULL;
295 size_t linesiz = 0;
296 ssize_t n;
297 struct pkgname *p;
298
299 while ((n = getline(&line, &linesiz, fp)) > 0) {
300 if (line[n - 1] == '\n')
301 line[--n] = '\0';
302
303 if (!(p = calloc(1, sizeof(*pkgcur))))
304 err(1, "calloc");
305 if (pkghead) {
306 pkgcur->next = p;
307 pkgcur = pkgcur->next;
308 } else {
309 pkghead = pkgcur = p;
310 }
311 if (!(p->name = strdup(line)))
312 err(1, "strdup");
313 }
314 if (ferror(fp))
315 err(1, "getline");
316
317 return pkghead;
318 }
319
320 int
321 checkname(const char *names, const char *name)
322 {
323 const char *s, *p;
324 size_t len;
325
326 if (!(p = strrchr(name, '-')))
327 return 0;
328 len = p - name;
329
330 /* TODO: optimize (strcasestr) */
331
332 for (s = names; ; s = p + 1) {
333 if (!strncasecmp(s, name, len) && (s[len] == '\0' || s[len] == ' '))
334 return 1;
335 if (!(p = strchr(s, ' ')))
336 break;
337 }
338
339 return 0;
340 }
341
342 void
343 filtervulns(FILE *fp, struct pkgname *head)
344 {
345 char *line = NULL;
346 size_t linesiz = 0;
347 ssize_t n;
348 struct pkgname *p;
349 size_t len;
350 char *names, *ranges;
351 char *s;
352
353 while ((n = getline(&line, &linesiz, fp)) > 0) {
354 if (line[n - 1] == '\n')
355 line[--n] = '\0';
356
357 names = line;
358 len = strcspn(names, "\t");
359 s = line + len;
360 if (!*s)
361 continue;
362 *s = '\0';
363 s++;
364
365 ranges = s;
366 len = strcspn(ranges, "\t");
367 s = ranges + len;
368 if (!*s)
369 continue;
370 *s = '\0';
371 s++;
372
373 /* TODO: matching */
374 for (p = head; p; p = p->next) {
375 if (!checkname(names, p->name))
376 continue;
377
378 const char *v;
379 if (!(v = strrchr(p->name, '-')))
380 continue;
381 v++;
382
383 int match;
384 match = checkversion(ranges, v);
385 /* TODO: FreeBSD uses this for "end of life" of port apparently, ignore for now */
386 if (!strcmp(ranges, ">=0"))
387 continue;
388
389 if (match == 1)
390 printf("OpenBSD package: %s\n"
391 "Name: %s\n"
392 "Version range: %s\n"
393 "Vuln: %s\n===\n",
394 p->name, names, ranges, s);
395 }
396 }
397 if (ferror(fp))
398 err(1, "getline");
399 }
400
401 int
402 main(void)
403 {
404 FILE *pkgfp, *vulnfp;
405 struct pkgname *pkgnames;
406
407 if (pledge("stdio rpath", NULL) == -1)
408 err(1, "pledge");
409
410 /* TODO: configurable files */
411 if (!(pkgfp = fopen("pkgs.csv", "r")))
412 err(1, "fopen");
413
414 if (!(vulnfp = fopen("vulns.csv", "r")))
415 err(1, "fopen");
416
417 if (pledge("stdio", NULL) == -1)
418 err(1, "pledge");
419
420 pkgnames = readpkgs(pkgfp);
421 filtervulns(vulnfp, pkgnames);
422
423 return 0;
424 }