Skip to content

Commit 4bbe407

Browse files
TheBestTvarynkaCBenoit
authored andcommitted
fix(sspi): Kerberos server WRAP token generation and validation (#463)
1 parent 12fbd70 commit 4bbe407

File tree

2 files changed

+68
-2
lines changed

2 files changed

+68
-2
lines changed

src/kerberos/mod.rs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,19 @@ impl Sspi for Kerberos {
277277
let key_usage = self.encryption_params.sspi_encrypt_key_usage;
278278

279279
let mut wrap_token = WrapToken::with_seq_number(seq_number as u64);
280+
if self.server.is_some() {
281+
// [Flags Field](https://datatracker.ietf.org/doc/html/rfc4121#section-4.2.2):
282+
//
283+
// The meanings of bits in this field (the least significant bit is bit 0) are as follows:
284+
// Bit Name Description
285+
// --------------------------------------------------------------
286+
// 0 SentByAcceptor When set, this flag indicates the sender
287+
// is the context acceptor. When not set,
288+
// it indicates the sender is the context
289+
// initiator.
290+
// When the Kerberos is used as the Kerberos server we have to set the `SentByAcceptor` flag.
291+
wrap_token.flags |= 0x01;
292+
}
280293
wrap_token.ec = self.encryption_params.ec;
281294

282295
let mut payload = data_to_encrypt.fold(Vec::new(), |mut acc, buffer| {
@@ -379,6 +392,33 @@ impl Sspi for Kerberos {
379392
let key_usage = self.encryption_params.sspi_decrypt_key_usage;
380393

381394
let wrap_token = WrapToken::decode(encrypted.as_slice())?;
395+
// [Flags Field](https://datatracker.ietf.org/doc/html/rfc4121#section-4.2.2):
396+
//
397+
// The meanings of bits in this field (the least significant bit is bit 0) are as follows:
398+
// Bit Name Description
399+
// --------------------------------------------------------------
400+
// 0 SentByAcceptor When set, this flag indicates the sender
401+
// is the context acceptor. When not set,
402+
// it indicates the sender is the context
403+
// initiator.
404+
let is_server = u8::from(self.server.is_some());
405+
// If the Kerberos acts as the Kerberos application server, then the `SentByAcceptor` flag
406+
// of the incoming WRAP token must be disabled (because it is sent by initiator).
407+
if wrap_token.flags & 0x01 == is_server {
408+
return Err(Error::new(
409+
ErrorKind::InvalidToken,
410+
"invalid WRAP token SentByAcceptor flag",
411+
));
412+
}
413+
// 1 Sealed When set in Wrap tokens, this flag
414+
// indicates confidentiality is provided
415+
// for. It SHALL NOT be set in MIC tokens.
416+
if wrap_token.flags & 0b10 != 0b10 {
417+
return Err(Error::new(
418+
ErrorKind::InvalidToken,
419+
"the Sealed flag has to be set in WRAP token",
420+
));
421+
}
382422

383423
let mut checksum = wrap_token.checksum;
384424
// [3.4.5.4.1 Kerberos Binding of GSS_WrapEx()](learn.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/e94b3acd-8415-4d0d-9786-749d0c39d550):
@@ -623,10 +663,18 @@ impl SspiEx for Kerberos {
623663

624664
#[cfg(any(feature = "__test-data", test))]
625665
pub mod test_data {
666+
use std::time::Duration;
667+
668+
use picky_asn1::restricted_string::IA5String;
669+
use picky_asn1::wrapper::{Asn1SequenceOf, ExplicitContextTag0, ExplicitContextTag1, IntegerAsn1};
626670
use picky_krb::constants::key_usages::{ACCEPTOR_SEAL, INITIATOR_SEAL};
671+
use picky_krb::constants::types::NT_SRV_INST;
627672
use picky_krb::crypto::CipherSuite;
673+
use picky_krb::data_types::{KerberosStringAsn1, PrincipalName};
674+
use picky_krb::gss_api::MechTypeList;
628675

629676
use super::{EncryptionParams, KerberosConfig, KerberosState};
677+
use crate::kerberos::ServerProperties;
630678
use crate::Kerberos;
631679

632680
const SESSION_KEY: &[u8] = &[
@@ -664,6 +712,24 @@ pub mod test_data {
664712
}
665713
}
666714

715+
pub fn fake_server_properties() -> ServerProperties {
716+
ServerProperties {
717+
mech_types: MechTypeList::from(Vec::new()),
718+
max_time_skew: Duration::from_secs(3 * 60),
719+
ticket_decryption_key: None,
720+
service_name: PrincipalName {
721+
name_type: ExplicitContextTag0::from(IntegerAsn1::from(vec![NT_SRV_INST])),
722+
name_string: ExplicitContextTag1::from(Asn1SequenceOf::from(vec![
723+
KerberosStringAsn1::from(IA5String::from_string("TERMSRV".to_owned()).unwrap()),
724+
KerberosStringAsn1::from(IA5String::from_string("VM1.example.com".to_owned()).unwrap()),
725+
])),
726+
},
727+
user: None,
728+
client: None,
729+
authenticators_cache: Default::default(),
730+
}
731+
}
732+
667733
pub fn fake_server() -> Kerberos {
668734
Kerberos {
669735
state: KerberosState::Final,
@@ -686,7 +752,7 @@ pub mod test_data {
686752
channel_bindings: None,
687753
dh_parameters: None,
688754
krb5_user_to_user: false,
689-
server: None,
755+
server: Some(Box::new(fake_server_properties())),
690756
}
691757
}
692758
}

src/kerberos/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ fn secbuffer_readonly_with_checksum() {
7373
channel_bindings: None,
7474
dh_parameters: None,
7575
krb5_user_to_user: false,
76-
server: None,
76+
server: Some(Box::new(test_data::fake_server_properties())),
7777
};
7878

7979
// RPC header

0 commit comments

Comments
 (0)