1
=======================================
2
Writing a Liberty service provider in C
3
=======================================
5
:Author: Frederic Peters
6
:Contact: fpeters@entrouvert.com
7
:date: $Date: 2004/09/04 09:04:19 $
8
:revision: $Revision: 1.35 $
9
:copyright: Copyright Ā© 2004 Entr'ouvert
11
.. contents:: Table of Contents
12
.. section-numbering::
18
Lasso functions are defined in several header files typically located in
19
``/usr/include/lasso/`` or ``/usr/local/include/lasso/``. It is possible to
20
include individual files but in most case it is enough to include the main
23
The first thing to do is then to call ``lasso_init()``. Similarly the last
24
thing will be to call ``lasso_shutdown()``. The smallest and useless Lasso
25
project will therefore be::
27
#include <lasso/lasso.h>
29
int main(int argc, char *argv[])
32
printf("Hello world.\n");
37
Lasso uses a tool called ``pkg-config`` to know the necessary flags for
38
compilation and linking.
42
$ pkg-config lasso --cflags
43
-DXMLSEC_CRYPTO=\"openssl\" -DXMLSEC_LIBXML_260=1 -D__XMLSEC_FUNCTION__=__FUNCTION__
44
-DXMLSEC_NO_XKMS=1 -DXMLSEC_NO_CRYPTO_DYNAMIC_LOADING=1 -DXMLSEC_CRYPTO_OPENSSL=1
45
-I/usr/include/lasso -I/usr/include/libxml2 -I/usr/include/xmlsec1 -I/usr/include/glib-2.0
46
-I/usr/lib/glib-2.0/include
47
$ pkg-config lasso --libs
48
-llasso -lxmlsec1-openssl -lxmlsec1 -lssl -lcrypto -ldl -lgobject-2.0 -lxslt -lxml2
49
-lpthread -lz -lm -lglib-2.0
52
Creating an executable from the previous sample would then a simple matter of
53
calling ``gcc`` with the right flags. But there is currently a bug in
54
XMLSec, the library used by Lasso to provide XML Signature and XML Encryption
55
support. It is possible to workaround the bug::
57
$ gcc hello.c -o hello $(pkg-config lasso --cflags --libs)
58
<command line>:4:16: missing terminating " character
59
$ gcc hello.c -o hello $(pkg-config xmlsec1 --cflags --libs | tr -d '\\')
64
Liberty and Lasso profiles
65
==========================
67
Lasso provides the necessary functions to implement Liberty Alliance profiles,
68
as defined in the `Liberty ID-FF Bindings and Profiles Specification`_. They
71
- Single Sign-On and Federation
73
- Federation Termination Notification
75
- Identity Provider Introduction
76
- Name Identifier Mapping
77
- Name Identifier Encryption
79
Each profile maps to a Lasso object such as ``LassoLogin``, ``LassoLogout``...
80
Those are initialized with data known about identity and service providers,
81
available in a ``LassoServer`` object.
83
The ``LassoServer`` object may be created as follows:
88
server = lasso_server_new("sp-metadata.xml",
89
NULL, "sp-private-key.pem", "sp-crt.pem", lassoSignatureMethodRsaSha1);
90
lasso_server_add_provider(server, "idp-metadata.xml",
91
"idp-public-key.pem", "ca-crt.pem");
93
- ``sp-metadata.xml`` is the Liberty metadata file for the service provider
94
- ``idp-metadata.xml`` is the Liberty metadata file for the identity provider
95
- ``sp-private-key.pem`` is the service provider private key; used to sign
97
- ``sp-crt.pem`` is the service provider certificate; sent inside signed
99
- ``idp-public-key.pem`` is the identity provider public key; used to verify
100
signature in documents sent by the identity provider
101
- ``ca-crt.pem`` is the certificate of the certification authority used by the
104
It is of course possible to have several calls so ``lasso_server_add_provider``
105
if there are more than one identity provider.
107
.. note:: Figures in the previously referred Binding and Profiles specification
108
document are quite helpful in figuring out the message passing.
114
``LassoServer`` objects can be serialized into XML files::
119
dump = lasso_server_dump(server);
120
/* write dump into a file, a database, whatever */
123
.. note:: ``lasso_server_dump`` (and other Lasso dump functions) allocates
124
memory through GLib. ``g_free`` is then the function to use instead
125
of ``free`` to release memory.
127
It is then really easy to get back properly constructed objects::
132
/* restore dump from file, database, whatever */
133
server = lasso_server_new_from_dump(dump);
135
.. warning:: The server dump only contains the filenames; not the actual file
136
contents. Files should not be moved afterwards.
140
Liberty Metadata Files
141
======================
144
They are descriptions of a provider containing ``providerID`` and various
147
<?xml version="1.0"?>
149
providerID="https://sp.example.com/" xmlns="urn:liberty:metadata:2003-08">
151
<SingleLogoutServiceURL>https://sp.example.com/singleLogout</SingleLogoutServiceURL>
152
<SingleLogoutProtocolProfile>http://projectliberty.org/profiles/slo-idp-soap</SingleLogoutProtocolProfile>
153
<SoapEndpoint>https://sp.example.com/soapEndpoint</SoapEndpoint>
154
<AssertionConsumerServiceURL id="AssertionConsumerServiceURL1" isDefault="true">
155
https://sp.example.com/assertionConsumer
156
</AssertionConsumerServiceURL>
157
<AuthnRequestsSigned>true</AuthnRequestsSigned>
161
Describe a service provider (with providerID ``https://sp.example.com``) whose
162
single logout service URL is ``https://sp.example.com/singleLogout``. Refer to
163
the Liberty Alliance specifications for details.
166
Single Sign-On and Federation Profile
167
=====================================
169
.. warning:: The source code presented in this section has for sole purpose
170
to explain the different steps necessary to implement this
171
profile; they notably lack proper error checking. See `Proper
172
Error Checking`_ for details on error checking.
175
As a first step the user points its browser to the service provider to the
176
login URL; the service provider must then respond with an HTTP 302 Redirect
177
response, pointing the user browser to the identity provider single sign on
180
.. note:: the login URL is not normative; any name will do.
183
``server`` is a ``LassoServer`` as seen earlier and ``idpProviderId`` is a
184
string with the identity provider Id (the string must match a providerID
185
defined in the metadata file).
191
login = lasso_login_new(server);
192
lasso_login_init_authn_request(login, lassoHttpMethodRedirect);
194
lasso_lib_authn_request_set_forceAuthn(
195
LASSO_LIB_AUTHN_REQUEST(LASSO_PROFILE(login)->request), 1);
196
lasso_lib_authn_request_set_isPassive(
197
LASSO_LIB_AUTHN_REQUEST(LASSO_PROFILE(login)->request), 0);
198
lasso_lib_authn_request_set_nameIDPolicy(
199
LASSO_LIB_AUTHN_REQUEST(LASSO_PROFILE(login)->request), lassoLibNameIDPolicyTypeFederated);
200
lasso_lib_authn_request_set_consent(
201
LASSO_LIB_AUTHN_REQUEST(LASSO_PROFILE(login)->request), lassoLibConsentObtained);
202
lasso_login_build_authn_request_msg(login, idpProviderId);
205
You can now redirect the user to the URL defined in ``LASSO_PROFILE(login)->msg_url``; for
208
printf("Location: %s\n", LASSO_PROFILE(login)->msg_url);
211
The user then logs in on the identity provider which ultimately redirects back
212
to the service provider; to the assertion consumer URL. A SAML artifact is
213
passed in the query parameter.
215
.. note:: the assertion consumer URL is defined by Liberty; it must be declared
216
in the ``AssertionConsumerServiceURL`` element of the metadata file.
222
login = lasso_login_new(server);
223
lasso_login_init_request(login, query_string, lassoHttpMethodRedirect);
224
lasso_login_build_request_msg(login);
226
The service provider must check this artifact using a SOAP request to the
227
identity provider. The URL is ``LASSO_PROFILE(login)->msg_url`` while the
228
request is ``LASSO_PROFILE(login)->msg_body``. The request must succeed with
229
an HTTP 200 status code; let's consider its content is put in the ``answer``,
230
the next statement would be::
232
lasso_login_process_response_msg(login, answer);
234
The users are defined by a ``nameIdentifier`` (accessible through
235
``LASSO_PROFILE(login)->nameIdentifier``). Those typically map to users
236
and sessions in some database on the service provider. If existing; the
237
session should probably contains a ``session_dump`` element and the user a
238
``identity_dump`` element. See `Database Considerations`_ below for more
241
It is now time to get them out of the database and apply them to the ``login``
246
if (session_dump != NULL) {
247
lasso_profile_set_session_from_dump(LASSO_PROFILE(login), session_dump);
249
if (identity_dump != NULL) {
250
lasso_profile_set_identity_from_dump(LASSO_PROFILE(login), identity_dump);
252
lasso_login_accept_sso(login);
254
After ``lasso_login_accept_sso`` the session and the identity are updated (or
255
created) and should then be saved. If the identity has not regnogized by the
256
service provider an account will probably have to be created on the service
257
provider; this is a good opportunity to ask the user for more information.
259
You can get respective dumps like this::
261
LassoIdentity *identity;
262
LassoSession *session;
263
char *identity_dump = NULL, *session_dump = NULL;
265
if (lasso_profile_is_identity_dirty(LASSO_PROFILE(login))) {
266
identity = lasso_profile_get_identity(LASSO_PROFILE(login));
267
identity_dump = lasso_identity_dump(identity);
268
lasso_identity_destroy(identity);
271
if (lasso_profile_is_session_dirty(LASSO_PROFILE(login))) {
272
session = lasso_profile_get_session(LASSO_PROFILE(login));
273
session_dump = lasso_session_dump(session);
274
lasso_session_destroy(session);
277
/* code to store identity_dump and session_dump */
279
Finally the ``login`` object can then be destroyed::
281
lasso_login_destroy(login);
283
And a success web page displayed.
286
Single Logout Profile
287
=====================
289
There are different single logout profiles; some initiated on the identity
290
provider, others initiated on the service provider, using either HTTP redirects
293
This part is about a logout using SOAP and initiated on the service provider.
299
logout = lasso_logout_new(lassoServer, lassoProviderTypeSp);
302
Identity and session dumps should be restored to prepare the logout request.
306
if (session_dump != NULL) {
307
lasso_profile_set_session_from_dump(LASSO_PROFILE(logout), session_dump);
309
if (identity_dump != NULL) {
310
lasso_profile_set_identity_from_dump(LASSO_PROFILE(logout), identity_dump);
313
lasso_logout_init_request(logout, NULL, lassoHttpMethodSoap);
314
lasso_logout_build_request_msg(logout);
317
The service provider must then make a SOAP request to the identity provider;
318
``msg_url`` and ``msg_body``. You should then pass the answer to Lasso::
320
lasso_logout_process_response_msg(logout, answer, lassoHttpMethodSoap)
322
And save back session and user dump; the process is similar as the one at the
323
end of the single sign on profile.
326
Proper Error Checking
327
=====================
329
Most Lasso functions returns 0 on success and a negative number on failure. It
330
is strongly advised to check this return code on each call.
336
rc = lasso_logout_process_response_msg(logout, answer, lassoHttpMethodSoap)
338
fprintf(stderr, "Lasso Error: %d\n", rc);
339
/* handling error; most probably bailing out */
343
Database Considerations
344
=======================
346
Lasso has been designed to let the service provider keep on using existing
347
databases. Typically there is already a table describing users; just add an
348
identity dump column to the existing table:
350
======= ======================================== ==============
351
User Id existing data (name, address...) Identity dump
352
======= ======================================== ==============
355
======= ======================================== ==============
357
Mapping between existing users and name identifiers sent by the identity
358
provider can be done with a simple table.
360
=============== =======
361
Name Identifier User Id
362
=============== =======
366
=============== =======
368
.. note:: A separate table is needed because one user Id could map
369
to several name identifiers; in case there are several identity
372
Sessions are also commonly stored in databases; just add a session dump column
373
to the existing session table:
375
========== ================= =============
376
Session Id misc session data Session dump
377
========== ================= =============
378
6744066 ... <Session> ...
379
3338824 ... <Session> ...
380
========== ================= =============
382
Likewise sessions should be mapped to name identifiers.
384
=============== ==========
385
Name Identifier Session Id
386
=============== ==========
388
=============== ==========
404
.. _Liberty ID-FF Bindings and Profiles Specification:
405
http://www.projectliberty.org/specs/draft-liberty-idff-bindings-profiles-1.2-errata-v1.0.pdf
407
.. _LassoLogin: /lasso-api/lassologin.html
408
.. _LassoLogout: /lasso-api/lassologout.html
409
.. _LassoIdentity: /lasso-api/lassoidentity.html
410
.. _LassoServer: /lasso-api/lassoserver.html
411
.. _LassoSession: /lasso-api/lassosession.html