lasso/docs/lasso-book/writing-a-c-sp.txt

414 lines
14 KiB
Plaintext
Raw Normal View History

2004-08-17 21:48:15 +02:00
=======================================
Writing a Liberty service provider in C
=======================================
:author: Frederic Peters
:contact: fpeters@entrouvert.com
:date: $Date$
:revision: $Revision$
:copyright: Copyright © 2004-2007 Entr'ouvert
2004-08-12 18:09:41 +02:00
.. contents:: Table of Contents
.. section-numbering::
Lasso Projects Basics
=====================
Lasso functions are defined in several header files typically located in
``/usr/include/lasso/`` or ``/usr/local/include/lasso/``. It is possible to
include individual files but in most case it is enough to include the main
``lasso.h``.
The first thing to do is then to call ``lasso_init()``. Similarly the last
thing will be to call ``lasso_shutdown()``. The smallest and useless Lasso
project will therefore be::
#include <lasso/lasso.h>
int main(int argc, char *argv[])
{
lasso_init();
printf("Hello world.\n");
lasso_shutdown();
return 0;
}
Lasso uses a tool called ``pkg-config`` to know the necessary flags for
compilation and linking.
::
$ pkg-config lasso --cflags
-DXMLSEC_CRYPTO=\"openssl\" -DXMLSEC_LIBXML_260=1 -D__XMLSEC_FUNCTION__=__FUNCTION__
-DXMLSEC_NO_XKMS=1 -DXMLSEC_NO_CRYPTO_DYNAMIC_LOADING=1 -DXMLSEC_CRYPTO_OPENSSL=1
-I/usr/include/lasso -I/usr/include/libxml2 -I/usr/include/xmlsec1 -I/usr/include/glib-2.0
-I/usr/lib/glib-2.0/include
$ pkg-config lasso --libs
-llasso -lxmlsec1-openssl -lxmlsec1 -lssl -lcrypto -ldl -lgobject-2.0 -lxslt -lxml2
-lpthread -lz -lm -lglib-2.0
2004-08-12 17:54:42 +02:00
Creating an executable from the previous sample would then a simple matter of
2004-08-17 18:57:48 +02:00
calling ``gcc`` with the right flags. But there is currently a bug in
2004-08-12 17:54:42 +02:00
XMLSec, the library used by Lasso to provide XML Signature and XML Encryption
support. It is possible to workaround the bug::
$ gcc hello.c -o hello $(pkg-config lasso --cflags --libs)
<command line>:4:16: missing terminating " character
$ gcc hello.c -o hello $(pkg-config xmlsec1 --cflags --libs | tr -d '\\')
$ ./hello
Hello world.
Liberty and Lasso profiles
==========================
2004-08-11 12:56:23 +02:00
Lasso provides the necessary functions to implement Liberty Alliance profiles,
as defined in the `Liberty ID-FF Bindings and Profiles Specification`_. They
are:
- Single Sign-On and Federation
- Name Registration
- Federation Termination Notification
- Single Logout
- Identity Provider Introduction
- Name Identifier Mapping
- Name Identifier Encryption
Each profile maps to a Lasso object such as ``LassoLogin``, ``LassoLogout``...
Those are initialised with data known about identity and service providers,
2004-08-11 12:56:23 +02:00
available in a ``LassoServer`` object.
The ``LassoServer`` object may be created as follows:
::
LassoServer *server;
server = lasso_server_new("sp-metadata.xml",
2006-09-19 10:55:04 +02:00
"sp-private-key.pem", NULL, "sp-crt.pem");
lasso_server_add_provider(server, LASSO_PROVIDER_ROLE_IDP,
"idp-metadata.xml", "idp-public-key.pem", "ca-crt.pem");
2004-08-11 12:56:23 +02:00
2004-08-12 17:54:42 +02:00
- ``sp-metadata.xml`` is the Liberty metadata file for the service provider
- ``idp-metadata.xml`` is the Liberty metadata file for the identity provider
2004-08-11 12:56:23 +02:00
- ``sp-private-key.pem`` is the service provider private key; used to sign
documents
- ``sp-crt.pem`` is the service provider certificate; sent inside signed
documents
- ``idp-public-key.pem`` is the identity provider public key; used to verify
signature in documents sent by the identity provider
- ``ca-crt.pem`` is the certificate of the certification authority used by the
2006-09-19 10:55:04 +02:00
identity provider
- NULL, the third argument, would be used if the private key was protected by a
password.
2004-08-11 12:56:23 +02:00
It is of course possible to have several calls so ``lasso_server_add_provider``
if there are more than one identity provider.
2004-08-12 17:54:42 +02:00
.. note:: Figures in the previously referred Binding and Profiles specification
document are quite helpful in figuring out the message passing.
Serialisation
-------------
``LassoServer`` objects can be serialised into XML files::
gchar *dump;
FILE *fd;
dump = lasso_server_dump(server);
/* write dump into a file, a database, whatever */
g_free(dump);
.. note:: ``lasso_server_dump`` (and other Lasso dump functions) allocates
memory through GLib. ``g_free`` is then the function to use instead
of ``free`` to release memory.
It is then really easy to get back properly constructed objects::
LassoServer *server;
gchar *dump;
/* restore dump from file, database, whatever */
server = lasso_server_new_from_dump(dump);
.. warning:: The server dump only contains the file names; not the actual file
contents. Files should not be moved afterwards.
2004-08-12 17:54:42 +02:00
Liberty Metadata Files
======================
They are descriptions of a provider containing ``providerID`` and various
normative URLs::
2004-08-12 17:54:42 +02:00
<?xml version="1.0"?>
<EntityDescriptor
providerID="https://sp.example.com/" xmlns="urn:liberty:metadata:2003-08">
<SPDescriptor>
<SingleLogoutServiceURL>https://sp.example.com/singleLogout</SingleLogoutServiceURL>
<SingleLogoutProtocolProfile>http://projectliberty.org/profiles/slo-idp-soap</SingleLogoutProtocolProfile>
<SoapEndpoint>https://sp.example.com/soapEndpoint</SoapEndpoint>
<AssertionConsumerServiceURL id="AssertionConsumerServiceURL1" isDefault="true">
https://sp.example.com/assertionConsumer
</AssertionConsumerServiceURL>
2004-08-12 17:54:42 +02:00
<AuthnRequestsSigned>true</AuthnRequestsSigned>
</SPDescriptor>
</EntityDescriptor>
Describe a service provider (with providerID ``https://sp.example.com``) whose
single logout service URL is ``https://sp.example.com/singleLogout``. Refer to
the Liberty Alliance specifications for details.
Single Sign-On and Federation Profile
=====================================
.. warning:: The source code presented in this section has for sole purpose
to explain the different steps necessary to implement this
2004-08-12 16:39:08 +02:00
profile; they notably lack proper error checking. See `Proper
Error Checking`_ for details on error checking.
2004-08-11 01:14:12 +02:00
As a first step the user points its browser to the service provider to the
login URL; the service provider must then respond with an HTTP 302 Redirect
response, pointing the user browser to the identity provider single sign on
service.
2004-09-04 11:04:19 +02:00
.. note:: the login URL is not normative; any name will do.
2004-08-12 17:54:42 +02:00
2004-08-12 16:39:08 +02:00
2004-08-20 17:06:49 +02:00
``server`` is a ``LassoServer`` as seen earlier and ``idpProviderId`` is a
string with the identity provider Id (the string must match a providerID
defined in the metadata file).
::
LassoLogin *login;
login = lasso_login_new(server);
lasso_login_init_authn_request(login, idpProviderId, LASSO_HTTP_METHOD_REDIRECT);
LASSO_LIB_AUTHN_REQUEST(LASSO_PROFILE(login)->request)->ForceAuthn = TRUE;
LASSO_LIB_AUTHN_REQUEST(LASSO_PROFILE(login)->request)->IsPassive = FALSE;
LASSO_LIB_AUTHN_REQUEST(LASSO_PROFILE(login)->request)->NameIDPolicy =
strdup(LASSO_LIB_NAMEID_POLICY_TYPE_FEDERATED);
LASSO_LIB_AUTHN_REQUEST(LASSO_PROFILE(login)->request)->consent =
strdup(LASSO_LIB_CONSENT_OBTAINED);
lasso_login_build_authn_request_msg(login);
2004-08-16 15:17:51 +02:00
You can now redirect the user to the URL defined in ``LASSO_PROFILE(login)->msg_url``; for
example, in a CGI::
2004-08-16 15:16:25 +02:00
printf("Location: %s\n", LASSO_PROFILE(login)->msg_url);
The user then logs in on the identity provider which ultimately redirects back
to the service provider; to the assertion consumer URL. A SAML artifact is
passed in the query parameter.
2004-08-12 17:54:42 +02:00
.. note:: the assertion consumer URL is defined by Liberty; it must be declared
in the ``AssertionConsumerServiceURL`` element of the metadata file.
::
LassoLogin *login;
login = lasso_login_new(server);
lasso_login_init_request(login, query_string, LASSO_HTTP_METHOD_REDIRECT);
lasso_login_build_request_msg(login);
The service provider must check this artifact using a SOAP request to the
2004-08-16 15:16:25 +02:00
identity provider. The URL is ``LASSO_PROFILE(login)->msg_url`` while the
request is ``LASSO_PROFILE(login)->msg_body``. The request must succeed with
an HTTP 200 status code; let's consider its content is put in the ``answer``,
the next statement would be::
lasso_login_process_response_msg(login, answer);
2004-08-16 17:01:41 +02:00
The users are defined by a ``nameIdentifier`` (accessible through
``LASSO_PROFILE(login)->nameIdentifier``). Those typically map to users
2004-08-11 17:57:13 +02:00
and sessions in some database on the service provider. If existing; the
session should probably contains a ``session_dump`` element and the user a
``identity_dump`` element. See `Database Considerations`_ below for more
information.
2004-08-11 17:57:13 +02:00
It is now time to get them out of the database and apply them to the ``login``
object.
::
if (session_dump != NULL) {
lasso_profile_set_session_from_dump(LASSO_PROFILE(login), session_dump);
}
if (identity_dump != NULL) {
lasso_profile_set_identity_from_dump(LASSO_PROFILE(login), identity_dump);
}
lasso_login_accept_sso(login);
After ``lasso_login_accept_sso`` the session and the identity are updated (or
created) and should then be saved. If the identity has not recognised by the
2004-09-04 11:04:19 +02:00
service provider an account will probably have to be created on the service
provider; this is a good opportunity to ask the user for more information.
2004-08-11 17:57:13 +02:00
You can get respective dumps like this::
2004-08-11 01:14:12 +02:00
LassoIdentity *identity;
LassoSession *session;
char *identity_dump = NULL, *session_dump = NULL;
if (lasso_profile_is_identity_dirty(LASSO_PROFILE(login))) {
identity = lasso_profile_get_identity(LASSO_PROFILE(login));
identity_dump = lasso_identity_dump(identity);
lasso_identity_destroy(identity);
}
if (lasso_profile_is_session_dirty(LASSO_PROFILE(login))) {
session = lasso_profile_get_session(LASSO_PROFILE(login));
session_dump = lasso_session_dump(session);
lasso_session_destroy(session);
}
2004-08-11 17:57:13 +02:00
/* code to store identity_dump and session_dump */
2004-08-11 01:14:12 +02:00
Finally the ``login`` object can then be destroyed::
lasso_login_destroy(login);
2004-08-11 01:14:12 +02:00
And a success web page displayed.
Single Logout Profile
=====================
2004-08-11 12:56:23 +02:00
There are different single logout profiles; some initiated on the identity
provider, others initiated on the service provider, using either HTTP redirects
or SOAP requests.
This part is about a logout using SOAP and initiated on the service provider.
::
2004-08-10 17:41:52 +02:00
LassoLogout *logout;
logout = lasso_logout_new(lassoServer);
Identity and session dumps should be restored to prepare the logout request.
::
if (session_dump != NULL) {
lasso_profile_set_session_from_dump(LASSO_PROFILE(logout), session_dump);
}
if (identity_dump != NULL) {
lasso_profile_set_identity_from_dump(LASSO_PROFILE(logout), identity_dump);
}
lasso_logout_init_request(logout, idpProviderId, LASSO_HTTP_METHOD_SOAP);
lasso_logout_build_request_msg(logout);
The service provider must then make a SOAP request to the identity provider;
``msg_url`` and ``msg_body``. You should then pass the answer to Lasso::
lasso_logout_process_response_msg(logout, answer);
2004-08-11 01:14:12 +02:00
And save back session and user dump; the process is similar as the one at the
end of the single sign on profile.
2004-08-11 12:56:23 +02:00
Proper Error Checking
=====================
Most Lasso functions returns 0 on success and a negative number on failure. It
is strongly advised to check this return code on each call.
::
int rc;
rc = lasso_logout_process_response_msg(logout, answer)
if (rc) {
fprintf(stderr, "Lasso Error: %d\n", rc);
/* handling error; most probably bailing out */
}
2004-08-11 17:57:13 +02:00
Database Considerations
=======================
Lasso has been designed to let the service provider keep on using existing
2004-09-04 11:04:19 +02:00
databases. Typically there is already a table describing users; just add an
2004-08-11 17:57:13 +02:00
identity dump column to the existing table:
2004-08-11 17:59:06 +02:00
======= ======================================== ==============
2004-08-11 17:57:13 +02:00
User Id existing data (name, address...) Identity dump
2004-08-11 17:59:06 +02:00
======= ======================================== ==============
1 ... <Identity> ...
2 ... <Identity> ...
======= ======================================== ==============
2004-08-11 17:57:13 +02:00
2004-08-11 18:17:19 +02:00
Mapping between existing users and name identifiers sent by the identity
provider can be done with a simple table.
=============== =======
Name Identifier User Id
=============== =======
AQWWRRS... 1
CGFASDE... 2
YYSSSDS... 1
=============== =======
.. note:: A separate table is needed because one user Id could map
to several name identifiers; in case there are several identity
providers.
2004-08-11 17:57:13 +02:00
Sessions are also commonly stored in databases; just add a session dump column
to the existing session table:
2004-08-11 17:59:06 +02:00
========== ================= =============
2004-08-11 17:57:13 +02:00
Session Id misc session data Session dump
2004-08-11 17:59:06 +02:00
========== ================= =============
6744066 ... <Session> ...
3338824 ... <Session> ...
========== ================= =============
2004-08-11 17:57:13 +02:00
2004-08-11 18:20:09 +02:00
Likewise sessions should be mapped to name identifiers.
2004-08-11 18:17:19 +02:00
=============== ==========
Name Identifier Session Id
=============== ==========
AQWWRRS... 3338824
=============== ==========
2004-08-11 17:57:13 +02:00
2004-08-12 17:00:50 +02:00
API Reference
=============
- LassoLogin_
- LassoLogout_
- LassoIdentity_
- LassoServer_
- LassoSession_
2004-08-11 17:57:13 +02:00
2004-08-11 12:56:23 +02:00
.. _Liberty ID-FF Bindings and Profiles Specification:
2007-04-14 18:42:47 +02:00
http://www.projectliberty.org/liberty/content/download/319/2369/file/draft-liberty-idff-bindings-profiles-1.2-errata-v2.0.pdf
2004-08-11 12:56:23 +02:00
2005-03-01 12:36:46 +01:00
.. _LassoLogin: /documentation/api-reference/lassologin.html
.. _LassoLogout: /documentation/api-reference/lassologout.html
.. _LassoIdentity: /documentation/api-reference/lassoidentity.html
.. _LassoServer: /documentation/api-reference/lassoserver.html
.. _LassoSession: /documentation/api-reference/lassosession.html
2004-08-12 17:00:50 +02:00