241 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			241 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | |
| From: Peter Jones <pjones@redhat.com>
 | |
| Date: Mon, 17 Aug 2020 14:18:31 -0400
 | |
| Subject: [PATCH] initial mok-variables code
 | |
| 
 | |
| This patch adds support for getting mok variables from
 | |
| /sys/firmware/fi/mok-variables/$NAME , if they are present, as well as
 | |
| for checking MokListRT, MokListRT1, MokListRT2, etc., for any of the mok
 | |
| variables.
 | |
| 
 | |
| Resolves: rhbz#1868820
 | |
| 
 | |
| Signed-off-by: Peter Jones <pjones@redhat.com>
 | |
| ---
 | |
|  src/mokutil.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++--------
 | |
|  1 file changed, 151 insertions(+), 24 deletions(-)
 | |
| 
 | |
| diff --git a/src/mokutil.c b/src/mokutil.c
 | |
| index ac15c73..838599c 100644
 | |
| --- a/src/mokutil.c
 | |
| +++ b/src/mokutil.c
 | |
| @@ -229,6 +229,63 @@ signature_size (const efi_guid_t *hash_type)
 | |
|  	return 0;
 | |
|  }
 | |
|  
 | |
| +static int
 | |
| +mok_get_variable(const char *name, uint8_t **datap, size_t *data_sizep)
 | |
