1 /* t-json.c - Regression test.
2  * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
3  *                    Software engineering by Intevation GmbH
4  *
5  * This file is part of GPGME.
6  *
7  * GPGME is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as
9  * published by the Free Software Foundation; either version 2.1 of
10  * the License, or (at your option) any later version.
11  *
12  * GPGME is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program; if not, see <https://gnu.org/licenses/>.
19  * SPDX-License-Identifier: LGPL-2.1-or-later
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25 
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <sys/stat.h>
31 
32 #include <gpgme.h>
33 #include <gpg-error.h>
34 
35 #include "../gpg/t-support.h"
36 #include "../../src/cJSON.h"
37 
38 /* Register tests here */
39 static const char*tests[] = { "t-config", "t-version",
40     "t-keylist", "t-keylist-secret", "t-decrypt", "t-config-opt",
41     "t-encrypt", "t-encrypt-sign", "t-sign", "t-verify",
42     "t-decrypt-verify", "t-export", "t-createkey",
43     "t-export-secret-info", "t-chunking", "t-sig-notations",
44     /* For these two the order is important
45      * as t-import imports the deleted key from t-delete */
46     "t-delete", "t-import",
47     NULL };
48 
49 static int verbose = 0;
50 
51 
52 /* Read the next number in the version string STR and return it in
53    *NUMBER.  Return a pointer to the tail of STR after parsing, or
54    *NULL if the version string was invalid.  */
55 static const char *
parse_version_number(const char * str,int * number)56 parse_version_number (const char *str, int *number)
57 {
58 #define MAXVAL ((INT_MAX - 10) / 10)
59   int val = 0;
60 
61   /* Leading zeros are not allowed.  */
62   if (*str == '0' && isdigit(str[1]))
63     return NULL;
64 
65   while (isdigit (*str) && val <= MAXVAL)
66     {
67       val *= 10;
68       val += *(str++) - '0';
69     }
70   *number = val;
71   return val > MAXVAL ? NULL : str;
72 }
73 
74 
75 /* Parse the version string STR in the format MAJOR.MINOR.MICRO (for
76    example, 9.3.2) and return the components in MAJOR, MINOR and MICRO
77    as integers.  The function returns the tail of the string that
78    follows the version number.  This might be the empty string if there
79    is nothing following the version number, or a patchlevel.  The
80    function returns NULL if the version string is not valid.  */
81 static const char *
parse_version_string(const char * str,int * major,int * minor,int * micro)82 parse_version_string (const char *str, int *major, int *minor, int *micro)
83 {
84   str = parse_version_number (str, major);
85   if (!str || *str != '.')
86     return NULL;
87   str++;
88 
89   str = parse_version_number (str, minor);
90   if (!str || *str != '.')
91     return NULL;
92   str++;
93 
94   str = parse_version_number (str, micro);
95   if (!str)
96     return NULL;
97 
98   /* A patchlevel might follow.  */
99   return str;
100 }
101 
102 
103 /* Return true if MY_VERSION is at least REQ_VERSION, and false
104    otherwise.  */
105 static int
compare_versions(const char * my_version,const char * rq_version)106 compare_versions (const char *my_version,
107                   const char *rq_version)
108 {
109   int my_major, my_minor, my_micro;
110   int rq_major, rq_minor, rq_micro;
111   const char *my_plvl, *rq_plvl;
112 
113   if (!rq_version)
114     return 1;
115   if (!my_version)
116     return 0;
117 
118   my_plvl = parse_version_string (my_version, &my_major, &my_minor, &my_micro);
119   if (!my_plvl)
120     return 0;
121 
122   rq_plvl = parse_version_string (rq_version, &rq_major, &rq_minor, &rq_micro);
123   if (!rq_plvl)
124     return 0;
125 
126   if (my_major > rq_major
127       || (my_major == rq_major && my_minor > rq_minor)
128       || (my_major == rq_major && my_minor == rq_minor
129 	  && my_micro > rq_micro)
130       || (my_major == rq_major && my_minor == rq_minor
131 	  && my_micro == rq_micro && strcmp (my_plvl, rq_plvl) >= 0))
132     return 1;
133 
134   return 0;
135 }
136 
137 /* Return true if we have the required gpg version.
138 
139    This should probably go into gpgrt or gpgme proper.
140 */
141 static int
check_gpg_version(const char * req_version)142 check_gpg_version (const char *req_version)
143 {
144   gpgme_engine_info_t engine_info;
145   init_gpgme (GPGME_PROTOCOL_OpenPGP);
146 
147   fail_if_err (gpgme_get_engine_info (&engine_info));
148   for (; engine_info; engine_info = engine_info->next)
149     if (engine_info->protocol == GPGME_PROTOCOL_OpenPGP)
150       break;
151 
152   test (engine_info);
153 
154   return compare_versions (engine_info->version, req_version);
155 }
156 
157 static char *
get_file(const char * fname)158 get_file (const char *fname)
159 {
160   gpg_error_t err;
161   gpgrt_stream_t fp;
162   struct stat st;
163   char *buf;
164   size_t buflen;
165 
166   fp = gpgrt_fopen (fname, "r");
167   if (!fp)
168     {
169       err = gpg_error_from_syserror ();
170       fprintf (stderr, "Error: can't open '%s': %s\n", fname,
171                gpg_strerror (err));
172       return NULL;
173     }
174 
175   if (fstat (gpgrt_fileno(fp), &st))
176     {
177       err = gpg_error_from_syserror ();
178       fprintf (stderr, "Error: can't stat '%s': %s\n", fname,
179                gpg_strerror (err));
180       gpgrt_fclose (fp);
181       return NULL;
182     }
183 
184   buflen = st.st_size;
185   buf = malloc (buflen+1);
186   if (!buf)
187     {
188       fprintf (stderr, "Error: no mem\n");
189       gpgrt_fclose (fp);
190       return NULL;
191     }
192 
193   if (gpgrt_fread (buf, buflen, 1, fp) != 1)
194     {
195       err = gpg_error_from_syserror ();
196       fprintf (stderr, "error reading '%s': %s\n", fname, gpg_strerror (err));
197       gpgrt_fclose (fp);
198       free (buf);
199       return NULL;
200     }
201   buf[buflen] = 0;
202   gpgrt_fclose (fp);
203 
204   return buf;
205 }
206 
207 /* Check that the element needle exists in hay. Returns 0 if
208    the needle was found. */
209 int
test_contains(cjson_t needle,cjson_t hay)210 test_contains (cjson_t needle, cjson_t hay)
211 {
212   cjson_t it;
213 
214   if (verbose == 2)
215     fprintf (stderr, "%s: -------checking-------- "
216                      "\n%s\n -------against-------- \n%s\n",
217              nonnull (needle->string), cJSON_Print (needle),
218              cJSON_Print (hay));
219 
220   /* Type check. This automatically checks bool vals and NULL */
221   if (needle->type != hay->type)
222     {
223       if (verbose)
224         fprintf (stderr, "%s: type mismatch expected %i got %i\n",
225                  nonnull (needle->string), needle->type,
226                  hay->type);
227       return 1;
228     }
229 
230   /* First the simple types */
231   if (cjson_is_number (needle))
232     {
233       if (needle->valueint != hay->valueint)
234         {
235           if (verbose)
236             fprintf (stderr, "%s: value mismatch. Expected %i got %i\n",
237                      nonnull (needle->string), needle->valueint,
238                      hay->valueint);
239           return 1;
240         }
241     }
242   if (cjson_is_string (needle))
243     {
244       if (strcmp (needle->valuestring, hay->valuestring) &&
245           /* Use * as a general don't care placeholder */
246           strcmp (needle->valuestring, "*"))
247         {
248           if (verbose)
249             fprintf (stderr, "%s: string mismatch Expected '%s' got '%s'\n",
250                      needle->string, needle->valuestring, hay->valuestring);
251           return 1;
252         }
253     }
254 
255   /* Now the complex types */
256   if (needle->child)
257     {
258       if (!hay->child)
259         {
260           fprintf (stderr, "Depth mismatch. Expected child for %s\n",
261                    nonnull (needle->string));
262         }
263       if (test_contains (needle->child, hay->child))
264         {
265           int found = 0;
266           cjson_t hit;
267 
268           for (hit = hay->child; hit; hit = hit->next)
269             {
270               found |= !test_contains (needle->child, hit);
271               if (found)
272                 {
273                   break;
274                 }
275             }
276           if (!found)
277             {
278               return 1;
279             }
280         }
281     }
282 
283   if (needle->prev)
284     {
285       return 0;
286     }
287 
288   /* Walk elements of an array */
289   for (it = needle->next; it; it = it->next)
290     {
291       int found = 0;
292       cjson_t hit;
293 
294       if (!it->string && it->child)
295         {
296           /* Try out all other anonymous children on the same level */
297           hit = hay;
298 
299           /* Return to the beginning */
300           while (hit->prev)
301             {
302               hit = hit->prev;
303             }
304           for (; hit && hit->child; hit = hit->next)
305             {
306               found |= !test_contains (it->child, hit->child);
307               if (found)
308                 {
309                   break;
310                 }
311             }
312           if (!found)
313             {
314               return 1;
315             }
316           continue;
317         }
318 
319       /* Try the children in the haystack */
320       for (hit = hay; hit; hit = hit->next)
321         {
322           if (hit->string && it->string &&
323               !strcmp (hit->string, it->string))
324             {
325               found = 1;
326               if (test_contains (it, hit))
327                 {
328                   return 1;
329                 }
330             }
331         }
332       if (!found)
333         {
334           if (verbose)
335             fprintf (stderr, "Failed to find '%s' in list\n",
336                      nonnull (it->string));
337           return 1;
338         }
339     }
340   return 0;
341 }
342 
343 
344 int
check_response(const char * response,const char * expected)345 check_response (const char *response, const char *expected)
346 {
347   cjson_t hay;
348   cjson_t needle;
349   int rc;
350   size_t erroff;
351 
352   hay = cJSON_Parse (response, &erroff);
353 
354   if (!hay)
355     {
356       fprintf (stderr, "Failed to parse json at %i:\n%s\n", (int) erroff,
357                response);
358       return 1;
359     }
360   needle = cJSON_Parse (expected, &erroff);
361   if (!needle)
362     {
363       fprintf (stderr, "Failed to parse json at %i:\n%s\n", (int) erroff,
364                expected);
365       cJSON_Delete (hay);
366       return 1;
367     }
368 
369   rc = test_contains (needle, hay);
370 
371   cJSON_Delete (needle);
372   cJSON_Delete (hay);
373   return rc;
374 }
375 
376 
377 int
run_test(const char * test,const char * gpgme_json)378 run_test (const char *test, const char *gpgme_json)
379 {
380   gpgme_ctx_t ctx;
381   gpgme_data_t json_stdin = NULL;
382   gpgme_data_t json_stdout = NULL;
383   gpgme_data_t json_stderr = NULL;
384   char *test_in;
385   char *test_out;
386   const char *argv[3];
387   char *response;
388   char *expected = NULL;
389   size_t response_size;
390   int rc = 0;
391   const char *top_srcdir = getenv ("top_srcdir");
392 
393   if (!top_srcdir)
394     {
395       fprintf (stderr, "Error top_srcdir environment variable not set\n");
396       exit(1);
397     }
398 
399   gpgrt_asprintf (&test_in, "%s/tests/json/%s.in.json",
400                   top_srcdir, test);
401   gpgrt_asprintf (&test_out, "%s/tests/json/%s.out.json",
402                   top_srcdir, test);
403 
404   printf ("Running %s...\n", test);
405 
406   fail_if_err (gpgme_new (&ctx));
407 
408   gpgme_set_protocol (ctx, GPGME_PROTOCOL_SPAWN);
409 
410   fail_if_err (gpgme_data_new_from_file (&json_stdin, test_in, 1));
411   fail_if_err (gpgme_data_new (&json_stdout));
412   fail_if_err (gpgme_data_new (&json_stderr));
413 
414   argv[0] = gpgme_json;
415   argv[1] = "-s";
416   argv[2] = NULL;
417 
418   fail_if_err (gpgme_op_spawn (ctx, gpgme_json, argv,
419                                json_stdin,
420                                json_stdout,
421                                json_stderr,
422                                0));
423   response = gpgme_data_release_and_get_mem (json_stdout,
424                                              &response_size);
425   if (response_size)
426     {
427       expected = get_file (test_out);
428 
429       test (expected);
430 
431       rc = check_response (response, expected);
432     }
433   else
434     {
435       rc = 1;
436     }
437 
438   if (!rc)
439     {
440       printf (" success\n");
441       gpgme_data_release (json_stderr);
442     }
443   else
444     {
445       char *buf;
446       size_t size;
447 
448       buf = gpgme_data_release_and_get_mem (json_stderr, &size);
449       printf (" failed%s\n", response_size ? "" :
450                              ", no response from gpgme-json");
451       if (size)
452         {
453           printf ("gpgme-json stderr:\n%.*s\n", (int)size, buf);
454         }
455       free (buf);
456     }
457 
458   free (test_out);
459   free (test_in);
460   free (response);
461   free (expected);
462   gpgme_data_release (json_stdin);
463   gpgme_release (ctx);
464 
465   return rc;
466 }
467 
468 int
main(int argc,char * argv[])469 main (int argc, char *argv[])
470 {
471   const char *gpgme_json = getenv ("gpgme_json");
472   int last_argc = -1;
473   const char **test;
474 
475   if (argc)
476     { argc--; argv++; }
477 
478 
479   while (argc && last_argc != argc )
480     {
481       last_argc = argc;
482       if (!strcmp (*argv, "--verbose"))
483         {
484           verbose++;
485           argc--; argv++;
486         }
487     }
488 
489   if (!check_gpg_version ("2.1.18"))
490     {
491       /* Lets not break too much or have to test all combinations */
492       printf ("Testsuite skipped. Minimum GnuPG version (2.1.18) "
493               "not found.\n");
494       exit(0);
495     }
496 
497   init_gpgme (GPGME_PROTOCOL_SPAWN);
498 
499   for (test = tests; *test; test++)
500     {
501       if (run_test (*test, gpgme_json))
502         {
503           exit(1);
504         }
505     }
506   return 0;
507 }
508