1use std::convert::TryInto;
28use std::ffi::CString;
29use std::ptr;
30
31use log::error;
32use uninit::out_ref::Out;
33
34use crate::{DefaultKeyring, KeyPermissions, KeyringSerial, TimeoutSeconds};
35
36type Error = errno::Errno;
38type Result<T> = std::result::Result<T, Error>;
40
41fn check_syscall(res: libc::c_long) -> Result<libc::c_long> {
42 if res == -1 {
43 Err(errno::errno())
44 } else {
45 Ok(res)
46 }
47}
48
49static THE_KERNEL_LIED: &str = concat!(
50 "It appears as though the kernel made a 64-bit key ID. Please report a bug.\n\n",
51 env!("CARGO_PKG_REPOSITORY"),
52);
53static ZERO_KEY_ID_FOUND: &str = concat!(
54 "It appears as though a key ID of zero was found. This is novel and should not happen. Please \
55 report a bug.\n\n",
56 env!("CARGO_PKG_REPOSITORY"),
57);
58static BUFFER_OVERFLOW: &str = concat!(
59 "The kernel returned a size that could not be represented as a `usize`. This should not be \
60 possible. Please report a bug.\n\n",
61 env!("CARGO_PKG_REPOSITORY"),
62);
63
64fn cstring(s: &str) -> CString {
65 CString::new(s.as_bytes()).unwrap()
66}
67
68fn opt_cstring(opt: Option<&str>) -> Option<CString> {
69 opt.map(cstring)
70}
71
72fn opt_cstring_ptr(opt: &Option<CString>) -> *const libc::c_char {
73 opt.as_ref().map_or(ptr::null(), |cs| cs.as_ptr())
74}
75
76fn opt_key_serial(opt: Option<KeyringSerial>) -> i32 {
77 opt.map(KeyringSerial::get).unwrap_or(0)
78}
79
80fn keyring_serial(res: libc::c_long) -> KeyringSerial {
81 KeyringSerial::new(res.try_into().expect(THE_KERNEL_LIED)).expect(ZERO_KEY_ID_FOUND)
82}
83
84fn default_keyring(res: libc::c_long) -> Result<DefaultKeyring> {
85 res.try_into().map_err(|err: crate::UnknownDefault| {
86 error!(
87 concat!(
88 "The kernel has returned an unexpected default keyring ID: {}. Please report a \
89 bug.\n\n",
90 env!("CARGO_PKG_REPOSITORY"),
91 ),
92 err.0,
93 );
94 errno::Errno(libc::EINVAL)
95 })
96}
97
98fn size(res: libc::c_long) -> usize {
99 res.try_into().expect(BUFFER_OVERFLOW)
100}
101
102fn ignore(res: libc::c_long) {
103 assert_eq!(res, 0);
104}
105
106fn safe_len<T>(len: usize) -> Result<T>
107where
108 usize: TryInto<T>,
109{
110 len.try_into().map_err(|_| errno::Errno(libc::EINVAL))
111}
112
113macro_rules! syscall {
114 ( $( $arg:expr, )* ) => {
115 check_syscall(libc::syscall($( $arg, )*))
116 };
117}
118
119macro_rules! keyctl {
120 ( $( $arg:expr, )* ) => {
121 syscall!(libc::SYS_keyctl, $( $arg, )*)
122 };
123}
124
125pub fn add_key(
126 type_: &str,
127 description: &str,
128 payload: &[u8],
129 keyring: KeyringSerial,
130) -> Result<KeyringSerial> {
131 let type_cstr = cstring(type_);
132 let desc_cstr = cstring(description);
133 unsafe {
134 syscall!(
135 libc::SYS_add_key,
136 type_cstr.as_ptr(),
137 desc_cstr.as_ptr(),
138 payload.as_ptr() as *const libc::c_void,
139 payload.len(),
140 keyring.get(),
141 )
142 }
143 .map(keyring_serial)
144}
145
146pub fn request_key(
147 type_: &str,
148 description: &str,
149 callout_info: Option<&str>,
150 keyring: Option<KeyringSerial>,
151) -> Result<KeyringSerial> {
152 let type_cstr = cstring(type_);
153 let desc_cstr = cstring(description);
154 let callout_cstr = opt_cstring(callout_info);
155 let callout_ptr = opt_cstring_ptr(&callout_cstr);
156
157 unsafe {
158 syscall!(
159 libc::SYS_request_key,
160 type_cstr.as_ptr(),
161 desc_cstr.as_ptr(),
162 callout_ptr,
163 opt_key_serial(keyring),
164 )
165 }
166 .map(keyring_serial)
167}
168
169pub fn keyctl_get_keyring_id(id: KeyringSerial, create: bool) -> Result<KeyringSerial> {
170 unsafe {
171 keyctl!(
172 libc::KEYCTL_GET_KEYRING_ID,
173 id.get(),
174 if create { 1 } else { 0 },
175 )
176 }
177 .map(keyring_serial)
178}
179
180pub fn keyctl_join_session_keyring(name: Option<&str>) -> Result<KeyringSerial> {
181 let name_cstr = opt_cstring(name);
182 let name_ptr = opt_cstring_ptr(&name_cstr);
183
184 unsafe { keyctl!(libc::KEYCTL_JOIN_SESSION_KEYRING, name_ptr,) }.map(keyring_serial)
185}
186
187pub fn keyctl_update(id: KeyringSerial, payload: &[u8]) -> Result<()> {
188 unsafe {
189 keyctl!(
190 libc::KEYCTL_UPDATE,
191 id.get(),
192 payload.as_ptr() as *const libc::c_void,
193 payload.len(),
194 )
195 }
196 .map(ignore)
197}
198
199pub fn keyctl_revoke(id: KeyringSerial) -> Result<()> {
200 unsafe { keyctl!(libc::KEYCTL_REVOKE, id.get(),) }.map(ignore)
201}
202
203pub fn keyctl_chown(
204 id: KeyringSerial,
205 uid: Option<libc::uid_t>,
206 gid: Option<libc::gid_t>,
207) -> Result<()> {
208 unsafe {
209 keyctl!(
210 libc::KEYCTL_CHOWN,
211 id.get(),
212 uid.unwrap_or(!0),
213 gid.unwrap_or(!0),
214 )
215 }
216 .map(ignore)
217}
218
219pub fn keyctl_setperm(id: KeyringSerial, perm: KeyPermissions) -> Result<()> {
220 unsafe { keyctl!(libc::KEYCTL_SETPERM, id.get(), perm,) }.map(ignore)
221}
222
223pub fn keyctl_describe(id: KeyringSerial, mut buffer: Option<Out<[u8]>>) -> Result<usize> {
224 let capacity = buffer.as_mut().map_or(0, |b| b.len());
225 unsafe {
226 keyctl!(
227 libc::KEYCTL_DESCRIBE,
228 id.get(),
229 buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()),
230 capacity,
231 )
232 }
233 .map(size)
234}
235
236pub fn keyctl_clear(id: KeyringSerial) -> Result<()> {
237 unsafe { keyctl!(libc::KEYCTL_CLEAR, id.get(),) }.map(ignore)
238}
239
240pub fn keyctl_link(id: KeyringSerial, ringid: KeyringSerial) -> Result<()> {
241 unsafe { keyctl!(libc::KEYCTL_LINK, id.get(), ringid.get(),) }.map(ignore)
242}
243
244pub fn keyctl_unlink(id: KeyringSerial, ringid: KeyringSerial) -> Result<()> {
245 unsafe { keyctl!(libc::KEYCTL_UNLINK, id.get(), ringid.get(),) }.map(ignore)
246}
247
248pub fn keyctl_search(
249 ringid: KeyringSerial,
250 type_: &str,
251 description: &str,
252 destringid: Option<KeyringSerial>,
253) -> Result<KeyringSerial> {
254 let type_cstr = cstring(type_);
255 let desc_cstr = cstring(description);
256 unsafe {
257 keyctl!(
258 libc::KEYCTL_SEARCH,
259 ringid.get(),
260 type_cstr.as_ptr(),
261 desc_cstr.as_ptr(),
262 opt_key_serial(destringid),
263 )
264 }
265 .map(keyring_serial)
266}
267
268pub fn keyctl_read(id: KeyringSerial, mut buffer: Option<Out<[u8]>>) -> Result<usize> {
269 let capacity = buffer.as_mut().map_or(0, |b| b.len());
270 unsafe {
271 keyctl!(
272 libc::KEYCTL_READ,
273 id.get(),
274 buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()),
275 capacity,
276 )
277 }
278 .map(size)
279}
280
281pub fn keyctl_instantiate(
282 id: KeyringSerial,
283 payload: &[u8],
284 ringid: Option<KeyringSerial>,
285) -> Result<()> {
286 unsafe {
287 keyctl!(
288 libc::KEYCTL_INSTANTIATE,
289 id.get(),
290 payload.as_ptr() as *const libc::c_void,
291 payload.len(),
292 opt_key_serial(ringid),
293 )
294 }
295 .map(ignore)
296}
297
298pub fn keyctl_negate(
299 id: KeyringSerial,
300 timeout: TimeoutSeconds,
301 ringid: Option<KeyringSerial>,
302) -> Result<()> {
303 unsafe {
304 keyctl!(
305 libc::KEYCTL_NEGATE,
306 id.get(),
307 timeout,
308 opt_key_serial(ringid),
309 )
310 }
311 .map(ignore)
312}
313
314pub fn keyctl_set_reqkey_keyring(reqkey_defl: DefaultKeyring) -> Result<DefaultKeyring> {
315 unsafe { keyctl!(libc::KEYCTL_SET_REQKEY_KEYRING, reqkey_defl,) }.and_then(default_keyring)
316}
317
318pub fn keyctl_set_timeout(key: KeyringSerial, timeout: TimeoutSeconds) -> Result<()> {
319 unsafe { keyctl!(libc::KEYCTL_SET_TIMEOUT, key.get(), timeout,) }.map(ignore)
320}
321
322pub fn keyctl_assume_authority(key: Option<KeyringSerial>) -> Result<()> {
323 unsafe { keyctl!(libc::KEYCTL_ASSUME_AUTHORITY, opt_key_serial(key),) }.map(ignore)
324}
325
326pub fn keyctl_get_security(key: KeyringSerial, mut buffer: Option<Out<[u8]>>) -> Result<usize> {
327 let capacity = buffer.as_mut().map_or(0, |b| b.len());
328 unsafe {
329 keyctl!(
330 libc::KEYCTL_GET_SECURITY,
331 key.get(),
332 buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()),
333 capacity,
334 )
335 }
336 .map(size)
337}
338
339pub fn keyctl_reject(
340 id: KeyringSerial,
341 timeout: TimeoutSeconds,
342 error: errno::Errno,
343 ringid: Option<KeyringSerial>,
344) -> Result<()> {
345 unsafe {
346 keyctl!(
347 libc::KEYCTL_REJECT,
348 id.get(),
349 timeout,
350 error,
351 opt_key_serial(ringid),
352 )
353 }
354 .map(ignore)
355}
356
357pub fn keyctl_invalidate(id: KeyringSerial) -> Result<()> {
358 unsafe { keyctl!(libc::KEYCTL_INVALIDATE, id.get(),) }.map(ignore)
359}
360
361pub fn keyctl_get_persistent(uid: libc::uid_t, id: KeyringSerial) -> Result<KeyringSerial> {
362 unsafe { keyctl!(libc::KEYCTL_GET_PERSISTENT, uid, id.get(),) }.map(keyring_serial)
363}
364
365pub fn keyctl_session_to_parent() -> Result<()> {
366 unsafe { keyctl!(libc::KEYCTL_SESSION_TO_PARENT,) }.map(ignore)
367}
368
369#[repr(C)]
370struct DhComputeParamsKernel {
371 priv_: i32,
372 prime: i32,
373 base: i32,
374}
375
376#[repr(C)]
377struct DhKdfParamsKernel {
378 hashname: *const libc::c_char,
379 otherinfo: *const libc::c_void,
380 otherinfolen: u32,
381 _spare: [u32; 8],
382}
383
384pub fn keyctl_dh_compute(
385 private: KeyringSerial,
386 prime: KeyringSerial,
387 base: KeyringSerial,
388 mut buffer: Option<Out<[u8]>>,
389) -> Result<usize> {
390 let params = DhComputeParamsKernel {
391 priv_: private.get(),
392 prime: prime.get(),
393 base: base.get(),
394 };
395 let capacity = buffer.as_mut().map_or(0, |b| b.len());
396 unsafe {
397 keyctl!(
398 libc::KEYCTL_DH_COMPUTE,
399 ¶ms as *const DhComputeParamsKernel,
400 buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()),
401 capacity,
402 ptr::null() as *const DhKdfParamsKernel,
403 )
404 }
405 .map(size)
406}
407
408pub fn keyctl_dh_compute_kdf(
409 private: KeyringSerial,
410 prime: KeyringSerial,
411 base: KeyringSerial,
412 hashname: &str,
413 otherinfo: Option<&[u8]>,
414 mut buffer: Option<Out<[u8]>>,
415) -> Result<usize> {
416 let params = DhComputeParamsKernel {
417 priv_: private.get(),
418 prime: prime.get(),
419 base: base.get(),
420 };
421 let hash_cstr = cstring(hashname);
422 let kdf_params = DhKdfParamsKernel {
423 hashname: hash_cstr.as_ptr(),
424 otherinfo: otherinfo.map_or(ptr::null(), |d| d.as_ptr()) as *const libc::c_void,
425 otherinfolen: safe_len(otherinfo.map_or(0, |d| d.len()))?,
426 _spare: [0; 8],
427 };
428 let capacity = buffer.as_mut().map_or(0, |b| b.len());
429 unsafe {
430 keyctl!(
431 libc::KEYCTL_DH_COMPUTE,
432 ¶ms as *const DhComputeParamsKernel,
433 buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()),
434 capacity,
435 &kdf_params as *const DhKdfParamsKernel,
436 )
437 }
438 .map(size)
439}
440
441pub enum Restriction<'a> {
442 AllLinks,
443 ByType {
444 type_: &'a str,
445 restriction: &'a str,
446 },
447}
448
449pub fn keyctl_restrict_keyring(keyring: KeyringSerial, restriction: Restriction) -> Result<()> {
450 let type_cstr;
451 let restriction_cstr;
452
453 let (type_ptr, restriction_ptr) = match restriction {
454 Restriction::AllLinks => (ptr::null(), ptr::null()),
455 Restriction::ByType {
456 type_,
457 restriction,
458 } => {
459 type_cstr = cstring(type_);
460 restriction_cstr = cstring(restriction);
461
462 (type_cstr.as_ptr(), restriction_cstr.as_ptr())
463 },
464 };
465 unsafe {
466 keyctl!(
467 libc::KEYCTL_RESTRICT_KEYRING,
468 keyring.get(),
469 type_ptr,
470 restriction_ptr,
471 )
472 }
473 .map(ignore)
474}
475
476#[derive(Debug, Clone, Copy, PartialEq, Eq)]
477pub struct PKeyQuery {
479 pub supported_ops: u32,
480 pub key_size: u32,
481 pub max_data_size: u16,
482 pub max_sig_size: u16,
483 pub max_enc_size: u16,
484 pub max_dec_size: u16,
485}
486
487#[repr(C)]
488pub struct PKeyQueryKernel {
489 supported_ops: u32,
490 key_size: u32,
491 max_data_size: u16,
492 max_sig_size: u16,
493 max_enc_size: u16,
494 max_dec_size: u16,
495 _spare: [u32; 10],
496}
497
498impl PKeyQueryKernel {
499 fn zeroed() -> Self {
500 PKeyQueryKernel {
501 supported_ops: 0,
502 key_size: 0,
503 max_data_size: 0,
504 max_sig_size: 0,
505 max_enc_size: 0,
506 max_dec_size: 0,
507 _spare: [0; 10],
508 }
509 }
510}
511
512impl From<PKeyQueryKernel> for PKeyQuery {
513 fn from(kernel: PKeyQueryKernel) -> Self {
514 PKeyQuery {
515 supported_ops: kernel.supported_ops,
516 key_size: kernel.key_size,
517 max_data_size: kernel.max_data_size,
518 max_sig_size: kernel.max_sig_size,
519 max_enc_size: kernel.max_enc_size,
520 max_dec_size: kernel.max_dec_size,
521 }
522 }
523}
524
525pub fn keyctl_pkey_query(key: KeyringSerial, info: &str) -> Result<PKeyQuery> {
526 let mut query = PKeyQueryKernel::zeroed();
527 let info_cstr = cstring(info);
528 unsafe {
529 keyctl!(
530 libc::KEYCTL_PKEY_QUERY,
531 key.get(),
532 0,
533 info_cstr.as_ptr(),
534 &mut query as *mut PKeyQueryKernel,
535 )
536 }
537 .map(ignore)?;
538
539 Ok(query.into())
540}
541
542#[repr(C)]
543struct PKeyOpParamsKernel {
544 key_id: i32,
545 in_len: u32,
546 out_len: u32,
547 in2_len: u32,
548}
549
550pub fn keyctl_pkey_encrypt(
551 key: KeyringSerial,
552 info: &str,
553 data: &[u8],
554 mut buffer: Out<[u8]>,
555) -> Result<usize> {
556 let params = PKeyOpParamsKernel {
557 key_id: key.get(),
558 in_len: safe_len(data.len())?,
559 out_len: safe_len(buffer.len())?,
560 in2_len: 0,
561 };
562 let info_cstr = cstring(info);
563 unsafe {
564 keyctl!(
565 libc::KEYCTL_PKEY_ENCRYPT,
566 ¶ms as *const PKeyOpParamsKernel,
567 info_cstr.as_ptr(),
568 data.as_ptr(),
569 buffer.as_mut_ptr(),
570 )
571 }
572 .map(size)
573}
574
575pub fn keyctl_pkey_decrypt(
576 key: KeyringSerial,
577 info: &str,
578 data: &[u8],
579 mut buffer: Out<[u8]>,
580) -> Result<usize> {
581 let params = PKeyOpParamsKernel {
582 key_id: key.get(),
583 in_len: safe_len(data.len())?,
584 out_len: safe_len(buffer.len())?,
585 in2_len: 0,
586 };
587 let info_cstr = cstring(info);
588 unsafe {
589 keyctl!(
590 libc::KEYCTL_PKEY_DECRYPT,
591 ¶ms as *const PKeyOpParamsKernel,
592 info_cstr.as_ptr(),
593 data.as_ptr(),
594 buffer.as_mut_ptr(),
595 )
596 }
597 .map(size)
598}
599
600pub fn keyctl_pkey_sign(
601 key: KeyringSerial,
602 info: &str,
603 data: &[u8],
604 mut buffer: Out<[u8]>,
605) -> Result<usize> {
606 let params = PKeyOpParamsKernel {
607 key_id: key.get(),
608 in_len: safe_len(data.len())?,
609 out_len: safe_len(buffer.len())?,
610 in2_len: 0,
611 };
612 let info_cstr = cstring(info);
613 unsafe {
614 keyctl!(
615 libc::KEYCTL_PKEY_SIGN,
616 ¶ms as *const PKeyOpParamsKernel,
617 info_cstr.as_ptr(),
618 data.as_ptr(),
619 buffer.as_mut_ptr(),
620 )
621 }
622 .map(size)
623}
624
625pub fn keyctl_pkey_verify(key: KeyringSerial, info: &str, data: &[u8], sig: &[u8]) -> Result<bool> {
626 let params = PKeyOpParamsKernel {
627 key_id: key.get(),
628 in_len: safe_len(data.len())?,
629 out_len: 0,
630 in2_len: safe_len(sig.len())?,
631 };
632 let info_cstr = cstring(info);
633 unsafe {
634 keyctl!(
635 libc::KEYCTL_PKEY_VERIFY,
636 ¶ms as *const PKeyOpParamsKernel,
637 info_cstr.as_ptr(),
638 data.as_ptr(),
639 sig.as_ptr(),
640 )
641 }
642 .map(|res| res == 0)
643}