Closed mw-a closed 3 years ago
Thanks for the PR btw! Very welcome!
I just had a discussion with a friend about the necessity and danger of the trusted
flag for my use-case. I might be abusing it here and creating a problem where none actually exists.
So to recap: What I'm actually trying to do is get the fallback to krb5_principal
for the machine identity if none is provided by the client, in this case cifs.upcall
upon mount as root[1]. This is guarded by use_service_principal
which can only be true if the trusted
flag is enabled[2]. Since the service also has impersonate
enabled for all other usage by all other uids, this unconditionally sends me down the impersonation codepath.
Perhaps the issue can be avoided by tweaking try_impersonate()
for what I'm trying to do?
Looking at the code and documentation again I get the feeling that the trusted flag opens the service up to other use-cases as well, i.e. actual impersonation requests by the client (case ACQ_IMPNAME). Is that correct? Am I creating a security problem with this?
[1] https://github.com/gssapi/gssproxy/blob/main/src/gp_creds.c#L389 [2] https://github.com/gssapi/gssproxy/blob/main/src/gp_creds.c#L354
Apparently, to impersonate oneself is not allowed
It's not just AD that denies this. The problem is articulated in MS-SFU 3.1.5.1.2, which basically says that a self-s4u request generates the same reply that a KDC without s4u support would generate.
So it's really better not to attempt the operation at all.
I did a packet capture. I see no actual error from the KDC: Here's the TGS-REQ: And here's the TGS-REP: Looks to me as if we get exactly what we asked for.
The error seems to actually come from libkrb5 here https://github.com/krb5/krb5/blob/f7b3cb8bbe90817f7bfbc545f1e427c16f52a79c/src/lib/krb5/krb/gc_via_tkt.c#L258. It seems to me, the check assumes that S4U2Self failed if the KDC responds with a client name that equals the service name we asked for in the req-body and not the client name from the PA-FOR-USER field. But in the case that both are identical that assumption is wrong because then the KDC did actually fulfil the S4U2Self request as ordered. It's still a needlessly complex request but technically correct and fulfilled.
It seems to me, the check assumes that S4U2Self failed if the KDC responds with a client name that equals the service name we asked for in the req-body and not the client name from the PA-FOR-USER field. But in the case that both are identical that assumption is wrong because then the KDC did actually fulfil the S4U2Self request as ordered. It's still a needlessly complex request but technically correct and fulfilled.
This is working as intended - see my comment on MS-SFU above :)
Looking at the code and documentation again I get the feeling that the trusted flag opens the service up to other use-cases as well, i.e. actual impersonation requests by the client (case ACQ_IMPNAME). Is that correct? Am I creating a security problem with this?
Would you mind pasting your configuration here?
Usually the trusted flag is used together with a euid flag restricting access to the service to a specific user that you trust. You can also restrict by other means (ie file permissions on the socket) of course, but the concept is the same.
So you really have to trust that user not to try "bad things"(TM).
It seems to me, the check assumes that S4U2Self failed if the KDC responds with a client name that equals the service name we asked for in the req-body and not the client name from the PA-FOR-USER field. But in the case that both are identical that assumption is wrong because then the KDC did actually fulfil the S4U2Self request as ordered. It's still a needlessly complex request but technically correct and fulfilled.
This is working as intended - see my comment on MS-SFU above :)
It is a matter of semantics. In theory MIT could decide that the response is ok and not return a failure to the calling application (gss-proxy), but in this case MIT decide to consider this a failure because a s4u operation was requested and it was not "technically" fulfilled.
It is merely an implementation choice. That said, this is how MIT behaves so we need to fix gssproxy to take that into account :-)
Looking at the code and documentation again I get the feeling that the trusted flag opens the service up to other use-cases as well, i.e. actual impersonation requests by the client (case ACQ_IMPNAME). Is that correct? Am I creating a security problem with this?
Would you mind pasting your configuration here?
My config which exercises the code path is (the comments reflect my intent based on my unterstanding of the code):
[service/cifs]
mechs = krb5
socket = /var/lib/gssproxy/cifs.sock
cred_store = keytab:/etc/krb5.keytab
cred_usage = initiate
# allow process with any euid to use the service and receive impersonated
# tickets for services
allow_any_uid = yes
# allow euid access to keytab
trusted = yes
# allow impersonation
impersonate = yes
# allow process with euid 0 to use the keytab
euid = 0
# fall back to default principal if no name is given
krb5_principal = FEDORA33$
Usually the trusted flag is used together with a euid flag restricting access to the service to a specific user that you trust. You can also restrict by other means (ie file permissions on the socket) of course, but the concept is the same. So you really have to trust that user not to try "bad things"(TM).
The idea of the config is to allow any user to receive a personal Kerberos ticket for access to cifs mounts, acquired for them by gssproxy using impersonation (allow_any_uid
+ impersonate
). This works because in conjunction with trusted
because it is checked against euid == target_uid
in gp_cred.c
and usage of the service keytab denied if not.
For the special case of euid 0 I would like it to fallback to krb5_principal
so that the initial mount happens with the machine or some kind of non-privileged mount identity, similar to what NFS' gssd does but without the need to extend cifs.upcall
to do it itself. This principal might need impersonation by gssproxy to acquire a ticket as well. It could be argued that trusted
is indeed required here because this principal will likely not match the name associated with euid 0 (e.g. root). So euid does want to impersonate someone else and needs to be trusted not to abuse that.
What shouldn't happen is that any uid can explicitly request impersonation of anyone else (case ACQ_IMPNAME
). Is that a possibility or is it already handled and I just haven't found the check?
Alternatively, it would be an option to disable impersonate
for the configured euid so that it doesn't impersonate but just gets a credential using the keytab like any other privilege-separated service that doesn't have direct access to its keytab managed by gssproxy. Is this currently possible in conjuction with allow_any_uid
and impersonate
and I just haven't found the knob to turn?
Force-pushed again because I found I was memory-leaking target_name
in the case that a name was requested but did not match the acquired impersonation credential (e.g. krb5_principal = cifs-mount
, i.e. a non-privileged mount-user which is not the machine identity is configured).
Also, I had updated the comment to reflect that it's MIT krb5 which gives the padata support error, not the AD KDC.
Looking at the code and documentation again I get the feeling that the trusted flag opens the service up to other use-cases as well, i.e. actual impersonation requests by the client (case ACQ_IMPNAME). Is that correct? Am I creating a security problem with this?
Would you mind pasting your configuration here?
My config which exercises the code path is (the comments reflect my intent based on my unterstanding of the code):
[service/cifs] mechs = krb5 socket = /var/lib/gssproxy/cifs.sock cred_store = keytab:/etc/krb5.keytab cred_usage = initiate # allow process with any euid to use the service and receive impersonated # tickets for services allow_any_uid = yes # allow euid access to keytab trusted = yes # allow impersonation impersonate = yes # allow process with euid 0 to use the keytab euid = 0 # fall back to default principal if no name is given krb5_principal = FEDORA33$
Usually the trusted flag is used together with a euid flag restricting access to the service to a specific user that you trust. You can also restrict by other means (ie file permissions on the socket) of course, but the concept is the same. So you really have to trust that user not to try "bad things"(TM).
The idea of the config is to allow any user to receive a personal Kerberos ticket for access to cifs mounts, acquired for them by gssproxy using impersonation (
allow_any_uid
+impersonate
). This works because in conjunction withtrusted
because it is checked againsteuid == target_uid
ingp_cred.c
and usage of the service keytab denied if not.
Ok, your configuration does that.
For the special case of euid 0 I would like it to fallback to
krb5_principal
so that the initial mount happens with the machine or some kind of non-privileged mount identity, similar to what NFS' gssd does but without the need to extendcifs.upcall
to do it itself. This principal might need impersonation by gssproxy to acquire a ticket as well. It could be argued thattrusted
is indeed required here because this principal will likely not match the name associated with euid 0 (e.g. root). So euid does want to impersonate someone else and needs to be trusted not to abuse that.
Correct.
What shouldn't happen is that any uid can explicitly request impersonation of anyone else (case
ACQ_IMPNAME
). Is that a possibility or is it already handled and I just haven't found the check?
In case of a user_requested is true (which is if target_uid != euid), then we forcibly overwrite requested_name with the name of the user obtained via uid_to_name(), so non-trusted users cannot impersonate anyone but themselves.
Alternatively, it would be an option to disable
impersonate
for the configured euid so that it doesn't impersonate but just gets a credential using the keytab like any other privilege-separated service that doesn't have direct access to its keytab managed by gssproxy. Is this currently possible in conjuction withallow_any_uid
andimpersonate
and I just haven't found the knob to turn?
I think your current patch is the simplest way to go about it, and I now verified your configuration is a legitimate case, so let's fix the last issue and merge it in.
Thanks for your explanations. So everything should be fine and ready to go.
Thanks a lot!
@frozencemetery I think we may want to backport this to Fedora/RHEL, WDYT?
Rolled it into gssproxy-0.8.4 and built that for rawhide. Willing to backport elsewhere if it would be helpful, of course; just file a bugzilla for it.
As discussed on the mailing list[1]:
When trying to impersonate the user which has been selected as impersonation credential, an AD KDC returns error:
GSSX_RES_ACQUIRE_CRED( status: { 851968 { 1 2 840 113554 1 2 2 } 2529638928 "Unspecified GSS failure. Minor code may provide more information" "KDC has no support for padata type" [ ] } output_cred_handle: )
Apparently, to impersonate oneself is not allowed. Also, it is likely not even necessary: If we can get impersonation credentials from credstores, we can at least try to short circuit and get actual user credentials the same way.
With this patch it becomes possible to delegate the acquisition of e.g. cifs mount credentials from cifs.upcall into gssproxy and use the host identity (e.g. HOSTNAME$@REALM of AD) while it is also being selected as impersonation credential due to the order of keys in the keytab.
[1] https://lists.fedorahosted.org/archives/list/gss-proxy@lists.fedorahosted.org/thread/WO3IN7Q3BKQ5VCNDAPIBNFANBJEZ4OSD/.