~sircmpwn/hare-linux

03fd77111b14b21ac7f3d93a04bdee3a818a80dc — Drew DeVault 2 years ago 8f66feb
linux::keyctl: initial commit

Signed-off-by: Drew DeVault <sir@cmpwn.com>
3 files changed, 244 insertions(+), 0 deletions(-)

A keyctl/README
A keyctl/keyctl.ha
A keyctl/types.ha
A keyctl/README => keyctl/README +3 -0
@@ 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.

A keyctl/keyctl.ha => keyctl/keyctl.ha +102 -0
@@ 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));
};

A keyctl/types.ha => keyctl/types.ha +139 -0
@@ 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,
};