]> git.ipfire.org Git - thirdparty/libsolv.git/commitdiff
conda: add support for package signature verification
authorMichael Schroeder <mls@suse.de>
Thu, 7 Jul 2022 09:23:21 +0000 (11:23 +0200)
committerMichael Schroeder <mls@suse.de>
Thu, 7 Jul 2022 09:23:21 +0000 (11:23 +0200)
This commits adds SOLVABLE_SIGNATUREDATA which contains the data
needed to verify the signatures of a package. You will need
to pass CONDA_ADD_WITH_SIGNATUREDATA to the flags in the
repo_add_conda call to enable this.

This is still experimental.

ext/repo_conda.c
ext/repo_conda.h
src/knownid.h
tools/conda2solv.c

index fb7c1d57657f5baeb8d684ae3af756ced6e8527e..9211cbeac8e7348471b280c10a65c428a7073925 100644 (file)
 #include "solv_jsonparser.h"
 #include "conda.h"
 #include "repo_conda.h"
+#include "solv_xfopen.h"
+
+struct sigdata {
+  char *sigs;
+};
+
+struct xdata {
+  char *fn;
+  char *pkgjson;
+  int delayedlocation;
+};
 
 struct parsedata {
   Pool *pool;
   Repo *repo;
   Repodata *data;
+  int flags;
+
+  char *subdir;
+  char *error;
 
   Stringpool fnpool;
   Queue fndata;
 
-  char *subdir;
-  char **fnlist;
-  int nfnlist;
+  Stringpool sigpool;
+  struct sigdata *sigdata;
+  int nsigdata;
+
+  struct xdata *xdata;
+  int nxdata;
 };
 
 static int
@@ -139,40 +157,103 @@ fn2data(struct parsedata *pd, const char *fn, Id *fntypep, int create)
   return pd->fndata.elements + 2 * fnid;
 }
 
+struct sigdata *
+fn2sigdata(struct parsedata *pd, const char *fn, int create)
+{
+  Id id = stringpool_str2id(&pd->sigpool, fn, create);
+  if (!id && !create)
+    return 0;
+  if (id >= pd->nsigdata)
+    {
+      int n = id - pd->nsigdata + 1;
+      pd->sigdata = solv_realloc2(pd->sigdata, pd->nsigdata + n, sizeof(struct sigdata));
+      memset(pd->sigdata + pd->nsigdata, 0, n * sizeof(struct sigdata));
+      pd->nsigdata += n;
+    }
+  return pd->sigdata + id;
+}
+
+void
+freesigdata(struct parsedata *pd)
+{
+  int i;
+  for (i = 0; i < pd->nsigdata; i++)
+    solv_free(pd->sigdata[i].sigs);
+  pd->sigdata = solv_free(pd->sigdata);
+  pd->nsigdata = 0;
+}
+
 static void
-set_fnlist(struct parsedata *pd, int handle, char *fn)
+set_xdata(struct parsedata *pd, int handle, char *fn, char *pkgjson, int delayedlocation)
 {
+  struct xdata *xd;
   handle -= pd->repo->start;
-  if (handle >= pd->nfnlist)
+  if (handle >= pd->nxdata)
     {
       int n;
-      if (!fn)
+      if (!fn && !pkgjson && !delayedlocation)
        return;
-      n = handle - pd->nfnlist + 16;
-      pd->fnlist = solv_realloc2(pd->fnlist, pd->nfnlist + n, sizeof(char *));
-      memset(pd->fnlist + pd->nfnlist, 0, n * sizeof(char *));
-      pd->nfnlist += n;
+      n = handle - pd->nxdata + 16;
+      pd->xdata = solv_realloc2(pd->xdata, pd->nxdata + n, sizeof(struct xdata));
+      memset(pd->xdata + pd->nxdata, 0, n * sizeof(struct xdata));
+      pd->nxdata += n;
     }
-  if (pd->fnlist[handle])
-    solv_free(pd->fnlist[handle]);
-  pd->fnlist[handle] = fn;
+  xd = pd->xdata + handle;
+  if (xd->fn)
+    solv_free(xd->fn);
+  if (xd->pkgjson)
+    solv_free(xd->pkgjson);
+  xd->fn = fn;
+  xd->pkgjson = pkgjson;
+  xd->delayedlocation = delayedlocation;
 }
 
 static void
