pkg_cpe.c
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 * Common Platform Enumeration (CPE) is a standardized method of 11 * describing and identifying classes of applications, operating 12 * systems, and hardware devices present among an enterprise's 13 * computing assets 14 * 15 * CPE (current version 2.3) looks something like this: 16 * cpe:2.3:a:test:test_product:1.0:sp1:1:en-us:14.3:FreeBSD:x86_64:other_things 17 * 18 * Where parts are named like this: 19 * cpe:<cpe_version>:<part>:<vendor>:<product>:<version>:<update>:<edition>:<language>:<sw_edition>:<target_sw>:<target_hw>:<other> 20 * 21 * Whole spec can be found: 22 * https://csrc.nist.gov/pubs/ir/7695/final 23 */ 24 25 #include <xmalloc.h> 26 #include <ctype.h> 27 #include <stdlib.h> 28 #include <xstring.h> 29 30 #include "pkg.h" 31 #include "pkg/audit.h" 32 #include "private/pkg_cpe.h" 33 34 static struct pkg_audit_cpe * 35 pkg_cpe_new(void) 36 { 37 return (struct pkg_audit_cpe *)xcalloc(1, sizeof(struct pkg_audit_cpe)); 38 } 39 40 void 41 pkg_cpe_free(struct pkg_audit_cpe *cpe) 42 { 43 if(!cpe) 44 { 45 return; 46 } 47 48 if(cpe->vendor) 49 { 50 free(cpe->vendor); 51 cpe->vendor = NULL; 52 } 53 54 if(cpe->product) 55 { 56 free(cpe->product); 57 cpe->product = NULL; 58 } 59 60 if(cpe->version) 61 { 62 free(cpe->version); 63 cpe->version = NULL; 64 } 65 66 if(cpe->update) 67 { 68 free(cpe->update); 69 cpe->update = NULL; 70 } 71 72 if(cpe->edition) 73 { 74 free(cpe->edition); 75 cpe->edition = NULL; 76 } 77 78 if(cpe->language) 79 { 80 free(cpe->language); 81 cpe->language = NULL; 82 } 83 84 if(cpe->sw_edition) 85 { 86 free(cpe->sw_edition); 87 cpe->sw_edition = NULL; 88 } 89 90 if(cpe->target_sw) 91 { 92 free(cpe->target_sw); 93 cpe->target_sw = NULL; 94 } 95 96 if(cpe->target_hw) 97 { 98 free(cpe->target_hw); 99 cpe->target_hw = NULL; 100 } 101 102 if(cpe->other) 103 { 104 free(cpe->other); 105 cpe->other = NULL; 106 } 107 108 free(cpe); 109 } 110 111 char * 112 pkg_cpe_create(struct pkg_audit_cpe *cpe) 113 { 114 char *cpe_str = NULL; 115 /* To avoid (null) string in return string*/ 116 char *cpe_parts[10] = {"", "", "", "", "", "", "", "", "", ""}; 117 118 if(cpe->vendor) 119 { 120 cpe_parts[0] = cpe->vendor; 121 } 122 123 if(cpe->product) 124 { 125 cpe_parts[1] = cpe->product; 126 } 127 128 if(cpe->version) 129 { 130 cpe_parts[2] = cpe->version; 131 } 132 133 if(cpe->update) 134 { 135 cpe_parts[3] = cpe->update; 136 } 137 138 if(cpe->edition) 139 { 140 cpe_parts[4] = cpe->edition; 141 } 142 143 if(cpe->language) 144 { 145 cpe_parts[5] = cpe->language; 146 } 147 148 if(cpe->sw_edition) 149 { 150 cpe_parts[6] = cpe->sw_edition; 151 } 152 153 if(cpe->target_sw) 154 { 155 cpe_parts[7] = cpe->target_sw; 156 } 157 158 if(cpe->target_hw) 159 { 160 cpe_parts[8] = cpe->target_hw; 161 } 162 163 if(cpe->other) 164 { 165 cpe_parts[9] = cpe->other; 166 } 167 168 xasprintf(&cpe_str, "cpe:2.3:%c:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s", cpe->part, cpe_parts[0], cpe_parts[1], cpe_parts[2], cpe_parts[3], cpe_parts[4], cpe_parts[5], cpe_parts[6], cpe_parts[7], cpe_parts[8], cpe_parts[9]); 169 170 return cpe_str; 171 } 172 173 struct pkg_audit_cpe * 174 pkg_cpe_parse(const char *cpe_str) 175 { 176 char cpe_delimiter[] = ":"; 177 char cpe_atoi[2] = {0x00, 0x00}; 178 char *cpe_copy = NULL; 179 char *cpe_token = NULL; 180 struct pkg_audit_cpe *rtn_cpe = NULL; 181 unsigned int loc = 0; 182 bool is_error = false; 183 184 if(!cpe_str) 185 { 186 return NULL; 187 } 188 189 if(strnlen(cpe_str, 8) < 8) 190 { 191 return NULL; 192 } 193 194 cpe_copy = xstrdup(cpe_str); 195 cpe_token = strtok(cpe_copy, cpe_delimiter); 196 197 if(!cpe_token) 198 { 199 rtn_cpe = NULL; 200 goto cpe_return; 201 } 202 203 rtn_cpe = pkg_cpe_new(); 204 205 while(cpe_token) 206 { 207 is_error = false; 208 switch(loc) 209 { 210 case 0: 211 if(strncmp(cpe_token,"cpe", 3)) 212 { 213 is_error = true; 214 } 215 break; 216 217 case 1: 218 if(!isdigit(cpe_token[0]) || !isdigit(cpe_token[2])) 219 { 220 is_error = true; 221 break; 222 } 223 224 225 cpe_atoi[0] = cpe_token[0]; 226 rtn_cpe->version_major = atoi(cpe_atoi); 227 228 cpe_atoi[0] = cpe_token[2]; 229 rtn_cpe->version_minor = atoi(cpe_atoi); 230 231 if(rtn_cpe->version_major != 2 || rtn_cpe->version_minor != 3) 232 { 233 is_error = true; 234 } 235 break; 236 237 case 2: 238 if(cpe_token[0] != CPE_APPLICATIONS && cpe_token[0] != CPE_HARWARE && cpe_token[0] != CPE_OPERATING_SYSTEMS) 239 { 240 is_error = true; 241 } 242 243 rtn_cpe->part = cpe_token[0]; 244 break; 245 246 case 3: 247 rtn_cpe->vendor = xstrdup(cpe_token); 248 break; 249 250 case 4: 251 rtn_cpe->product = xstrdup(cpe_token); 252 break; 253 254 case 5: 255 rtn_cpe->version = xstrdup(cpe_token); 256 break; 257 258 case 6: 259 rtn_cpe->update = xstrdup(cpe_token); 260 break; 261 262 case 7: 263 rtn_cpe->edition = xstrdup(cpe_token); 264 break; 265 266 case 8: 267 rtn_cpe->language = xstrdup(cpe_token); 268 break; 269 270 case 9: 271 rtn_cpe->sw_edition = xstrdup(cpe_token); 272 break; 273 274 case 10: 275 rtn_cpe->target_sw = xstrdup(cpe_token); 276 break; 277 278 case 11: 279 rtn_cpe->target_hw = xstrdup(cpe_token); 280 break; 281 282 case 12: 283 rtn_cpe->other = xstrdup(cpe_token); 284 break; 285 286 default: 287 break; 288 } 289 290 if(is_error) 291 { 292 pkg_cpe_free(rtn_cpe); 293 rtn_cpe = NULL; 294 goto cpe_return; 295 } 296 297 298 loc ++; 299 cpe_token = strtok(NULL, cpe_delimiter); 300 301 if(loc >= 13 && cpe_token) 302 { 303 break; 304 } 305 } 306 307 cpe_return: 308 if(loc <= 3) 309 { 310 pkg_cpe_free(rtn_cpe); 311 rtn_cpe = NULL; 312 } 313 314 free(cpe_copy); 315 cpe_copy = NULL; 316 return rtn_cpe; 317 }