@@ -194,8 +194,6 @@ private static KrbEncTicketPart CreateEncTicketPart(
194194 KrbEncryptionKey sessionKey
195195 )
196196 {
197- var cname = CreateCNameForTicket ( request ) ;
198-
199197 var flags = request . Flags ;
200198
201199 if ( request . PreAuthenticationData ? . Any ( r => r . Type == PaDataType . PA_REQ_ENC_PA_REP ) ?? false )
@@ -209,7 +207,7 @@ KrbEncryptionKey sessionKey
209207
210208 var encTicketPart = new KrbEncTicketPart ( )
211209 {
212- CName = cname ,
210+ CName = CreateCNameForTicket ( request ) ,
213211 CRealm = request . Compatibility . HasFlag ( KerberosCompatibilityFlags . IsolateRealmsConsistently ) ? request . ClientRealmName : request . RealmName ,
214212 Key = sessionKey ,
215213 AuthTime = request . Now ,
@@ -233,21 +231,44 @@ KrbEncryptionKey sessionKey
233231
234232 private static KrbPrincipalName CreateCNameForTicket ( ServiceTicketRequest request )
235233 {
236- if ( string . IsNullOrEmpty ( request . SamAccountName ) )
234+ // If ClientName is explicitly set, use that. This is the preferred method for the caller to indicate what
235+ // cname should be used.
236+ if ( request . ClientName != null )
237237 {
238- return KrbPrincipalName . FromPrincipal (
239- request . Principal ,
240- realm : request . Compatibility . HasFlag ( KerberosCompatibilityFlags . IsolateRealmsConsistently ) ?
241- request . ClientRealmName :
242- request . RealmName
243- ) ;
238+ return request . ClientName ;
244239 }
245240
246- return new KrbPrincipalName
241+ // Otherwise, if SamAccountName is set, use that as the principal name.
242+ // This is not the recommended method, but is supported for backwards compatibility.
243+ #pragma warning disable CS0618 // Type or member is obsolete
244+ if ( ! string . IsNullOrEmpty ( request . SamAccountName ) )
247245 {
248- Type = PrincipalNameType . NT_PRINCIPAL ,
249- Name = new [ ] { request . SamAccountName }
250- } ;
246+ // Note that the name is returned in a single part here, even if the request may have had multiple parts.
247+ // This may be okay for AS-REQs with Canonicalize set, but it is not spec-compliant for other scenarios.
248+ return new KrbPrincipalName
249+ {
250+ Type = PrincipalNameType . NT_PRINCIPAL ,
251+ Name = new [ ] { request . SamAccountName }
252+ } ;
253+ }
254+ #pragma warning restore CS0618 // Type or member is obsolete
255+
256+ // Lastly, if neither are set, derive from the principal.
257+ //
258+ // Note: this might not be correct in all scenarios. For instance, if the client does not accept
259+ // name canonicalization (i.e., the Canonicalize flag is not set), then it's not spec-compliant to deviate
260+ // from the requested cname. Also, in TGS-REP, the cname should match what's in the TGT, and should not be
261+ // derived from the found principal. It is the responsibility of the caller to decide whether the request
262+ // warrants passing Principal only, or forcing a specific cname via ClientName.
263+ //
264+ // Note: historically Kerberos.NET had a bug where the service realm was used to derive cname from the principal.
265+ // However, it should be the client realm. This has been corrected under the IsolateRealmsConsistently flag.
266+ return KrbPrincipalName . FromPrincipal (
267+ request . Principal ,
268+ realm : request . Compatibility . HasFlag ( KerberosCompatibilityFlags . IsolateRealmsConsistently ) ?
269+ request . ClientRealmName :
270+ request . RealmName
271+ ) ;
251272 }
252273
253274 private static IEnumerable < KrbAuthorizationData > GenerateAuthorizationData ( ServiceTicketRequest request )
0 commit comments