From: Eugene Syromyatnikov Date: Fri, 1 Sep 2017 14:14:25 +0000 (+0200) Subject: keyctl: add support for KDF parameters decoding in KEYCTL_DH_COMPUTE X-Git-Tag: v4.19~12 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=46a4dcf4c3c287c48da91a391f2e82e96e44cc5c;p=strace keyctl: add support for KDF parameters decoding in KEYCTL_DH_COMPUTE * fetch_struct_keyctl_kdf_params.c: New file. * keyctl_kdf_params.h: Likewise. * Makefile.am (strace_SOURCES): Add them. * configure.ac: Add check for struct keyctl_kdf_params presence in . * defs.h (struct strace_keyctl_kdf_params): Add forward declaration. * keyctl.c (keyctl_dh_compute): Add new parameter kdf_addr, print it on exiting. (SYS_FUNC(keyctl)) : Pass arg5 to keyctl_dh_compute. * tests/keyctl.c: Include assert.h. (struct keyctl_kdf_params) [!HAVE_STRUCT_KEYCTL_KDF_PARAMS]: New definition. (STR32): New definition, copied from ioctl_dm.c. (append_str, kckdfp_to_str): New functions. (main): Update expected output, add checks for struct keyctl_kdf_params decoding. --- diff --git a/Makefile.am b/Makefile.am index b3afc7c5..4aa9846c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -126,6 +126,7 @@ strace_SOURCES = \ fcntl.c \ fetch_bpf_fprog.c \ fetch_struct_flock.c \ + fetch_struct_keyctl_kdf_params.c \ fetch_struct_mmsghdr.c \ fetch_struct_msghdr.c \ fetch_struct_stat.c \ @@ -162,6 +163,7 @@ strace_SOURCES = \ kernel_types.h \ kexec.c \ keyctl.c \ + keyctl_kdf_params.h \ ldt.c \ link.c \ linux/asm_stat.h \ diff --git a/configure.ac b/configure.ac index 81f7dad7..5cb76ce6 100644 --- a/configure.ac +++ b/configure.ac @@ -333,6 +333,10 @@ AC_CHECK_TYPES(m4_normalize([ struct crypto_report_rng ]),,, [#include ]) +AC_CHECK_TYPES(m4_normalize([ + struct keyctl_kdf_params +]),,, [#include ]) + AC_CHECK_MEMBERS([struct timex.tai],,, [#include ]) AC_CHECK_MEMBERS([struct utsname.domainname],,, [#include ]) diff --git a/defs.h b/defs.h index 5323920c..34261f2e 100644 --- a/defs.h +++ b/defs.h @@ -645,6 +645,7 @@ struct strace_stat; extern void print_struct_stat(struct tcb *, const struct strace_stat *const st); struct strace_statfs; +struct strace_keyctl_kdf_params; extern void print_struct_statfs(struct tcb *, kernel_ulong_t addr); diff --git a/fetch_struct_keyctl_kdf_params.c b/fetch_struct_keyctl_kdf_params.c new file mode 100644 index 00000000..50fc9d11 --- /dev/null +++ b/fetch_struct_keyctl_kdf_params.c @@ -0,0 +1,34 @@ +#include "defs.h" + +#include DEF_MPERS_TYPE(struct_keyctl_kdf_params) + +#include "keyctl_kdf_params.h" +typedef struct keyctl_kdf_params struct_keyctl_kdf_params; + +#include MPERS_DEFS + +MPERS_PRINTER_DECL(int, fetch_keyctl_kdf_params, struct tcb *const tcp, + kernel_ulong_t addr, struct strace_keyctl_kdf_params *p) +{ + struct_keyctl_kdf_params kdf; + int ret; + + if ((ret = umove(tcp, addr, &kdf))) + return ret; + + p->hashname = (kernel_ulong_t) +#ifndef IN_MPERS + (uintptr_t) +#endif + kdf.hashname; + p->otherinfo = (kernel_ulong_t) +#ifndef IN_MPERS + (uintptr_t) +#endif + kdf.otherinfo; + p->otherinfolen = kdf.otherinfolen; + + memcpy(p->__spare, kdf.__spare, sizeof(kdf.__spare)); + + return 0; +} diff --git a/keyctl.c b/keyctl.c index a11bd3d3..9ee08034 100644 --- a/keyctl.c +++ b/keyctl.c @@ -28,6 +28,9 @@ #include "defs.h" +#include "keyctl_kdf_params.h" +#include "print_fields.h" + typedef int32_t key_serial_t; #include "xlat/key_spec.h" @@ -245,12 +248,14 @@ print_dh_params(struct tcb *tcp, kernel_ulong_t addr) static void keyctl_dh_compute(struct tcb *tcp, kernel_ulong_t params, kernel_ulong_t buf, - kernel_ulong_t len) + kernel_ulong_t len, kernel_ulong_t kdf_addr) { if (entering(tcp)) { print_dh_params(tcp, params); tprints(", "); } else { + struct strace_keyctl_kdf_params kdf; + if (syserror(tcp)) { printaddr(buf); } else { @@ -259,7 +264,48 @@ keyctl_dh_compute(struct tcb *tcp, kernel_ulong_t params, kernel_ulong_t buf, (kernel_ulong_t) tcp->u_rval; printstrn(tcp, buf, rval); } - tprintf(", %llu", zero_extend_signed_to_ull(len)); + tprintf(", %llu, ", zero_extend_signed_to_ull(len)); + + if (fetch_keyctl_kdf_params(tcp, kdf_addr, &kdf)) { + printaddr(kdf_addr); + } else { + size_t i; + + PRINT_FIELD_STR("{", kdf, hashname, tcp); + + /* + * Kernel doesn't touch otherinfo + * if otherinfolen is zero. + */ + if (kdf.otherinfolen) + PRINT_FIELD_STRN(", ", kdf, otherinfo, + kdf.otherinfolen, tcp); + else + PRINT_FIELD_PTR(", ", kdf, otherinfo); + + PRINT_FIELD_U(", ", kdf, otherinfolen); + + /* Some future-proofing */ + for (i = 0; i < ARRAY_SIZE(kdf.__spare); i++) { + if (kdf.__spare[i]) + break; + } + + if (i < ARRAY_SIZE(kdf.__spare)) { + tprints(", __spare=["); + + for (i = 0; i < ARRAY_SIZE(kdf.__spare); i++) { + if (i) + tprints(", "); + + tprintf("%#x", kdf.__spare[i]); + } + + tprints("]"); + } + + tprints("}"); + } } } @@ -373,7 +419,7 @@ SYS_FUNC(keyctl) break; case KEYCTL_DH_COMPUTE: - keyctl_dh_compute(tcp, arg2, arg3, arg4); + keyctl_dh_compute(tcp, arg2, arg3, arg4, arg5); return 0; case KEYCTL_RESTRICT_KEYRING: diff --git a/keyctl_kdf_params.h b/keyctl_kdf_params.h new file mode 100644 index 00000000..7f3a9dba --- /dev/null +++ b/keyctl_kdf_params.h @@ -0,0 +1,27 @@ +#ifndef STRACE_KEYCTL_KDF_PARAMS_H +#define STRACE_KEYCTL_KDF_PARAMS_H + +#include +#include "kernel_types.h" + +/* from include/linux/crypto.h */ +#define CRYPTO_MAX_ALG_NAME 128 + +/* from security/keys/internal.h */ +#define KEYCTL_KDF_MAX_OI_LEN 64 /* max length of otherinfo */ + +struct keyctl_kdf_params { + char *hashname; + char *otherinfo; + uint32_t otherinfolen; + uint32_t __spare[8]; +}; + +struct strace_keyctl_kdf_params { + kernel_ulong_t hashname; + kernel_ulong_t otherinfo; + uint32_t otherinfolen; + uint32_t __spare[8]; +}; + +#endif /* STRACE_KEYCTL_KDF_PARAMS_H */ diff --git a/tests/keyctl.c b/tests/keyctl.c index c16bbcc7..cde46926 100644 --- a/tests/keyctl.c +++ b/tests/keyctl.c @@ -36,6 +36,7 @@ # include # include +# include # include # include # include @@ -55,6 +56,15 @@ struct keyctl_dh_params { }; # endif +# ifndef HAVE_STRUCT_KEYCTL_KDF_PARAMS +struct keyctl_kdf_params { + char *hashname; + char *otherinfo; + uint32_t otherinfolen; + uint32_t __spare[8]; +}; +# endif + # include "xlat.h" # include "xlat/keyctl_commands.h" @@ -76,6 +86,9 @@ static const size_t limit = 10; bool nul_terminated_buf = true; bool buf_in_arg; +/* From ioctl_dm.c */ +# define STR32 "AbCdEfGhIjKlMnOpQrStUvWxYz012345" + /* * When this is called with positive size, the buffer provided is an "out" * argument and rc contains resulting size (globally defined nul_terminated_buf @@ -182,6 +195,85 @@ do_keyctl(kernel_ulong_t cmd, const char *cmd_str, ...) printf(") = %s\n", errstr); } +int +append_str(char **buf, size_t *left, const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = vsnprintf(*buf, *left, fmt, ap); + va_end(ap); + + assert((ret >= 0) && ((unsigned) ret < *left)); + + *left -= ret; + *buf += ret; + + return ret; +} + +const char * +kckdfp_to_str(struct keyctl_kdf_params *kdf, bool deref_hash, bool deref_oi, + bool print_spare, const char *hash_str, const char *oi_str) +{ + static char buf[4096]; + + size_t left = sizeof(buf); + char *pos = buf; + + append_str(&pos, &left, "{hashname="); + + if (deref_hash && hash_str) { + append_str(&pos, &left, "%s", hash_str); + } else if (!kdf->hashname) { + append_str(&pos, &left, "NULL"); + } else if (deref_hash) { + append_str(&pos, &left, "\"%.*s\"", limit, kdf->hashname); + + if (strnlen(kdf->hashname, limit + 1) > limit) + append_str(&pos, &left, "..."); + } else { + append_str(&pos, &left, "%p", kdf->hashname); + } + + append_str(&pos, &left, ", otherinfo="); + + if (deref_oi && oi_str) { + append_str(&pos, &left, "%s", oi_str); + } else if (!kdf->otherinfo) { + append_str(&pos, &left, "NULL"); + } else if (deref_oi) { + append_str(&pos, &left, "\"%.*s\"", limit, kdf->otherinfo); + + if (strnlen(kdf->otherinfo, limit + 1) > limit) + append_str(&pos, &left, "..."); + } else { + append_str(&pos, &left, "%p", kdf->otherinfo); + } + + append_str(&pos, &left, ", otherinfolen=%u", kdf->otherinfolen); + + if (print_spare) { + size_t i; + + append_str(&pos, &left, ", __spare=["); + + for (i = 0; i < ARRAY_SIZE(kdf->__spare); i++) { + if (i) + append_str(&pos, &left, ", "); + + append_str(&pos, &left, "%#x", kdf->__spare[i]); + } + + append_str(&pos, &left, "]"); + } + + append_str(&pos, &left, "}"); + + return buf; +} + int main(void) { @@ -210,6 +302,32 @@ main(void) static const char *kcdhp_str = "{private=KEY_SPEC_GROUP_KEYRING, " "prime=1234567890, base=-1153374643}"; + /* + * It's bigger than current hash name size limit, but since it's + * implementation-dependent and totally internal, we do not rely + * on it much. + */ + static const char long_hash_data[] = STR32 STR32 STR32 STR32 "xxx"; + static const char short_hash_data[] = "hmac(aes)"; + static const char otherinfo1_data[] = "\1\2 OH HAI THAR\255\0\1"; + static const char otherinfo2_data[] = "\1\2\n\255\0\1"; + static const struct keyctl_kdf_params kckdfp_data[] = { + [0] = { NULL, NULL, 0, { 0 } }, + [1] = { NULL /* Changed to unaccessible address in copy */, + NULL, 0xbadc0dedU, { [7] = 0xdeadfeedU } }, + [2] = { NULL /* long_hash_data */, + NULL /* Changed to unaccessible address in copy */, + 0, { 0 } }, + [3] = { NULL /* unterminated1 */, + NULL /* otherinfo_data */, 0, { 1 } }, + [4] = { NULL /* short_hash_data */, + NULL /* otherinfo1_data */, sizeof(otherinfo1_data), + { 0, 0xfacebeef, 0, 0xba5e1ead } }, + [5] = { NULL /* short_hash_data */, + NULL /* otherinfo2_data */, sizeof(otherinfo2_data), + { 0 } }, + }; + char *bogus_str = tail_memdup(unterminated1, sizeof(unterminated1)); char *bogus_desc = tail_memdup(unterminated2, sizeof(unterminated2)); char *short_type = tail_memdup(short_type_str, sizeof(short_type_str)); @@ -217,6 +335,15 @@ main(void) char *long_type = tail_memdup(long_type_str, sizeof(long_type_str)); char *long_desc = tail_memdup(long_desc_str, sizeof(long_desc_str)); char *kcdhp = tail_memdup(&kcdhp_data, sizeof(kcdhp_data)); + char *kckdfp_long_hash = tail_memdup(long_hash_data, + sizeof(long_hash_data)); + char *kckdfp_short_hash = tail_memdup(short_hash_data, + sizeof(short_hash_data)); + char *kckdfp_otherinfo1 = tail_memdup(otherinfo1_data, + sizeof(otherinfo1_data)); + char *kckdfp_otherinfo2 = tail_memdup(otherinfo2_data, + sizeof(otherinfo2_data)); + char *kckdfp_char = tail_alloc(sizeof(kckdfp_data[0])); struct iovec *key_iov = tail_alloc(sizeof(*key_iov) * IOV_SIZE); char *bogus_buf1 = tail_alloc(9); char *bogus_buf2 = tail_alloc(256); @@ -224,7 +351,7 @@ main(void) char *key_iov_str2 = tail_alloc(4096); ssize_t ret; ssize_t kis_size = 0; - int i; + size_t i; key_iov[0].iov_base = short_type; key_iov[0].iov_len = sizeof(short_type_str); @@ -845,28 +972,85 @@ main(void) sizeof(char *), ARG_STR(NULL), ptr_fmt, sizeof(kernel_ulong_t), (kernel_ulong_t) 0xfeedf157badc0dedLLU, NULL, ksize_fmt, - 0UL); + sizeof(char *), ARG_STR(NULL), ptr_fmt); do_keyctl(ARG_STR(KEYCTL_DH_COMPUTE), sizeof(char *), kcdhp + 1, NULL, ptr_fmt, sizeof(char *), (char *) 0xfffff157ffffdeadULL, NULL, ptr_fmt, sizeof(kernel_ulong_t), (kernel_ulong_t) 0xfeedf157badc0dedLLU, NULL, ksize_fmt, - 0UL); + sizeof(char *), ARG_STR(NULL), ptr_fmt); do_keyctl(ARG_STR(KEYCTL_DH_COMPUTE), sizeof(kcdhp), kcdhp, kcdhp_str, NULL, (size_t) 9, (uintptr_t) bogus_buf1, NULL, NULL, sizeof(kernel_ulong_t), (kernel_ulong_t) 9, NULL, ksize_fmt, - 0UL); + sizeof(char *), ARG_STR(NULL), ptr_fmt); do_keyctl(ARG_STR(KEYCTL_DH_COMPUTE), sizeof(kcdhp), kcdhp, kcdhp_str, NULL, (size_t) 256, (uintptr_t) bogus_buf2, NULL, NULL, sizeof(kernel_ulong_t), (kernel_ulong_t) 256, NULL, ksize_fmt, - 0UL); + sizeof(char *), ARG_STR(NULL), ptr_fmt); do_keyctl(ARG_STR(KEYCTL_DH_COMPUTE), sizeof(kcdhp), kcdhp, kcdhp_str, NULL, (size_t) -1, (uintptr_t) bogus_buf2, NULL, NULL, sizeof(kernel_ulong_t), (kernel_ulong_t) -1, NULL, ksize_fmt, - 0UL); + sizeof(char *), kckdfp_char + 1, NULL, ptr_fmt); + + /* KEYCTL_DH_COMPUTE + KDF */ + + for (i = 0; i < ARRAY_SIZE(kckdfp_data); i++) { + struct keyctl_kdf_params *kckdfp = + (struct keyctl_kdf_params *) kckdfp_char; + bool deref_hash = true; + bool deref_opts = true; + bool print_spare = false; + const char *hash_str = NULL; + const char *oi_str = NULL; + + memcpy(kckdfp, kckdfp_data + i, sizeof(kckdfp_data[i])); + + switch (i) { + case 1: + deref_hash = false; + print_spare = true; + kckdfp->hashname = + kckdfp_short_hash + sizeof(short_hash_data); + break; + case 2: + deref_opts = false; + kckdfp->hashname = kckdfp_long_hash; + kckdfp->otherinfo = + kckdfp_otherinfo1 + sizeof(otherinfo1_data); + break; + case 3: + deref_opts = false; + deref_hash = false; + print_spare = true; + kckdfp->hashname = bogus_str; + kckdfp->otherinfo = kckdfp_otherinfo1; + break; + case 4: + oi_str = "\"\\1\\2 OH HAI \"..."; + print_spare = true; + kckdfp->hashname = kckdfp_short_hash; + kckdfp->otherinfo = kckdfp_otherinfo1; + break; + case 5: + oi_str = "\"\\1\\2\\n\\255\\0\\1\\0\""; + kckdfp->hashname = kckdfp_short_hash; + kckdfp->otherinfo = kckdfp_otherinfo2; + break; + } + + do_keyctl(ARG_STR(KEYCTL_DH_COMPUTE), + sizeof(kcdhp), kcdhp, kcdhp_str, NULL, + (size_t) -1, (uintptr_t) bogus_buf2, NULL, NULL, + sizeof(kernel_ulong_t), (kernel_ulong_t) -1, NULL, + ksize_fmt, + sizeof(kckdfp), kckdfp_char, + kckdfp_to_str(kckdfp, deref_hash, deref_opts, + print_spare, hash_str, oi_str), + NULL); + } nul_terminated_buf = true;