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
<linux/keyctl.h>.
* 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)) <case KEYCTL_DH_COMPUTE>: 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.
This commit is contained in:
Eugene Syromyatnikov 2017-09-01 16:14:25 +02:00 committed by Dmitry V. Levin
parent 5a8b032f2f
commit 46a4dcf4c3
7 changed files with 307 additions and 9 deletions

View File

@ -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 \

View File

@ -333,6 +333,10 @@ AC_CHECK_TYPES(m4_normalize([
struct crypto_report_rng
]),,, [#include <linux/cryptouser.h>])
AC_CHECK_TYPES(m4_normalize([
struct keyctl_kdf_params
]),,, [#include <linux/keyctl.h>])
AC_CHECK_MEMBERS([struct timex.tai],,, [#include <sys/timex.h>])
AC_CHECK_MEMBERS([struct utsname.domainname],,, [#include <sys/utsname.h>])

1
defs.h
View File

@ -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);

View File

@ -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;
}

View File

@ -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:

27
keyctl_kdf_params.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef STRACE_KEYCTL_KDF_PARAMS_H
#define STRACE_KEYCTL_KDF_PARAMS_H
#include <stdint.h>
#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 */

View File

@ -36,6 +36,7 @@
# include <linux/types.h>
# include <linux/keyctl.h>
# include <assert.h>
# include <errno.h>
# include <inttypes.h>
# include <stdarg.h>
@ -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;