]> granicus.if.org Git - strace/commitdiff
keyctl: add support for KDF parameters decoding in KEYCTL_DH_COMPUTE
authorEugene Syromyatnikov <evgsyr@gmail.com>
Fri, 1 Sep 2017 14:14:25 +0000 (16:14 +0200)
committerDmitry V. Levin <ldv@altlinux.org>
Sat, 2 Sep 2017 21:48:14 +0000 (21:48 +0000)
* 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.

Makefile.am
configure.ac
defs.h
fetch_struct_keyctl_kdf_params.c [new file with mode: 0644]
keyctl.c
keyctl_kdf_params.h [new file with mode: 0644]
tests/keyctl.c

index b3afc7c5c6fb14dbe38087515311da344de83fd4..4aa9846c296b507ba4f4b790a008fa480623e540 100644 (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 \
index 81f7dad728ee64ccd091af4e55a45db0190e007d..5cb76ce6bdd2ce9a9d1eb6b48fec7585da56e8bc 100644 (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>])
diff --git a/defs.h b/defs.h
index 5323920cceee774a4517592ae5cd6e87a8528653..34261f2e650e09f1ee0b2846bf00a62e1d6621dd 100644 (file)
--- 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 (file)
index 0000000..50fc9d1
--- /dev/null
@@ -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;
+}
index a11bd3d3a2a266abae4db1acd01eecaf9bced6c9..9ee08034d6c6e2f53ae49ec3f0585dbc72a6ad09 100644 (file)
--- 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 (file)
index 0000000..7f3a9db
--- /dev/null
@@ -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 */
index c16bbcc75dacc926d4d7d572981c2c7ea9a5f79c..cde4692613cda9b7216d4a14d476e934f9fe4590 100644 (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;