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 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 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