On Wed, May 13, 2015 at 12:06:07PM +0200, Sumit Bose wrote:
Hi,
I have created a design page for the InfoPipe method to look up users
based on a certificate as e.g. requested by
https://fedorahosted.org/sssd/ticket/2596 . Most of it will be needed
for certificate based authentication
https://fedorahosted.org/sssd/ticket/546 as well. It is available at
https://fedorahosted.org/sssd/wiki/DesignDocs/LookupUsersByCertificate
or for your convenience below.
Comments and suggestions are welcome.
Hi,
I only have a couple of questions and comments. Mostly I agree with you,
thanks for the design page!
bye,
Sumit
= Lookup Users by Certificate =
Related ticket(s):
*
https://fedorahosted.org/sssd/ticket/2596
*
https://fedorahosted.org/sssd/ticket/546
*
https://fedorahosted.org/freeipa/ticket/4238 (design page:
http://www.freeipa.org/page/V4/User_Certificates)
=== Problem statement ===
As stated in ticket #2596 applications doing user authentication based
on certificates, e.g. web servers, need a way to map the certificate
presented by the client to a specific user. Although there are various
ways to derive a user name from special entries in the certificate
so far there is no generally accepted scheme. The most general and in
some cases the only possible way is to look up the certificate directly
in the LDAP server. This requires that the certificate is stored in
the LDAP server which we will assume for this initial design. (In
a second part user lookups based on the certificate content will be
added, this requires that the syntax for the mapping is specified in
http://www.freeipa.org/page/V4/User_Certificates#Certificate_Identity_Map...)
The primary interface to lookup users by certificate would be D-BUS.
=== Use cases ===
The primary use case is described in ticket #2596. If Apache is
configured to use certificate based client authentication modules like
mod_lookup_identity has access to the PAM encoded certificate via environment
~~~~~~~~~~~
PEM encoded, right? :-)
But later the design page talks about DER only..so PEM here was just an
example, correct?
variables. With this data as input mod_lookup_identity should call a
D-BUS
method like ''org.freedesktop.sssd.infopipe.!GetUserAttrByCert'' which
will return the data of the user the certificate belongs similar to the
''!GetUserAttr'' method.
=== Overview of the solution ===
Besides adding the D-Bus method to the !InfoPipe responder the generic LDAP
backend should be able to search and read the certificate data if available
from a LDAP server and store it to the cache. The internal sysdb interface
must be extended to search cached entries with the certificate as input.
=== Implementation details ===
==== LDAP backend ====
Reading certificate data if available just requires adding a new user
attribute which will be requested during LDAP searches for a user. In
general the certificate is stored as a DER encoded binary on the LDAP
server. '''Question: should we add an option like ldap_user_cert_encoding
to support other encodings a server might send to us, or shall we add it
only when there is a real use case?'''
I think we should keep it simple for the time being. A middle ground
might be to add the option, so an external contributor knows where to
extend the code, but only allow one value and state in the man page that
only DER is supported at the time being.
Internally the certificate should
be stored DER encoded to the cache as well because this encoding is the
most unambiguous encoding (e.g. with PEM encoding it is not clear if the
base64 blob should have line breaks or not and if the enclosing '-----BEGIN
CERTIFICATE-----' and '-----END CERTIFICATE-----' should be stored as well
and if line break should be added here or not?)
To search a user with the help of the certificate the DER encoded binary
ticket must be transformed into a search filter. In this case it would be
something like 'userCertificate=\23\a5\3e......' where each byte from the
certificate is is represented by a hex value pre-pendened by a '\'. The
filter should be generated in a subroutine which accepts the DER encoded
certificate with base64 ascii armor and returns the search filter. This
way the subroutine can later be extended to accept configuration options
for the identity mapping and can return different search filters for those
cases. Since the requirement for LDAP and sysdb search filters are the same
there should be an option indicating if a LDAP or sysdb filter is needed,
because the attribute names might be different.
Any reason to not just pass in the attr name and create a wrapper? Like:
const char *sss_cert_filter(const char *filter, const char *base64cert);
const char *sdap_cert_filter(struct sdap_options *opts, const char *base64cert)
{
return sss_cert_filter(opts[SDAP_USER_CERT].name, base64cert);
}
const char *sysdb_cert_filter(const char *base64cert)
{
return sss_cert_filter(SYSDB_USER_CERT, base64cert);
}
Although it would be possible to handle the binary DER data directly I think using a
base64 ascii armor to handle the data as a string is useful to avoid adding code for
handling binaries e.g. in the S-BUS requests to the backends.
Yes, I think this is a good idea to use an ASCII representation..
==== SYSDB API ====
A new call sysdb_search_user_by_cert() should be added which get the DER encoded
certificate with base64 ascii armor as input and use the function described above to get a
proper search filter. Currently it will be only the search filter for the binary
certificate. Other than that the new call will act like to other sysdb_search_user_by_*()
calls.
==== !InfoPipe ====
A new method GetUSerAttrByCert() must be implemented which expected
I agree with Pavel about object paths..
the PEM encoded certificate and an array of attrbute names.
'''Question:
Should we only support PEM here or other formats as well? In this case
we need a third parameter indicating the encoding of the certificate
data.'''.
I think it would make the API future-proof to add another parameter
where only one value would be supported. If anything else than DER is
used, than we throw an error. Pavel even adds support for custom D-Bus
errors in his latest patchset (currently still pending review).
Do you see any other extension we'd have to make in the API if we
decided to support other formats in the future?
InfoPipe will convert the certificate into DER encoding with
base64 ascii armor, search the cache and eventually forward the request to
the backend. The request to the backend is processed similar to a request
by name, only that a new filter name, e.g. DP_CERT_ID "cert", is needed.
Since it is in general not obvious to which domain a certificate belongs,
the search must iterate over all domains in case no matching certificate
was found. For the cases where there is a strong 1:1 relationship between
the issuer of a certificate and a domain, configuration options for this
can be added later.
What about default_domain_suffix? Do you plan to support domain-specific
searches in the code at all?
=== Configuration changes ===
A new user attribute open 'ldap_user_certificate' will be added to the
LDAP provider. By default only the IPA provider will set a value for
it to avoid reading about 1k of data which is not needed in the other
providers. '''Question: Does this make sense or shall we enable it for
other providers as well?'''
No, not in the beginning. From the experience with SSH keys I assume
we'll be asked sooner or later, so we shouldn't make it hard for
contributors to extend the support to the LDAP provider, but I don't
think we need to do it ourselves, not now.
=== How To Test ===
First a certificate must be load to a IPA user entry, it can be any kind of
certificate as long as it is valid an DER or PEM encoded.
So..which encoding do we support? The ldap_user_cert_encoding proposal
made it sound like only DER would be?
Until IPA has some
import utilities ldapmodify should be used. A LDIF file might look like this:
{{{
dn: uid=cert_user,cn=users,cn=accounts,dc=ipa,dc=devel
changetype: modify
add: userCertificate
userCertificate::MII...=
}}}
where MII...= indicates the base64 encoded certificate data. If you have a PEM encoded
certificate you can just use the base64 part here. If the certificate is DER encoded it
can be transformed to base64 with
{{{
base64 < ./certificate_file.der | tr -d '\n'
}}}
Testing can be done with the help of the dbus-send utility:
{{{
# dbus-send --system --print-reply --dest=org.freedesktop.sssd.infopipe \
/org/freedesktop/sssd/infopipe \
org.freedesktop.sssd.infopipe.GetUserAttrByCert
string:"-----BEGIN
CERTIFICATE-----.......-----END CERTIFICATE-----" \
array:string:"name","uidNumber"
method return sender=:1.1807 -> dest=:1.1819 reply_serial=2
array [
dict entry(
string "name"
variant array [
string "cert_user"
]
)
dict entry(
string "uidNumber"
variant array [
string "1234567"
]
)
]
}}}
This would return an object path, but otherwise ACK
=== Authors ===
* Sumit Bose <sbose(a)redhat.com>