staprun/modverify.c - systemtap
Functions defined
Source code
#include "../config.h"
#include "staprun.h"
#include <stdio.h>
#include <nspr.h>
#include <nss.h>
#include <pk11pub.h>
#include <cryptohi.h>
#include <cert.h>
#include <certt.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include "../nsscommon.h"
#include "modverify.h"
void
nsscommon_error (const char *msg, int logit __attribute ((unused)))
{
fprintf (stderr, "%s\n", msg);
fflush (stderr);
}
static int
check_db_file_permissions (const char *cert_db_file) {
struct stat info;
int rc;
rc = stat (cert_db_file, & info);
if (rc)
{
fprintf (stderr, "Could not obtain information on certificate database file %s.\n",
cert_db_file);
perror ("");
return 0;
}
rc = 1;
if (info.st_uid != 0)
{
fprintf (stderr, "Certificate database file %s must be owned by root.\n",
cert_db_file);
rc = 0;
}
if ((info.st_mode & S_IRUSR) == 0)
fprintf (stderr, "Certificate database file %s should be readable by the owner.\n", cert_db_file);
if ((info.st_mode & S_IWUSR) == 0)
fprintf (stderr, "Certificate database file %s should be writable by the owner.\n", cert_db_file);
if ((info.st_mode & S_IXUSR) != 0)
{
fprintf (stderr, "Certificate database file %s must not be executable by the owner.\n", cert_db_file);
rc = 0;
}
if ((info.st_mode & S_IRGRP) == 0)
{
fprintf (stderr, "Certificate database file %s should be readable by the group.\n", cert_db_file);
rc = 0;
}
if ((info.st_mode & S_IWGRP) != 0)
{
fprintf (stderr, "Certificate database file %s must not be writable by the group.\n", cert_db_file);
rc = 0;
}
if ((info.st_mode & S_IXGRP) != 0)
{
fprintf (stderr, "Certificate database file %s must not be executable by the group.\n", cert_db_file);
rc = 0;
}
if ((info.st_mode & S_IROTH) == 0)
{
fprintf (stderr, "Certificate database file %s should be readable by others.\n", cert_db_file);
rc = 0;
}
if ((info.st_mode & S_IWOTH) != 0)
{
fprintf (stderr, "Certificate database file %s must not be writable by others.\n", cert_db_file);
rc = 0;
}
if ((info.st_mode & S_IXOTH) != 0)
{
fprintf (stderr, "Certificate database file %s must not be executable by others.\n", cert_db_file);
rc = 0;
}
return rc;
}
static int
check_cert_db_permissions (const char *cert_db_path) {
struct stat info;
char *fileName;
int rc;
rc = stat (cert_db_path, & info);
if (rc)
{
if (errno == ENOENT)
return 0;
fprintf (stderr, "Could not obtain information on certificate database directory %s.\n",
cert_db_path);
perror ("");
return 0;
}
if (! S_ISDIR (info.st_mode))
{
fprintf (stderr, "Certificate database %s is not a directory.\n", cert_db_path);
return 0;
}
if (info.st_uid != 0)
{
fprintf (stderr, "Certificate database directory %s must be owned by root.\n", cert_db_path);
return 0;
}
rc = 1;
if ((info.st_mode & S_IRUSR) == 0)
fprintf (stderr, "Certificate database %s should be readable by the owner.\n", cert_db_path);
if ((info.st_mode & S_IWUSR) == 0)
fprintf (stderr, "Certificate database %s should be writable by the owner.\n", cert_db_path);
if ((info.st_mode & S_IXUSR) == 0)
fprintf (stderr, "Certificate database %s should be searchable by the owner.\n", cert_db_path);
if ((info.st_mode & S_IRGRP) == 0)
fprintf (stderr, "Certificate database %s should be readable by the group.\n", cert_db_path);
if ((info.st_mode & S_IWGRP) != 0)
{
fprintf (stderr, "Certificate database %s must not be writable by the group.\n", cert_db_path);
rc = 0;
}
if ((info.st_mode & S_IXGRP) == 0)
fprintf (stderr, "Certificate database %s should be searchable by the group.\n", cert_db_path);
if ((info.st_mode & S_IROTH) == 0)
fprintf (stderr, "Certificate database %s should be readable by others.\n", cert_db_path);
if ((info.st_mode & S_IWOTH) != 0)
{
fprintf (stderr, "Certificate database %s must not be writable by others.\n", cert_db_path);
rc = 0;
}
if ((info.st_mode & S_IXOTH) == 0)
fprintf (stderr, "Certificate database %s should be searchable by others.\n", cert_db_path);
fileName = PORT_Alloc (strlen (cert_db_path) + 11);
if (! fileName)
{
fprintf (stderr, "Unable to allocate memory for certificate database file names\n");
return 0;
}
sprintf (fileName, "%s/cert8.db", cert_db_path);
rc &= check_db_file_permissions (fileName);
sprintf (fileName, "%s/key3.db", cert_db_path);
rc &= check_db_file_permissions (fileName);
sprintf (fileName, "%s/secmod.db", cert_db_path);
rc &= check_db_file_permissions (fileName);
PORT_Free (fileName);
if (rc == 0)
fprintf (stderr, "Unable to use certificate database %s due to errors.\n", cert_db_path);
return rc;
}
static int
verify_it (const char *signatureName, const SECItem *signature,
const char *module_name, const void *module_data, off_t module_size,
const SECKEYPublicKey *pubKey)
{
VFYContext *vfy;
SECStatus secStatus;
int rc = MODULE_OK;
vfy = VFY_CreateContextDirect (pubKey, signature, SEC_OID_PKCS1_RSA_ENCRYPTION,
SEC_OID_UNKNOWN, NULL, NULL);
if (! vfy)
{
rc = MODULE_UNTRUSTED;
goto done;
}
secStatus = VFY_Begin(vfy);
if (secStatus != SECSuccess)
{
fprintf (stderr, "Unable to initialize verification context while verifying %s using the signature in %s.\n",
module_name, signatureName);
nssError ();
rc = MODULE_CHECK_ERROR;
goto done;
}
secStatus = VFY_Update (vfy, module_data, module_size);
if (secStatus != SECSuccess)
{
fprintf (stderr, "Error while verifying %s using the signature in %s.\n",
module_name, signatureName);
nssError ();
rc = MODULE_CHECK_ERROR;
goto done;
}
secStatus = VFY_End (vfy);
if (secStatus != SECSuccess) {
fprintf (stderr, "Unable to verify the signed module %s. It may have been altered since it was created.\n",
module_name);
nssError ();
rc = MODULE_ALTERED;
}
done:
if (vfy)
VFY_DestroyContext(vfy, PR_TRUE );
return rc;
}
int verify_module (const char *signatureName, const char* module_name,
const void *module_data, off_t module_size)
{
const char *dbdir = SYSCONFDIR "/systemtap/staprun";
SECKEYPublicKey *pubKey;
SECStatus secStatus;
CERTCertList *certList;
CERTCertListNode *certListNode;
CERTCertificate *cert;
PRStatus prStatus;
PRFileInfo info;
PRInt32 numBytes;
PRFileDesc *local_file_fd;
SECItem signature;
int rc = 0;
XXX PR_Init (PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
prStatus = PR_GetFileInfo (signatureName, &info);
if (prStatus != PR_SUCCESS || info.type != PR_FILE_FILE || info.size < 0)
{
if (verbose>1) fprintf (stderr, "Signature file %s not found\n", signatureName);
PR_Cleanup ();
return MODULE_UNTRUSTED; }
local_file_fd = PR_Open (signatureName, PR_RDONLY, 0);
if (local_file_fd == NULL)
{
fprintf (stderr, "Could not open the signature file %s\n.", signatureName);
nssError ();
PR_Cleanup ();
return MODULE_CHECK_ERROR;
}
signature.data = PORT_Alloc (info.size);
if (! signature.data)
{
fprintf (stderr, "Unable to allocate memory for the signature in %s.\n", signatureName);
nssError ();
PR_Cleanup ();
return MODULE_CHECK_ERROR;
}
numBytes = PR_Read_Complete (local_file_fd, signature.data, info.size);
if (numBytes == 0) {
fprintf (stderr, "EOF reading signature file %s.\n", signatureName);
PR_Cleanup ();
return MODULE_CHECK_ERROR;
}
if (numBytes < 0)
{
fprintf (stderr, "Error reading signature file %s.\n", signatureName);
nssError ();
PR_Cleanup ();
return MODULE_CHECK_ERROR;
}
if (numBytes != info.size)
{
fprintf (stderr, "Incomplete data while reading signature file %s.\n", signatureName);
PR_Cleanup ();
return MODULE_CHECK_ERROR;
}
signature.len = info.size;
PR_Close (local_file_fd);
if (! check_cert_db_permissions (dbdir))
{
if (verbose>1) fprintf (stderr, "Certificate db %s permissions too loose\n", dbdir);
PR_Cleanup ();
return MODULE_UNTRUSTED;
}
secStatus = nssInit (dbdir, 0, 1);
if (secStatus != SECSuccess)
{
return MODULE_CHECK_ERROR;
}
certList = PK11_ListCerts (PK11CertListAll, NULL);
if (certList == NULL)
{
fprintf (stderr, "Unable to find certificates in the certificate database in %s.\n",
dbdir);
nssError ();
nssCleanup (dbdir);
return MODULE_UNTRUSTED;
}
for (certListNode = CERT_LIST_HEAD (certList);
! CERT_LIST_END (certListNode, certList);
certListNode = CERT_LIST_NEXT (certListNode))
{
cert = certListNode->cert;
pubKey = CERT_ExtractPublicKey (cert);
if (pubKey == NULL)
{
fprintf (stderr, "Unable to extract public key from the certificate with nickname %s from the certificate database in %s.\n",
cert->nickname, dbdir);
nssError ();
rc = MODULE_CHECK_ERROR;
break;
}
rc = verify_it (signatureName, & signature,
module_name, module_data, module_size, pubKey);
if (rc == MODULE_OK || rc == MODULE_ALTERED || rc == MODULE_CHECK_ERROR)
break; }
CERT_DestroyCertList (certList);
nssCleanup (dbdir);
PR_Cleanup ();
return rc;
}