Skip to content

Commit

Permalink
Kerberos smart card logon implementation (#145)
Browse files Browse the repository at this point in the history
Adds Kerberos smart card logon implementation.

* Moves a lot of code from the Pku2u submodules into the `pk-init` module.
   Motivation: This code is reused in the Kerberos AS-messages exchange.
* Moves PA-DATA (pre-authentication data) generation and session key extraction
   into a separate module.
* Small builders refactoring. I wrote comments about it in the code.

References:

* RFC 4556: Public Key Cryptography for Initial Authentication in Kerberos (PKINIT):
   https://www.rfc-editor.org/rfc/rfc4556.html
* MS-CSSP TSCredentials:
   https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cssp/94a1ab00-5500-42fd-8d3d-7a84e6c2cf03
* Winscard.h header - Win32 apps:
   https://learn.microsoft.com/en-us/windows/win32/api/winscard/
* Microsoft Base Smart Card Cryptographic Service Provider:
   https://learn.microsoft.com/en-us/previous-versions/windows/desktop/secsmart/microsoft-base-smart-card-cryptographic-service-provider
  • Loading branch information
TheBestTvarynka authored Aug 28, 2023
1 parent 1e26f97 commit 6ee6157
Show file tree
Hide file tree
Showing 17 changed files with 726 additions and 387 deletions.
2 changes: 2 additions & 0 deletions src/builders/accept_sec_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ impl<
}

impl<'b, 'a: 'b, AuthData, CredsHandle> FilledAcceptSecurityContext<'a, AuthData, CredsHandle> {
/// Transforms the builder into new one with the other `AuthData` and `CredsHandle` types.
/// Useful when we need to pass the builder into the security package with other `AuthData` and `CredsHandle` types.
pub(crate) fn full_transform<CredsHandle2, AuthData2>(
self,
inner: SspiPackage<'a, CredsHandle2, AuthData2>,
Expand Down
2 changes: 2 additions & 0 deletions src/builders/acq_cred_handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ where
}

impl<'b, 'a: 'b, CredsHandle, AuthData> FilledAcquireCredentialsHandle<'a, CredsHandle, AuthData> {
/// Transforms the builder into new one with the other `AuthData` and `CredsHandle` types.
/// Useful when we need to pass the builder into the security package with other `AuthData` and `CredsHandle` types.
pub(crate) fn full_transform<NewCredsHandle, NewAuthData>(
self,
inner: SspiPackage<'a, NewCredsHandle, NewAuthData>,
Expand Down
4 changes: 4 additions & 0 deletions src/builders/init_sec_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ impl<
&mut self.credentials_handle
}

pub fn credentials_handle(&mut self) -> &Option<&'a mut CredsHandle> {
&self.credentials_handle
}

pub fn new() -> Self {
Self {
phantom_creds_use_set: PhantomData,
Expand Down
1 change: 0 additions & 1 deletion src/cert_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ unsafe fn find_raw_cert_by_thumbprint(thumbprint: &[u8], cert_store: *mut c_void

while !certificate.is_null() {
let cert_der = from_raw_parts((*certificate).pbCertEncoded, (*certificate).cbCertEncoded as usize);

let mut sha1 = Sha1::new();
sha1.update(cert_der);
let cert_thumbprint = sha1.finalize().to_vec();
Expand Down
4 changes: 2 additions & 2 deletions src/credssp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ impl SspiImpl for SspiContext {
} else {
return Err(Error::new(
ErrorKind::NoCredentials,
"Auth identity is not provided for the Pku2u",
"Auth identity is not provided for the Ntlm",
));
};
builder
Expand Down Expand Up @@ -691,7 +691,7 @@ impl SspiImpl for SspiContext {
} else {
return Err(Error::new(
ErrorKind::NoCredentials,
"Auth identity is not provided for the Pku2u",
"Auth identity is not provided for the Ntlm",
));
};
builder.full_transform(ntlm, Some(&mut Some(auth_identity))).execute()
Expand Down
3 changes: 3 additions & 0 deletions src/credssp/sspi_cred_ssp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,9 @@ impl SspiImpl for SspiCredSsp {
// The CredSSP Protocol enables an application to securely delegate a user's credentials from a client to a target server.
builder.context_requirements |= ClientRequestFlags::DELEGATE;

// The CredSSP flag should be always set in the CredSsp protocol.
builder.context_requirements.set(ClientRequestFlags::MUTUAL_AUTH, true);

let status = match &self.state {
CredSspState::Tls => {
// input token can not present on the first call
Expand Down
7 changes: 3 additions & 4 deletions src/kerberos/client/extractors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ pub fn extract_session_key_from_tgs_rep(
.decrypt(session_key, TGS_REP_ENC_SESSION_KEY, &tgs_rep.0.enc_part.0.cipher.0 .0)
.map_err(|e| Error::new(ErrorKind::InternalError, format!("{:?}", e)))?;

trace!(?enc_data, "Plain TgsRep::EncData");

let enc_as_rep_part: EncTgsRepPart = picky_asn1_der::from_bytes(&enc_data)?;

Ok(enc_as_rep_part.0.key.0.key_value.0.to_vec())
Expand Down Expand Up @@ -104,10 +106,7 @@ pub fn extract_encryption_params_from_as_rep(as_rep: &AsRep) -> Result<(u8, Stri
.ok_or_else(|| Error::new(ErrorKind::InternalError, "Missing salt in EtypeInto2Entry"))?,
))
}
None => Err(Error::new(
ErrorKind::NoPaData,
format!("Missing PaData: PA_ETYPE_INFO2 ({:0x?})", PA_ETYPE_INFO2_TYPE),
)),
None => Ok((*as_rep.0.enc_part.0.etype.0 .0.first().unwrap(), Default::default())),
}
}

Expand Down
6 changes: 2 additions & 4 deletions src/kerberos/client/generators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,11 @@ pub fn generate_as_req_kdc_body(options: &GenerateAsReqOptions) -> Result<KdcReq
}

#[instrument(level = "debug", ret, skip_all)]
pub fn generate_as_req(pa_datas: &[PaData], kdc_req_body: KdcReqBody) -> AsReq {
pub fn generate_as_req(pa_datas: Vec<PaData>, kdc_req_body: KdcReqBody) -> AsReq {
AsReq::from(KdcReq {
pvno: ExplicitContextTag1::from(IntegerAsn1::from(vec![KERBEROS_VERSION])),
msg_type: ExplicitContextTag2::from(IntegerAsn1::from(vec![AS_REQ_MSG_TYPE])),
padata: Optional::from(Some(ExplicitContextTag3::from(Asn1SequenceOf::from(
pa_datas.to_owned(),
)))),
padata: Optional::from(Some(ExplicitContextTag3::from(Asn1SequenceOf::from(pa_datas)))),
req_body: ExplicitContextTag4::from(kdc_req_body),
})
}
Expand Down
Loading

0 comments on commit 6ee6157

Please sign in to comment.