public static T GenerateServiceTicket <T>(ServiceTicketRequest request) where T : KrbKdcRep, new() { if (request.EncryptedPartKey == null) { throw new InvalidOperationException("A client key must be provided to encrypt the response"); } request = GenerateServiceTicket <T>( request, out KrbEncTicketPart encTicketPart, out KrbTicket ticket, out KrbEncKdcRepPart encKdcRepPart, out KeyUsage keyUsage, out MessageType messageType ); var rep = new T { CName = encTicketPart.CName, CRealm = request.RealmName, MessageType = messageType, Ticket = ticket, EncPart = KrbEncryptedData.Encrypt( encKdcRepPart.EncodeApplication(), request.EncryptedPartKey, keyUsage ) }; return(rep); }
private static IEnumerable <KrbAuthorizationData> GenerateAuthorizationData(ServiceTicketRequest request) { // authorization-data is annoying because it's a sequence of // ad-if-relevant, which is a sequence of sequences // it ends up looking something like // // [ // { // Type = ad-if-relevant, // Data = // [ // { // Type = pac, // Data = encoded-pac // }, // ... // ], // }, // ... // ] var authz = new List <KrbAuthorizationData>(); if (request.IncludePac) { var pac = request.Principal.GeneratePac(); if (pac != null) { pac.ClientInformation = new PacClientInfo { ClientId = RpcFileTime.ConvertWithoutMicroseconds(request.Now), Name = request.Principal.PrincipalName }; var sequence = new KrbAuthorizationDataSequence { AuthorizationData = new[] { new KrbAuthorizationData { Type = AuthorizationDataType.AdWin2kPac, Data = pac.Encode(request.KdcAuthorizationKey, request.ServicePrincipalKey) } } }; authz.Add(new KrbAuthorizationData { Type = AuthorizationDataType.AdIfRelevant, Data = sequence.Encode() }); } } return(authz); }
public static KrbAsRep GenerateTgt( ServiceTicketRequest rst, IRealmService realmService ) { if (realmService == null) { throw new ArgumentNullException(nameof(realmService)); } rst.Compatibility = realmService.Settings.Compatibility; // This is approximately correct such that a client doesn't barf on it // The krbtgt Ticket structure is probably correct as far as AD thinks // Modulo the PAC, at least. if (string.IsNullOrWhiteSpace(rst.RealmName)) { // TODO: Possible bug. Realm service now has multiple krbtgt's so the name is always set // to the name of our (cloud) KDC name. Will this be an issue for trust ticket or mcticket? rst.RealmName = realmService.Name; } KrbPrincipalName krbtgtName = KrbPrincipalName.WellKnown.Krbtgt(rst.RealmName); if (rst.ServicePrincipal == null) { rst.ServicePrincipal = realmService.Principals.Find(krbtgtName, rst.RealmName); } if (rst.ServicePrincipalKey == null) { rst.ServicePrincipalKey = rst.ServicePrincipal.RetrieveLongTermCredential(); } if (rst.KdcAuthorizationKey == null) { // Not using rst.ServicePrincipal because it may not actually be krbtgt var krbtgt = realmService.Principals.Find(krbtgtName, rst.RealmName); rst.KdcAuthorizationKey = krbtgt.RetrieveLongTermCredential(); } rst.Now = realmService.Now(); rst.MaximumTicketLifetime = realmService.Settings.SessionLifetime; rst.MaximumRenewalWindow = realmService.Settings.MaximumRenewalWindow; if (rst.Flags == 0) { rst.Flags = DefaultFlags; } return(GenerateServiceTicket <KrbAsRep>(rst)); }
private static async Task <IEnumerable <KrbAuthorizationData> > GenerateAuthorizationData( IKerberosPrincipal principal, ServiceTicketRequest request ) { // authorization-data is annoying because it's a sequence of // ad-if-relevant, which is a sequence of sequences // it ends up looking something like // // [ // { // Type = ad-if-relevant, // Data = // [ // { // Type = pac, // Data = encoded-pac // }, // ... // ], // }, // ... // ] var authz = new List <KrbAuthorizationData>(); if (request.IncludePac) { var pac = await principal.GeneratePac(); if (pac != null) { var sequence = new KrbAuthorizationDataSequence { AuthorizationData = new[] { new KrbAuthorizationData { Type = AuthorizationDataType.AdWin2kPac, Data = pac.Encode(request.ServicePrincipalKey, request.ServicePrincipalKey) } } }; authz.Add(new KrbAuthorizationData { Type = AuthorizationDataType.AdIfRelevant, Data = sequence.Encode() }); } } return(authz); }
private static KrbPrincipalName CreateCNameForTicket(ServiceTicketRequest request) { if (string.IsNullOrEmpty(request.SamAccountName)) { return(KrbPrincipalName.FromPrincipal(request.Principal, realm: request.RealmName)); } return(new KrbPrincipalName { Type = PrincipalNameType.NT_PRINCIPAL, Name = new[] { request.SamAccountName } }); }
public static KrbCred GenerateWrappedServiceTicket(ServiceTicketRequest request) { GenerateServiceTicket <KrbTgsRep>( request, out KrbEncTicketPart encTicketPart, out KrbTicket ticket, out _, out _, out _ ); return(KrbCred.WrapTicket(ticket, encTicketPart)); }
public static KrbAsRep GenerateTgt( ServiceTicketRequest rst, IRealmService realmService ) { // This is approximately correct such that a client doesn't barf on it // The krbtgt Ticket structure is probably correct as far as AD thinks // Modulo the PAC, at least. if (string.IsNullOrWhiteSpace(rst.RealmName)) { rst.RealmName = realmService.Name; } if (rst.ServicePrincipal == null) { rst.ServicePrincipal = realmService.Principals.Find(KrbPrincipalName.WellKnown.Krbtgt()); } if (rst.ServicePrincipalKey == null) { rst.ServicePrincipalKey = rst.ServicePrincipal.RetrieveLongTermCredential(); } if (rst.KdcAuthorizationKey == null) { // Not using rst.ServicePrincipal because it may not actually be krbtgt var krbtgt = realmService.Principals.Find(KrbPrincipalName.WellKnown.Krbtgt()); rst.KdcAuthorizationKey = krbtgt.RetrieveLongTermCredential(); } var now = realmService.Now(); rst.Now = now; rst.RenewTill = now + realmService.Settings.MaximumRenewalWindow; rst.StartTime = now - realmService.Settings.MaximumSkew; rst.EndTime = now + realmService.Settings.SessionLifetime; if (rst.Flags == 0) { rst.Flags = DefaultFlags; } return(GenerateServiceTicket <KrbAsRep>(rst)); }
private static KrbEncTicketPart CreateEncTicketPart( ServiceTicketRequest request, KrbAuthorizationData[] authorizationDatas, KrbEncryptionKey sessionKey) { var cname = CreateCNameForTicket(request); var flags = request.Flags; if (request.PreAuthenticationData?.Any(r => r.Type == PaDataType.PA_REQ_ENC_PA_REP) ?? false) { flags |= TicketFlags.EncryptedPreAuthentication; } var addresses = request.Addresses; if (addresses == null) { addresses = Array.Empty <KrbHostAddress>(); } var encTicketPart = new KrbEncTicketPart() { CName = cname, Key = sessionKey, AuthTime = request.Now, StartTime = request.StartTime, EndTime = request.EndTime, CRealm = request.RealmName, Flags = flags, AuthorizationData = authorizationDatas, CAddr = addresses.ToArray(), Transited = new KrbTransitedEncoding() }; if (flags.HasFlag(TicketFlags.Renewable)) { // RenewTill should never increase if it was set previously even if this is a renewal pass encTicketPart.RenewTill = request.RenewTill; } return(encTicketPart); }
public static async Task <KrbAsRep> GenerateTgt( ServiceTicketRequest rst, IRealmService realmService ) { // This is approximately correct such that a client doesn't barf on it // The krbtgt Ticket structure is probably correct as far as AD thinks // Modulo the PAC, at least. if (string.IsNullOrWhiteSpace(rst.RealmName)) { rst.RealmName = realmService.Name; } if (rst.ServicePrincipal == null) { rst.ServicePrincipal = await realmService.Principals.RetrieveKrbtgt(); } if (rst.ServicePrincipalKey == null) { rst.ServicePrincipalKey = await rst.ServicePrincipal.RetrieveLongTermCredential(); } var now = realmService.Now(); rst.Now = now; rst.RenewTill = now + realmService.Settings.MaximumRenewalWindow; rst.StartTime = now - realmService.Settings.MaximumSkew; rst.EndTime = now + realmService.Settings.SessionLifetime; if (rst.Flags == 0) { rst.Flags = DefaultFlags; } return(await GenerateServiceTicket <KrbAsRep>(rst)); }
public static async Task <T> GenerateServiceTicket <T>(ServiceTicketRequest request) where T : KrbKdcRep, new() { var sessionKey = KrbEncryptionKey.Generate(request.ServicePrincipalKey.EncryptionType); var authz = await GenerateAuthorizationData(request.Principal, request.ServicePrincipalKey); var cname = KrbPrincipalName.FromPrincipal(request.Principal, realm: request.RealmName); var flags = request.Flags; if (request.Principal.SupportedPreAuthenticationTypes.Any()) { // This is not strictly an accurate way of detecting if the user was pre-authenticated. // If pre-auth handlers are registered and the principal has PA-Types available, a request // will never make it to this point without getting authenticated. // // However if no pre-auth handlers are registered, then the PA check is skipped // and this isn't technically accurate anymore. // // TODO: this should tie into the make-believe policy check being used in the // auth handler section flags |= TicketFlags.EncryptedPreAuthentication | TicketFlags.PreAuthenticated; } var addresses = request.Addresses; if (addresses == null) { addresses = new KrbHostAddress[0]; } var encTicketPart = new KrbEncTicketPart() { CName = cname, Key = sessionKey, AuthTime = request.Now, StartTime = request.StartTime, EndTime = request.EndTime, CRealm = request.RealmName, Flags = flags, AuthorizationData = authz.ToArray(), CAddr = addresses.ToArray(), Transited = new KrbTransitedEncoding() }; if (flags.HasFlag(TicketFlags.Renewable)) { // RenewTill should never increase if it was set previously even if this is a renewal pass encTicketPart.RenewTill = request.RenewTill; } var ticket = new KrbTicket() { Realm = request.RealmName, SName = KrbPrincipalName.FromPrincipal( request.ServicePrincipal, PrincipalNameType.NT_SRV_INST, request.RealmName ), EncryptedPart = KrbEncryptedData.Encrypt( encTicketPart.EncodeApplication(), request.ServicePrincipalKey, KeyUsage.Ticket ) }; KrbEncKdcRepPart encKdcRepPart; if (typeof(T) == typeof(KrbAsRep)) { encKdcRepPart = new KrbEncAsRepPart(); } else if (typeof(T) == typeof(KrbTgsRep)) { encKdcRepPart = new KrbEncTgsRepPart(); } else { throw new InvalidOperationException($"Requested Service Ticket type is neither KrbAsRep nor KrbTgsRep. Type: {typeof(T)}"); } encKdcRepPart.AuthTime = encTicketPart.AuthTime; encKdcRepPart.StartTime = encTicketPart.StartTime; encKdcRepPart.EndTime = encTicketPart.EndTime; encKdcRepPart.RenewTill = encTicketPart.RenewTill; encKdcRepPart.KeyExpiration = request.Principal.Expires; encKdcRepPart.Realm = request.RealmName; encKdcRepPart.SName = ticket.SName; encKdcRepPart.Flags = encTicketPart.Flags; encKdcRepPart.CAddr = encTicketPart.CAddr; encKdcRepPart.Key = sessionKey; encKdcRepPart.Nonce = KerberosConstants.GetNonce(); encKdcRepPart.LastReq = new[] { new KrbLastReq { Type = 0, Value = request.Now } }; encKdcRepPart.EncryptedPaData = new KrbMethodData { MethodData = new[] { new KrbPaData { Type = PaDataType.PA_SUPPORTED_ETYPES, Value = request.Principal.SupportedEncryptionTypes.AsReadOnly(littleEndian: true).AsMemory() } } }; encKdcRepPart.EncodeApplication(); var rep = new T { CName = cname, CRealm = request.RealmName, MessageType = MessageType.KRB_AS_REP, Ticket = ticket, EncPart = KrbEncryptedData.Encrypt( encKdcRepPart.EncodeApplication(), request.EncryptedPartKey, encKdcRepPart.KeyUsage ) }; return(rep); }
public static async Task <T> GenerateServiceTicket <T>(ServiceTicketRequest request) where T : KrbKdcRep, new() { if (request.EncryptedPartKey == null) { throw new ArgumentException("A session key must be provided to encrypt the response", nameof(request.EncryptedPartKey)); } if (request.Principal == null) { throw new ArgumentException("A Principal identity must be provided", nameof(request.Principal)); } if (request.ServicePrincipal == null) { throw new ArgumentException("A service principal must be provided", nameof(request.ServicePrincipal)); } if (request.ServicePrincipalKey == null) { throw new ArgumentException("A service principal key must be provided", nameof(request.ServicePrincipalKey)); } var authz = await GenerateAuthorizationData(request.Principal, request); var sessionKey = KrbEncryptionKey.Generate(request.ServicePrincipalKey.EncryptionType); var encTicketPart = CreateEncTicketPart(request, authz.ToArray(), sessionKey); var ticket = new KrbTicket() { Realm = request.RealmName, SName = KrbPrincipalName.FromPrincipal( request.ServicePrincipal, PrincipalNameType.NT_SRV_INST, request.RealmName ), EncryptedPart = KrbEncryptedData.Encrypt( encTicketPart.EncodeApplication(), request.ServicePrincipalKey, KeyUsage.Ticket ) }; KrbEncKdcRepPart encKdcRepPart; KeyUsage keyUsage; if (typeof(T) == typeof(KrbAsRep)) { encKdcRepPart = new KrbEncAsRepPart(); keyUsage = KeyUsage.EncAsRepPart; } else if (typeof(T) == typeof(KrbTgsRep)) { encKdcRepPart = new KrbEncTgsRepPart(); keyUsage = request.EncryptedPartKey.Usage ?? KeyUsage.EncTgsRepPartSessionKey; } else { throw new InvalidOperationException($"Requested Service Ticket type is neither KrbAsRep nor KrbTgsRep. Type: {typeof(T)}"); } encKdcRepPart.AuthTime = encTicketPart.AuthTime; encKdcRepPart.StartTime = encTicketPart.StartTime; encKdcRepPart.EndTime = encTicketPart.EndTime; encKdcRepPart.RenewTill = encTicketPart.RenewTill; encKdcRepPart.KeyExpiration = request.Principal.Expires; encKdcRepPart.Realm = request.RealmName; encKdcRepPart.SName = ticket.SName; encKdcRepPart.Flags = encTicketPart.Flags; encKdcRepPart.CAddr = encTicketPart.CAddr; encKdcRepPart.Key = sessionKey; encKdcRepPart.Nonce = request.Nonce; encKdcRepPart.LastReq = new[] { new KrbLastReq { Type = 0, Value = request.Now } }; encKdcRepPart.EncryptedPaData = new KrbMethodData { MethodData = new[] { new KrbPaData { Type = PaDataType.PA_SUPPORTED_ETYPES, Value = request.Principal.SupportedEncryptionTypes.AsReadOnly(littleEndian: true).AsMemory() } } }; var cname = KrbPrincipalName.FromPrincipal(request.Principal, realm: request.RealmName); var rep = new T { CName = cname, CRealm = request.RealmName, MessageType = MessageType.KRB_AS_REP, Ticket = ticket, EncPart = KrbEncryptedData.Encrypt( encKdcRepPart.EncodeApplication(), request.EncryptedPartKey, keyUsage ) }; return(rep); }
private static ServiceTicketRequest GenerateServiceTicket <T>( ServiceTicketRequest request, out KrbEncTicketPart encTicketPart, out KrbTicket ticket, out KrbEncKdcRepPart encKdcRepPart, out KeyUsage keyUsage, out MessageType messageType ) where T : KrbKdcRep, new() { if (request.Principal == null) { throw new InvalidOperationException("A Principal identity must be provided"); } if (request.ServicePrincipal == null) { throw new InvalidOperationException("A service principal must be provided"); } if (request.ServicePrincipalKey == null) { throw new InvalidOperationException("A service principal key must be provided"); } var authz = GenerateAuthorizationData(request); var sessionKey = KrbEncryptionKey.Generate(request.PreferredClientEType ?? request.ServicePrincipalKey.EncryptionType); encTicketPart = CreateEncTicketPart(request, authz.ToArray(), sessionKey); bool appendRealm = false; if (request.ServicePrincipal.PrincipalName.Contains("/")) { appendRealm = true; } ticket = new KrbTicket() { Realm = request.RealmName, SName = KrbPrincipalName.FromPrincipal( request.ServicePrincipal, PrincipalNameType.NT_SRV_INST, appendRealm ? null : request.RealmName ), EncryptedPart = KrbEncryptedData.Encrypt( encTicketPart.EncodeApplication(), request.ServicePrincipalKey, KeyUsage.Ticket ) }; if (typeof(T) == typeof(KrbAsRep)) { encKdcRepPart = new KrbEncAsRepPart(); keyUsage = KeyUsage.EncAsRepPart; messageType = MessageType.KRB_AS_REP; } else if (typeof(T) == typeof(KrbTgsRep)) { encKdcRepPart = new KrbEncTgsRepPart(); keyUsage = request.EncryptedPartKey?.Usage ?? KeyUsage.EncTgsRepPartSessionKey; messageType = MessageType.KRB_TGS_REP; } else { throw new InvalidOperationException($"Requested Service Ticket type is neither KrbAsRep nor KrbTgsRep. Type: {typeof(T)}"); } encKdcRepPart.AuthTime = encTicketPart.AuthTime; encKdcRepPart.StartTime = encTicketPart.StartTime; encKdcRepPart.EndTime = encTicketPart.EndTime; encKdcRepPart.RenewTill = encTicketPart.RenewTill; encKdcRepPart.KeyExpiration = request.Principal.Expires; encKdcRepPart.Realm = request.RealmName; encKdcRepPart.SName = ticket.SName; encKdcRepPart.Flags = encTicketPart.Flags; encKdcRepPart.CAddr = encTicketPart.CAddr; encKdcRepPart.Key = sessionKey; encKdcRepPart.Nonce = request.Nonce; encKdcRepPart.LastReq = new[] { new KrbLastReq { Type = 0, Value = request.Now } }; encKdcRepPart.EncryptedPaData = new KrbMethodData { MethodData = new[] { new KrbPaData { Type = PaDataType.PA_SUPPORTED_ETYPES, Value = request.Principal.SupportedEncryptionTypes.AsReadOnly(littleEndian: true).AsMemory() } } }; return(request); }
public static async Task <T> GenerateServiceTicket <T>(ServiceTicketRequest request) where T : KrbKdcRep, new() { if (request.EncryptedPartKey == null) { throw new ArgumentException("A session key must be provided to encrypt the response", nameof(request.EncryptedPartKey)); } if (request.Principal == null) { throw new ArgumentException("A Principal identity must be provided", nameof(request.Principal)); } if (request.ServicePrincipal == null) { throw new ArgumentException("A service principal must be provided", nameof(request.ServicePrincipal)); } if (request.ServicePrincipalKey == null) { throw new ArgumentException("A service principal key must be provided", nameof(request.ServicePrincipalKey)); } var authz = await GenerateAuthorizationData(request.Principal, request); var cname = KrbPrincipalName.FromPrincipal(request.Principal, realm: request.RealmName); var sessionKey = KrbEncryptionKey.Generate(request.ServicePrincipalKey.EncryptionType); var flags = request.Flags; if (request.PreAuthenticationData?.Any(r => r.Type == PaDataType.PA_REQ_ENC_PA_REP) ?? false) { flags |= TicketFlags.EncryptedPreAuthentication; } var addresses = request.Addresses; if (addresses == null) { addresses = new KrbHostAddress[0]; } var encTicketPart = new KrbEncTicketPart() { CName = cname, Key = sessionKey, AuthTime = request.Now, StartTime = request.StartTime, EndTime = request.EndTime, CRealm = request.RealmName, Flags = flags, AuthorizationData = authz.ToArray(), CAddr = addresses.ToArray(), Transited = new KrbTransitedEncoding() }; if (flags.HasFlag(TicketFlags.Renewable)) { // RenewTill should never increase if it was set previously even if this is a renewal pass encTicketPart.RenewTill = request.RenewTill; } var ticket = new KrbTicket() { Realm = request.RealmName, SName = KrbPrincipalName.FromPrincipal( request.ServicePrincipal, PrincipalNameType.NT_SRV_INST, request.RealmName ), EncryptedPart = KrbEncryptedData.Encrypt( encTicketPart.EncodeApplication(), request.ServicePrincipalKey, KeyUsage.Ticket ) }; KrbEncKdcRepPart encKdcRepPart; if (typeof(T) == typeof(KrbAsRep)) { encKdcRepPart = new KrbEncAsRepPart(); } else if (typeof(T) == typeof(KrbTgsRep)) { encKdcRepPart = new KrbEncTgsRepPart(); } else { throw new InvalidOperationException($"Requested Service Ticket type is neither KrbAsRep nor KrbTgsRep. Type: {typeof(T)}"); } encKdcRepPart.AuthTime = encTicketPart.AuthTime; encKdcRepPart.StartTime = encTicketPart.StartTime; encKdcRepPart.EndTime = encTicketPart.EndTime; encKdcRepPart.RenewTill = encTicketPart.RenewTill; encKdcRepPart.KeyExpiration = request.Principal.Expires; encKdcRepPart.Realm = request.RealmName; encKdcRepPart.SName = ticket.SName; encKdcRepPart.Flags = encTicketPart.Flags; encKdcRepPart.CAddr = encTicketPart.CAddr; encKdcRepPart.Key = sessionKey; encKdcRepPart.Nonce = request.Nonce; encKdcRepPart.LastReq = new[] { new KrbLastReq { Type = 0, Value = request.Now } }; encKdcRepPart.EncryptedPaData = new KrbMethodData { MethodData = new[] { new KrbPaData { Type = PaDataType.PA_SUPPORTED_ETYPES, Value = request.Principal.SupportedEncryptionTypes.AsReadOnly(littleEndian: true).AsMemory() } } }; var rep = new T { CName = cname, CRealm = request.RealmName, MessageType = MessageType.KRB_AS_REP, Ticket = ticket, EncPart = KrbEncryptedData.Encrypt( encKdcRepPart.EncodeApplication(), request.EncryptedPartKey, encKdcRepPart.KeyUsage ) }; return(rep); }