-move_fnlist(struct parsedata *pd, int fromhandle, int tohandle)
+move_xdata(struct parsedata *pd, int fromhandle, int tohandle)
 {
-  char *fn = 0;
+  char *fn = 0, *pkgjson = 0;
+  int delayedlocation = 0;
   fromhandle -= pd->repo->start;
-  if (fromhandle < pd->nfnlist)
+  if (fromhandle < pd->nxdata)
     {
-      fn = pd->fnlist[fromhandle];
-      pd->fnlist[fromhandle] = 0;
+      struct xdata *xd = pd->xdata + fromhandle;
+      fn = xd->fn;
+      pkgjson = xd->pkgjson;
+      delayedlocation = xd->delayedlocation;
+      xd->fn = 0;
+      xd->pkgjson = 0;
+      xd->delayedlocation = 0;
     }
-  set_fnlist(pd, tohandle, fn);
+  set_xdata(pd, tohandle, fn, pkgjson, delayedlocation);
 }
 
+static int parse_package(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn, char *pkgjson);
+
 static int
-parse_package(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn)
+parse_package_with_pkgjson(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn)
+{
+  FILE *fp;
+  int type;
+  char *pkgjson = NULL;
+  int line = jp->line;
+
+  type = jsonparser_collect(jp, JP_OBJECT, &pkgjson);
+  if (type == JP_OBJECT_END && (fp = solv_fmemopen(pkgjson, strlen(pkgjson), "r")) != 0)
+    {
+      struct solv_jsonparser jp2;
+      jsonparser_init(&jp2, fp);
+      jp2.line = line;
+      type = jsonparser_parse(&jp2);
+      type = type == JP_OBJECT ? parse_package(pd, &jp2, kfn, pkgjson) : JP_ERROR;
+      jsonparser_free(&jp2);
+      fclose(fp);
+    }
+  solv_free(pkgjson);
+  return type;
+}
+
+static int
+parse_package(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn, char *pkgjson)
 {
   int type = JP_OBJECT;
   Pool *pool= pd->pool;
@@ -183,6 +264,9 @@ parse_package(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn)
   char *subdir = 0;
   Id *fndata = 0, fntype = 0;
 
+  if (!pkgjson && (pd->flags & CONDA_ADD_WITH_SIGNATUREDATA) != 0)
+    return parse_package_with_pkgjson(pd, jp, kfn);
+
   handle = repo_add_solvable(pd->repo);
   s = pool_id2solvable(pool, handle);
   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
@@ -227,12 +311,20 @@ parse_package(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn)
       else
        type = jsonparser_skip(jp, type);
     }
+  /* if we have a global subdir make sure that it matches */
+  if (subdir && pd->subdir && strcmp(subdir, pd->subdir) != 0)
+    {
+      pd->error = "subdir mismatch";
+      return JP_ERROR;
+    }
+
   if (fn || kfn)
     {
-      if (subdir)
-       repodata_set_location(data, handle, 0, subdir, fn ? fn : kfn);
-      else
-       set_fnlist(pd, handle, solv_strdup(fn ? fn : kfn));     /* delay location setting */
+      int delayedlocation = (subdir || pd->subdir) ? 0 : 1;
+      if (pkgjson || delayedlocation)
+       set_xdata(pd, handle, solv_strdup(fn ? fn : kfn), pkgjson ? solv_strdup(pkgjson) : 0, delayedlocation);
+      if (!delayedlocation)
+        repodata_set_location(data, handle, 0, subdir ? subdir : pd->subdir, fn ? fn : kfn);
       fndata = fn2data(pd, fn ? fn : kfn, &fntype, 1);
     }
   solv_free(fn);
@@ -249,14 +341,14 @@ parse_package(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn)
        {
          /* ignore this package */
          repo_free_solvable(pd->repo, handle, 1);
-         set_fnlist(pd, handle, 0);
+         set_xdata(pd, handle, 0, 0, 0);
          return type;
        }
       if (fndata[0] && fndata[0] < fntype)
        {
          /* replace old package */
          swap_solvables(pool, data, handle, fndata[1]);
-         move_fnlist(pd, handle, fndata[1]);
+         move_xdata(pd, handle, fndata[1]);
          repo_free_solvable(pd->repo, handle, 1);
          handle = fndata[1];
        }
@@ -275,7 +367,7 @@ parse_packages(struct parsedata *pd, struct solv_jsonparser *jp)
       if (type == JP_OBJECT)
        {
          char *fn = solv_strdup(jp->key);
-         type = parse_package(pd, jp, fn);
+         type = parse_package(pd, jp, fn, 0);
          solv_free(fn);
        }
       else
