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 }