Add a basic SHA-1 implementation to lib/, and make lib/bpf_legacy.c use it to calculate the SHA-1 digest of BPF objects instead of the previous AF_ALG-based code. This eliminates the dependency on the kernel config options CONFIG_CRYPTO_USER_API_HASH and CONFIG_CRYPTO_SHA1. Signed-off-by: Eric Biggers --- include/bpf_util.h | 5 -- include/sha1.h | 18 ++++++ include/uapi/linux/if_alg.h | 61 -------------------- lib/Makefile | 2 +- lib/bpf_legacy.c | 109 +++++++++--------------------------- lib/sha1.c | 108 +++++++++++++++++++++++++++++++++++ 6 files changed, 154 insertions(+), 149 deletions(-) create mode 100644 include/sha1.h delete mode 100644 include/uapi/linux/if_alg.h create mode 100644 lib/sha1.c diff --git a/include/bpf_util.h b/include/bpf_util.h index 8951a5e8..e1b8d327 100644 --- a/include/bpf_util.h +++ b/include/bpf_util.h @@ -12,11 +12,10 @@ #include #include #include #include #include -#include #include "utils.h" #include "bpf_scm.h" #define BPF_ENV_UDS "TC_BPF_UDS" @@ -38,14 +37,10 @@ # define TRACEFS_MAGIC 0x74726163 #endif #define TRACE_DIR_MNT "/sys/kernel/tracing" -#ifndef AF_ALG -# define AF_ALG 38 -#endif - #ifndef EM_BPF # define EM_BPF 247 #endif struct bpf_cfg_ops { diff --git a/include/sha1.h b/include/sha1.h new file mode 100644 index 00000000..4a2ed513 --- /dev/null +++ b/include/sha1.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * SHA-1 message digest algorithm + * + * Copyright 2025 Google LLC + */ +#ifndef __SHA1_H__ +#define __SHA1_H__ + +#include +#include + +#define SHA1_DIGEST_SIZE 20 +#define SHA1_BLOCK_SIZE 64 + +void sha1(const __u8 *data, size_t len, __u8 out[SHA1_DIGEST_SIZE]); + +#endif /* __SHA1_H__ */ diff --git a/include/uapi/linux/if_alg.h b/include/uapi/linux/if_alg.h deleted file mode 100644 index 0824fbc0..00000000 --- a/include/uapi/linux/if_alg.h +++ /dev/null @@ -1,61 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ -/* - * if_alg: User-space algorithm interface - * - * Copyright (c) 2010 Herbert Xu - * - * This program 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 2 of the License, or (at your option) - * any later version. - * - */ - -#ifndef _LINUX_IF_ALG_H -#define _LINUX_IF_ALG_H - -#include - -struct sockaddr_alg { - __u16 salg_family; - __u8 salg_type[14]; - __u32 salg_feat; - __u32 salg_mask; - __u8 salg_name[64]; -}; - -/* - * Linux v4.12 and later removed the 64-byte limit on salg_name[]; it's now an - * arbitrary-length field. We had to keep the original struct above for source - * compatibility with existing userspace programs, though. Use the new struct - * below if support for very long algorithm names is needed. To do this, - * allocate 'sizeof(struct sockaddr_alg_new) + strlen(algname) + 1' bytes, and - * copy algname (including the null terminator) into salg_name. - */ -struct sockaddr_alg_new { - __u16 salg_family; - __u8 salg_type[14]; - __u32 salg_feat; - __u32 salg_mask; - __u8 salg_name[]; -}; - -struct af_alg_iv { - __u32 ivlen; - __u8 iv[]; -}; - -/* Socket options */ -#define ALG_SET_KEY 1 -#define ALG_SET_IV 2 -#define ALG_SET_OP 3 -#define ALG_SET_AEAD_ASSOCLEN 4 -#define ALG_SET_AEAD_AUTHSIZE 5 -#define ALG_SET_DRBG_ENTROPY 6 -#define ALG_SET_KEY_BY_KEY_SERIAL 7 - -/* Operations */ -#define ALG_OP_DECRYPT 0 -#define ALG_OP_ENCRYPT 1 - -#endif /* _LINUX_IF_ALG_H */ diff --git a/lib/Makefile b/lib/Makefile index 0ba62942..ee1e2e87 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -4,11 +4,11 @@ include ../config.mk CFLAGS += -fPIC UTILOBJ = utils.o utils_math.o rt_names.o ll_map.o ll_types.o ll_proto.o ll_addr.o \ inet_proto.o namespace.o json_writer.o json_print.o json_print_math.o \ names.o color.o bpf_legacy.o bpf_glue.o exec.o fs.o cg_map.o \ - ppp_proto.o bridge.o + ppp_proto.o bridge.o sha1.o ifeq ($(HAVE_ELF),y) ifeq ($(HAVE_LIBBPF),y) UTILOBJ += bpf_libbpf.o endif diff --git a/lib/bpf_legacy.c b/lib/bpf_legacy.c index c8da4a3e..c4b1d5de 100644 --- a/lib/bpf_legacy.c +++ b/lib/bpf_legacy.c @@ -27,18 +27,19 @@ #include #include #include #include +#include #include -#include #include #include #include "utils.h" #include "json_print.h" +#include "sha1.h" #include "bpf_util.h" #include "bpf_elf.h" #include "bpf_scm.h" @@ -1178,11 +1179,10 @@ struct bpf_elf_ctx { int sec_btf; char license[ELF_MAX_LICENSE_LEN]; enum bpf_prog_type type; __u32 ifindex; bool verbose; - bool noafalg; struct bpf_elf_st stat; struct bpf_hash_entry *ht[256]; char *log; size_t log_size; }; @@ -1306,76 +1306,32 @@ static int bpf_obj_pin(int fd, const char *pathname) attr.bpf_fd = fd; return bpf(BPF_OBJ_PIN, &attr, sizeof(attr)); } -static int bpf_obj_hash(const char *object, uint8_t *out, size_t len) +static int bpf_obj_hash(int fd, const char *object, __u8 out[SHA1_DIGEST_SIZE]) { - struct sockaddr_alg alg = { - .salg_family = AF_ALG, - .salg_type = "hash", - .salg_name = "sha1", - }; - int ret, cfd, ofd, ffd; struct stat stbuff; - ssize_t size; - - if (!object || len != 20) - return -EINVAL; - - cfd = socket(AF_ALG, SOCK_SEQPACKET, 0); - if (cfd < 0) - return cfd; + void *data; - ret = bind(cfd, (struct sockaddr *)&alg, sizeof(alg)); - if (ret < 0) - goto out_cfd; - - ofd = accept(cfd, NULL, 0); - if (ofd < 0) { - ret = ofd; - goto out_cfd; + if (fstat(fd, &stbuff) < 0) { + fprintf(stderr, "Error doing fstat: %s\n", strerror(errno)); + return -1; } - - ffd = open(object, O_RDONLY); - if (ffd < 0) { - fprintf(stderr, "Error opening object %s: %s\n", - object, strerror(errno)); - ret = ffd; - goto out_ofd; + if ((size_t)stbuff.st_size != stbuff.st_size) { + fprintf(stderr, "Object %s is too big\n", object); + return -EFBIG; } - - ret = fstat(ffd, &stbuff); - if (ret < 0) { - fprintf(stderr, "Error doing fstat: %s\n", + data = mmap(NULL, stbuff.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) { + fprintf(stderr, "Error mapping object %s: %s\n", object, strerror(errno)); - goto out_ffd; - } - - size = sendfile(ofd, ffd, NULL, stbuff.st_size); - if (size != stbuff.st_size) { - fprintf(stderr, "Error from sendfile (%zd vs %zu bytes): %s\n", - size, stbuff.st_size, strerror(errno)); - ret = -1; - goto out_ffd; + return -1; } - - size = read(ofd, out, len); - if (size != len) { - fprintf(stderr, "Error from read (%zd vs %zu bytes): %s\n", - size, len, strerror(errno)); - ret = -1; - } else { - ret = 0; - } -out_ffd: - close(ffd); -out_ofd: - close(ofd); -out_cfd: - close(cfd); - return ret; + sha1(data, stbuff.st_size, out); + munmap(data, stbuff.st_size); + return 0; } static void bpf_init_env(void) { struct rlimit limit = { @@ -1812,16 +1768,10 @@ static int bpf_maps_attach_all(struct bpf_elf_ctx *ctx) { int i, j, ret, fd, inner_fd, inner_idx, have_map_in_map = 0; const char *map_name; for (i = 0; i < ctx->map_num; i++) { - if (ctx->maps[i].pinning == PIN_OBJECT_NS && - ctx->noafalg) { - fprintf(stderr, "Missing kernel AF_ALG support for PIN_OBJECT_NS!\n"); - return -ENOTSUP; - } - map_name = bpf_map_fetch_name(ctx, i); if (!map_name) return -EIO; fd = bpf_map_attach(map_name, ctx, &ctx->maps[i], @@ -2867,35 +2817,36 @@ static void bpf_get_cfg(struct bpf_elf_ctx *ctx) static int bpf_elf_ctx_init(struct bpf_elf_ctx *ctx, const char *pathname, enum bpf_prog_type type, __u32 ifindex, bool verbose) { - uint8_t tmp[20]; + __u8 tmp[SHA1_DIGEST_SIZE]; int ret; if (elf_version(EV_CURRENT) == EV_NONE) return -EINVAL; bpf_init_env(); memset(ctx, 0, sizeof(*ctx)); bpf_get_cfg(ctx); - ret = bpf_obj_hash(pathname, tmp, sizeof(tmp)); - if (ret) - ctx->noafalg = true; - else - hexstring_n2a(tmp, sizeof(tmp), ctx->obj_uid, - sizeof(ctx->obj_uid)); - ctx->verbose = verbose; ctx->type = type; ctx->ifindex = ifindex; ctx->obj_fd = open(pathname, O_RDONLY); - if (ctx->obj_fd < 0) + if (ctx->obj_fd < 0) { + fprintf(stderr, "Error opening object %s: %s\n", pathname, + strerror(errno)); return ctx->obj_fd; + } + + ret = bpf_obj_hash(ctx->obj_fd, pathname, tmp); + if (ret) + return ret; + hexstring_n2a(tmp, sizeof(tmp), ctx->obj_uid, sizeof(ctx->obj_uid)); ctx->elf_fd = elf_begin(ctx->obj_fd, ELF_C_READ, NULL); if (!ctx->elf_fd) { ret = -EINVAL; goto out_fd; @@ -3257,16 +3208,10 @@ bool iproute2_is_pin_map(const char *libbpf_map_name, char *pathname) const char *map_name, *tmp; unsigned int pinning; int i, ret = 0; for (i = 0; i < ctx->map_num; i++) { - if (ctx->maps[i].pinning == PIN_OBJECT_NS && - ctx->noafalg) { - fprintf(stderr, "Missing kernel AF_ALG support for PIN_OBJECT_NS!\n"); - return false; - } - map_name = bpf_map_fetch_name(ctx, i); if (!map_name) { return false; } diff --git a/lib/sha1.c b/lib/sha1.c new file mode 100644 index 00000000..1aa8fd83 --- /dev/null +++ b/lib/sha1.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * SHA-1 message digest algorithm + * + * Copyright 2025 Google LLC + */ + +#include +#include + +#include "sha1.h" +#include "utils.h" + +static const __u32 sha1_K[4] = { 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, + 0xCA62C1D6 }; + +static inline __u32 rol32(__u32 v, int bits) +{ + return (v << bits) | (v >> (32 - bits)); +} + +#define round_up(a, b) (((a) + (b) - 1) & ~((b) - 1)) + +#define SHA1_ROUND(i, a, b, c, d, e) \ + do { \ + if ((i) >= 16) \ + w[i] = rol32(w[(i) - 16] ^ w[(i) - 14] ^ w[(i) - 8] ^ \ + w[(i) - 3], \ + 1); \ + e += w[i] + rol32(a, 5) + sha1_K[(i) / 20]; \ + if ((i) < 20) \ + e += (b & (c ^ d)) ^ d; \ + else if ((i) < 40 || (i) >= 60) \ + e += b ^ c ^ d; \ + else \ + e += (c & d) ^ (b & (c ^ d)); \ + b = rol32(b, 30); \ + /* The new (a, b, c, d, e) is the old (e, a, b, c, d). */ \ + } while (0) + +#define SHA1_5ROUNDS(i) \ + do { \ + SHA1_ROUND((i) + 0, a, b, c, d, e); \ + SHA1_ROUND((i) + 1, e, a, b, c, d); \ + SHA1_ROUND((i) + 2, d, e, a, b, c); \ + SHA1_ROUND((i) + 3, c, d, e, a, b); \ + SHA1_ROUND((i) + 4, b, c, d, e, a); \ + } while (0) + +#define SHA1_20ROUNDS(i) \ + do { \ + SHA1_5ROUNDS((i) + 0); \ + SHA1_5ROUNDS((i) + 5); \ + SHA1_5ROUNDS((i) + 10); \ + SHA1_5ROUNDS((i) + 15); \ + } while (0) + +static void sha1_blocks(__u32 h[5], const __u8 *data, size_t nblocks) +{ + while (nblocks--) { + __u32 a = h[0]; + __u32 b = h[1]; + __u32 c = h[2]; + __u32 d = h[3]; + __u32 e = h[4]; + __u32 w[80]; + int i; + + memcpy(w, data, SHA1_BLOCK_SIZE); + for (i = 0; i < 16; i++) + w[i] = ntohl(w[i]); + SHA1_20ROUNDS(0); + SHA1_20ROUNDS(20); + SHA1_20ROUNDS(40); + SHA1_20ROUNDS(60); + + h[0] += a; + h[1] += b; + h[2] += c; + h[3] += d; + h[4] += e; + data += SHA1_BLOCK_SIZE; + } +} + +/* Calculate the SHA-1 message digest of the given data. */ +void sha1(const __u8 *data, size_t len, __u8 out[SHA1_DIGEST_SIZE]) +{ + __u32 h[5] = { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, + 0xC3D2E1F0 }; + const __be64 bitcount = htonll((__u64)len * 8); + __u8 final_data[2 * SHA1_BLOCK_SIZE] = { 0 }; + size_t final_len = len % SHA1_BLOCK_SIZE; + int i; + + sha1_blocks(h, data, len / SHA1_BLOCK_SIZE); + + memcpy(final_data, data + len - final_len, final_len); + final_data[final_len] = 0x80; + final_len = round_up(final_len + 9, SHA1_BLOCK_SIZE); + memcpy(&final_data[final_len - 8], &bitcount, 8); + + sha1_blocks(h, final_data, final_len / SHA1_BLOCK_SIZE); + + for (i = 0; i < ARRAY_SIZE(h); i++) + h[i] = htonl(h[i]); + memcpy(out, h, SHA1_DIGEST_SIZE); +} base-commit: afceddf61037440628a5612f15a6eaefd28d9fd3 -- 2.51.0