RFR 6722928: Support SSPI as a native GSS-API provider

Weijun Wang weijun.wang at oracle.com
Fri Dec 7 02:45:02 UTC 2018


Hi Nico,

Your detailed analysis is very valuable and I've included the whole text into https://bugs.openjdk.java.net/browse/JDK-8214988. I knew GSSName was complicated at the beginning and I remember I've especially asked you to look at it in my initial code review request. I think I have a better understanding now but unfortunately we are not likely to fix the native library very soon since RDP 1 for JDK 12 is near [1].

Let's come back to this bug. Yesterday I sent out a plan (pasted below):

> Since GSSName::isMN is always true, every gss_name_t must be a MN and I decide the name to be always krb5 style and the type to be always NT_KRB5_NAME. However, somewhere in the JGSS native bridge implementation the name and type are cached on the Java side, therefore the result of gSSMananger.createName("service at host", NT_HOSTBASED_SERVICE) does have toString() being service at host and type being NT_HOSTBASED_SERVICE. When canonicalize() is called, even if the GSS-API canonicalize_name() is not called a new gss_name_t is created and the Java side cache will be recreated, and this time name becomes service/host and type becomes NT_KRB5_NAME.
> 
> Is this what you like to see?
> 
> As for export(), I can also output a/b at . When the result is imported as a NT_EXPORT_NAME, I'll remember to remove the @ at the end.
> 
> The result is:
> 
>    GSSManager m = GSSManager.getInstance();
>    GSSName n = m.createName("service at host", GSSName.NT_HOSTBASED_SERVICE);
>    // n is now (service at host, NT_HOSTBASED_SERVICE)
>    n = n.canonicalize(new Oid("1.2.840.113554.1.2.2"));
>    // n is now (service/host, NT_KRB5_NAME)
>    byte[] x = n.export();
>    // 0000: 04 01 00 0B 06 09 2A 86   48 86 F7 12 01 02 02 00  ......*.H.......
>    // 0010: 00 00 0D 73 65 72 76 69   63 65 2F 68 6F 73 74 40  ...service/host@
>    n = m.createName(x, GSSName.NT_EXPORT_NAME);
>    // n is now (service/host, NT_KRB5_NAME)

I think the result is quite good. It's like the native bridge itself has stored a non-MN (except that its isMN() is true) although inside the new library there is only MN.

And you mentioned about ServicePermission check, this means I cannot export a/b@ only and the realm is needed. The latest webrev is at

   https://cr.openjdk.java.net/~weijun/6722928/webrev.02/

Any other comment? You said you wanted to check the krb5 <-> SPNEGO token translation codes.

Thanks,
Max

[1] https://openjdk.java.net/projects/jdk/12/

