#include <Kerberos/krb.h> #include <Kerberos/UKerberos5Context.h> #include <Kerberos/UPrincipal.h> #include "ThrowUtils.h" #include <cstring> #if __MACH__ && __MWERKS__ namespace std { using ::strlen; } #endif /* __MACH__ && __MWERKS__ */ //#include <CoreServices/CoreServices.h> /* * List of possible errors returned by krb5 functions, not specified in * krb5 API... Actually any of these functions can return profile errors... * * krb5_425_conv_principal: * krb5_get_realm_domain + krb5_build_principal * krb5_get_realm_domain: * profile_get_string + ENOMEM * krb5_build_principal: * ENOMEM, 0 * profile_get_string: * PROF_NO_SECTION, PROF_NO_RELATION, ENOMEM, 0 * krb5_build_principal_ext: * ENOMEM, 0 * krb5_parse_name: * KRB5_PARSE_MALFORMED, ENOMEM, 0 * krb5_unparse_name: * KRB5_PARSE_MALFORMED, ENOMEM, 0 * krb5_524_conv_principal * KRB5_INVALID_PRINCIPAL, 0 * */ void UPrincipalAutoPtrDeleter::Delete ( krb5_principal inPrincipal) { ULazyKerberos5Context context; krb5_free_principal (context.Get (), inPrincipal); } // create principal from version and triplet UPrincipal::UPrincipal ( UPrincipal::EVersion inVersion, const char* inName, const char* inInstance, const char* inRealm) { krb5_principal principal; if (inVersion == kerberosV4) { // v4 converted to v5 first krb5_error_code err = krb5_425_conv_principal (mContext.Get (), inName, inInstance, inRealm, &principal); Assert_ ((err == ENOMEM) || (err == PROF_NO_SECTION) || (err == PROF_NO_RELATION) || (err == 0)); if (err == ENOMEM) { std::bad_alloc foo; // Mac OS X gcc sucks. You have to do it this stupid way DebugThrow_ (foo); } else if (err != 0) DebugThrow_ (UProfileConfigurationError (err)); } else { krb5_error_code err = krb5_build_principal_ext (mContext.Get (), &principal, std::strlen (inRealm), inRealm, std::strlen (inName), inName, std::strlen (inInstance), inInstance, NULL); Assert_ ((err == ENOMEM) || (err == PROF_NO_SECTION) || (err == PROF_NO_RELATION) || (err == 0)); if (err == ENOMEM) { std::bad_alloc foo; // Mac OS X gcc sucks. You have to do it this stupid way DebugThrow_ (foo); } else if (err != 0) DebugThrow_ (UProfileConfigurationError (err)); } Reset (principal); } // Create principal from version and string UPrincipal::UPrincipal ( EVersion inVersion, const char* inPrincipal) { krb5_principal principal; if (inVersion == kerberosV4) { // v4 converted to v5 first char name [ANAME_SZ]; char instance [INST_SZ]; char realm [REALM_SZ]; name[0] = '\0'; instance[0] = '\0'; realm[0] = '\0'; // So we can detect that kname_parse didn't change realm int v4err = kname_parse (name, instance, realm, const_cast <char*> (inPrincipal)); ThrowIfKerberos4Error (v4err, std::runtime_error ("UPrincipal::UPrincipal: Bad Kerberos v4 principal.")); if (realm[0] == '\0') { v4err = krb_get_lrealm (realm, 1); ThrowIfKerberos4Error (v4err, std::runtime_error ("UPrincipal::UPrincipal: Bad Kerberos configuration.")); } krb5_error_code v5err = krb5_425_conv_principal (mContext.Get (), name, instance, realm, &principal); Assert_ ((v5err == ENOMEM) || (v5err == PROF_NO_SECTION) || (v5err == PROF_NO_RELATION) || (v5err == 0)); if (v5err == ENOMEM) { std::bad_alloc foo; // Mac OS X gcc sucks. You have to do it this stupid way DebugThrow_ (foo); } else if (v5err != 0) DebugThrow_ (UProfileConfigurationError (v5err)); } else { krb5_error_code v5err = krb5_parse_name (mContext.Get (), inPrincipal, &principal); Assert_ ((v5err == ENOMEM) || (v5err == KRB5_PARSE_MALFORMED) || (v5err == PROF_NO_SECTION) || (v5err == PROF_NO_RELATION) || (v5err == 0)); if (v5err == ENOMEM) { std::bad_alloc foo; // Mac OS X gcc sucks. You have to do it this stupid way DebugThrow_ (foo); } else if (v5err != 0) DebugThrow_ (UProfileConfigurationError (v5err)); } Reset (principal); } // Convert principal to string representation in specific version std::string UPrincipal::GetString ( EVersion inVersion) const { ThrowIfInvalid (*this); if (inVersion == kerberosV4) { // Since internal rep of principals is v5, we have to convert back to v4 char name [ANAME_SZ]; char instance [INST_SZ]; char realm [REALM_SZ]; char principal [MAX_K_NAME_SZ]; krb5_error_code v5err = krb5_524_conv_principal ( mContext.Get (), Get (), name, instance, realm); Assert_ ((v5err == KRB5_LNAME_BADFORMAT) || (v5err == 0)); if (v5err == KRB5_LNAME_BADFORMAT) DebugThrow_ (std::invalid_argument ("UPrincipal::GetString: Principal cannot be converted to Kerberos v4 format")); int v4err = kname_unparse (principal, name, instance, realm); Assert_ (v4err == KSUCCESS); return std::string (principal); } else { char* principal; krb5_error_code v5err = krb5_unparse_name ( mContext.Get (), Get (), &principal); Assert_ ((v5err == KRB5_PARSE_MALFORMED) || (v5err == ENOMEM) || (v5err == 0)); if (v5err == ENOMEM) { std::bad_alloc foo; // Mac OS X gcc sucks. You have to do it this stupid way DebugThrow_ (foo); } else if (v5err == KRB5_PARSE_MALFORMED) DebugThrow_ (std::logic_error ("UPrincipal::GetString: Principal is not valid")); std::string result = principal; krb5_free_unparsed_name (mContext.Get (), principal); return result; } } // Convert principal to display string (for user presentation, with \ removed) std::string UPrincipal::GetDisplayString ( EVersion inVersion) const { // To make the string pretty for display, we remove all backslashes used to escape // special characters. For example, "John Q\. Public@ATHENA.MIT.EDU" becomes // "John Q. Public@ATHENA.MIT.EDU". This is lossy and unrecoverable! std::string result = GetString (inVersion); std::string::size_type where = 0; for (;;) { where = result.find ('\\', where); if (where == std::string::npos) { // not found break; } result.erase (where, 1); } return result; } // Get name (1st component) of the principal std::string UPrincipal::GetName ( EVersion inVersion) const { ThrowIfInvalid (*this); if (inVersion == kerberosV4) { char name [ANAME_SZ]; char instance [INST_SZ]; char realm [REALM_SZ]; krb5_error_code v5err = krb5_524_conv_principal ( mContext.Get (), Get (), name, instance, realm); Assert_ ((v5err == KRB5_LNAME_BADFORMAT) || (v5err == 0)); if (v5err == KRB5_LNAME_BADFORMAT) DebugThrow_ (std::invalid_argument ("UPrincipal::GetName: Principal cannot be converted to Kerberos v4 format")); return std::string (name); } else { return std::string (krb5_princ_name (mContext.Get (), Get ()) -> data, krb5_princ_name (mContext.Get (), Get ()) -> length); } } // Get the instance (in the case of a multi-instance principal, just the first instance) std::string UPrincipal::GetInstance ( EVersion inVersion, u_int32_t inIndex) const { ThrowIfInvalid (*this); if (inVersion == kerberosV4) { if (inIndex != 1) DebugThrow_ (std::invalid_argument ("UPrincipal::GetInstance: index != 0 for a v4 principal.")); char name [ANAME_SZ]; char instance [INST_SZ]; char realm [REALM_SZ]; krb5_error_code v5err = krb5_524_conv_principal ( mContext.Get (), Get (), name, instance, realm); Assert_ ((v5err == KRB5_LNAME_BADFORMAT) || (v5err == 0)); if (v5err == KRB5_LNAME_BADFORMAT) DebugThrow_ (std::invalid_argument ("UPrincipal::GetInstance: Principal cannot be converted to Kerberos v4 format")); return std::string (instance); } else { if (static_cast<int32_t>(inIndex) < krb5_princ_size (mContext.Get (), Get ())) { return std::string (krb5_princ_component (mContext.Get (), Get (), static_cast<int32_t>(inIndex)) -> data, krb5_princ_component (mContext.Get (), Get (), static_cast<int32_t>(inIndex)) -> length); } return std::string (""); } } // Get the realm for a principal std::string UPrincipal::GetRealm ( EVersion inVersion) const { ThrowIfInvalid (*this); if (inVersion == kerberosV4) { char name [ANAME_SZ]; char instance [INST_SZ]; char realm [REALM_SZ]; krb5_error_code v5err = krb5_524_conv_principal ( mContext.Get (), Get (), name, instance, realm); Assert_ ((v5err == KRB5_LNAME_BADFORMAT) || (v5err == 0)); if (v5err == KRB5_LNAME_BADFORMAT) DebugThrow_ (std::invalid_argument ("UPrincipal::GetRealm: Principal cannot be converted to Kerberos v4 format")); return std::string (realm); } else { if (krb5_princ_realm (mContext.Get (), Get ()) -> length == 0) { return std::string (""); } return std::string (krb5_princ_realm (mContext.Get (), Get ()) -> data, krb5_princ_realm (mContext.Get (), Get ()) -> length); } } // Get all three components of the principal (only the first instance for a multi-instance principal) void UPrincipal::GetTriplet ( EVersion inVersion, std::string& outName, std::string& outInstance, std::string& outRealm) const { ThrowIfInvalid (*this); if (inVersion == kerberosV4) { char name [ANAME_SZ]; char instance [INST_SZ]; char realm [REALM_SZ]; krb5_error_code v5err = krb5_524_conv_principal ( mContext.Get (), Get (), name, instance, realm); Assert_ ((v5err == KRB5_LNAME_BADFORMAT) || (v5err == 0)); if (v5err == KRB5_LNAME_BADFORMAT) DebugThrow_ (std::invalid_argument ("UPrincipal::GetTriplet: Principal cannot be converted to Kerberos v4 format")); outName = name; outInstance = instance; outRealm = realm; } else { // Name outName = std::string (krb5_princ_name (mContext.Get (), Get ()) -> data, krb5_princ_name (mContext.Get (), Get ()) -> length); // Instance if (krb5_princ_size (mContext.Get (), Get ()) > 1) outInstance = std::string (krb5_princ_component (mContext.Get (), Get (), 1) -> data, krb5_princ_component (mContext.Get (), Get (), 1) -> length); else outInstance = ""; // Realm if (krb5_princ_realm (mContext.Get (), Get ()) -> length == 0) outRealm = ""; else outRealm = std::string (krb5_princ_realm (mContext.Get (), Get ()) -> data, krb5_princ_realm (mContext.Get (), Get ()) -> length); } } // Convert to KLPrincipal KLPrincipal UPrincipal::GetKLPrincipal () const { ThrowIfInvalid (*this); KLPrincipal newPrincipal; KLStatus err = KLCreatePrincipalFromString (GetString (kerberosV5).c_str (), kerberosVersion_V5, &newPrincipal); if (err != klNoErr) { Assert_ ((err == klMemFullErr) || (err == klV5InitializationFailedErr) || (err == klParameterErr) || (err == klBadPrincipalErr) || (err == klPreferencesReadErr)); if (err == klMemFullErr) { std::bad_alloc foo; // Mac OS X gcc sucks. You have to do it this stupid way DebugThrow_ (foo); } else if ((err == klV5InitializationFailedErr) || (err == klPreferencesReadErr)) { DebugThrow_ (UPrincipalRuntimeError (err)); } else { DebugThrow_ (UPrincipalLogicError (err)); } } return newPrincipal; } // Clone by making a new copy of the principal data UPrincipal UPrincipal::Clone () const { ThrowIfInvalid (*this); krb5_principal newPrincipal = NULL; krb5_error_code v5err = krb5_copy_principal (mContext.Get (), Get (), &newPrincipal); Assert_ ((v5err == 0) || (v5err == ENOMEM)); if (v5err == ENOMEM){ std::bad_alloc foo; // Mac OS X gcc sucks. You have to do it this stupid way DebugThrow_ (foo); } return UPrincipal (newPrincipal); } // Compare principals bool UPrincipal::operator == (const UPrincipal& inCompareTo) { ThrowIfInvalid (inCompareTo); return krb5_principal_compare (mContext.Get (), Get (), inCompareTo.Get ()); }