RFR 6722928: Support SSPI as a native GSS-API provider
Nico Williams
Nico.Williams at twosigma.com
Wed Mar 20 16:10:23 UTC 2019
On Wed, Mar 20, 2019 at 10:20:28AM +0800, Weijun Wang wrote:
> New webrev at https://cr.openjdk.java.net/~weijun/6722928/webrev.05, and interdiff at
>
> https://cr.openjdk.java.net/~weijun/6722928/webrev.05/interdiff.patch.html
Thanks. I'll take a look.
> I'll think more about secondsUntil() and getTGTEndTime(). Other comments below.
>
> > - sspi.cpp:61
> >
> > 61 #define SEC_SUCCESS(status) (*minor_status = status, (status) >= SEC_E_OK)
> > ^^^^^^^^^^^^^^^^^^^^^^
> >
> > Please add parenthesis around the whole assignment expression.
>
> OK, but what kind of bad things could happen?
First, there's a matter of hygiene: you need to parenthesize `status`,
and you do in the second part but not the first. Second, assignments in
if statements need to be parenthesized to avoid compiler warnings -- and
this one is parenthesized, but who knows if a compiler might be confused
by the comma operator and emit a warning anyways.
> > - sspi.cpp:NewContext()
> >
> > Please fully-initialize all fields of the allocated context structure before
> > returning it.
>
> Some of them are of struct types, do I need to go inside and set each to zero? Or ZeroMemory the whole struct?
C does not guarantee that (void*)0 is an all-bits-zero pattern, but I
imagine all the supported Windows platforms do (certainly x86 and
x86_64). So ZeroMemory will do. For more portable code you should
ZeroMemory the struct then manually assign all the pointer values.
> > - sspi.cpp:flagSspi2Gss() and flagGss2Sspi()
> >
> > I realize that this would be a change to the Java bindings, so this is just
> > a note, but, we should add support for GSS_C_DELEG_POLICY_FLAG.
>
> How? Always set it with GSS_C_DELEG_FLAG?
No, we'd have to add the Java bindings too. Forget this request.
> > - sspi.cpp:getTGTEndTime()
> >
> > Please immediately write to the `endTime' output parameter upon entry.
>
> Are you afraid of garbages for "return 1"s? But I won't touch the parameter at all in this case.
Yes. It's just defensive programming.
> > - sspi.cpp:315
> >
> > 314 // input has realm, no need to add one
> > 315 if (wcschr(input, '@')) {
> >
> > This is not going to work with UPNs.
>
> Why not? I just want to find out if there is already a realm.
UPNs are of the form name\@domain at REALM. You're not checking for `\@`.
You can't search for `@` backwards either because realm names can
actually have `\@` as well in them.
> > You should probably implement proper
> > RFC1864 name format support, including backslash-quoting.
>
> Ahh...
Right :)
> > - sspi.cpp:315
> >
> > 315 if (wcschr(input, '@')) {
> > ^
> >
> > I don't know if it is necessary that this be L'@' or if the compiler will do
> > the right thing because the `wc' argument of `wcschr()' is a `wchar_t'.
> >
> > Elsewhere you do use wide character literals, so I assume even if type
> > promotion works correctly for char->WCHAR (does it?) you might want to be
> > consistent.
>
> I'll made them all L'@'.
Or stop using wchar_t... :)
> > wchar_t/WCHAR is cancer.
> >
> > I worry that adding any more wchar_t-using code makes worse the legacy
> > cleanup as Windows starts to prefer UTF-8. But whatever.
> >
> > - sspi.cpp:351
> >
> > 351 wcscpy_s(fullname + oldlen + 1, wcslen(realm) + 1, realm);
> >
> > It would be safer to use wcscat_s().
>
> How is this unsafe? If using wcscat_s(), I'll need to add a '\0' after the '@' first.
You wouldn't have to compute the offset to copy at. I call that safer.
> > - sspi.cpp:362
> >
> > 362 #define CHECK_OUTPUT(x) if (!x) return GSS_S_CALL_INACCESSIBLE_WRITE;
> >
> > I would much prefer the more standard and safer do { ... } while (0)
> > construction:
> >
> > 362 #define CHECK_OUTPUT(x) \
> > do { if (!x) return GSS_S_CALL_INACCESSIBLE_WRITE; } while (0)
>
> I can do that, but why is this safer?
You don't have to worry about this
if (something)
CHECK_OUTPUT(out_arg);
--->else
other stuff
That `else` will bind to the `if` that `CHECK_OUTPUT()` expands into.
> You mean there is actually no need to check the arguments?
No, I mean that they don't save that much work.
> > - sspi.cpp:527
> >
> > 527 int len = (int)wcslen(fullname);
> >
> > You don't need that cast.
>
> There was a warning.
Yes, but see the comments that followed:
> > But you should check first that `wcslen()' did
> > not return a value larger than `INT_MAX'.
> >
> > You only even need an `int len' because `WideCharToMultiByte()' returns
> > `int'...
> >
> > I'd write this like:
> >
> > 527 int len;
> > size_t namelen = wcslen(fullname);
> > if (namelen > 255)
> > goto err;
> > // 04 01 00 ** 06 ** OID len:int32 name
> > // ...
> > //
> > - sspi.cpp:532,574
> >
> > You can't assume the length of a multi-byte string will be the same number
> > of `char' as the number of `wchar_t' in the wide-char version of the string.
> >
> > Determinining the correct length is a bit tricky, though since we should be
> > converting to UTF-8 (see above), you can assume that 4x is enough, use
> > 4*len for the buffer allocation, then take the correct length from
> > WideCharToMultiByte().
>
> OK.
(And even this assumes that we Windows won't do normalization on the
string, though if they normalize to NFC, I think the number of
codepoints can't go up.)
> > - sspi.cpp:607
> >
> > 606 TimeStamp ts;
> > 607 ts.QuadPart = 0;
> >
> > Huh? TimeStamp is a typedef of SECURITY_INTEGER, and SECURITY_INTEGER is a
> > struct of the same shape and size as FILETIME, and has no QuadPart field.
> >
> > What am I missing?
>
> It compiles. I think it's a union of one Quad or two DWORD.
Ok.
> > - sspi.cpp:954
> >
> > Should you check if there's an output token before doing this:
> >
> > 954 ss = CompleteAuthToken(&pc->hCtxt, &outBuffDesc);
> >
> > ?
>
> Does this matter? Do you think CompleteAuthToken cannot deal with no token?
Hmm, probably not.
> > - sspi.cpp:874 and others
> >
> > delete `output_token->value' and set it to NULL (and the length to 0) before
> > returning errors after 865.
>
> OK. Is this just a good habit or do you know someone using it even at a failure?
It's perfectly safe to gss_release_buffer() after a failure. After all,
the failure might involve an error token.
> > - sspi.cpp:878,962-963
> >
> > The local variable `pfDone' is set but not used. Drop it.
>
> OK,
>
> >
> > (The compiler should have warned about this, no?)
>
> No.
Why not.
> > - sspi.cpp:964-966
> >
> > Change this:
> >
> > 964 outFlag = flagSspi2Gss(outFlag);
> > 965
> > 966 *ret_flags = (OM_uint32)outFlag;
> >
> > to:
> >
> > 964 *ret_flags = flagSspi2Gss(outFlag);
> >
> > You don't use outFlag again. Setting it to a different kind of set of flags
> > seems like a trap for someone to fall into in the future.
>
> Good.
Huh?
> > - sspi.cpp:948
> >
> > 948 if (!SEC_SUCCESS(ss)) {
> > 949 return GSS_S_FAILURE;
> > 950 }
> > 951
> > 952 if ((SEC_I_COMPLETE_NEEDED == ss)
> > 953 || (SEC_I_COMPLETE_AND_CONTINUE == ss)) {
> >
> > I can't find documentation on SEC_SUCCES(). I don't know if it treats
> > SEC_I_COMPLETE_NEEDED as an error or not... I gues it must not. Can you
> > confirm?
>
> Not an error. I defined SEC_SUCCESS in this file.
Oy, sorry!
> > - sspi.cpp:1002
> >
> > It should be simple now to implement this...
> >
> > 1002 PP(">>>> Calling UNIMPLEMENTED gss_accept_sec_context...");
> > 1003 return GSS_S_FAILURE;
>
> Not our goal this time, and I don't know how to test. Does server on Windows only run as services?
Do we have servers that run on Windows? I think we do. Our shim
(forked from SAP's) works for us.
> > - gssapi.h:gss_acquire_cred()
> >
> > @@ -387,13 +399,13 @@
> >
> > /* Function Prototypes */
> >
> > GSS_DLLIMP OM_uint32 gss_acquire_cred(
> > OM_uint32 *, /* minor_status */
> > - gss_name_t, /* desired_name */
> > + gss_const_name_t, /* desired_name */
> > OM_uint32, /* time_req */
> > - gss_OID_set, /* desired_mechs */
> > ---->+ const gss_OID_set, /* desired_mechs */
> > gss_cred_usage_t, /* cred_usage */
> > gss_cred_id_t *, /* output_cred_handle */
> > gss_OID_set *, /* actual_mechs */
> > OM_uint32 * /* time_rec */
> > );
> >
> > That needs to be `gss_const_OID_set', not `const gss_OID_set'.
> >
> > There's a number of these. Just search for "const gss_" -- any time you see
> > that in a function prototype, it's wrong.
>
> I think I copied all of them from Heimdal. Do you mean Heimdal is not complete at using the types?
Yes. Thanks for noticing. I'll fix it.
Nico
--
More information about the security-dev
mailing list