> On Dec 7, 2018, at 2:48 AM, Nico Williams <Nico.Williams at twosigma.com> wrote:
> 
> On Wed, Dec 05, 2018 at 11:41:44AM +0800, Weijun Wang wrote:
>> Java's GSSName::isMN always returns true, therefore to my observation, the
>> GSS-API canonicalize_name() is not called if GSSName::canonicalize is called.
> 
> That *would* be a valid design choice (per RFCs 2743 and 2744), but it requires
> always calling gss_canonicalize_name() immediately when you gss_import_name() a
> string (as opposed to an exported name token).
> 
> However, to do this you have to canonicalize the name for every mechanism
> indicated by gss_indicate_mechs()!  And then it wouldn't be clear what to
> output from toString() when the native provider supports multiple mechanisms,
> so I don't recommend this design choice.
> 
> But the JGSS JNI bindings isn't actually doing any of that.
> 
> Instead the JGSS JNI bindings outright lies about the name claiming it's an MN
> when it may not be.  I'm not sure how much appetite we should have to fix this
> sort of buglet, but it should be fixed, really.
> 
> If I'd noticed this buglet a couple of years ago then our contribution would
> include a fix for it :(
> 
>> It is only called in export(), where the first call to export_name() might
> 
> (And in getKrbName(), which is used only for JAAS permissions checks.)
> 
>> return "Name is not MN" (MIT krb5 behavior) and then the bridge automatically
>> call canonicalize_name() and export_name() again.
> 
> Right.
> 
>> And then, the export format is only useful when importing a NT_EXPORT_NAME.
> 
> The export name is meant for two things:
> 
> - to be able to get a stable token you can store in things like ACLs
> 
> - to be able to memcmp() for name equality, which has different semantics than
>   gss_compare_name()
> 
>> So, seems like both export_name() and canonicalize_name() are quite useless
>> (at least in the view of the native bridge).
> 
> Exported name tokens have uses (see above), and it does work in JGSS.  So
> exportName is not at all useless.
> 
> canonicalizeName(), however, is pretty useless.  Because
> gss_canonicalize_name() is pretty useless.
> 
> gss_canonicalize_name() was originally intended so that one could write an
> application that takes a user-input principal name, canonicalizes it, exports
> it, and stores the exported name token in an ACL.
> 
> The problems with gss_canonicalize_name() are:
> 
> a) It can require credentials in order to talk to a directory, but there's no
>   input credentials handle argument.
> 
>   E.g., given referrals, to properly canonicalize a name with Kerberos
>   requires doing TGS exchanges, which requires Kerberos client credentials.
> 
> b) A directory may be required for the canonicalization step, but none may be
>   available.
> 
>   E.g., imagine a GSS mechanism that uses PKIX certificates...  How would you
>   resolve a name "nico" of username name-type to something like CN=nico,...?
> 
>   For some realms you could have local mapping rules configuration to do that
>   with, but in the general case you'd need a directory (LDAP, say), and you'd
>   have to know how to search it for this.  There isn't always a directory that
>   allows these searches, and it's not always the case that certificates issued
>   by some CA adhere to a rigid naming convention.
> 
>   Even if you use various subjectAlternativeName name types, you'd still have
>   canonicalization issues that might require a directory.
> 
>   Oh, and searching any such directory would presumably require credentials;
>   see (a).
> 
> We've had a few lengthy discussions of this on the KRB-WG and KITTEN mailing
> lists.  And I suspect if you look you might find discussions of this in the old
> CAT WG mailing list from the early- and mid-90s.
> 
> Because of its shortcomings, I'm inclined to not worry too much about
> gss_canonicalize_name(), but isMN() *must not* lie, that's for sure, so in the
> end I'm inclined to just fix GSSNameElement to know whether a name is an MN,
> and to have a proper, public canonicalize() method that calls the native
> canonicalizeName() method (and so gss_canonicalize_name()).
> 
> If you do all this then exportName() need not have that fallback code path to
> call gss_canonicalize_name().  In principle it's a bug to do that because,
> again, if the native GSS provider supports multiple mechanisms, which mechanism
> should exportName() pick?!  This sort of bug has been papered over by
> supporting only Kerberos and SPNEGO, since ultimately that means supporting
> only Kerberos.  But there are other mechanisms that might be supported.  E.g.,
> MSFT has one called PKU2U that uses PKIX credentials instead of Kerberos, and
> Globus has one that uses TLS for the same purpose.
> 
>> What's your ideal output? The toString of canonicalize() and import(export)
>> always showing krb5 style and name type being KRB5_NAME?
> 
> In the JNI bindings toString() should always output whatever gss_display_name()
> outputs.  Full stop.
> 
> The effect of that, if the bindings only call gss_canonicalize_name() when the
> Java canonicalize() method is called, would be this:
> 
> - if a name is not an MN, meaning it wasn't output by gss_canonicalize_name(),
>   gss_accept_sec_context(), gss_inquire_context(), or gss_import_name() of a
>   GSS_C_NT_EXPORTED_NAME token, then toString() should return the original
>   name from the gss_import_name() call (the GSSNameElement constructor);
> 
> - otherwise (if a name is an MN), then toString() should return the output of 
>   gss_display_name() as applied to the MN
> 
> If the JNI bindings were to aggressively call gss_canonicalize_name() early,
> then you'd have to choose a mechanism whose name display form to output in
> toString() (see above).
> 
> Nico
> -- 




More information about the security-dev mailing list