This MR improves Wine's DMI tables by: - no longer exposing /etc/machine-id content. Current documentation of systemd strongly suggests to use a hash of it (with app dedicated key). (1) - generates DMI entries for files that are no longer user readable on Linux (like bios serial number). The values are derived from hashed value in step #1.
(1) current implementation integrates into unix ntdll a bunch of sha 256 related code. It's basically a reimplementation of libsystemd's sd_id128_get_machine_app_specific(). We could alternatively dynamically link to libsystemd.so.
From: Eric Pouech epouech@codeweavers.com
As per systemd recommendations.
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/ntdll/Makefile.in | 1 + dlls/ntdll/unix/sha256.c | 227 +++++++++++++++++++++++++++++++++ dlls/ntdll/unix/system.c | 25 +++- dlls/ntdll/unix/unix_private.h | 13 ++ 4 files changed, 264 insertions(+), 2 deletions(-) create mode 100644 dlls/ntdll/unix/sha256.c
diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in index 2e862c68b7e..d9eae9e5d33 100644 --- a/dlls/ntdll/Makefile.in +++ b/dlls/ntdll/Makefile.in @@ -55,6 +55,7 @@ C_SRCS = \ unix/security.c \ unix/serial.c \ unix/server.c \ + unix/sha256.c \ unix/signal_arm.c \ unix/signal_arm64.c \ unix/signal_i386.c \ diff --git a/dlls/ntdll/unix/sha256.c b/dlls/ntdll/unix/sha256.c new file mode 100644 index 00000000000..221bc3240bd --- /dev/null +++ b/dlls/ntdll/unix/sha256.c @@ -0,0 +1,227 @@ +/* + * Copyright 2016 Michael Müller + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#if 0 +#pragma makedep unix +#endif + +#include <stdint.h> +#include "unix_private.h" + +/* Based on public domain implementation from + https://git.musl-libc.org/cgit/musl/tree/src/crypt/crypt_sha256.c */ + +static DWORD ror(DWORD n, int k) { return (n >> k) | (n << (32-k)); } +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) ((x & y) | (z & (x | y))) +#define S0(x) (ror(x,2) ^ ror(x,13) ^ ror(x,22)) +#define S1(x) (ror(x,6) ^ ror(x,11) ^ ror(x,25)) +#define R0(x) (ror(x,7) ^ ror(x,18) ^ (x>>3)) +#define R1(x) (ror(x,17) ^ ror(x,19) ^ (x>>10)) + +static const DWORD 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 +}; + +static void processblock(SHA256_CTX *ctx, const UCHAR *buffer) +{ + DWORD W[64], t1, t2, a, b, c, d, e, f, g, h; + int i; + + for (i = 0; i < 16; i++) + { + W[i] = (DWORD)buffer[4*i]<<24; + W[i] |= (DWORD)buffer[4*i+1]<<16; + W[i] |= (DWORD)buffer[4*i+2]<<8; + W[i] |= buffer[4*i+3]; + } + + for (; i < 64; i++) + W[i] = R1(W[i-2]) + W[i-7] + R0(W[i-15]) + W[i-16]; + + a = ctx->h[0]; + b = ctx->h[1]; + c = ctx->h[2]; + d = ctx->h[3]; + e = ctx->h[4]; + f = ctx->h[5]; + g = ctx->h[6]; + h = ctx->h[7]; + + for (i = 0; i < 64; i++) + { + t1 = h + S1(e) + Ch(e,f,g) + K[i] + W[i]; + t2 = S0(a) + Maj(a,b,c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + ctx->h[0] += a; + ctx->h[1] += b; + ctx->h[2] += c; + ctx->h[3] += d; + ctx->h[4] += e; + ctx->h[5] += f; + ctx->h[6] += g; + ctx->h[7] += h; +} + +static void pad(SHA256_CTX *ctx) +{ + ULONG64 r = ctx->len % 64; + + ctx->buf[r++] = 0x80; + + if (r > 56) + { + memset(ctx->buf + r, 0, 64 - r); + r = 0; + processblock(ctx, ctx->buf); + } + + memset(ctx->buf + r, 0, 56 - r); + ctx->len *= 8; + ctx->buf[56] = ctx->len >> 56; + ctx->buf[57] = ctx->len >> 48; + ctx->buf[58] = ctx->len >> 40; + ctx->buf[59] = ctx->len >> 32; + ctx->buf[60] = ctx->len >> 24; + ctx->buf[61] = ctx->len >> 16; + ctx->buf[62] = ctx->len >> 8; + ctx->buf[63] = ctx->len; + + processblock(ctx, ctx->buf); +} + +void sha256_init(SHA256_CTX *ctx) +{ + ctx->len = 0; + ctx->h[0] = 0x6a09e667; + ctx->h[1] = 0xbb67ae85; + ctx->h[2] = 0x3c6ef372; + ctx->h[3] = 0xa54ff53a; + ctx->h[4] = 0x510e527f; + ctx->h[5] = 0x9b05688c; + ctx->h[6] = 0x1f83d9ab; + ctx->h[7] = 0x5be0cd19; +} + +void sha256_update(SHA256_CTX *ctx, const UCHAR *buffer, ULONG len) +{ + const UCHAR *p = buffer; + ULONG64 r = ctx->len % 64; + + ctx->len += len; + if (r) + { + if (len < 64 - r) + { + memcpy(ctx->buf + r, p, len); + return; + } + memcpy(ctx->buf + r, p, 64 - r); + len -= 64 - r; + p += 64 - r; + processblock(ctx, ctx->buf); + } + for (; len >= 64; len -= 64, p += 64) + processblock(ctx, p); + memcpy(ctx->buf, p, len); +} + +void sha256_finalize(SHA256_CTX *ctx, UCHAR buffer[SHA256_DIGEST_SIZE]) +{ + int i; + + pad(ctx); + for (i = 0; i < 8; i++) + { + buffer[4*i] = ctx->h[i] >> 24; + buffer[4*i+1] = ctx->h[i] >> 16; + buffer[4*i+2] = ctx->h[i] >> 8; + buffer[4*i+3] = ctx->h[i]; + } +} + +/* derived from libsystemd, under LGPL 2.1 license + * https://github.com/systemd/systemd/blob/main/src/basic/hmac.c + */ +#define HMAC_BLOCK_SIZE 64 +#define INNER_PADDING_BYTE 0x36 +#define OUTER_PADDING_BYTE 0x5c + +void hmac_sha256(const void *key, size_t key_size, const void *input, size_t input_size, uint8_t res[SHA256_DIGEST_SIZE]) +{ + uint8_t inner_padding[HMAC_BLOCK_SIZE] = { }; + uint8_t outer_padding[HMAC_BLOCK_SIZE] = { }; + uint8_t replacement_key[SHA256_DIGEST_SIZE]; + SHA256_CTX hash; + + /* Implement algorithm as described by FIPS 198. */ + + /* The key needs to be block size length or less, hash it if it's longer. */ + if (key_size > HMAC_BLOCK_SIZE) + { + sha256_init(&hash); + sha256_update(&hash, input, input_size); + sha256_finalize(&hash, replacement_key); + + key = replacement_key; + key_size = SHA256_DIGEST_SIZE; + } + + /* First, copy the key into the padding arrays. If it's shorter than + * the block size, the arrays are already initialized to 0. */ + memcpy(inner_padding, key, key_size); + memcpy(outer_padding, key, key_size); + + /* Then, XOR the provided key and any padding leftovers with the fixed + * padding bytes as defined in FIPS 198. */ + for (size_t i = 0; i < HMAC_BLOCK_SIZE; i++) + { + inner_padding[i] ^= INNER_PADDING_BYTE; + outer_padding[i] ^= OUTER_PADDING_BYTE; + } + + /* First pass: hash the inner padding array and the input. */ + sha256_init(&hash); + sha256_update(&hash, inner_padding, HMAC_BLOCK_SIZE); + sha256_update(&hash, input, input_size); + sha256_finalize(&hash, res); + + /* Second pass: hash the outer padding array and the result of the first pass. */ + sha256_init(&hash); + sha256_update(&hash, outer_padding, HMAC_BLOCK_SIZE); + sha256_update(&hash, res, SHA256_DIGEST_SIZE); + sha256_finalize(&hash, res); +} diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index 4fbe45b2dd9..80bbcfd4c70 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -1579,7 +1579,7 @@ static size_t get_smbios_string( const char *path, char *str, size_t size ) return len; }
-static void get_system_uuid( GUID *uuid ) +static void read_machine_id( GUID *uuid ) { static const unsigned char hex[] = { @@ -1613,11 +1613,32 @@ static void get_system_uuid( GUID *uuid ) uuid->Data4[5] = hex[p[26]] << 4 | hex[p[27]]; uuid->Data4[6] = hex[p[28]] << 4 | hex[p[29]]; uuid->Data4[7] = hex[p[30]] << 4 | hex[p[31]]; + memset(buf, 0, sizeof(buf)); } close( fd ); } }
+static void derive_machineid_to_uuid( GUID *uuid ) +{ + static unsigned char wine_uuid[16] = { 0xe8, 0x65, 0x52, 0x8d, 0x0a, 0xa2, 0x45, 0x27, + 0xa8, 0x3b, 0x15, 0xed, 0x41, 0xb5, 0xd0, 0x91 }; + GUID machine_uuid; + unsigned char out256[32]; /* returning first 128 bits only */ + + read_machine_id( &machine_uuid ); + + hmac_sha256( wine_uuid, sizeof(wine_uuid), (const unsigned char *)&machine_uuid, sizeof(machine_uuid), out256 ); + + memset( &machine_uuid, 0, sizeof(machine_uuid) ); + + /* set Variant 1 Version 4 (cf. DMI specs & RFC 4421) */ + out256[6] = (out256[6] & 0x0F) | 0x40; + out256[8] = (out256[8] & 0x3F) | 0x80; + + memcpy( uuid, out256, sizeof(*uuid) ); +} + static NTSTATUS get_firmware_info( SYSTEM_FIRMWARE_TABLE_INFORMATION *sfti, ULONG available_len, ULONG *required_len ) { @@ -1652,7 +1673,7 @@ static NTSTATUS get_firmware_info( SYSTEM_FIRMWARE_TABLE_INFORMATION *sfti, ULON system_args.version = system_version; system_args.serial_len = get_smbios_string("/sys/class/dmi/id/product_serial", S(system_serial)); system_args.serial = system_serial; - get_system_uuid(&system_args.uuid); + derive_machineid_to_uuid(&system_args.uuid); system_args.sku_len = get_smbios_string("/sys/class/dmi/id/product_sku", S(system_sku)); system_args.sku = system_sku; system_args.family_len = get_smbios_string("/sys/class/dmi/id/product_family", S(system_family)); diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index d9d830a7462..785a8fca4c1 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -522,4 +522,17 @@ static inline NTSTATUS map_section( HANDLE mapping, void **ptr, SIZE_T *size, UL 0, NULL, size, ViewShare, 0, protect ); }
+typedef struct +{ + ULONG64 len; + DWORD h[8]; + UCHAR buf[64]; +} SHA256_CTX; + +#define SHA256_DIGEST_SIZE 32 +void sha256_init(SHA256_CTX *ctx); +void sha256_update(SHA256_CTX *ctx, const UCHAR *buffer, ULONG len); +void sha256_finalize(SHA256_CTX *ctx, UCHAR buffer[SHA256_DIGEST_SIZE]); +void hmac_sha256(const void *key, size_t key_size, const void *input, size_t input_size, UCHAR res[SHA256_DIGEST_SIZE]); + #endif /* __NTDLL_UNIX_PRIVATE_H */
From: Eric Pouech epouech@codeweavers.com
Project Kunai associates cloud account with a mix of various items, including board serial number, resulting in several machines being bound to the same cloud account.
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/ntdll/unix/sha256.c | 2 ++ dlls/ntdll/unix/system.c | 17 +++++++++++++++++ 2 files changed, 19 insertions(+)
diff --git a/dlls/ntdll/unix/sha256.c b/dlls/ntdll/unix/sha256.c index 221bc3240bd..27f5f822535 100644 --- a/dlls/ntdll/unix/sha256.c +++ b/dlls/ntdll/unix/sha256.c @@ -21,6 +21,8 @@ #pragma makedep unix #endif
+#include "config.h" + #include <stdint.h> #include "unix_private.h"
diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index 80bbcfd4c70..b21bc71cd77 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -1639,6 +1639,13 @@ static void derive_machineid_to_uuid( GUID *uuid ) memcpy( uuid, out256, sizeof(*uuid) ); }
+static void fixup_missing_information( size_t *len, char *buffer, size_t buflen, GUID *uuid, const char *divert ) +{ + const unsigned *pu = (const unsigned *)uuid; + *len = snprintf(buffer, buflen, "%s%08x%08x%08x%08x", divert, pu[0], pu[1], pu[2], pu[3]); + if (*len >= buflen) *len = buflen - 1; +} + static NTSTATUS get_firmware_info( SYSTEM_FIRMWARE_TABLE_INFORMATION *sfti, ULONG available_len, ULONG *required_len ) { @@ -1702,6 +1709,16 @@ static NTSTATUS get_firmware_info( SYSTEM_FIRMWARE_TABLE_INFORMATION *sfti, ULON chassis_args.asset_tag = chassis_asset_tag; #undef S
+ /* Some files above can only be readable by root. + * In that case, replace it by a simple derivation of machine GUID. + */ + if (!board_args.serial_len) + fixup_missing_information( &board_args.serial_len, board_serial, sizeof(board_serial), &system_args.uuid, "1" ); + if (!chassis_args.serial_len) + fixup_missing_information( &chassis_args.serial_len, chassis_serial, sizeof(chassis_serial), &system_args.uuid, "2" ); + if (!system_args.serial_len) + fixup_missing_information( &system_args.serial_len, system_serial, sizeof(system_serial), &system_args.uuid, "3" ); + return create_smbios_tables( sfti, available_len, required_len, &bios_args, &system_args, &board_args, &chassis_args ); }
Why do we need to follow systemd recommendations here? Wine is not a regular application, it's just passing on the ID to Windows applications. Also note that if we're going to do this all these linked accounts that rely on this ID will be invalidated.
from a Unix point of view, Wine is just an application, so I don't see no reason why Wine should behave differently than any other application.
systemd requests, for privacy reasons, that usage in application A shall not be bound via that identifier to usage in application B ; hence putting a Chinese wall between application A and B
for the very same reason that /sys/class/dmi/id/board_serial, chassis_serial, product_serial and product_uuid are not readable by user
exposing directly /etc/machine-id is hence a major privacy concern
Note: Windows expose these HW id:s without constraints whereas Linux puts some limitations.
IANAL, but these privacy concerns are embedded inside the Windows EULA, whereas there's nothing for Wine.
And also, this conforms to what is required in common privacy regulation (eg GDPR) where:
a) these (HW) identifiers are considered as 'personal data'
b) when possible, shouldn't be used directly but in a non reversible hashed form to create a unique link based on that id
This merge request was closed by eric pouech.
concerning the migration phase:
* do you have an idea of which applications make use of it? (asking in case you have some idea, but likely we cannot be sure of all of it) * a countermeasure could be to set a flag in the Wine prefix (dont_hash_machineid, at value 0 when not defined) to control the activation of the hashing ; every new prefix would be created with the flag set to 1 ; that would limit the impact, but not eradicate it (if user reinstalls his/her app in newly created prefix) ; that could eventually controlled in winecfg with more context on the pros&cons