// See the file "COPYING" in the main distribution directory for copyright. #include "zeek/analyzer/protocol/krb/KRB.h" #include namespace zeek::analyzer::krb { bool KRB_Analyzer::krb_available = false; #ifdef USE_KRB5 krb5_context KRB_Analyzer::krb_context = nullptr; krb5_keytab KRB_Analyzer::krb_keytab = nullptr; std::once_flag KRB_Analyzer::krb_initialized; #endif KRB_Analyzer::KRB_Analyzer(Connection* conn) : Analyzer("KRB", conn) { interp = new binpac::KRB::KRB_Conn(this); #ifdef USE_KRB5 std::call_once(krb_initialized, Initialize_Krb); #endif } #ifdef USE_KRB5 static void warn_krb(const char* msg, krb5_context ctx, krb5_error_code code) { auto err = krb5_get_error_message(ctx, code); reporter->Warning("%s (%s)", msg, err); krb5_free_error_message(ctx, err); } void KRB_Analyzer::Initialize_Krb() { if ( BifConst::KRB::keytab->Len() == 0 ) return; // no keytab set const char* keytab_filename = BifConst::KRB::keytab->CheckString(); if ( access(keytab_filename, R_OK) != 0 ) { reporter->Warning("KRB: Can't access keytab (%s)", keytab_filename); return; } krb5_error_code retval = krb5_init_context(&krb_context); if ( retval ) { warn_krb("KRB: Couldn't initialize the context", krb_context, retval); return; } retval = krb5_kt_resolve(krb_context, keytab_filename, &krb_keytab); if ( retval ) { warn_krb("KRB: Couldn't resolve keytab", krb_context, retval); return; } krb_available = true; } #endif KRB_Analyzer::~KRB_Analyzer() { delete interp; } void KRB_Analyzer::Done() { Analyzer::Done(); } void KRB_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, uint64_t seq, const IP_Hdr* ip, int caplen) { Analyzer::DeliverPacket(len, data, orig, seq, ip, caplen); try { interp->NewData(orig, data, data + len); } catch ( const binpac::Exception& e ) { AnalyzerViolation(util::fmt("Binpac exception: %s", e.c_msg())); } } StringValPtr KRB_Analyzer::GetAuthenticationInfo(const String* principal, const String* ciphertext, const zeek_uint_t enctype) { #ifdef USE_KRB5 if ( ! krb_available ) return nullptr; String delim("/"); int pos = principal->FindSubstring(&delim); if ( pos == -1 ) { reporter->Warning("KRB: Couldn't parse principal (%s)", principal->CheckString()); return nullptr; } std::unique_ptr service = unique_ptr(principal->GetSubstring(0, pos)); std::unique_ptr hostname = unique_ptr(principal->GetSubstring(pos + 1, -1)); if ( ! service || ! hostname ) { reporter->Warning("KRB: Couldn't parse principal (%s)", principal->CheckString()); return nullptr; } krb5_principal sprinc; krb5_error_code retval = krb5_sname_to_principal(krb_context, hostname->CheckString(), service->CheckString(), KRB5_NT_SRV_HST, &sprinc); if ( retval ) { warn_krb("KRB: Couldn't generate principal name", krb_context, retval); return nullptr; } auto tkt = static_cast(util::safe_malloc(sizeof(krb5_ticket))); memset(tkt, 0, sizeof(krb5_ticket)); tkt->server = sprinc; tkt->enc_part.enctype = enctype; auto ctd = static_cast(util::safe_malloc(ciphertext->Len())); memcpy(ctd, ciphertext->Bytes(), ciphertext->Len()); tkt->enc_part.ciphertext.data = ctd; tkt->enc_part.ciphertext.length = ciphertext->Len(); retval = krb5_server_decrypt_ticket_keytab(krb_context, krb_keytab, tkt); if ( retval ) { krb5_free_ticket(krb_context, tkt); warn_krb("KRB: Couldn't decrypt ticket", krb_context, retval); return nullptr; } char* cp; retval = krb5_unparse_name(krb_context, tkt->enc_part2->client, &cp); if ( retval ) { krb5_free_ticket(krb_context, tkt); warn_krb("KRB: Couldn't unparse name", krb_context, retval); return nullptr; } auto ret = make_intrusive(cp); krb5_free_unparsed_name(krb_context, cp); krb5_free_ticket(krb_context, tkt); return ret; #else return nullptr; #endif } } // namespace zeek::analyzer::krb