@@ -291,7 +383,7 @@ parse_packages2(struct parsedata *pd, struct solv_jsonparser *jp)
   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_ARRAY_END)
     {
       if (type == JP_OBJECT)
-       type = parse_package(pd, jp, 0);
+       type = parse_package(pd, jp, 0, 0);
       else
        type = jsonparser_skip(jp, type);
     }
@@ -304,14 +396,41 @@ parse_info(struct parsedata *pd, struct solv_jsonparser *jp)
   int type = JP_OBJECT;
   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
     {
-      if (type == JP_STRING && !strcmp(jp->key, "subdir") && !pd->subdir)
-       pd->subdir = strdup(jp->value);
+      if (type == JP_STRING && !strcmp(jp->key, "subdir"))
+       {
+         if (!pd->subdir)
+           pd->subdir = strdup(jp->value);
+         else if (strcmp(pd->subdir, jp->value))
+           {
+             pd->error = "subdir mismatch";
+             return JP_ERROR;
+           }
+       }
+    }
+  return type;
+}
+
+static int
+parse_signatures(struct parsedata *pd, struct solv_jsonparser *jp)
+{
+  int type = JP_OBJECT;
+  while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
+    {
+      struct sigdata *sd;
+      if (type != JP_OBJECT)
+       {
+         type = jsonparser_skip(jp, type);
+         continue;
+       }
+      sd = fn2sigdata(pd, jp->key, 1);
+      sd->sigs = solv_free(sd->sigs);
+      type = jsonparser_collect(jp, type, &sd->sigs);
     }
   return type;
 }
 
 static int
-parse_main(struct parsedata *pd, struct solv_jsonparser *jp, int flags)
+parse_main(struct parsedata *pd, struct solv_jsonparser *jp)
 {
   int type = JP_OBJECT;
   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
@@ -322,10 +441,12 @@ parse_main(struct parsedata *pd, struct solv_jsonparser *jp, int flags)
        type = parse_packages(pd, jp);
       else if (type == JP_ARRAY && !strcmp("packages", jp->key))
        type = parse_packages2(pd, jp);
-      else if (type == JP_OBJECT && !strcmp("packages.conda", jp->key) && !(flags & CONDA_ADD_USE_ONLY_TAR_BZ2))
+      else if (type == JP_OBJECT && !strcmp("packages.conda", jp->key) && !(pd->flags & CONDA_ADD_USE_ONLY_TAR_BZ2))
        type = parse_packages(pd, jp);
-      else if (type == JP_ARRAY && !strcmp("packages.conda", jp->key) && !(flags & CONDA_ADD_USE_ONLY_TAR_BZ2))
+      else if (type == JP_ARRAY && !strcmp("packages.conda", jp->key) && !(pd->flags & CONDA_ADD_USE_ONLY_TAR_BZ2))
        type = parse_packages2(pd, jp);
+      if (type == JP_OBJECT && !strcmp("signatures", jp->key))
+       type = parse_signatures(pd, jp);
       else
        type = jsonparser_skip(jp, type);
     }
@@ -347,30 +468,54 @@ repo_add_conda(Repo *repo, FILE *fp, int flags)
   pd.pool = pool;
   pd.repo = repo;
   pd.data = data;
+  pd.flags = flags;
   stringpool_init_empty(&pd.fnpool);
+  stringpool_init_empty(&pd.sigpool);
   queue_init(&pd.fndata);
 
   jsonparser_init(&jp, fp);
   if ((type = jsonparser_parse(&jp)) != JP_OBJECT)
     ret = pool_error(pool, -1, "repository does not start with an object");
-  else if ((type = parse_main(&pd, &jp, flags)) != JP_OBJECT_END)
-    ret = pool_error(pool, -1, "parse error line %d", jp.line);
+  else if ((type = parse_main(&pd, &jp)) != JP_OBJECT_END)
+    {
+      if (pd.error)
+        ret = pool_error(pool, -1, "parse error line %d: %s", jp.line, pd.error);
+      else
+        ret = pool_error(pool, -1, "parse error line %d", jp.line);
+    }
   jsonparser_free(&jp);
 
