-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix handling of krb5_data
structs.
#21
base: main
Are you sure you want to change the base?
Fix handling of krb5_data
structs.
#21
Conversation
The caller (at least, MIT Kerberos) expects us to return our data in an existing buffer referred to by the krb5_data struct we get, rather than *update* that struct with a new buffer of our own. I ran into this when using pykrb5 with PKINIT, and needed to supply a passphrase for the client's private key via a Krb5Prompt object. It mysteriously failed to decrypt the key even though the passphrase returned by the prompter was right. Eventually I tracked it down to: https://github.com/krb5/krb5/blob/30429ade54bfe66f9145a30487e43b19bde76701/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c#L1196 This: rdat.data = buf; rdat.length = size is where it sets the krb5_data rdat it will pass to us to refer to its own buffer, and if you follow the code you see that it expects the returned data to be in buf. We were instead returning an updated rdat.data, which it never looks at.
krb5_data
structs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to test this out by creating a prompter that returns a massive amount of data guaranteed to be larger than the buffer. We currently do a very simple test for the prompt in https://github.com/jborean93/pykrb5/blob/main/tests/test_creds.py.
@@ -159,7 +159,8 @@ cdef krb5_error_code prompt_callback( | |||
replies.append(reply) | |||
|
|||
for idx, reply in enumerate(replies): | |||
pykrb5_set_krb5_data(prompts[idx].reply, len(reply), <char *>reply) | |||
if pykrb5_set_krb5_data(prompts[idx].reply, len(reply), <char *>reply) != 0: | |||
raise Exception("[prompter callback] Our reply is too big for the caller's buffer.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably better to use a ValueError
rather than a generic Exception
.
Indeed, and we should do that, but when I mentioned testing I actually meant something else: a test that triggers the problem that prompted me to discover this bug. I can supply a PKINIT client/KDC configuration if you don't have it already in your test harness and it would be helpful. I'm actually curious as to why, if there are existing tests that actually use the prompt mechanism "for real" (i.e. actually call MIT/Heimdal for it), they aren't already broken. It would be particularly unpleasant if in fact MIT is not consistent about this: some prompt consumers use This will need some more investigation, of that and of how Heimdal works, plus tests, before it's ready to go. |
So the current tests is legitimately using the prompting mechanism for sending the password of a user. It's actually working today but it could be that the different mechanisms using this prompter expect different behaviour, or maybe the PKINIT stuff is stricter? I would have to look into the code to try and figure out what's happening here but unfortunately that's easier said than done. But still it should be possible to have a mock prompter that tries to set a large amount of data to exercise the exception. One thing to keep in mind is that this is causing a segmentation fault in another call that's using On the plus side |
Yup, definitely. I had my head down in fixing my specific bug when I made this preliminary patch, but clearly in general there are consumers that require both incompatible update styles for
Yeah — I spent 5 hours with my own debug builds of both MIT Kerberos and OpenSSL, |
The caller (at least, MIT Kerberos) expects us to return our data in an existing buffer referred to by the
krb5_data
struct we get, rather than update that struct with a new buffer of our own.I ran into this when using pykrb5 with PKINIT, and needed to supply a passphrase for the client's private key via a
Krb5Prompt
object. It mysteriously failed to decrypt the key even though the passphrase returned by the prompter was right. Eventually I tracked it down topkinit_crypto_openssl.c
. This:is where it sets the
krb5_data rdat
it will pass to us to refer to its own buffer, and if you follow the code you see that it expects the returned data to be inbuf
. We were instead returning an updatedrdat.data
, which it never looks at. I was getting thisKRB5_TRACE
:(which is actually itself a minor bug; it should say "bad decryption"). With this fix, the PKINIT succeeds.
Since it is now possible for
pykrb5_set_krb5_data()
to fail if the caller's buffer is not big enough, I changed its signature and raise an exception in this case; I'm not sure if that's done as you would want. Ideally we should come up with a test to catch this; I assume it wasn't caught because it's mocked in the tests, but I haven't looked closely at that yet. And, a good question is: does Heimdal have the same calling convention for this?