Storing the initial (encryption) key in SMF on Solaris 11
By DarrenMoffat on Aug 24, 2011
When deploying services that use encryption as part of their network protocols it is often desirable to protect the long term keys that these services use. For example a web server serving up TLS needs an RSA private key, similarly so do most SSH servers and IKE daemons. Developers, administrators and security officers have to make an appropriate trade off between the requirements of unattended reboot (for high availability with or without clustering) and securing the long term key material from unauthorised access.
Even when using a Hardware Security Module (HSM) such as the CA-6000 card to store the RSA private keys there is still a requirement for an "initial" key, in the case of devices like the CA-6000 this is the PKCS#11 token PIN (passphrase) that has to be provided to get access to the sensitive and non extractable keys stored in it.
So were do we get this "initial" key from ? We can't prompt for it because we want unattended restart of the services (or even whole machine).
This is where the compromises come in, it isn't unusual to store this PIN/passphrase in a root owned and access protected (usually 0600) file in the root file system. However that then means that the service must run as the same uid as the PIN file or run with privilege (say file_dac_read) necessary to read it which might not always be appropriate.
It is possible to use the Solaris Service Management Framework (SMF) as storage for these initial keys and provide access only to authorised users for updating them and authorised services for reading them. This allows us to easily implement a separation of duty between the user id the service runs as and administrators that can manage the initial key but without requiring them to be root or even run any program that runs with privilege or as the same user the service runs as.
This is possible because the SMF repository is backed by an sqlite database that is stored in the filesystem owned and readable/writable only by root. All access to its contents is mediated by the svc.configd daemon. Normally all properties of a given service are readable by any user (because they aren't really sensitive) but writable only by users with the specified value_authorization (which can be specified to the granularity of an individual property group). SMF also provides the ability to require a property specific read_authorization in order to be able to view the value of a property. We can use this functionality of SMF as an alternate to the PIN/passphrase in a file model. The risks a similar in that the PIN/passphrase is still stored in the root file sytem but we can now have it stored in the root owned SMF database but allow very fine grained access from unprivileged processes (no need to have the file_dac_read privilege or run with uid root), so there is an additional little bit of protection via least privilege.
For example the following SMF manifest fragment shows a property group called config that requires a different authorisation for reading and updating the property 'pin':
<property_group name='config' type='application'> <propval name='pin' type='astring' value='1234' /> <propval name='read_authorization' type='astring' value='solaris.smf.value.myserv.readpin' /> <propval name='value_authorization' type='astring' value='solaris.smf.value.myserv.writepin' /> </property_group>
If this were part of a service that ran as the user 'myserv' then we would need to assign the solaris.smf.value.myserv.readpin authorisation to that user (using useradd(1M)), any user that doesn't have that authorisation (or a higher level one eg solaris.smf.value) will get permission denied on attempting to read the property. That would allow the processes of the service to read but not change the stored PIN without requiring any privileges(5) to do so, and the pin value remains in a root 0600 file (the SMF repository) . To change the PIN value we may have an admin account (or role) that runs as a different user that has the solaris.smf.value.myserv.writepin. The following shell script fragment shows how to get the value of the property in a method script:
svcprop -p config/pin $SMF_FMRI
What the service does with the pin or initial key is upto the service developer but it could for example be the value passed to C_Login() to get access to sensitive keys stored in a CA-6000 card or even those stored encrypted on disk with the pkcs11_softtoken(5) keystore.
The SMF repository database is not currently encrypted on disk so a user that can read it can still run strings over it or use sqlite to read the raw database. If we were to encrypt the SMF repository on disk (using an Sqlite extension since it isn't supported natively) we of course now again have an "inital key" problem. So were do we get that key from ? One possible place is from something like the TPM (Trusted Platform Module), however accessing the TPM currently requires the tscd service to be running so we have a start up ordering problem so we couldn't currently attempt to do that. Also we can't encrypt the root file system with ZFS (the SMF repository lives in /etc/svc/repository.db) due to similar "initial key" issues.
There are alternate ways to address the risks, for example using a network accessible keystore that hosts have to connect to and get their long term keys from (the assumption here is the server can adequately authenticate the client - but where does the client securely store that initial credential?). Solaris has provides the pkcs11_kms(5) module for getting AES keys from the Oracle Key Manager product, but using that requires a C_Login() with a PIN since the client authentication credentials used to authenticate the client to the Oracle Key Manager server instance are stored encrypted on the client.
So have we really "solved" the problem ? Not really, but we have
provided an alternative method of locally storing the initial
key/pin/passphrase that for some services under the control of SMF may
be more appropriate than "initial key in a file".