-  /* finalize delayed location setting */
-  if (pd.fnlist)
+  /* finalize parsed packages */
+  if (pd.xdata)
     {
       int i;
-      for (i = 0; i < pd.nfnlist; i++)
+      struct xdata *xd = pd.xdata;
+      for (i = 0; i < pd.nxdata; i++, xd++)
        {
-         if (!pd.fnlist[i])
+         if (!xd->fn)
            continue;
-         repodata_set_location(data, repo->start + i, 0, pd.subdir, pd.fnlist[i]);
-         solv_free(pd.fnlist[i]);
+         if (xd->delayedlocation)
+           repodata_set_location(data, repo->start + i, 0, pd.subdir, xd->fn);
+         if (xd->pkgjson && pd.nsigdata)
+           {
+             struct sigdata *sd = fn2sigdata(&pd, xd->fn, 0);
+             if (sd && sd->sigs)
+               {
+                 char *s = pool_tmpjoin(pool, "{\"info\":", xd->pkgjson, ",\"signatures\":");
+                 s = pool_tmpappend(pool, s, sd->sigs, "}");
+                 repodata_set_str(data, repo->start + i, SOLVABLE_SIGNATUREDATA, s);
+               }
+           }
+         solv_free(xd->fn);
+         solv_free(xd->pkgjson);
        }
-      solv_free(pd.fnlist);
+      solv_free(pd.xdata);
     }
 
+  if (pd.sigdata)
+    freesigdata(&pd);
+  stringpool_free(&pd.sigpool);
+
   queue_free(&pd.fndata);
   stringpool_free(&pd.fnpool);
   solv_free(pd.subdir);
index fa626f18b284b3f49f8ee693788ff859febabf49..72ae6425b0b5980737920fd52bf049631d1f1a7e 100644 (file)
@@ -5,6 +5,7 @@
  * for further information
  */
 
-#define CONDA_ADD_USE_ONLY_TAR_BZ2  (1 << 8)
+#define CONDA_ADD_USE_ONLY_TAR_BZ2     (1 << 8)
+#define CONDA_ADD_WITH_SIGNATUREDATA   (1 << 9)
 
 extern int repo_add_conda(Repo *repo, FILE *fp, int flags);
index 2bb27b260ca11f45dbd9c31f1b040f5b2b65cbc6..19f67aef273b714318330599612d1ad1919564a5 100644 (file)
@@ -272,6 +272,7 @@ KNOWNID(SOLVABLE_LANGONLY,          "solvable:langonly"),
 
 KNOWNID(UPDATE_COLLECTIONLIST,         "update:collectionlist"),       /* list of UPDATE_COLLECTION (actually packages) and UPDATE_MODULE */
 KNOWNID(SOLVABLE_MULTIARCH,            "solvable:multiarch"),          /* debian multi-arch field */
+KNOWNID(SOLVABLE_SIGNATUREDATA,                "solvable:signaturedata"),      /* conda */
 
 KNOWNID(ID_NUM_INTERNAL,               0)
 
index 2b8f3c4e8ead97a807cea11374624b3798081e9f..cf5d43b8ffbfd58ac90a8e90de9334de51247f0d 100644 (file)
@@ -32,7 +32,8 @@ usage(int status)
 {
   fprintf(stderr, "\nUsage:\n"
           "conda2solv\n"
-          "  reads a 'synthesis' repository from <stdin> and writes a .solv file to <stdout>\n"
+          "  reads a conda repository from <stdin> and writes a .solv file to <stdout>\n"
+          "  -S : include signature data\n"
           "  -h : print help & exit\n"
          );
    exit(status);
@@ -44,14 +45,18 @@ main(int argc, char **argv)
   Pool *pool;
   Repo *repo;
   int c;
+  int flags = 0;
 
-  while ((c = getopt(argc, argv, "h")) >= 0)
+  while ((c = getopt(argc, argv, "hS")) >= 0)
     {
       switch(c)
        {
        case 'h':
          usage(0);
          break;
+       case 'S':
+         flags |= CONDA_ADD_WITH_SIGNATUREDATA;
+         break;
        default:
          usage(1);
          break;
@@ -59,7 +64,7 @@ main(int argc, char **argv)
     }
   pool = pool_create();
   repo = repo_create(pool, "<stdin>");
-  if (repo_add_conda(repo, stdin, 0))
+  if (repo_add_conda(repo, stdin, flags))
     {
       fprintf(stderr, "conda2solv: %s\n", pool_errstr(pool));
       exit(1);