229 lines
5.5 KiB
C
229 lines
5.5 KiB
C
/*
|
|
* VAS_EBOOT -- GRand Unified Bootloader
|
|
* Copyright (C) 2017 Free Software Foundation, Inc.
|
|
*
|
|
* VAS_EBOOT is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* VAS_EBOOT is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with VAS_EBOOT. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Verifiers helper.
|
|
*/
|
|
|
|
#include <VasEBoot/file.h>
|
|
#include <VasEBoot/verify.h>
|
|
#include <VasEBoot/dl.h>
|
|
|
|
VAS_EBOOT_MOD_LICENSE ("GPLv3+");
|
|
|
|
struct VasEBoot_file_verifier *VasEBoot_file_verifiers;
|
|
|
|
struct VasEBoot_verified
|
|
{
|
|
VasEBoot_file_t file;
|
|
void *buf;
|
|
};
|
|
typedef struct VasEBoot_verified *VasEBoot_verified_t;
|
|
|
|
static void
|
|
verified_free (VasEBoot_verified_t verified)
|
|
{
|
|
if (verified)
|
|
{
|
|
VasEBoot_free (verified->buf);
|
|
VasEBoot_free (verified);
|
|
}
|
|
}
|
|
|
|
static VasEBoot_ssize_t
|
|
verified_read (struct VasEBoot_file *file, char *buf, VasEBoot_size_t len)
|
|
{
|
|
VasEBoot_verified_t verified = file->data;
|
|
|
|
VasEBoot_memcpy (buf, (char *) verified->buf + file->offset, len);
|
|
return len;
|
|
}
|
|
|
|
static VasEBoot_err_t
|
|
verified_close (struct VasEBoot_file *file)
|
|
{
|
|
VasEBoot_verified_t verified = file->data;
|
|
|
|
VasEBoot_file_close (verified->file);
|
|
verified_free (verified);
|
|
file->data = 0;
|
|
|
|
/* Device and name are freed by parent. */
|
|
file->device = 0;
|
|
file->name = 0;
|
|
|
|
return VasEBoot_errno;
|
|
}
|
|
|
|
struct VasEBoot_fs verified_fs =
|
|
{
|
|
.name = "verified_read",
|
|
.fs_read = verified_read,
|
|
.fs_close = verified_close
|
|
};
|
|
|
|
static VasEBoot_file_t
|
|
VasEBoot_verifiers_open (VasEBoot_file_t io, enum VasEBoot_file_type type)
|
|
{
|
|
VasEBoot_verified_t verified = NULL;
|
|
struct VasEBoot_file_verifier *ver;
|
|
void *context;
|
|
VasEBoot_file_t ret = 0;
|
|
VasEBoot_err_t err;
|
|
int defer = 0;
|
|
|
|
VasEBoot_dprintf ("verify", "file: %s type: %d\n", io->name, type);
|
|
|
|
if ((type & VAS_EBOOT_FILE_TYPE_MASK) == VAS_EBOOT_FILE_TYPE_SIGNATURE
|
|
|| (type & VAS_EBOOT_FILE_TYPE_MASK) == VAS_EBOOT_FILE_TYPE_VERIFY_SIGNATURE
|
|
|| (type & VAS_EBOOT_FILE_TYPE_SKIP_SIGNATURE))
|
|
return io;
|
|
|
|
if (io->device->disk &&
|
|
(io->device->disk->dev->id == VAS_EBOOT_DISK_DEVICE_MEMDISK_ID
|
|
|| io->device->disk->dev->id == VAS_EBOOT_DISK_DEVICE_PROCFS_ID))
|
|
return io;
|
|
|
|
FOR_LIST_ELEMENTS(ver, VasEBoot_file_verifiers)
|
|
{
|
|
enum VasEBoot_verify_flags flags = 0;
|
|
err = ver->init (io, type, &context, &flags);
|
|
if (err)
|
|
goto fail_noclose;
|
|
if (flags & VAS_EBOOT_VERIFY_FLAGS_DEFER_AUTH)
|
|
{
|
|
defer = 1;
|
|
continue;
|
|
}
|
|
if (!(flags & VAS_EBOOT_VERIFY_FLAGS_SKIP_VERIFICATION))
|
|
break;
|
|
}
|
|
|
|
if (!ver)
|
|
{
|
|
if (defer)
|
|
{
|
|
VasEBoot_error (VAS_EBOOT_ERR_ACCESS_DENIED,
|
|
N_("verification requested but nobody cares: %s"), io->name);
|
|
goto fail_noclose;
|
|
}
|
|
|
|
/* No verifiers wanted to verify. Just return underlying file. */
|
|
return io;
|
|
}
|
|
|
|
ret = VasEBoot_malloc (sizeof (*ret));
|
|
if (!ret)
|
|
{
|
|
goto fail;
|
|
}
|
|
*ret = *io;
|
|
|
|
ret->fs = &verified_fs;
|
|
ret->not_easily_seekable = 0;
|
|
if (ret->size >> (sizeof (VasEBoot_size_t) * VAS_EBOOT_CHAR_BIT - 1))
|
|
{
|
|
VasEBoot_error (VAS_EBOOT_ERR_NOT_IMPLEMENTED_YET,
|
|
N_("big file signature isn't implemented yet"));
|
|
goto fail;
|
|
}
|
|
verified = VasEBoot_malloc (sizeof (*verified));
|
|
if (!verified)
|
|
{
|
|
goto fail;
|
|
}
|
|
verified->buf = VasEBoot_malloc (ret->size);
|
|
if (!verified->buf)
|
|
{
|
|
goto fail;
|
|
}
|
|
if (VasEBoot_file_read (io, verified->buf, ret->size) != (VasEBoot_ssize_t) ret->size)
|
|
{
|
|
if (!VasEBoot_errno)
|
|
VasEBoot_error (VAS_EBOOT_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
|
|
io->name);
|
|
goto fail;
|
|
}
|
|
|
|
err = ver->write (context, verified->buf, ret->size);
|
|
if (err)
|
|
goto fail;
|
|
|
|
err = ver->fini ? ver->fini (context) : VAS_EBOOT_ERR_NONE;
|
|
if (err)
|
|
goto fail;
|
|
|
|
if (ver->close)
|
|
ver->close (context);
|
|
|
|
FOR_LIST_ELEMENTS_NEXT(ver, VasEBoot_file_verifiers)
|
|
{
|
|
enum VasEBoot_verify_flags flags = 0;
|
|
err = ver->init (io, type, &context, &flags);
|
|
if (err)
|
|
goto fail_noclose;
|
|
if (flags & VAS_EBOOT_VERIFY_FLAGS_SKIP_VERIFICATION ||
|
|
/* Verification done earlier. So, we are happy here. */
|
|
flags & VAS_EBOOT_VERIFY_FLAGS_DEFER_AUTH)
|
|
continue;
|
|
err = ver->write (context, verified->buf, ret->size);
|
|
if (err)
|
|
goto fail;
|
|
|
|
err = ver->fini ? ver->fini (context) : VAS_EBOOT_ERR_NONE;
|
|
if (err)
|
|
goto fail;
|
|
|
|
if (ver->close)
|
|
ver->close (context);
|
|
}
|
|
|
|
verified->file = io;
|
|
ret->data = verified;
|
|
return ret;
|
|
|
|
fail:
|
|
if (ver->close)
|
|
ver->close (context);
|
|
fail_noclose:
|
|
verified_free (verified);
|
|
VasEBoot_free (ret);
|
|
return NULL;
|
|
}
|
|
|
|
VasEBoot_err_t
|
|
VasEBoot_verify_string (char *str, enum VasEBoot_verify_string_type type)
|
|
{
|
|
struct VasEBoot_file_verifier *ver;
|
|
|
|
VasEBoot_dprintf ("verify", "string: %s, type: %d\n", str, type);
|
|
|
|
FOR_LIST_ELEMENTS(ver, VasEBoot_file_verifiers)
|
|
{
|
|
VasEBoot_err_t err;
|
|
err = ver->verify_string ? ver->verify_string (str, type) : VAS_EBOOT_ERR_NONE;
|
|
if (err)
|
|
return err;
|
|
}
|
|
return VAS_EBOOT_ERR_NONE;
|
|
}
|
|
|
|
void
|
|
VasEBoot_verifiers_init (void)
|
|
{
|
|
VasEBoot_file_filter_register (VAS_EBOOT_FILE_FILTER_VERIFY, VasEBoot_verifiers_open);
|
|
}
|