| +{
 | |
| +	char filename[] = "/sys/firmware/efi/mok-variables/implausibly-long-mok-variable-name";
 | |
| +	size_t filename_sz = sizeof(filename);
 | |
| +	int fd, rc;
 | |
| +	struct stat sb = { 0, };
 | |
| +	uint8_t *buf;
 | |
| +	size_t bufsz, pos = 0;
 | |
| +	ssize_t ssz;
 | |
| +
 | |
| +	*datap = 0;
 | |
| +	*data_sizep = 0;
 | |
| +
 | |
| +	snprintf(filename, filename_sz, "/sys/firmware/efi/mok-variables/%s", name);
 | |
| +
 | |
| +	fd = open(filename, O_RDONLY);
 | |
| +	if (fd < 0)
 | |
| +		return fd;
 | |
| +
 | |
| +	rc = fstat(fd, &sb);
 | |
| +	if (rc < 0) {
 | |
| +err_close:
 | |
| +		close(fd);
 | |
| +		return rc;
 | |
| +	}
 | |
| +
 | |
| +	if (sb.st_size == 0) {
 | |
| +		errno = ENOENT;
 | |
| +		rc = -1;
 | |
| +		goto err_close;
 | |
| +	}
 | |
| +
 | |
| +	bufsz = sb.st_size;
 | |
| +	buf = calloc(1, bufsz);
 | |
| +	if (!buf)
 | |
| +		goto err_close;
 | |
| +
 | |
| +	while (pos < bufsz) {
 | |
| +		ssz = read(fd, &buf[pos], bufsz - pos);
 | |
| +		if (ssz < 0) {
 | |
| +			if (errno == EAGAIN ||
 | |
| +			    errno == EWOULDBLOCK ||
 | |
| +			    errno == EINTR)
 | |
| +				continue;
 | |
| +			free(buf);
 | |
| +			goto err_close;
 | |
| +		}
 | |
| +
 | |
| +		pos += ssz;
 | |
| +	}
 | |
| +	*datap = buf;
 | |
| +	*data_sizep = pos;
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
|  static MokListNode*
 | |
|  build_mok_list (void *data, unsigned long data_size, uint32_t *mok_num)
 | |
|  {
 | |
| @@ -596,25 +653,44 @@ static int
 | |
|  list_keys_in_var (const char *var_name, const efi_guid_t guid)
 | |
|  {
 | |
|  	uint8_t *data = NULL;
 | |
| -	size_t data_size;
 | |
| +	char varname[] = "implausibly-long-mok-variable-name";
 | |
| +	size_t data_sz, i, varname_sz = sizeof(varname);
 | |
|  	uint32_t attributes;
 | |
|  	int ret;
 | |
|  
 | |
| -	ret = efi_get_variable (guid, var_name, &data, &data_size, &attributes);
 | |
| -	if (ret < 0) {
 | |
| -		if (errno == ENOENT) {
 | |
| -			printf ("%s is empty\n", var_name);
 | |
| -			return 0;
 | |
| +	ret = mok_get_variable(var_name, &data, &data_sz);
 | |
| +	if (ret >= 0) {
 | |
| +		ret = list_keys (data, data_sz);
 | |
| +		free(data);
 | |
| +		return ret;
 | |
| +	}
 | |
| +
 | |
| +	for (i = 0; i < SIZE_MAX; i++) {
 | |
| +		if (i == 0) {
 | |
| +			snprintf(varname, varname_sz, "%s", var_name);
 | |
| +		} else {
 | |
| +			snprintf(varname, varname_sz, "%s%zu", var_name, i);
 | |
|  		}
 | |
|  
 | |
| -		fprintf (stderr, "Failed to read %s: %m\n", var_name);
 | |
| -		return -1;
 | |
| +		ret = efi_get_variable (guid, varname, &data, &data_sz,
 | |
| +					&attributes);
 | |
| +		if (ret < 0)
 | |
| +			return 0;
 | |
| +
 | |
| +		ret = list_keys (data, data_sz);
 | |
| +		free(data);
 | |
| +		/*
 | |
| +		 * If ret is < 0, the next one will error as well.
 | |
| +		 * If ret is 0, we need to test the next variable.
 | |
| +		 * If it's 1, that's a real answer.
 | |
| +		 */
 | |
| +		if (ret < 0)
 | |
| +			return 0;
 | |
| +		if (ret > 0)
 | |
| +			return ret;
 | |
|  	}
 | |
|  
 | |
| -	ret = list_keys (data, data_size);
 | |
| -	free (data);
 | |
| -
 | |
| -	return ret;
 | |
| +	return 0;
 | |
|  }
 | |
|  
 | |
|  static int
 | |
| @@ -1013,22 +1089,15 @@ is_valid_cert (void *cert, uint32_t cert_size)
 | |
|  }
 | |
|  
 | |
|  static int
 | |
| -is_duplicate (const efi_guid_t *type, const void *data, const uint32_t data_size,
 | |
| -	      const efi_guid_t *vendor, const char *db_name)
 | |
| +is_one_duplicate (const efi_guid_t *type,
 | |
| +		  const void *data, const uint32_t data_size,
 | |
| +		  uint8_t *var_data, size_t var_data_size)
 | |
|  {
 | |
| -	uint8_t *var_data;
 | |
| -	size_t var_data_size;
 | |
| -	uint32_t attributes;
 | |
|  	uint32_t node_num;
 | |
|  	MokListNode *list;
 | |
|  	int ret = 0;
 | |
|  
 | |
| -	if (!data || data_size == 0 || !db_name)
 | |
| -		return 0;
 | |
| -
 | |
| -	ret = efi_get_variable (*vendor, db_name, &var_data, &var_data_size,
 | |
| -				&attributes);
 | |
| -	if (ret < 0)
 | |
| +	if (!data || data_size == 0)
 | |
|  		return 0;
 | |
|  
 | |
|  	list = build_mok_list (var_data, var_data_size, &node_num);
 | |
| @@ -1060,11 +1129,69 @@ is_duplicate (const efi_guid_t *type, const void *data, const uint32_t data_size
 | |
|  done:
 | |
|  	if (list)
 | |
|  		free (list);
 | |
| -	free (var_data);
 | |
|  
 | |
|  	return ret;
 | |
|  }
 | |
|  
 | |
| +static int
 | |
| +is_duplicate (const efi_guid_t *type,
 | |
| +	      const void *data, const uint32_t data_size,
 | |
| +	      const efi_guid_t *vendor, const char *db_name)
 | |
| +{
 | |
| +	uint32_t attributes;
 | |
| +	char varname[] = "implausibly-long-mok-variable-name";
 | |
| +	size_t varname_sz = sizeof(varname);
 | |
| +	int ret = 0;
 | |
| +	size_t i;
 | |
| +
 | |
| +	if (!strncmp(db_name, "Mok", 3)) {
 | |
| +		uint8_t *var_data = NULL;
 | |
| +		size_t var_data_size = 0;
 | |
| +		ret = mok_get_variable(db_name, &var_data, &var_data_size);
 | |
| +		if (ret >= 0) {
 | |
| +			ret = is_one_duplicate(type, data, data_size,
 | |
| +					       var_data, var_data_size);
 | |
| +			if (ret >= 0) {
 | |
| +				free (var_data);
 | |
| +				return ret;
 | |
| +			}
 | |
| +			var_data = NULL;
 | |
| +			var_data_size = 0;
 | |
| +		}
 | |
| +	}
 | |
| +
 | |
| +	for (i = 0; i < SIZE_MAX; i++) {
 | |
| +		uint8_t *var_data = NULL;
 | |
| +		size_t var_data_size = 0;
 | |
| +		if (i == 0) {
 | |
| +			snprintf(varname, varname_sz, "%s", db_name);
 | |
| +		} else {
 | |
| +			snprintf(varname, varname_sz, "%s%zu", db_name, i);
 | |
| +		}
 | |
| +
 | |
| +		ret = efi_get_variable (*vendor, varname,
 | |
| +					&var_data, &var_data_size,
 | |
| +					&attributes);
 | |
| +		if (ret < 0)
 | |
| +			return 0;
 | |
| +
 | |
| +		ret = is_one_duplicate(type, data, data_size,
 | |
| +				       var_data, var_data_size);
 | |
| +		free (var_data);
 | |
| +		/*
 | |
| +		 * If ret is < 0, the next one will error as well.
 | |
| +		 * If ret is 0, we need to test the next variable.
 | |
| +		 * If it's 1, that's a real answer.
 | |
| +		 */
 | |
| +		if (ret < 0)
 | |
| +			return 0;
 | |
| +		if (ret > 0)
 | |
| +			return ret;
 | |
| +	}
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
|  static int
 | |
|  is_valid_request (const efi_guid_t *type, void *mok, uint32_t mok_size,
 | |
|  		  MokRequest req)
 |