1 /*
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2025 The FreeBSD Foundation
5 *
6 * Portions of this software were developed by
7 * Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
8 * the FreeBSD Foundation
9 */
10
11 #include <libpkgconf/stdinc.h>
12 #include <libpkgconf/libpkgconf.h>
13 #include "util.h"
14 #include "core.h"
15 #include "software.h"
16 #include "serialize.h"
17 #include "simplelicensing.h"
18 #include "generate.h"
19
20 // NOTE: this function is passed to pkgconf_pkg_traverse
21 static void
generate_spdx_package(pkgconf_client_t * client,pkgconf_pkg_t * pkg,void * ptr)22 generate_spdx_package(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *ptr)
23 {
24 spdxtool_core_spdx_document_t *document = (spdxtool_core_spdx_document_t *)ptr;
25 pkgconf_node_t *node = NULL;
26 spdxtool_software_sbom_t *sbom = NULL;
27 char *package_spdx = NULL;
28 char *spdx_id_string = NULL;
29 char sep = spdxtool_util_get_uri_separator(client);
30
31 if (pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL)
32 return;
33
34 spdx_id_string = spdxtool_util_get_spdx_id_string(client, "software_Sbom", pkg->id);
35 if (!spdx_id_string)
36 goto err;
37
38 sbom = spdxtool_software_sbom_new(client, spdx_id_string, document->creation_info, "build");
39 free(spdx_id_string);
40 spdx_id_string = NULL;
41 if (!sbom)
42 goto err;
43
44 sbom->spdx_document = document;
45 sbom->rootElement = pkg;
46
47 package_spdx = spdxtool_util_get_spdx_id_string(client, "Package", pkg->id);
48 if (!package_spdx)
49 goto err;
50
51 pkgconf_tuple_add(client, &pkg->vars, "spdxId", package_spdx, false, 0);
52 free(package_spdx);
53 package_spdx = NULL;
54
55 pkgconf_tuple_add(client, &pkg->vars, "creationInfo", document->creation_info, false, 0);
56 pkgconf_tuple_add(client, &pkg->vars, "agent", document->agent, false, 0);
57
58 if (pkg->maintainer != NULL)
59 {
60 const char *supplier = spdxtool_core_spdx_document_add_maintainer(client, document, pkg->maintainer);
61 if (!supplier)
62 goto err;
63
64 pkgconf_tuple_add(client, &pkg->vars, "suppliedBy", supplier, false, 0);
65 }
66
67 if (pkg->license.head != NULL)
68 {
69 pkgconf_buffer_t spdx_id_buf = PKGCONF_BUFFER_INITIALIZER;
70
71 pkgconf_buffer_append_fmt(&spdx_id_buf, "%s%chasDeclaredLicense", pkg->id, sep);
72 char *spdx_id_name = pkgconf_buffer_freeze(&spdx_id_buf);
73 if (!spdx_id_name)
74 goto err;
75
76 package_spdx = spdxtool_util_get_spdx_id_string(client, "Relationship", spdx_id_name);
77 free(spdx_id_name);
78 if (!package_spdx)
79 goto err;
80
81 pkgconf_tuple_add(client, &pkg->vars, "hasDeclaredLicense", package_spdx, false, 0);
82 free(package_spdx);
83 package_spdx = NULL;
84
85 pkgconf_buffer_t concluded_buf = PKGCONF_BUFFER_INITIALIZER;
86 pkgconf_buffer_append_fmt(&concluded_buf, "%s%chasConcludedLicense", pkg->id, sep);
87 spdx_id_name = pkgconf_buffer_freeze(&concluded_buf);
88 if (!spdx_id_name)
89 goto err;
90
91 package_spdx = spdxtool_util_get_spdx_id_string(client, "Relationship", spdx_id_name);
92 free(spdx_id_name);
93 if (!package_spdx)
94 goto err;
95
96 pkgconf_tuple_add(client, &pkg->vars, "hasConcludedLicense", package_spdx, false, 0);
97 free(package_spdx);
98 package_spdx = NULL;
99
100 PKGCONF_FOREACH_LIST_ENTRY(pkg->license.head, node)
101 {
102 const pkgconf_license_t *license = node->data;
103 if (license->type == PKGCONF_LICENSE_EXPRESSION)
104 {
105 if (!spdxtool_core_spdx_document_add_license(client, document, license->data))
106 goto err;
107 }
108 }
109 }
110
111 node = calloc(1, sizeof(pkgconf_node_t));
112 if (!node)
113 goto err;
114
115 pkgconf_node_insert_tail(node, sbom, &document->rootElement);
116 return;
117
118 err:
119 pkgconf_error(client, "generate_spdx_package: failed for %s", pkg->id);
120 free(package_spdx);
121 free(spdx_id_string);
122 spdxtool_software_sbom_free(sbom);
123 }
124
125 bool
spdxtool_generate(pkgconf_client_t * client,pkgconf_pkg_t * world,FILE * out,int maxdepth,const char * creation_time,const char * creation_id,const char * agent_name)126 spdxtool_generate(pkgconf_client_t *client, pkgconf_pkg_t *world, FILE *out, int maxdepth,
127 const char *creation_time, const char *creation_id, const char *agent_name)
128 {
129 const char *agent_name_string = agent_name ? agent_name : "Default";
130 const char *creation_id_string = creation_id ? creation_id : "_:creationinfo_1";
131
132 spdxtool_core_agent_t *agent = spdxtool_core_agent_new(client, creation_id_string, agent_name_string);
133 if (!agent)
134 {
135 pkgconf_error(client, "Could not create agent struct");
136 return false;
137 }
138
139 spdxtool_core_creation_info_t *creation = spdxtool_core_creation_info_new(client, agent->spdx_id, creation_id_string, creation_time);
140 if (!creation)
141 {
142 pkgconf_error(client, "Could not create creation info struct");
143 spdxtool_core_agent_free(agent);
144 return false;
145 }
146
147 char *spdx_id_int = spdxtool_util_get_spdx_id_int(client, "spdxDocument");
148 spdxtool_core_spdx_document_t *document = spdxtool_core_spdx_document_new(client, spdx_id_int, creation_id_string, agent->spdx_id);
149 free(spdx_id_int);
150 if (!document)
151 {
152 pkgconf_error(client, "Could not create document");
153 spdxtool_core_creation_info_free(creation);
154 spdxtool_core_agent_free(agent);
155 return false;
156 }
157
158 int eflag = pkgconf_pkg_traverse(client, world, generate_spdx_package, document, maxdepth, 0);
159 if (eflag != PKGCONF_PKG_ERRF_OK)
160 {
161 spdxtool_core_spdx_document_free(document);
162 spdxtool_core_creation_info_free(creation);
163 spdxtool_core_agent_free(agent);
164 return false;
165 }
166
167 spdxtool_serialize_value_t *root = spdxtool_serialize_sbom(client, agent, creation, document);
168 if (!root)
169 {
170 spdxtool_core_spdx_document_free(document);
171 spdxtool_core_creation_info_free(creation);
172 spdxtool_core_agent_free(agent);
173 return false;
174 }
175
176 pkgconf_buffer_t buffer = PKGCONF_BUFFER_INITIALIZER;
177 spdxtool_serialize_value_to_buf(&buffer, root, 0);
178 spdxtool_serialize_value_free(root);
179
180 bool ret = pkgconf_output_file_fmt(out, "%s\n", pkgconf_buffer_str(&buffer));
181 pkgconf_buffer_finalize(&buffer);
182
183 spdxtool_core_spdx_document_free(document);
184 spdxtool_core_creation_info_free(creation);
185 spdxtool_core_agent_free(agent);
186
187 if (!ret)
188 pkgconf_error(client, "spdxtool: Could not output to file: %s", strerror(errno));
189 return ret;
190 }
191