Skip to main content

auth/
auth.rs

1use bytes::Bytes;
2use cross_krb5::{AcceptFlags, ClientCtx, InitiateFlags, K5Ctx, ServerCtx, Step};
3use std::{env::args, process::exit, sync::mpsc, thread};
4
5// The auth example pushes the server and the client in one process, which SSPI
6// has issues with because keytabs are not a thing in windows. You can work around
7// this by running it as SYSTEM and targeting the machine account, assuming you are
8// joined to a domain.
9//
10// ````
11// schtasks /create /tn krbtest /ru SYSTEM /sc once /st 23:59 /f /tr "cmd /c cd /d C:\Users\eric\proj\cross-krb5 && .\target\debug\examples\auth.exe HOST/win11-test-vm.ryu-oh.org > C:\krbtest.txt 2>&1"
12// schtasks /run /tn krbtest & timeout 2 & type C:\krbtest.txt
13// schtasks /delete /tn krbtest /f
14// ````
15
16enum Msg {
17    Token(Bytes),
18    Msg(Bytes),
19}
20
21fn server(spn: String, input: mpsc::Receiver<Msg>, output: mpsc::Sender<Msg>) {
22    let mut server = ServerCtx::new(AcceptFlags::empty(), Some(&spn), None).expect("new");
23    let mut server = loop {
24        let token = match input.recv().expect("expected data") {
25            Msg::Msg(_) => panic!("server not finished initializing"),
26            Msg::Token(t) => t,
27        };
28        match server.step(&*token).expect("step") {
29            Step::Finished((ctx, token)) => {
30                if let Some(token) = token {
31                    output
32                        .send(Msg::Token(Bytes::copy_from_slice(&*token)))
33                        .expect("send");
34                }
35                break ctx;
36            }
37            Step::Continue((ctx, token)) => {
38                output.send(Msg::Token(Bytes::copy_from_slice(&*token))).expect("send");
39                server = ctx;
40            }
41        }
42    };
43    match input.recv().expect("expected data msg") {
44        Msg::Token(_) => panic!("unexpected extra token"),
45        Msg::Msg(secret_msg) => println!(
46            "{}",
47            String::from_utf8_lossy(&server.unwrap(&*secret_msg).expect("unwrap"))
48        ),
49    }
50}
51
52fn client(spn: &str, input: mpsc::Receiver<Msg>, output: mpsc::Sender<Msg>) {
53    let (mut client, token) =
54        ClientCtx::new(InitiateFlags::empty(), None, spn, None).expect("new");
55    output.send(Msg::Token(Bytes::copy_from_slice(&*token))).expect("send");
56    let mut client = loop {
57        let token = match input.recv().expect("expected data") {
58            Msg::Msg(_) => panic!("client not finished initializing"),
59            Msg::Token(t) => t,
60        };
61        match client.step(&*token).expect("step") {
62            Step::Finished((ctx, token)) => {
63                if let Some(token) = token {
64                    output
65                        .send(Msg::Token(Bytes::copy_from_slice(&*token)))
66                        .expect("send");
67                }
68                break ctx;
69            }
70            Step::Continue((ctx, token)) => {
71                output.send(Msg::Token(Bytes::copy_from_slice(&*token))).expect("send");
72                client = ctx;
73            }
74        }
75    };
76    let msg = client.wrap(true, b"super secret message").expect("wrap");
77    output.send(Msg::Msg(Bytes::copy_from_slice(&*msg))).expect("send");
78}
79
80fn main() {
81    let args = args().collect::<Vec<_>>();
82    if args.len() != 2 {
83        println!("usage: {}: <service/host@REALM>", args[0]);
84        exit(1);
85    }
86    let spn = String::from(&args[1]);
87    let (server_snd, server_recv) = mpsc::channel();
88    let (client_snd, client_recv) = mpsc::channel();
89    thread::spawn(move || server(spn, server_recv, client_snd));
90    client(&args[1], client_recv, server_snd);
91}