keyutils_raw/
functions.rs

1// Copyright (c) 2018, Ben Boeckel
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without modification,
5// are permitted provided that the following conditions are met:
6//
7//     * Redistributions of source code must retain the above copyright notice,
8//       this list of conditions and the following disclaimer.
9//     * Redistributions in binary form must reproduce the above copyright notice,
10//       this list of conditions and the following disclaimer in the documentation
11//       and/or other materials provided with the distribution.
12//     * Neither the name of this project nor the names of its contributors
13//       may be used to endorse or promote products derived from this software
14//       without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27use 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
36/// Reexport of `Errno` as `Error`.
37type Error = errno::Errno;
38/// Simpler `Result` type with the error already set.
39type 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            &params 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            &params 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)]
477// #[non_exhaustive]
478pub 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            &params 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            &params 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            &params 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            &params as *const PKeyOpParamsKernel,
637            info_cstr.as_ptr(),
638            data.as_ptr(),
639            sig.as_ptr(),
640        )
641    }
642    .map(|res| res == 0)
643}