use crate::error::{Error, MajorFlags};
use libgssapi_sys::{
GSS_S_COMPLETE, OM_uint32, gss_OID, gss_OID_desc, gss_OID_set, gss_OID_set_desc,
gss_add_oid_set_member, gss_create_empty_oid_set, gss_release_oid_set,
gss_test_oid_set_member,
};
use std::{
self,
cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd},
collections::HashMap,
fmt,
hash::{Hash, Hasher},
iter::{ExactSizeIterator, FromIterator, IntoIterator, Iterator},
marker::PhantomData,
ops::Deref,
os::raw::c_int,
ptr, slice,
sync::LazyLock,
};
pub static GSS_NT_USER_NAME: Oid<'static> =
Oid::from_slice(b"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01");
pub static GSS_NT_MACHINE_UID_NAME: Oid<'static> =
Oid::from_slice(b"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02");
pub static GSS_NT_STRING_UID_NAME: Oid<'static> =
Oid::from_slice(b"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03");
pub static GSS_NT_HOSTBASED_SERVICE: Oid<'static> =
Oid::from_slice(b"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04");
pub static GSS_NT_ANONYMOUS: Oid<'static> = Oid::from_slice(b"\x2b\x06\x01\x05\x06\x03");
pub static GSS_NT_EXPORT_NAME: Oid<'static> =
Oid::from_slice(b"\x2b\x06\x01\x05\x06\x04");
pub static GSS_NT_COMPOSITE_EXPORT: Oid<'static> =
Oid::from_slice(b"\x2b\x06\x01\x05\x06\x06");
pub static GSS_NT_KRB5_PRINCIPAL: Oid<'static> =
Oid::from_slice(b"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01");
pub static GSS_NT_KRB5_ENTERPRISE_NAME: Oid<'static> =
Oid::from_slice(b"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x06");
pub static GSS_INQ_SSPI_SESSION_KEY: Oid<'static> =
Oid::from_slice(b"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05");
pub static GSS_INQ_NEGOEX_KEY: Oid<'static> =
Oid::from_slice(b"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x10");
pub static GSS_INQ_NEGOEX_VERIFY_KEY: Oid<'static> =
Oid::from_slice(b"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x11");
pub static GSS_MA_NEGOEX_AND_SPNEGO: Oid<'static> =
Oid::from_slice(b"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x12");
pub static GSS_SEC_CONTEXT_SASL_SSF: Oid<'static> =
Oid::from_slice(b"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0f");
pub static GSS_MECH_KRB5: Oid<'static> =
Oid::from_slice(b"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02");
pub static GSS_MECH_IAKERB: Oid<'static> = Oid::from_slice(b"\x2b\x06\x01\x05\x02\x05");
pub static GSS_MECH_SPNEGO: Oid<'static> = Oid::from_slice(b"\x2b\x06\x01\x05\x05\x02");
pub static GSS_KRB5_CRED_NO_CI_FLAGS_X: Oid<'static> =
Oid::from_slice(b"\x2a\x85\x70\x2b\x0d\x1d");
pub static GSS_KRB5_GET_CRED_IMPERSONATOR: Oid<'static> =
Oid::from_slice(b"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0e");
pub(crate) const NO_OID: gss_OID = ptr::null_mut();
pub(crate) const NO_OID_SET: gss_OID_set = ptr::null_mut();
static OIDS: LazyLock<HashMap<&'static [u8], &'static str>> = LazyLock::new(|| {
HashMap::from_iter([
(&*GSS_NT_USER_NAME, "GSS_NT_USER_NAME"),
(&*GSS_NT_MACHINE_UID_NAME, "GSS_NT_MACHINE_UID_NAME"),
(&*GSS_NT_STRING_UID_NAME, "GSS_NT_STRING_UID_NAME"),
(&*GSS_NT_HOSTBASED_SERVICE, "GSS_NT_HOSTBASED_SERVICE"),
(&*GSS_NT_ANONYMOUS, "GSS_NT_ANONYMOUS"),
(&*GSS_NT_EXPORT_NAME, "GSS_NT_EXPORT_NAME"),
(&*GSS_NT_COMPOSITE_EXPORT, "GSS_NT_COMPOSITE_EXPORT"),
(&*GSS_INQ_SSPI_SESSION_KEY, "GSS_INQ_SSPI_SESSION_KEY"),
(&*GSS_INQ_NEGOEX_KEY, "GSS_INQ_NEGOEX_KEY"),
(&*GSS_INQ_NEGOEX_VERIFY_KEY, "GSS_INQ_NEGOEX_VERIFY_KEY"),
(&*GSS_MA_NEGOEX_AND_SPNEGO, "GSS_MA_NEGOEX_AND_SPNEGO"),
(&*GSS_SEC_CONTEXT_SASL_SSF, "GSS_SEC_CONTEXT_SASL_SSF"),
(&*GSS_MECH_KRB5, "GSS_MECH_KRB5"),
(&*GSS_MECH_IAKERB, "GSS_MECH_IAKERB"),
(&*GSS_NT_KRB5_PRINCIPAL, "GSS_KRB5_NT_PRINCIPAL"),
(&*GSS_NT_KRB5_ENTERPRISE_NAME, "GSS_KRB5_NT_ENTERPRISE_NAME"),
(&*GSS_KRB5_CRED_NO_CI_FLAGS_X, "GSS_KRB5_CRED_NO_CI_FLAGS_X"),
(
&*GSS_KRB5_GET_CRED_IMPERSONATOR,
"GSS_KRB5_GET_CRED_IMPERSONATOR",
),
])
});
#[repr(transparent)]
pub struct Oid<'a>(gss_OID_desc, PhantomData<&'a [u8]>);
impl<'a> Clone for Oid<'a> {
fn clone(&self) -> Self {
*self
}
}
impl<'a> Copy for Oid<'a> {}
unsafe impl<'a> Send for Oid<'a> {}
unsafe impl<'a> Sync for Oid<'a> {}
impl<'a> fmt::Debug for Oid<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let bytes: &[u8] = self;
match OIDS.get(bytes) {
None => write!(f, "{:?}", bytes),
Some(name) => write!(f, "{}", name),
}
}
}
impl<'a> fmt::Display for Oid<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fmt::Debug::fmt(self, f)
}
}
impl<'a> Deref for Oid<'a> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
if self.0.elements.is_null() || self.0.length == 0 {
&[]
} else {
unsafe {
slice::from_raw_parts(self.0.elements.cast(), self.0.length as usize)
}
}
}
}
impl<'a, 'b> PartialEq<Oid<'b>> for Oid<'a> {
fn eq(&self, other: &Oid<'b>) -> bool {
(self as &[u8]) == (other as &[u8])
}
}
impl<'a> Eq for Oid<'a> {}
impl<'a, 'b> PartialOrd<Oid<'b>> for Oid<'a> {
fn partial_cmp(&self, other: &Oid<'b>) -> Option<Ordering> {
(self as &[u8]).partial_cmp(other as &[u8])
}
}
impl<'a> Ord for Oid<'a> {
fn cmp(&self, other: &Oid<'a>) -> Ordering {
(self as &[u8]).cmp(other as &[u8])
}
}
impl<'a> Hash for Oid<'a> {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
(self as &[u8]).hash(state)
}
}
impl Oid<'static> {
pub const fn from_slice(ber: &'static [u8]) -> Oid<'static> {
let length = ber.len() as OM_uint32;
let elements = ber.as_ptr() as *mut std::ffi::c_void;
Oid(gss_OID_desc { length, elements }, PhantomData)
}
#[allow(dead_code)]
pub(crate) unsafe fn from_c(ptr: gss_OID) -> Oid<'static> {
assert!(
!ptr.is_null(),
"Oid::from_c: gssapi returned a null OID pointer"
);
Oid(unsafe { *ptr }, PhantomData)
}
}
impl<'a> Oid<'a> {
pub(crate) unsafe fn to_c(&self) -> gss_OID {
self as *const Oid<'a> as gss_OID
}
pub unsafe fn from_raw_desc(desc: gss_OID_desc) -> Oid<'a> {
Oid(desc, PhantomData)
}
pub unsafe fn assume_static(self) -> Oid<'static> {
Oid(self.0, PhantomData)
}
}
pub struct OidSetIter<'a> {
current: usize,
set: &'a OidSet,
}
impl<'a> Iterator for OidSetIter<'a> {
type Item = Oid<'a>;
fn next(&mut self) -> Option<Self::Item> {
let res = self.set.get(self.current);
if res.is_some() {
self.current += 1;
}
res
}
}
impl<'a> ExactSizeIterator for OidSetIter<'a> {
fn len(&self) -> usize {
self.set.len() - self.current
}
}
pub struct OidSet(gss_OID_set);
unsafe impl Send for OidSet {}
unsafe impl Sync for OidSet {}
impl Drop for OidSet {
fn drop(&mut self) {
if !self.0.is_null() {
let mut _minor = GSS_S_COMPLETE;
let _major = unsafe {
gss_release_oid_set(
&mut _minor as *mut OM_uint32,
&mut self.0 as *mut gss_OID_set,
)
};
}
}
}
impl<'a> IntoIterator for &'a OidSet {
type Item = Oid<'a>;
type IntoIter = OidSetIter<'a>;
fn into_iter(self) -> Self::IntoIter {
OidSetIter {
current: 0,
set: self,
}
}
}
impl fmt::Debug for OidSet {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fmt::Debug::fmt(&self.into_iter().collect::<Vec<_>>(), f)
}
}
impl Default for OidSet {
fn default() -> Self {
Self::new()
}
}
impl OidSet {
pub fn new() -> OidSet {
OidSet(ptr::null_mut())
}
pub fn singleton(id: Oid<'_>) -> Result<OidSet, Error> {
let mut set = OidSet::new();
set.add(id)?;
Ok(set)
}
#[allow(dead_code)]
pub(crate) unsafe fn from_c(ptr: gss_OID_set) -> OidSet {
OidSet(ptr)
}
pub(crate) unsafe fn to_c(&self) -> gss_OID_set {
self.0
}
pub fn len(&self) -> usize {
if self.0.is_null() {
0
} else {
unsafe { (*self.0).count as usize }
}
}
pub fn get<'a>(&'a self, i: usize) -> Option<Oid<'a>> {
if i >= self.len() {
return None;
}
let desc = unsafe { *(*self.0).elements.add(i) };
Some(Oid(desc, PhantomData))
}
pub fn add(&mut self, id: Oid<'_>) -> Result<(), Error> {
if self.0.is_null() {
let mut minor = GSS_S_COMPLETE;
let mut out = ptr::null_mut::<gss_OID_set_desc>();
let major = unsafe {
gss_create_empty_oid_set(
&mut minor as *mut OM_uint32,
&mut out as *mut gss_OID_set,
)
};
if major != GSS_S_COMPLETE {
return Err(Error {
major: MajorFlags::from_bits_retain(major),
minor,
});
}
self.0 = out;
}
let mut minor = GSS_S_COMPLETE;
let major = unsafe {
gss_add_oid_set_member(
&mut minor as *mut OM_uint32,
id.to_c(),
&mut self.0 as *mut gss_OID_set,
)
};
if major == GSS_S_COMPLETE {
Ok(())
} else {
Err(Error {
major: MajorFlags::from_bits_retain(major),
minor,
})
}
}
pub fn contains(&self, id: Oid<'_>) -> Result<bool, Error> {
if self.0.is_null() {
return Ok(false);
}
let mut minor = GSS_S_COMPLETE;
let mut present = 0;
let major = unsafe {
gss_test_oid_set_member(
&mut minor as *mut OM_uint32,
id.to_c(),
self.0,
&mut present as *mut c_int,
)
};
if major == GSS_S_COMPLETE {
Ok(if present != 0 { true } else { false })
} else {
Err(Error {
major: MajorFlags::from_bits_retain(major),
minor,
})
}
}
}