hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 0725fd9d099514a525b57187c1d7bb60af11eafc
parent 0989175dfc76c56717426ac08a37071a46474ad7
Author: Drew DeVault <sir@cmpwn.com>
Date:   Tue, 29 Mar 2022 19:17:18 +0200

linux::keyctl: initial commit

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Alinux/keyctl/README | 3+++
Alinux/keyctl/keyctl.ha | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alinux/keyctl/types.ha | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mrt/+linux/syscalls.ha | 24++++++++++++++++++++++++
Mscripts/gen-stdlib | 12++++++++++--
Mstdlib.mk | 30++++++++++++++++++++++++++++++
6 files changed, 308 insertions(+), 2 deletions(-)

diff --git a/linux/keyctl/README b/linux/keyctl/README @@ -0,0 +1,3 @@ +linux::keyctl provides an interface to the Linux kernel's key management +facilities. Documentation for this module is sparse; the reader is encouraged to +consult the Linux man pages for complete details. diff --git a/linux/keyctl/keyctl.ha b/linux/keyctl/keyctl.ha @@ -0,0 +1,102 @@ +use bytes; +use errors; +use rt; +use strings; + +fn errno(errno: rt::errno) error = { + switch (errno) { + case rt::ENOKEY => + return nokey; + case => + return errors::errno(errno); + }; +}; + +// Adds a key to the kernel's key management facility. +export fn add_key( + keytype: str, + name: str, + payload: []u8, + keyring: serial, +) (serial | error) = { + const keytype = strings::to_c(keytype); + defer free(keytype); + const name = strings::to_c(name); + defer free(name); + match (rt::add_key(keytype, name, payload: *[*]u8: *void, + len(payload), keyring)) { + case let err: rt::errno => + return errno(err); + case let n: int => + return n: serial; + }; +}; + +fn keyctl( + cmd: command, + arg2: u64, + arg3: u64, + arg4: u64, + arg5: u64, +) (int | error) = { + match (rt::keyctl(cmd, arg2, arg3, arg4, arg5)) { + case let err: rt::errno => + return errno(err); + case let n: int => + return n; + }; +}; + +// Maps a special key or keyring ID to the serial number of the key actually +// representing that feature. If it does not exist and 'create' is true, then +// the key or keyring will be created if it is appropriate to do so. +export fn get_keyring_id(key: serial, create: bool) (serial | error) = { + return keyctl(command::GET_KEYRING_ID, + key: u64, if (create) 1 else 0, 0, 0)?: serial; +}; + +// Replace the session keyring this process subscribes to with a new session +// keyring using the given name, or, given an empty string, "_ses". +export fn join_session_keyring(name: str) (serial | error) = { + let name = if (name == "") { + yield null; + } else { + yield strings::to_c(name); + }; + defer free(name); + return keyctl(command::JOIN_SESSION_KEYRING, + name: uintptr: u64, 0, 0, 0)?: serial; +}; + +// Update a key's payload. +export fn update(id: serial, payload: []u8) (void | error) = { + keyctl(command::UPDATE, id: u64, + payload: *[*]u8: uintptr: u64, + len(payload): u64, 0)?; +}; + +// Revoke the key with the provided ID. +export fn revoke(id: serial) (void | error) = { + keyctl(command::REVOKE, id: u64, 0, 0, 0)?; +}; + +// Reads the payload from a key, returning the size of the key data. The +// provided buffer may be empty to probe the key size without reading. +export fn read(id: serial, buf: []u8) (size | error) = { + const bufln = len(buf); + const buf = if (len(buf) == 0) { + yield null; + } else { + yield buf: *[*]u8: *void; + }; + return keyctl(command::READ, id: u64, + buf: uintptr: u64, bufln: u64, 0)?: size; +}; + +@test fn keyctl() void = { + const payload = strings::toutf8("hello world"); + const key = add_key("user", "example", payload, PROCESS_KEYRING)!; + let buffer: [64]u8 = [0...]; + const n = read(key, buffer[..])!; + assert(bytes::equal(buffer[..n], payload)); +}; diff --git a/linux/keyctl/types.ha b/linux/keyctl/types.ha @@ -0,0 +1,139 @@ +use errors; + +// A key ID. +export type serial = i32; + +// Returned when a desired key was not found. +export type nokey = !void; + +export type error = !(nokey | errors::error); + +// The caller's thread-specific keyring. +export def THREAD_KEYRING: serial = -1; + +// The caller's process-specific keyring. +export def PROCESS_KEYRING: serial = -2; + +// The caller's session-specific keyring. +export def SESSION_KEYRING: serial = -3; + +// The caller's UID-specific keyring. +export def USER_KEYRING: serial = -4; + +// The caller's UID-session keyring. +export def USER_SESSION_KEYRING: serial = -5; + +// The caller's GID-specific keyring. +export def GROUP_KEYRING: serial = -6; + +// The caller's GID-session keyring. +export def REQKEY_AUTH_KEY: serial = -7; + +// The Key ID for the [[request_key]] destination keyring. +export def REQUESTOR_KEYRING: serial = -8; + +// request-key default keyrings +export type reqkey = enum int { + NO_CHANGE = -1, + DEFAULT = 0, + THREAD_KEYRING = 1, + PROCESS_KEYRING = 2, + SESSION_KEYRING = 3, + USER_KEYRING = 4, + USER_SESSION_KEYRING = 5, + GROUP_KEYRING = 6, + REQUESTOR_KEYRING = 7, +}; + +// keyctl commands +export type command = enum int { + GET_KEYRING_ID = 0, + JOIN_SESSION_KEYRING = 1, + UPDATE = 2, + REVOKE = 3, + CHOWN = 4, + SETPERM = 5, + DESCRIBE = 6, + CLEAR = 7, + LINK = 8, + UNLINK = 9, + SEARCH = 10, + READ = 11, + INSTANTIATE = 12, + NEGATE = 13, + SET_REQKEY_KEYRING = 14, + SET_TIMEOUT = 15, + ASSUME_AUTHORITY = 16, + GET_SECURITY = 17, + SESSION_TO_PARENT = 18, + REJECT = 19, + INSTANTIATE_IOV = 20, + INVALIDATE = 21, + GET_PERSISTENT = 22, + DH_COMPUTE = 23, + PKEY_QUERY = 24, + PKEY_ENCRYPT = 25, + PKEY_DECRYPT = 26, + PKEY_SIGN = 27, + PKEY_VERIFY = 28, + RESTRICT_KEYRING = 29, + MOVE = 30, + CAPABILITIES = 31, + WATCH_KEY = 32, +}; + +// Input for [[command::DH_COMPUTE]] +export type dh_params = struct { + private: i32, + prime: i32, + base: i32, +}; + +// Output for [[command::DH_COMPUTE]] +export type kdf_params = struct { + hashname: *char, + otherinfo: *char, + otherinfolen: u32, + __spare: [8]u32, +}; + +export type support = enum u32 { + SUPPORTS_ENCRYPT = 0x01, + SUPPORTS_DECRYPT = 0x02, + SUPPORTS_SIGN = 0x04, + SUPPORTS_VERIFY = 0x08, +}; + +export type pkey_query = struct { + supported_ops: u32, + key_size: u32, + max_data_size: u16, + max_sig_size: u16, + max_enc_size: u16, + max_dec_size: u16, + __spare: [10]u32, +}; + +export type pkey_params = struct { + key_id: i32, + in_len: u32, + union { + out_len: u32, + in2_len: u32, + }, + __spare: [7]u32, +}; + +export type caps = enum u8 { + CAPS0_CAPABILITIES = 0x01, + CAPS0_PERSISTENT_KEYRINGS = 0x02, + CAPS0_DIFFIE_HELLMAN = 0x04, + CAPS0_PUBLIC_KEY = 0x08, + CAPS0_BIG_KEY = 0x10, + CAPS0_INVALIDATE = 0x20, + CAPS0_RESTRICT_KEYRING = 0x40, + CAPS0_MOVE = 0x80, + CAPS1_NS_KEYRING_NAME = 0x01, + CAPS1_NS_KEY_TAG = 0x02, + CAPS1_NOTIFICATIONS = 0x04, +}; diff --git a/rt/+linux/syscalls.ha b/rt/+linux/syscalls.ha @@ -736,3 +736,27 @@ export fn prctl( return wrap_return(syscall5(SYS_prctl, option: u64, arg2, arg3, arg4, arg5))?: int; }; + +export fn add_key( + keytype: *const char, + name: *const char, + payload: *void, + plen: size, + keyring: int, +) (int | errno) = { + return wrap_return(syscall5(SYS_add_key, + keytype: uintptr: u64, name: uintptr: u64, + payload: uintptr: u64, plen: u64, + keyring: u64))?: int; +}; + +export fn keyctl( + operation: int, + arg2: u64, + arg3: u64, + arg4: u64, + arg5: u64, +) (int | errno) = { + return wrap_return(syscall5(SYS_keyctl, operation: u64, + arg2, arg3, arg4, arg5))?: int; +}; diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -769,6 +769,13 @@ linux() { gen_ssa -plinux linux format::elf } +linux_keyctl() { + gen_srcs -plinux linux::keyctl \ + keyctl.ha \ + types.ha + gen_ssa -plinux linux::keyctl rt errors strings bytes +} + linux_signalfd() { gen_srcs -plinux linux::signalfd \ signalfd.ha @@ -1260,8 +1267,9 @@ hash::fnv io linux freebsd iobus::io_uring linux linux linux -linux::signalfd linux -linux::io_uring linux +linux::keyctl linux +linux::signalfd linux +linux::io_uring linux linux::vdso linux math math::random diff --git a/stdlib.mk b/stdlib.mk @@ -434,6 +434,10 @@ stdlib_deps_linux+=$(stdlib_iobus_io_uring_linux) stdlib_linux_linux=$(HARECACHE)/linux/linux-linux.o stdlib_deps_linux+=$(stdlib_linux_linux) +# gen_lib linux::keyctl (linux) +stdlib_linux_keyctl_linux=$(HARECACHE)/linux/keyctl/linux_keyctl-linux.o +stdlib_deps_linux+=$(stdlib_linux_keyctl_linux) + # gen_lib linux::signalfd (linux) stdlib_linux_signalfd_linux=$(HARECACHE)/linux/signalfd/linux_signalfd-linux.o stdlib_deps_linux+=$(stdlib_linux_signalfd_linux) @@ -1283,6 +1287,17 @@ $(HARECACHE)/linux/linux-linux.ssa: $(stdlib_linux_linux_srcs) $(stdlib_rt) $(st @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nlinux \ -t$(HARECACHE)/linux/linux.td $(stdlib_linux_linux_srcs) +# linux::keyctl (+linux) +stdlib_linux_keyctl_linux_srcs= \ + $(STDLIB)/linux/keyctl/keyctl.ha \ + $(STDLIB)/linux/keyctl/types.ha + +$(HARECACHE)/linux/keyctl/linux_keyctl-linux.ssa: $(stdlib_linux_keyctl_linux_srcs) $(stdlib_rt) $(stdlib_rt_$(PLATFORM)) $(stdlib_errors_$(PLATFORM)) $(stdlib_strings_$(PLATFORM)) $(stdlib_bytes_$(PLATFORM)) + @printf 'HAREC \t$@\n' + @mkdir -p $(HARECACHE)/linux/keyctl + @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nlinux::keyctl \ + -t$(HARECACHE)/linux/keyctl/linux_keyctl.td $(stdlib_linux_keyctl_linux_srcs) + # linux::signalfd (+linux) stdlib_linux_signalfd_linux_srcs= \ $(STDLIB)/linux/signalfd/signalfd.ha @@ -2281,6 +2296,10 @@ testlib_deps_linux+=$(testlib_iobus_io_uring_linux) testlib_linux_linux=$(TESTCACHE)/linux/linux-linux.o testlib_deps_linux+=$(testlib_linux_linux) +# gen_lib linux::keyctl (linux) +testlib_linux_keyctl_linux=$(TESTCACHE)/linux/keyctl/linux_keyctl-linux.o +testlib_deps_linux+=$(testlib_linux_keyctl_linux) + # gen_lib linux::signalfd (linux) testlib_linux_signalfd_linux=$(TESTCACHE)/linux/signalfd/linux_signalfd-linux.o testlib_deps_linux+=$(testlib_linux_signalfd_linux) @@ -3165,6 +3184,17 @@ $(TESTCACHE)/linux/linux-linux.ssa: $(testlib_linux_linux_srcs) $(testlib_rt) $( @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nlinux \ -t$(TESTCACHE)/linux/linux.td $(testlib_linux_linux_srcs) +# linux::keyctl (+linux) +testlib_linux_keyctl_linux_srcs= \ + $(STDLIB)/linux/keyctl/keyctl.ha \ + $(STDLIB)/linux/keyctl/types.ha + +$(TESTCACHE)/linux/keyctl/linux_keyctl-linux.ssa: $(testlib_linux_keyctl_linux_srcs) $(testlib_rt) $(testlib_rt_$(PLATFORM)) $(testlib_errors_$(PLATFORM)) $(testlib_strings_$(PLATFORM)) $(testlib_bytes_$(PLATFORM)) + @printf 'HAREC \t$@\n' + @mkdir -p $(TESTCACHE)/linux/keyctl + @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nlinux::keyctl \ + -t$(TESTCACHE)/linux/keyctl/linux_keyctl.td $(testlib_linux_keyctl_linux_srcs) + # linux::signalfd (+linux) testlib_linux_signalfd_linux_srcs= \ $(STDLIB)/linux/signalfd/signalfd.ha