mirror of https://github.com/vmware/tdnf.git
372 lines
8.8 KiB
C
372 lines
8.8 KiB
C
/*
|
|
* Copyright (C) 2020-2023 VMware, Inc. All Rights Reserved.
|
|
*
|
|
* Licensed under the GNU Lesser General Public License v2.1 (the "License");
|
|
* you may not use this file except in compliance with the License. The terms
|
|
* of the License are located in the COPYING file of this distribution.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include <gpgme.h>
|
|
|
|
#include "../../llconf/nodes.h"
|
|
|
|
static
|
|
uint32_t
|
|
_TDNFVerifyResult(
|
|
gpgme_ctx_t pContext
|
|
)
|
|
{
|
|
uint32_t dwError = 0;
|
|
gpgme_signature_t pSig = NULL;
|
|
gpgme_verify_result_t pResult = NULL;
|
|
|
|
/* pContext release will free pResult. do not free otherwise */
|
|
pResult = gpgme_op_verify_result(pContext);
|
|
if (!pResult || !pResult->signatures)
|
|
{
|
|
dwError = ERROR_TDNF_GPG_VERIFY_RESULT;
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
}
|
|
|
|
for (pSig = pResult->signatures; pSig; pSig = pSig->next)
|
|
{
|
|
if (pSig->status)
|
|
{
|
|
pr_err("repo md signature check: %s\n", gpgme_strerror (pSig->status));
|
|
dwError = ERROR_TDNF_GPG_SIGNATURE_CHECK;
|
|
break;
|
|
}
|
|
}
|
|
cleanup:
|
|
return dwError;
|
|
|
|
error:
|
|
goto cleanup;
|
|
}
|
|
|
|
uint32_t
|
|
TDNFRepoGPGCheckVerifyVersion(
|
|
)
|
|
{
|
|
uint32_t dwError = 0;
|
|
const char *pszVersion = NULL;
|
|
|
|
pszVersion = gpgme_check_version(NULL);
|
|
if (!pszVersion)
|
|
{
|
|
dwError = ERROR_TDNF_GPG_VERSION_FAILED;
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
}
|
|
|
|
error:
|
|
return dwError;
|
|
}
|
|
|
|
uint32_t
|
|
TDNFVerifyRepoMDSignature(
|
|
PTDNF_PLUGIN_HANDLE pHandle,
|
|
const char *pszRepoMD,
|
|
const char *pszRepoMDSig
|
|
)
|
|
{
|
|
uint32_t dwError = 0;
|
|
FILE *fpRepoMD = NULL;
|
|
FILE *fpRepoMDSig = NULL;
|
|
gpgme_error_t nGPGError = 0;
|
|
gpgme_ctx_t pContext = NULL;
|
|
gpgme_protocol_t protocol = GPGME_PROTOCOL_OpenPGP;
|
|
gpgme_data_t dataSig = NULL;
|
|
gpgme_data_t dataText = NULL;
|
|
|
|
if (!pHandle || IsNullOrEmptyString(pszRepoMD) ||
|
|
IsNullOrEmptyString(pszRepoMDSig))
|
|
{
|
|
dwError = ERROR_TDNF_INVALID_PARAMETER;
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
}
|
|
|
|
nGPGError = gpgme_new(&pContext);
|
|
if (nGPGError)
|
|
{
|
|
pHandle->nGPGError = nGPGError;
|
|
dwError = ERROR_TDNF_GPG_ERROR;
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
}
|
|
|
|
gpgme_set_protocol (pContext, protocol);
|
|
|
|
fpRepoMDSig = fopen(pszRepoMDSig, "rb");
|
|
if (!fpRepoMDSig)
|
|
{
|
|
pr_err("repogpgcheck: failed to open %s\n", pszRepoMDSig);
|
|
dwError = errno;
|
|
BAIL_ON_TDNF_SYSTEM_ERROR(dwError);
|
|
}
|
|
|
|
dwError = gpgme_data_new_from_stream (&dataSig, fpRepoMDSig);
|
|
if (dwError)
|
|
{
|
|
pHandle->nGPGError = nGPGError;
|
|
dwError = ERROR_TDNF_GPG_ERROR;
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
}
|
|
|
|
fpRepoMD = fopen(pszRepoMD, "rb");
|
|
if (!fpRepoMD)
|
|
{
|
|
pr_err("repogpgcheck: failed to open %s\n", pszRepoMD);
|
|
dwError = errno;
|
|
BAIL_ON_TDNF_SYSTEM_ERROR(dwError);
|
|
}
|
|
|
|
dwError = gpgme_data_new_from_stream(&dataText, fpRepoMD);
|
|
if (dwError)
|
|
{
|
|
pHandle->nGPGError = nGPGError;
|
|
dwError = ERROR_TDNF_GPG_ERROR;
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
}
|
|
|
|
dwError = gpgme_op_verify(pContext, dataSig, dataText, NULL);
|
|
if (dwError)
|
|
{
|
|
pHandle->nGPGError = nGPGError;
|
|
pr_err("gpg verify failed: %s\n", gpgme_strerror(dwError));
|
|
dwError = ERROR_TDNF_GPG_ERROR;
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
}
|
|
|
|
dwError = _TDNFVerifyResult(pContext);
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
|
|
cleanup:
|
|
if (dataText)
|
|
{
|
|
gpgme_data_release(dataText);
|
|
}
|
|
if (dataSig)
|
|
{
|
|
gpgme_data_release(dataSig);
|
|
}
|
|
if (fpRepoMD)
|
|
{
|
|
fclose(fpRepoMD);
|
|
}
|
|
if (fpRepoMDSig)
|
|
{
|
|
fclose(fpRepoMDSig);
|
|
}
|
|
if (pContext)
|
|
{
|
|
gpgme_release(pContext);
|
|
}
|
|
return dwError;
|
|
|
|
error:
|
|
goto cleanup;
|
|
}
|
|
|
|
uint32_t
|
|
TDNFVerifySignature(
|
|
PTDNF_PLUGIN_HANDLE pHandle,
|
|
const char *pcszRepoId,
|
|
const char *pcszRepoMDFile
|
|
)
|
|
{
|
|
uint32_t dwError = 0;
|
|
char *pszRepoMDSigFile = NULL;
|
|
char *pszRepoMDSigLocation = NULL;
|
|
PTDNF_REPO_DATA pRepo = NULL;
|
|
|
|
if (!pHandle || !pHandle->pTdnf || IsNullOrEmptyString(pcszRepoId) ||
|
|
IsNullOrEmptyString(pcszRepoMDFile))
|
|
{
|
|
dwError = ERROR_TDNF_INVALID_PARAMETER;
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
}
|
|
|
|
dwError = TDNFAllocateStringPrintf(&pszRepoMDSigLocation,
|
|
"%s%s",
|
|
TDNF_REPO_METADATA_FILE_PATH,
|
|
TDNF_REPO_METADATA_SIG_EXT);
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
|
|
dwError = TDNFAllocateStringPrintf(&pszRepoMDSigFile,
|
|
"%s%s",
|
|
pcszRepoMDFile,
|
|
TDNF_REPO_METADATA_SIG_EXT);
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
|
|
dwError = TDNFFindRepoById(pHandle->pTdnf, pcszRepoId, &pRepo);
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
|
|
dwError = TDNFDownloadFileFromRepo(pHandle->pTdnf,
|
|
pRepo,
|
|
pszRepoMDSigLocation,
|
|
pszRepoMDSigFile,
|
|
pcszRepoId);
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
|
|
dwError = TDNFVerifyRepoMDSignature(pHandle, pcszRepoMDFile, pszRepoMDSigFile);
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
|
|
cleanup:
|
|
if (pszRepoMDSigFile)
|
|
{
|
|
unlink(pszRepoMDSigFile);
|
|
}
|
|
TDNF_SAFE_FREE_MEMORY(pszRepoMDSigLocation);
|
|
TDNF_SAFE_FREE_MEMORY(pszRepoMDSigFile);
|
|
return dwError;
|
|
|
|
error:
|
|
pr_err("Error: %s %u\n", __FUNCTION__, dwError);
|
|
goto cleanup;
|
|
}
|
|
|
|
static
|
|
uint32_t
|
|
TDNFHasRepo(
|
|
PTDNF_PLUGIN_HANDLE pHandle,
|
|
const char *pcszRepoId,
|
|
int *pnHasRepo
|
|
)
|
|
{
|
|
uint32_t dwError = 0;
|
|
PTDNF_REPO_GPG_CHECK_DATA pData = NULL;
|
|
int nHasRepo = 0;
|
|
|
|
if (!pHandle || IsNullOrEmptyString(pcszRepoId) || !pnHasRepo)
|
|
{
|
|
dwError = ERROR_TDNF_INVALID_PARAMETER;
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
}
|
|
|
|
for(pData = pHandle->pData; pData; pData = pData->pNext)
|
|
{
|
|
if (strcmp(pData->pszRepoId, pcszRepoId) == 0)
|
|
{
|
|
nHasRepo = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
*pnHasRepo = nHasRepo;
|
|
|
|
error:
|
|
return dwError;
|
|
}
|
|
|
|
uint32_t
|
|
TDNFRepoMDCheckSignature(
|
|
PTDNF_PLUGIN_HANDLE pHandle,
|
|
PTDNF_EVENT_CONTEXT pContext
|
|
)
|
|
{
|
|
uint32_t dwError = 0;
|
|
const char *pcszRepoId = NULL;
|
|
const char *pcszRepoMDFile = NULL;
|
|
int nHasRepo = 0;
|
|
|
|
if (!pHandle || !pHandle->pTdnf || !pContext)
|
|
{
|
|
dwError = ERROR_TDNF_INVALID_PARAMETER;
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
}
|
|
|
|
/* we are looking for repo id first */
|
|
dwError = TDNFEventContextGetItemString(
|
|
pContext,
|
|
TDNF_EVENT_ITEM_REPO_ID,
|
|
(const char **)&pcszRepoId);
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
|
|
/* check if this repo id is in list for repo_gpgcheck */
|
|
dwError = TDNFHasRepo(pHandle, pcszRepoId, &nHasRepo);
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
|
|
/* if repo is not in list, return immediately */
|
|
if (nHasRepo == 0)
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
dwError = TDNFEventContextGetItemString(
|
|
pContext,
|
|
TDNF_EVENT_ITEM_REPO_MD_FILE,
|
|
(const char **)&pcszRepoMDFile);
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
/* download signature file and do detached signature check
|
|
for repomd file
|
|
*/
|
|
dwError = TDNFVerifySignature(pHandle,
|
|
pcszRepoId,
|
|
pcszRepoMDFile);
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
|
|
cleanup:
|
|
return dwError;
|
|
|
|
error:
|
|
goto cleanup;
|
|
}
|
|
|
|
uint32_t
|
|
TDNFRepoGPGCheckReadConfig(
|
|
PTDNF_PLUGIN_HANDLE pHandle,
|
|
PTDNF_EVENT_CONTEXT pContext
|
|
)
|
|
{
|
|
uint32_t dwError = 0;
|
|
int nEnabled = 0;
|
|
struct cnfnode *cn_section = NULL, *cn;
|
|
PTDNF_REPO_GPG_CHECK_DATA pData = NULL;
|
|
|
|
if (!pHandle || !pHandle->pTdnf || !pContext)
|
|
{
|
|
dwError = ERROR_TDNF_INVALID_PARAMETER;
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
}
|
|
|
|
/* event has a repo section which has all the config data */
|
|
dwError = TDNFEventContextGetItemPtr(
|
|
pContext,
|
|
TDNF_EVENT_ITEM_REPO_SECTION,
|
|
(const void **)&cn_section);
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
|
|
for(cn = cn_section->first_child; cn; cn = cn->next)
|
|
{
|
|
if ((cn->name[0] == '.') || (cn->value == NULL))
|
|
continue;
|
|
|
|
if (strcmp(cn->name, TDNF_REPO_CONFIG_REPO_GPGCHECK_KEY) == 0)
|
|
{
|
|
nEnabled = isTrue(cn->value);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* if repo_gpgcheck is enabled, keep this repo id
|
|
* section name is the repo id
|
|
*/
|
|
if (nEnabled)
|
|
{
|
|
dwError = TDNFAllocateMemory(sizeof(*pData), 1, (void **)&pData);
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
|
|
dwError = TDNFAllocateString(cn_section->name, &pData->pszRepoId);
|
|
BAIL_ON_TDNF_ERROR(dwError);
|
|
|
|
pData->pNext = pHandle->pData;
|
|
pHandle->pData = pData;
|
|
}
|
|
cleanup:
|
|
return dwError;
|
|
|
|
error:
|
|
TDNFFreeRepoGPGCheckData(pData);
|
|
goto cleanup;
|
|
}
|