From patchwork Tue Mar 31 08:34:34 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: itamar.tal4@gmail.com X-Patchwork-Id: 456533 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 7352814008F for ; Tue, 31 Mar 2015 19:35:37 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="verification failed; unprotected key" header.d=gmail.com header.i=@gmail.com header.b=jpsTB2Gv; dkim-adsp=none (unprotected policy); dkim-atps=neutral Received: from localhost ([::1]:37519 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Ycre3-0001Vk-CX for incoming@patchwork.ozlabs.org; Tue, 31 Mar 2015 04:35:35 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:54289) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YcrdQ-0000aX-4B for qemu-devel@nongnu.org; Tue, 31 Mar 2015 04:34:58 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YcrdN-0004Et-6U for qemu-devel@nongnu.org; Tue, 31 Mar 2015 04:34:56 -0400 Received: from mail-wi0-x234.google.com ([2a00:1450:400c:c05::234]:38068) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YcrdM-0004En-R1 for qemu-devel@nongnu.org; Tue, 31 Mar 2015 04:34:53 -0400 Received: by wibgn9 with SMTP id gn9so16422500wib.1 for ; Tue, 31 Mar 2015 01:34:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references :signed-off-by; bh=9X2MJZ5O0SEb0DCITbFMsMMjYzF32sNIGIXeE/ffeow=; b=jpsTB2GvJfTE6O2hpWWVPSK1o2yWCbLf2en51C+dKEIXTEY/aY+kytAW443OAduCx0 7nU4TKSe751yGmN0JAYvyoocYWh4g/CbV4iVRTUOxwBIZblXMQYhvxeVGe+Omo19aU+m m1Vdb7GuxBVbn4bMbozLUR/1tKSDgEPweYYRD07l3B2JuBojyA/p2GgQaqfw4TBdn4hS BAYJsMfqtOX7BQOMUjUMA/IMKL04PzIJVLOviG4zMNAosT2fMy+TgEH9iz7YcWupi/WY 8NJMILmNzEpTRLUKnfMZOSubUxVZ4lQD1n8ECyrgIaiVBXaj37RaNyTZxyRdj/1HiT48 szog== X-Received: by 10.194.222.197 with SMTP id qo5mr70690285wjc.142.1427790891988; Tue, 31 Mar 2015 01:34:51 -0700 (PDT) Received: from hpvm.guardicore.com (bzq-233-168-31-124.red.bezeqint.net. [31.168.233.124]) by mx.google.com with ESMTPSA id fy2sm19857741wic.15.2015.03.31.01.34.50 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 31 Mar 2015 01:34:51 -0700 (PDT) From: itamar.tal4@gmail.com X-Google-Original-From: itamar@guardicore.com To: qemu-devel@nongnu.org Date: Tue, 31 Mar 2015 11:34:34 +0300 Message-Id: <1427790877-14544-2-git-send-email-itamar@guardicore.com> X-Mailer: git-send-email 2.3.4 In-Reply-To: <1427790877-14544-1-git-send-email-itamar@guardicore.com> References: <1427790877-14544-1-git-send-email-itamar@guardicore.com> Signed-off-by: Itamar Tal X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2a00:1450:400c:c05::234 Cc: ori@guardicore.com, ariel@guardicore.com, mdroth@linux.vnet.ibm.com, pavel@guardicore.com, Itamar Tal Subject: [Qemu-devel] [PATCH 1/4] added qga hash file command (win/linux) X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Itamar Tal Added support for retrieving file hash from the guest machine. This is command is useful for ensuring that no changes were made for a given file between consecutive read operations and reduce read/write operations. --- qga/Makefile.objs | 2 +- qga/commands.c | 43 +++++++++++++ qga/guest-agent-core.h | 6 ++ qga/qapi-schema.json | 13 ++++ qga/sha256.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++++ qga/sha256.h | 40 ++++++++++++ 6 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 qga/sha256.c create mode 100644 qga/sha256.h diff --git a/qga/Makefile.objs b/qga/Makefile.objs index 1c5986c..a68d78d 100644 --- a/qga/Makefile.objs +++ b/qga/Makefile.objs @@ -1,4 +1,4 @@ -qga-obj-y = commands.o guest-agent-command-state.o main.o +qga-obj-y = commands.o guest-agent-command-state.o main.o sha256.o qga-obj-$(CONFIG_POSIX) += commands-posix.o channel-posix.o qga-obj-$(CONFIG_WIN32) += commands-win32.o channel-win32.o service-win32.o qga-obj-$(CONFIG_WIN32) += vss-win32.o diff --git a/qga/commands.c b/qga/commands.c index 7834967..f9378f4 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -11,6 +11,7 @@ */ #include +#include "qga/sha256.h" #include "qga/guest-agent-core.h" #include "qga-qmp-commands.h" #include "qapi/qmp/qerror.h" @@ -70,3 +71,45 @@ struct GuestAgentInfo *qmp_guest_info(Error **errp) qmp_for_each_command(qmp_command_info, info); return info; } + +char *qmp_guest_file_hash(const char *path, Error **errp) +{ + const char *hex = "0123456789abcdef"; + BYTE sha256_block[SHA256_BLOCK_SIZE]; + char sha256_hexdigest[SHA256_BLOCK_SIZE * 2 + 1]; + BYTE buffer[1024]; + size_t len = 0; + SHA256_CTX ctx; + FILE *file = NULL; + int i = 0; + + sha256_init(&ctx); + + file = fopen(path, "rb"); + if (NULL == file) { + error_setg(errp, "Error opening file '%s'", path); + return NULL; + } + + for (;;) { + len = fread(buffer, 1, 1024, file); + if (0 == len) { + break; + } + + sha256_update(&ctx, buffer, len); + } + + fclose(file); + + sha256_final(&ctx, sha256_block); + + /* transform the digest to hex string */ + for (i = 0; i < SHA256_BLOCK_SIZE; ++i) { + sha256_hexdigest[i * 2] = hex[(sha256_block[i] >> 4) & 0xF]; + sha256_hexdigest[i * 2 + 1] = hex[sha256_block[i] & 0xF]; + } + sha256_hexdigest[SHA256_BLOCK_SIZE * 2] = '\0'; + + return g_strdup((char *)sha256_hexdigest); +} diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h index e92c6ab..b496c47 100644 --- a/qga/guest-agent-core.h +++ b/qga/guest-agent-core.h @@ -10,6 +10,10 @@ * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. */ + +#ifndef _GUEST_AGENT_CORE_H_ +#define _GUEST_AGENT_CORE_H_ + #include "qapi/qmp/dispatch.h" #include "qemu-common.h" @@ -41,3 +45,5 @@ int64_t ga_get_fd_handle(GAState *s, Error **errp); #ifndef _WIN32 void reopen_fd_to_null(int fd); #endif + +#endif /* _GUEST_AGENT_CORE_H_ */ diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 95f49e3..68b420d 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -891,3 +891,16 @@ ## { 'command': 'guest-get-memory-block-info', 'returns': 'GuestMemoryBlockInfo' } + +## +# @guest-file-hash: +# +# Get the a SHA256 hash for a file in the guest's operating system +# +# Returns: file SHA256 hash string +# +# Since 2.4 +## +{ 'command': 'guest-file-hash', + 'data': { 'path': 'str' }, + 'returns': 'str' } diff --git a/qga/sha256.c b/qga/sha256.c new file mode 100644 index 0000000..40abb42 --- /dev/null +++ b/qga/sha256.c @@ -0,0 +1,171 @@ +/********************************************************************* +* Filename: sha256.c +* Author: Brad Conte (brad AT bradconte.com) +* Copyright: +* Disclaimer: This code is presented "as is" without any guarantees. +* Details: Implementation of the SHA-256 hashing algorithm. + SHA-256 is one of the three algorithms in the SHA2 + specification. The others, SHA-384 and SHA-512, are not + offered in this implementation. + Algorithm specification can be found here: + * http://csrc.nist.gov/publications/fips/fips180-2/ + fips180-2withchangenotice.pdf + This implementation uses little endian byte order. +*********************************************************************/ + +/*************************** HEADER FILES ***************************/ + +#include +#include "sha256.h" + +/****************************** MACROS ******************************/ +#define ROTLEFT(a, b) (((a) << (b)) | ((a) >> (32-(b)))) +#define ROTRIGHT(a, b) (((a) >> (b)) | ((a) << (32-(b)))) + +#define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z))) +#define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define EP0(x) (ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22)) +#define EP1(x) (ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25)) +#define SIG0(x) (ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ ((x) >> 3)) +#define SIG1(x) (ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ ((x) >> 10)) + +/**************************** VARIABLES *****************************/ +static const uint32_t k[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +/*********************** FUNCTION DEFINITIONS ***********************/ + +void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) +{ + uint32_t a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; + + for (i = 0, j = 0; i < 16; ++i, j += 4) { + m[i] = (data[j] << 24) | + (data[j + 1] << 16) | + (data[j + 2] << 8) | + (data[j + 3]); + } + for ( ; i < 64; ++i) { + m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; + } + + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + e = ctx->state[4]; + f = ctx->state[5]; + g = ctx->state[6]; + h = ctx->state[7]; + + for (i = 0; i < 64; ++i) { + t1 = h + EP1(e) + CH(e, f, g) + k[i] + m[i]; + t2 = EP0(a) + MAJ(a, b, c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; + ctx->state[4] += e; + ctx->state[5] += f; + ctx->state[6] += g; + ctx->state[7] += h; +} + +void sha256_init(SHA256_CTX *ctx) +{ + ctx->datalen = 0; + ctx->bitlen = 0; + ctx->state[0] = 0x6a09e667; + ctx->state[1] = 0xbb67ae85; + ctx->state[2] = 0x3c6ef372; + ctx->state[3] = 0xa54ff53a; + ctx->state[4] = 0x510e527f; + ctx->state[5] = 0x9b05688c; + ctx->state[6] = 0x1f83d9ab; + ctx->state[7] = 0x5be0cd19; +} + +void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) +{ + uint32_t i; + + for (i = 0; i < len; ++i) { + ctx->data[ctx->datalen] = data[i]; + ctx->datalen++; + if (ctx->datalen == 64) { + sha256_transform(ctx, ctx->data); + ctx->bitlen += 512; + ctx->datalen = 0; + } + } +} + +void sha256_final(SHA256_CTX *ctx, BYTE hash[]) +{ + uint32_t i; + + i = ctx->datalen; + + /* Pad whatever data is left in the buffer */ + if (ctx->datalen < 56) { + ctx->data[i++] = 0x80; + while (i < 56) { + ctx->data[i++] = 0x00; + } + } else { + ctx->data[i++] = 0x80; + while (i < 64) { + ctx->data[i++] = 0x00; + } + sha256_transform(ctx, ctx->data); + memset(ctx->data, 0, 56); + } + + /* Append to the padding the total message's length in bits and + transform */ + ctx->bitlen += ctx->datalen * 8; + ctx->data[63] = ctx->bitlen; + ctx->data[62] = ctx->bitlen >> 8; + ctx->data[61] = ctx->bitlen >> 16; + ctx->data[60] = ctx->bitlen >> 24; + ctx->data[59] = ctx->bitlen >> 32; + ctx->data[58] = ctx->bitlen >> 40; + ctx->data[57] = ctx->bitlen >> 48; + ctx->data[56] = ctx->bitlen >> 56; + sha256_transform(ctx, ctx->data); + + /* Since this implementation uses little endian byte ordering and SHA uses + big endian, reverse all the bytes when copying the final state to the + output hash. */ + for (i = 0; i < 4; ++i) { + hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; + hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; + hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; + hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; + hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; + hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; + hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; + hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; + } +} diff --git a/qga/sha256.h b/qga/sha256.h new file mode 100644 index 0000000..e62f99b --- /dev/null +++ b/qga/sha256.h @@ -0,0 +1,40 @@ +/********************************************************************* +* Filename: sha256.h +* Author: Brad Conte (brad AT bradconte.com) +* Copyright: +* Disclaimer: This code is presented "as is" without any guarantees. +* Details: Defines the API for the corresponding SHA1 implementation. +*********************************************************************/ + +#ifndef _SHA256_H_INCLUDE +#define _SHA256_H_INCLUDE + +/*************************** HEADER FILES ***************************/ + +#include +#include +#include +#include "qga/guest-agent-core.h" + +/****************************** MACROS ******************************/ +#define SHA256_BLOCK_SIZE 32 /* SHA256 outputs a 32 byte digest */ + +/**************************** DATA TYPES ****************************/ + +typedef unsigned char BYTE; /* 8-bit byte */ + +typedef struct { + BYTE data[64]; + unsigned int datalen; + unsigned long long bitlen; + unsigned int state[8]; +} SHA256_CTX; + +/*********************** FUNCTION DECLARATIONS **********************/ + +void sha256_init(SHA256_CTX *ctx); +void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len); +void sha256_final(SHA256_CTX *ctx, BYTE hash[]); +void sha256_transform(SHA256_CTX *ctx, const BYTE data[]); + +#endif /* _SHA256_H_INCLUDE */