use bytes::BytesMut;
use libgssapi::{
context::{ClientCtx, CtxFlags, SecurityContext, ServerCtx},
credential::{Cred, CredUsage},
error::Error,
name::Name,
oid::{OidSet, GSS_MECH_KRB5, GSS_NT_HOSTBASED_SERVICE},
util::{Buf, GssIov, GssIovFake, GssIovType},
};
use std::env::args;
fn setup_server_ctx(
service_name: &[u8],
desired_mechs: &OidSet,
) -> Result<(ServerCtx, Name), Error> {
println!("import name");
let name = Name::new(service_name, Some(&GSS_NT_HOSTBASED_SERVICE))?;
let cname = name.canonicalize(Some(&GSS_MECH_KRB5))?;
println!("canonicalize name for kerberos 5");
println!("server name: {}, server cname: {}", name, cname);
let server_cred =
Cred::acquire(Some(&cname), None, CredUsage::Accept, Some(desired_mechs))?;
println!("acquired server credentials: {:#?}", server_cred.info()?);
Ok((ServerCtx::new(server_cred), cname))
}
fn setup_client_ctx(
service_name: Name,
desired_mechs: &OidSet,
) -> Result<ClientCtx, Error> {
let client_cred =
Cred::acquire(None, None, CredUsage::Initiate, Some(&desired_mechs))?;
println!(
"acquired default client credentials: {:#?}",
client_cred.info()?
);
Ok(ClientCtx::new(
Some(client_cred),
service_name,
CtxFlags::GSS_C_MUTUAL_FLAG,
Some(&GSS_MECH_KRB5),
))
}
fn wrap_secret_msg_alloc(ctx: &mut ClientCtx) -> Result<BytesMut, Error> {
let mut buf = BytesMut::new();
let mut data = {
buf.extend_from_slice(b"super secret message");
buf.split()
};
let mut iovs = [
GssIov::new_alloc(GssIovType::Header),
GssIov::new(GssIovType::Data, &mut *data),
GssIov::new_alloc(GssIovType::Padding),
GssIov::new_alloc(GssIovType::Trailer),
];
ctx.wrap_iov(true, &mut iovs[..])?;
println!("buffer lengths are as follows ...");
println!("header: {}", iovs[0].len());
println!("data: {}", iovs[1].len());
println!("padding: {}", iovs[2].len());
println!("trailer: {}", iovs[3].len());
buf.extend_from_slice(&*iovs[0]);
buf.extend_from_slice(&*iovs[1]);
buf.extend_from_slice(&*iovs[2]);
buf.extend_from_slice(&*iovs[3]);
Ok(buf.split())
}
fn wrap_secret_msg_noalloc(ctx: &mut ClientCtx) -> Result<BytesMut, Error> {
let mut buf = BytesMut::new();
let mut data = {
buf.extend_from_slice(b"super secret message");
buf.split()
};
let mut len_iovs = [
GssIovFake::new(GssIovType::Header),
GssIov::new(GssIovType::Data, &mut *data).as_fake(),
GssIovFake::new(GssIovType::Padding),
GssIovFake::new(GssIovType::Trailer),
];
ctx.wrap_iov_length(true, &mut len_iovs[..])?;
println!("requested buffer lengths are as follows ...");
println!("header: {}", len_iovs[0].len());
println!("data: {}", len_iovs[1].len());
println!("padding: {}", len_iovs[2].len());
println!("trailer: {}", len_iovs[3].len());
let mut header = {
buf.resize(len_iovs[0].len(), 0x0);
buf.split()
};
let mut padding = {
buf.resize(len_iovs[2].len(), 0x0);
buf.split()
};
let mut trailer = {
buf.resize(len_iovs[3].len(), 0x0);
buf.split()
};
{
let mut iovs = [
GssIov::new(GssIovType::Header, &mut *header),
GssIov::new(GssIovType::Data, &mut *data),
GssIov::new(GssIovType::Padding, &mut *padding),
GssIov::new(GssIovType::Trailer, &mut *trailer),
];
ctx.wrap_iov(true, &mut iovs[..])?;
}
buf.extend_from_slice(&*header);
buf.extend_from_slice(&*data);
buf.extend_from_slice(&*padding);
buf.extend_from_slice(&*trailer);
Ok(buf.split())
}
fn unwrap_secret_msg(ctx: &mut ServerCtx, mut msg: BytesMut) -> Result<BytesMut, Error> {
let (hdr_len, data_len) = {
let mut iov = [
GssIov::new(GssIovType::Stream, &mut *msg),
GssIov::new(GssIovType::Data, &mut []),
];
ctx.unwrap_iov(&mut iov[..])?;
let hdr_len = iov[0].header_length(&iov[1]).unwrap();
let data_len = iov[1].len();
(hdr_len, data_len)
};
let mut data = msg.split_off(hdr_len);
msg.clear(); data.truncate(data_len); Ok(data)
}
fn run(service_name: &[u8]) -> Result<(), Error> {
let desired_mechs = {
let mut s = OidSet::new()?;
s.add(&GSS_MECH_KRB5)?;
s
};
let (mut server_ctx, cname) = setup_server_ctx(service_name, &desired_mechs)?;
let mut client_ctx = setup_client_ctx(cname, &desired_mechs)?;
let mut server_tok: Option<Buf> = None;
loop {
match client_ctx.step(server_tok.as_ref().map(|b| &**b), None)? {
None => break,
Some(client_tok) => match server_ctx.step(&*client_tok)? {
None => break,
Some(tok) => {
server_tok = Some(tok);
}
},
}
}
println!("security context initialized successfully");
println!("client ctx info: {:#?}", client_ctx.info()?);
println!("server ctx info: {:#?}", server_ctx.info()?);
println!("wrapping secret message using no alloc method");
let encrypted = wrap_secret_msg_noalloc(&mut client_ctx)?;
let decrypted = unwrap_secret_msg(&mut server_ctx, encrypted)?;
println!(
"The secret message is \"{}\"",
String::from_utf8_lossy(&*decrypted)
);
println!("wrapping secret message using alloc method");
let encrypted = wrap_secret_msg_alloc(&mut client_ctx)?;
let decrypted = unwrap_secret_msg(&mut server_ctx, encrypted)?;
println!(
"The secret message is \"{}\"",
String::from_utf8_lossy(&*decrypted)
);
Ok(())
}
fn main() {
let args = args().collect::<Vec<_>>();
if args.len() != 2 {
println!("usage: {}: <service@host>", args[0]);
} else {
match run(&args[1].as_bytes()) {
Ok(()) => (),
Err(e) => println!("{}", e),
}
}
}