 Hi everyone. I'm Matt Martno. I work at Intel and just talking about key restrictions today. It's not something super sophisticated as much as it's something that's relatively new that maybe you could see some use in security applications and wanted to help everyone learn about it. So to start out with just giving a little baseline of what parts of the kernel key system I'm talking about so that we can all make sense of where I'm adding things later on. So overall the kernel keys, it gives you, there's internal kernel APIs and also visible from user space you can create these key objects. Everyone has, every object has a serial number that you can use to look it up and each of those keys contains some kind of payload where the meaning is dependent on what type of key it is and there's a few other kind of attributes hanging off of there and each key has a type so within when you're implementing a key type within kernel code you populate a structure with the whole bunch of function pointers that implement this key type. Parses the payload, let's you access the payload in a meaningful way, modify key, well yeah set up the keys and the values associated with it and those payloads include things like a user type is just a binary blob, be whatever you need it to be. Asymmetric keys, it'll parse X509 certificates and give you access to the key material to do crypto operations in the kernel. Big key is a blob like the user key but is can get paged out and so it has some different trade offs but those are the basics there. One special key type is a key ring which is a key in every way but one instead of a payload it contains a list of references to other keys so you use this to organize groups of keys for different purposes and this again is accessible from user space with the key control system call to link and unlink keys and you can have key rings within key rings and things like that. So getting to the restriction part of things, earlier on in the kernel like version 4.6 and earlier, well yeah so what restrictions were built for was to help manage trusted keys in the kernel so with version 4.6 and earlier you could have, you could load asymmetric keys from user space or within the kernel from wherever it would decode those keys and then using the key identifier the signing keys identifier embedded in those keys it could look that up in the system key ring and if the signing key was trusted had a key flag trusted flag set on it then if you were signed by a trusted key you became a trusted key no matter where it was used elsewhere in the system. So in for kernel version 4.7 about two years ago David Howells the key ring maintainer refactored that trust system so that instead of using key flag trusted you had you could have this concept of a trusted key ring within the kernel and just any key that was in that ring that was how you determined trust and to implement that he added a function pointer to the key rings that just allowed it you attach a function to it that function gets called when you attempt to link a key it can look at the key it can look at some other data and figure out do I trust this or not. So this was you know the initial purpose of this was that refactoring of the trusted keys but it was flexible enough to use for other things too. So extending this to user space so where I came at it was I had some team members who are working on the wireless demon IWD which is it's like a it is a WPA supplicant kind of replacement and to do enterprise Wi-Fi authentication there's certain types of that that need to verify RSA certificates make sure you know you can trace your signed keys back to a certain root and this this was you know fit well with this ability to use other use that infrastructure added for restricted key rings because it's kind of a different idea of of trust from what's in the kernel in terms of what you want to trust to load a module well this is what you want to trust to join a Wi-Fi network and kind of constraint we had set for ourselves on with that project was we're using IWD and it has a support library the embedded Linux library sort of a kind of a lightweight glib replacement that we're building a few projects on and so we only have those depend on the kernel itself and and the C library so we knew the kernel already knew how to do everything we wanted to do there was just this barrier to be able to do it from from user space so so I worked on the patches to add a key control operation that would let you set the the restrict function on a key ring from user space and so this this got merged for version 4.12 there's some user space support libraries so the key control function isn't part of the C library it's part of this key utils library and so version 1511 and later has a wrapper function for that syscall and a command line utility as well and every every tag release we have of ELL has that function supported too so from from the user space side um using using the key utils call it's just it's just this one function call to think about it's just three things you give it you want this serial number of the key ring that you're you're trying to configure to only allow certain keys to to be contained um the key type associated with that restriction so we were working with asymmetric keys so this that's the key type we added to and then this restriction string which just gets parsed out and whatever code you add to the kernel key type um looks up the right function pointer to plug in and and some make potentially some extra data too and sets that all up within the kernel so that then every link operation will be checked um you can do this on a key ring that's empty or one that's not empty um the keys that are already there aren't aren't checked by this function at all um so you can deliberately pre-populate it and then restrict and limit changes from there or you can um keep it empty so um what's in the kernel today from you know four dot I forget what I said 12 or 17 um up through today is uh just these asymmetric key examples so um similar to what the code inside the kernel uses you can you can use the trusted keys that are contained within the kernel or the built-in keys or the secondary key ring um or you can use this uh the second line key or key ring um with a key serial number and so what you're telling what you're telling the kernels to configure in that case is to say you know here I'm giving you a DER encoded certificate only link it if it is signed by this specific key that you gave it a serial number for or if you give it a serial number for a key ring it'll search through the entire key ring to to match the key identifiers and and match the signature um and you can also um if you add this chain to the end um it will also search the key ring itself so you're trying to link into um key ring X you already have keys in it it'll also search the keys that are already in it as you know potential signers um so what this system call looks like for key ring one two three four five you want to say only add signed keys to key ring one two three four five and only looked for the signing keys in key ring six seven eight nine you just do what's on the last line here um so kind of the simplest case of how you would validate a certificate chain with this is you you would create one key ring put your root certificates in it um create a second key will that call that one ca key ring um in this kind of bash like pseudo code here um you create a second keyring called verify key ring you you restrict that you say it's it's an asymmetric type that you're using to look it up it's this key or key ring you tell it who who has the the root certificates and and use this chain option and then just in order from the key that you think is supposed to be signed by the root you try to link that one first and then you go on down the intermediate certificates and if you anything that gets allowed to link is part of that chain um and like I said this is a simple case you can you can make it more strict by cleaning out um all but the most recent keys so that you're making sure you're only checking the signature by the the last thing you linked in um so for some cases and in our case um we use this kernel functionality instead of a crypto library um that was desirable just because of the size and complexity of of crypto libraries sometimes the licensing can get tricky sometimes they pull in additional extra um third party libraries too so um probably the the main benefit is if you have um other parts of the kernel or security infrastructure or or something that that needs to use these keys then they need to be in the kernel anyway you can you can use um you know might have more direct access to any crypto or security hardware or lower level functionality that you might use to evaluate the keys that you're adding um um if you are doing a lot of these operations to add in um add keys to the kernel to evaluate them um it might might have more overhead than doing it in user space because you know it takes system call after system call to load a key and try to link a key and and do all the setup and tear down and stuff and depending on your usage the program you're using this in that may or may not work for you um and um you know if you're you're trying to do a large volume of keys there you might trip over certain things in the the kernel key system um like quotas for example can fill up depending on how you remove keys if you revoke them um they might not get garbage collected right away and might hang around using a quota if you invalidate them they disappear more more immediately um and there are other um other ways to work around quotas with process and thread specific key rings that they're only visible visible to one processor thread um so that kind of covers what what a you what I've used key rings for so far um next step being to um add your own whether whether adding those for asymmetric keys extending what's there already or if you have another key in another type of subsystem or something like that or a different use for them this is how you you'd add more so like I showed earlier with with key types and key rings in the in the kernel you've added two new things one is a lookup function in the key type that takes that string that came from user space figures out a function pointer plugs it in and then the key ring has a restrict function that lives there that's just that's the function that gets called on every link operation so step one figure out which key type you're going to work with um if you're adding something that only works with asymmetric keys uh probably best thing to do is add a today's symmetric key type um if you have a restriction that might consider that might work across multiple key types that's totally possible the the um the kernel key subsystem it just you know it'll look anything up based on a serial number so you could conceivably have a mix involved but you know you might want to only only allow that restriction to be configured if all of those key types are available in the kernel or you might want to make sure to turn on certain configurations um if that is important to you too um and uh another another choice is to not use an existing key type at all but just create a whole new type where maybe you can't even create instances of those keys but you could add um the restriction lookup function to it that allows you to attach that code um might be good to experiment that way if you want to be able to build and load a kernel module try out your restriction do something that's not intended for upstream um and yeah i don't i don't know whether that would fly with the maintainers if you'd tried to upstream a key that you couldn't couldn't actually use for anything other than lookups but uh it's a useful tool to have for development at least um so the next step is just what do these functions look like that you attach to a key ring um they look like this um so you tell it which key ring you're trying to link to um the type and payload in the middle are um the key that you're trying to work with the one that's the candidate for linking um it's it might not be a key object yet it might be in the middle of being created so um that's why you get the payload here and then the final pointer to a key is um like in the example of the existing um a key or key ring this is a pointer to the your signing keys that that can look at so um like many functions in the kernel so return zero if it's okay or some error value if it's not and um if you're calling someone's trying to link the key from user space if there's an error that error gets propagated out to whoever made the system call some things to to consider um so this the key ring has the data structure has this restrict function pointer hanging out there um if you're writing kernel code using restrictions you don't want to just plug that in directly there's a key ring restrict function that'll kind of do all the details for you of locking and reference counting and making sure that you don't have a key ring that's trying to be restricted on itself through some cycle or something um since since a key serial number can be associated with any key type you want to double check that you're using what you think you are and there's also a useful key validate function that when you're trying to access each key you need to make sure that it hasn't been you know garbage collected in the middle of your process or something so if you validate it it'll be available for you to use safely um so that in previous couple slides just talking about this is what gets added to a key ring so the next step is what do we add to a key type and this is just the lookup function um that takes that string that came from user space and figures out what to do with it um so um all this the you know the main job of it takes that string returns a pointer to this structure and the structure has the function pointer the the the key that you're using to look up your signing keys and the key type that owns it because if you try to um say remove the module or deregister that key type it there's there's some cleanup for the key system to do so um let's see what else um and that structure should be dynamically allocated because when it gets garbage collected it's gonna it's gonna try to free that um anything else so that that struct key restriction that the lookup function returned um if you're adding more complicated functionality that needs more than a single key pointer you might need to add more uh members to that structure um and um you know just I mean kind of goes for a lot of kernel code uh you know if if you're dealing with uh something reference kind of make sure you're doing it right um key key lookup um is is how you grab a key pointer from a serial number and that that will give you something appropriately reference counted so that that um so where to go from here um this is kind of where hopefully somewhere in the security world this is this will be a useful tool for um managing any key based um things you might be doing with uh say setting up trust between you know virtually seen hosts and guests or you know kind of some of the topics we've heard talked about already today um in the last few days um something that happened after I mean added I added the initial uh asymmetric restrictions there in version 4.20 of the kernel there the um asymmetric key has a what has a subtype depending on where the key came from and now there is a subtype for TPM based keys so you could add a restriction base that that says oh well this will only accept keys that have been signed by something in the TPM um you could add other things than signatures um you could say I I only want to allow keys of you know with a certain number of bits or more um because on a lot of cases you know the the user space program is just passing off these encoded encoded certificate blobs to um to the kernel to do the decoding you might not know that much about what's inside them how big the key is um so that that could be used to check that and um I saw the raised hands earlier in the week for people who know about ebpf so um you know it kind of seemed like what we would have done so far is implement kernel code you use the string from user space to just say call this kernel function and and you're kind of locked in um but you know maybe somewhere down the line people want something more flexible um that an appropriately privileged daemon or something could add uh some bpf ebpf code that could have some fine tunable um restrictions that that would have access to um whatever criteria you needed to use to assess a key so that's all I've got um here's where to contact me um some mailing lists and whatnot um referencing some of the projects I talked about so any questions so if we're gonna open up the pandora's box of ebpf in the key ring um you know I immediately start asking other questions like hey can we start implementing something that looks more like a crypto api based on on the key ring to say hey I want to like um create a new key and give me a key um descriptor back and let me use that to do wrap and unwrap operations or derivations and other such things that you would normally do with a key management system right not sure how much discussion there's been on this but as long as we're talking about ebpf and key ring why not sure I mean some some of those key types there's like trusted and encrypted keys where those keys can be generated by through the key control operation um but it's I was trying to think about what else is in there already um there are so when you load an asymmetric key in the kernel there you can sign you can verify signatures you can use that to so you load multiple keys you can load two keys and say you know okay now verify a signature on a key that I pass in or you can um encrypt data and um there's there is a pkcs8 parser now I don't remember when that got merged but you can load a certificate that has private and public keys and you can encrypt and you can sign using the king control api as well already um so but it yeah it is uh it would be interesting to to go along those lines and expose more function functionality diamond dynamically there are certain uh storage protocols in the kernel like nfs ice guzzy and nvme on tcp that want to use tls and there's a certain lack of ability for the kernel to discover what certificate to use to authenticate and I'm wondering if this might be an answer I mean the answer that we have today is to build a net link infrastructure but I'm not sure that's really wise sure do you have any opinions about that um that I mean it sounds like something workable I mean you could have if you had a way to provide key IDs to you know a specific connection um and I mean this this kind of comes into play more of you know do I trust that do do I don't want to use the word trust because that has some very specific meaning but is is a certificate you know signed appropriately or or it doesn't meet whatever criteria I have that I don't want to use with nfs that you could use that to say okay I tried to load this and it accepted it and rejected it okay now that key it was accepted it's in the kernel now now it's kind of a matter of just conventional using the key sub system and tying that into whatever other you know nfs kernel code is running that you do want to grab that for but you know if it's something you're using across multiple connections you know it could make sense to load that key once know it's good and then use that key id over and over again so is that helpful or okay okay number of questions okay thanks Matt