/ libpkg / pkg_cpe.c
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  }