gsasl-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[SCM] GNU gsasl branch, master, updated. gsasl-1-6-0-20-gdb63c51


From: Simon Josefsson
Subject: [SCM] GNU gsasl branch, master, updated. gsasl-1-6-0-20-gdb63c51
Date: Thu, 30 Jun 2011 10:12:33 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU gsasl".

http://git.savannah.gnu.org/cgit/gsasl.git/commit/?id=db63c51b413fc5f75eab67c4f968055756d84cdf

The branch, master has been updated
       via  db63c51b413fc5f75eab67c4f968055756d84cdf (commit)
       via  8e1cb03e222cf219643f42fc95430e141e4de008 (commit)
       via  539a64ec53b159edf1e9954dfe566f8d453f6d60 (commit)
      from  9f1321107070d1cc99da48301b5a6bb634e62617 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit db63c51b413fc5f75eab67c4f968055756d84cdf
Author: Simon Josefsson <address@hidden>
Date:   Thu Jun 30 12:10:57 2011 +0200

    Clarify protocol version.

commit 8e1cb03e222cf219643f42fc95430e141e4de008
Author: Simon Josefsson <address@hidden>
Date:   Sat Nov 13 16:12:07 2010 +0100

    examples: Added client-xmpp-saml20 and server-xmpp-saml20.

commit 539a64ec53b159edf1e9954dfe566f8d453f6d60
Author: Simon Josefsson <address@hidden>
Date:   Wed Jun 29 08:51:17 2011 +0200

    Implement SAML20 as per draft-ietf-kitten-sasl-saml-01.

-----------------------------------------------------------------------

Summary of changes:
 .gitignore                                         |   15 ++
 NEWS                                               |    5 +-
 doc/Makefile.am                                    |    1 +
 doc/gsasl.texi                                     |   67 ++++++-
 examples/Makefile.am                               |    5 +-
 .../{client-callback.c => client-xmpp-saml20.c}    |   99 ++++++---
 examples/server-xmpp-saml20.c                      |  203 +++++++++++++++++
 lib/Makefile.am                                    |    6 +-
 lib/NEWS                                           |    4 +-
 lib/configure.ac                                   |   13 +
 lib/gs2/server.c                                   |   91 +--------
 lib/{plain => saml20}/Makefile.am                  |   10 +-
 lib/saml20/client.c                                |  126 +++++++++++
 lib/{external => saml20}/mechinfo.c                |   26 ++-
 lib/{gssapi/x-gssapi.h => saml20/saml20.h}         |   42 ++--
 lib/saml20/server.c                                |  137 ++++++++++++
 lib/src/Makefile.am                                |    9 +-
 lib/src/error.c                                    |    8 +-
 lib/src/gsasl.h                                    |   17 ++-
 lib/src/init.c                                     |    7 +
 lib/src/internal.h                                 |    9 +-
 lib/src/mechtools.c                                |  216 ++++++++++++++++++
 lib/{gs2/gs2helper.h => src/mechtools.h}           |   30 ++--
 lib/src/property.c                                 |   14 +-
 lib/src/xfinish.c                                  |    6 +-
 tests/Makefile.am                                  |    4 +-
 tests/saml20.c                                     |  230 ++++++++++++++++++++
 27 files changed, 1198 insertions(+), 202 deletions(-)
 copy examples/{client-callback.c => client-xmpp-saml20.c} (55%)
 create mode 100644 examples/server-xmpp-saml20.c
 copy lib/{plain => saml20}/Makefile.am (80%)
 create mode 100644 lib/saml20/client.c
 copy lib/{external => saml20}/mechinfo.c (75%)
 copy lib/{gssapi/x-gssapi.h => saml20/saml20.h} (53%)
 create mode 100644 lib/saml20/server.c
 create mode 100644 lib/src/mechtools.c
 copy lib/{gs2/gs2helper.h => src/mechtools.h} (63%)
 create mode 100644 tests/saml20.c

diff --git a/.gitignore b/.gitignore
index 26ea104..3998438 100644
--- a/.gitignore
+++ b/.gitignore
@@ -666,6 +666,17 @@ lib/po/quot.sed
 lib/po/remove-potcdate.sed
 lib/po/remove-potcdate.sin
 lib/po/stamp-po
+lib/saml20/.deps/
+lib/saml20/.libs/
+lib/saml20/Makefile
+lib/saml20/Makefile.in
+lib/saml20/client.lo
+lib/saml20/client.o
+lib/saml20/libgsasl-saml20.la
+lib/saml20/mechinfo.lo
+lib/saml20/mechinfo.o
+lib/saml20/server.lo
+lib/saml20/server.o
 lib/scram/.deps/
 lib/scram/.libs/
 lib/scram/Makefile
@@ -721,6 +732,8 @@ lib/src/md5pwd.lo
 lib/src/md5pwd.o
 lib/src/mechname.lo
 lib/src/mechname.o
+lib/src/mechtools.lo
+lib/src/mechtools.o
 lib/src/obsolete.lo
 lib/src/obsolete.o
 lib/src/property.lo
@@ -855,6 +868,8 @@ tests/old-simple
 tests/old-simple.o
 tests/readnz
 tests/readnz.o
+tests/saml20
+tests/saml20.o
 tests/scram
 tests/scram.o
 tests/scramplus
diff --git a/NEWS b/NEWS
index 22e05d0..5791549 100644
--- a/NEWS
+++ b/NEWS
@@ -6,7 +6,7 @@ Note that changes to the GNU SASL Library is documented in 
lib/NEWS.
 This file track changes to the remaining, non-library, parts of GNU
 SASL.  That include the manual, the command line tool, and self tests.
 
-* Version 1.7.1 (unreleased) [beta]
+* Version 1.7.1 (unreleased) [alpha]
 
 ** Demand gettext >= 0.18.1 in order to get newer M4 files.
 The old M4 files associated with 0.17 caused problems on Solaris,
@@ -19,11 +19,12 @@ Michelsen <address@hidden>.
 
 ** i18n: Updated translations.
 
-** SAML changes reverted, will be kept on a separate branch.
+** examples: Added client-xmpp-saml20 and server-xmpp-saml20.
 
 * Version 1.7.0 (released 2010-10-22) [alpha]
 
 ** Support for SAML20 mechanism as per -01 IETF draft.  See lib/NEWS.
+Implements draft-ietf-kitten-sasl-saml-01.
 
 ** doc: Mention new SAML properties.
 
diff --git a/doc/Makefile.am b/doc/Makefile.am
index d0f6ec2..f6bfcab 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -200,6 +200,7 @@ gdoc_TEXINFOS += texi/init.c.texi
 gdoc_TEXINFOS += texi/listmech.c.texi
 gdoc_TEXINFOS += texi/md5pwd.c.texi
 gdoc_TEXINFOS += texi/mechname.c.texi
+gdoc_TEXINFOS += texi/mechtools.c.texi
 gdoc_TEXINFOS += texi/obsolete.c.texi
 gdoc_TEXINFOS += texi/property.c.texi
 gdoc_TEXINFOS += texi/register.c.texi
diff --git a/doc/gsasl.texi b/doc/gsasl.texi
index 31115a1..ae76540 100644
--- a/doc/gsasl.texi
+++ b/doc/gsasl.texi
@@ -172,7 +172,7 @@ manual.  The library includes support for the framework 
(with
 authentication functions and application data privacy and integrity
 functions) and at least partial support for the ANONYMOUS, CRAM-MD5,
 DIGEST-MD5, EXTERNAL, GS2-KRB5, GSSAPI, LOGIN, NTLM, PLAIN,
-SCRAM-SHA-1 (and SCRAM-SHA-1-PLUS), and SECURID mechanisms.
+SCRAM-SHA-1 (and SCRAM-SHA-1-PLUS), SAML20, and SECURID mechanisms.
 
 The library is easily ported because it does not do network
 communication by itself, but rather leaves it up to the calling
@@ -517,6 +517,7 @@ want to limit the size of the library.
 @itemx --disable-gs2
 @itemx --enable-kerberos_v5
 @itemx --disable-scram-sha1
address@hidden --disable-saml20
 Disable or enable individual mechanisms (@pxref{Mechanisms}).
 
 @item --without-stringprep
@@ -1560,6 +1561,20 @@ data.  As a hint, if you use GnuTLS, the API
 @code{gnutls_session_channel_binding} can be used to extract channel
 bindings for a session.
 
address@hidden @code{GSASL_SAML20_IDP_IDENTIFIER}
+
address@hidden SAML IdP Identifier
address@hidden Identity Provider Identifier
+This property holds the SAML identifier of the user.  The SAML20
+mechanism in client mode will send it to the other end for
+identification purposes, and in server mode it will be accessible in
+the @code{GSASL_SAML20_REDIRECT_URL} callback.
+
address@hidden @code{GSASL_SAML20_REDIRECT_URL}
+This property holds the SAML redirect URL that the server wants the
+client to access.  It will be available in the
address@hidden callback for the client.
+
 @end itemize
 
 Next follows a list of data properties used to trigger the callback,
@@ -1569,8 +1584,10 @@ typically used in servers to validate client credentials:
 
 @item @code{GSASL_VALIDATE_SIMPLE}
 
-You may retrieve GSASL_AUTHID, GSASL_AUTHZID and GSASL_PASSWORD and
-use them to make an authentication and authorization decision.
+Used by multiple mechanisms in server mode.  The callback may retrieve
+the @code{GSASL_AUTHID}, @code{GSASL_AUTHZID} and
address@hidden property values and use them to make an
+authentication and authorization decision.
 
 @item @code{GSASL_VALIDATE_EXTERNAL}
 
@@ -1599,6 +1616,19 @@ ask the client to supply another passcode, and
 GSASL_SECURID_SERVER_NEED_NEW_PIN to require the client to supply a
 new PIN code.
 
address@hidden @code{GSASL_VALIDATE_SAML20}
+
+Used by the SAML20 mechanism on the server side to request that the
+application perform authentication.  The callback should return
address@hidden if the user should be permitted access, and
address@hidden (or another error code) otherwise.
+
address@hidden @code{GSASL_SAML20_AUTHENTICATE_IN_BROWSER}
+Used by the SAML20 mechanism in the client side to request that the
+client should launch the SAML redirect URL (the
address@hidden property) in a browser to continue
+with authentication.
+
 @end itemize
 
 
@@ -1647,6 +1677,7 @@ entirely dependent on callbacks.
 * GSSAPI::                  GSSAPI (Kerberos 5) authentication.
 * GS2-KRB5::                Improved GSSAPI (Kerberos 5) authentication.
 * KERBEROS_V5::             Experimental KERBEROS_V5 authentication.
+* SAML20::                  Experimental SAML20 authentication.
 @end menu
 
 @node EXTERNAL
@@ -2000,6 +2031,36 @@ authorize users, similar to the GSSAPI callback).
 
 XXX: update when implementation has matured
 
address@hidden SAML20
address@hidden The SAML20 mechanism
address@hidden SAML
+
+The SAML20 mechanism makes it possible to use SAML in SASL, in a way
+that offloads the authentication exchange to an external browser.  The
+protocol version implemented is as specified in
address@hidden
+
+The mechanism makes use of the following properties: GSASL_AUTHZID,
+GSASL_SAML20_IDP_IDENTIFIER, GSASL_SAML20_REDIRECT_URL,
+GSASL_SAML20_AUTHENTICATE_IN_BROWSER and GSASL_VALIDATE_SAML20.
+
+In client mode, the mechanism will retrieve the GSASL_AUTHZID and
+GSASL_SAML20_IDP_IDENTIFIER properties and form a request to the
+server.  The server will respond with a redirect URL stored in the
+GSASL_SAML20_REDIRECT_URL property, which the client can retrieve from
+the GSASL_SAML20_AUTHENTICATE_IN_BROWSER callback.  The intention is
+that the client launches a browser to the given URL, and then proceeds
+with authentication.  The server responds whether authentication was
+successful or not.
+
+In server mode, the mechanism will invoke the
+GSASL_SAML20_REDIRECT_URL callback and the application can inspect the
+GSASL_AUTHZID and GSASL_SAML20_IDP_IDENTIFIER properties when forming
+the redirect URL.  The URL is passed to the client which will
+hopefully complete authentication in the browser.  The server callback
+GSASL_VALIDATE_SAML20 should check whether the authentication attempt
+was successful.
+
 @c **********************************************************
 @c *****************  Global Functions  *********************
 @c **********************************************************
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 49325c9..ce3b7f1 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -1,5 +1,5 @@
 ## Process this file with automake to produce Makefile.in
-# Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2009 Simon Josefsson.
+# Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010 Simon Josefsson
 #
 # This file is part of GNU SASL.
 #
@@ -22,4 +22,5 @@ LDADD = ../lib/src/libgsasl.la
 
 EXTRA_DIST = README
 
-noinst_PROGRAMS = client client-serverfirst client-mech client-callback
+noinst_PROGRAMS = client client-serverfirst client-mech        \
+       client-callback client-xmpp-saml20 server-xmpp-saml20
diff --git a/examples/client-callback.c b/examples/client-xmpp-saml20.c
similarity index 55%
copy from examples/client-callback.c
copy to examples/client-xmpp-saml20.c
index ee014f7..b1a016a 100644
--- a/examples/client-callback.c
+++ b/examples/client-xmpp-saml20.c
@@ -1,5 +1,5 @@
-/* client-callback.c --- Example SASL client, with callback for user info.
- * Copyright (C) 2004, 2005, 2007, 2009  Simon Josefsson
+/* client-xmpp-saml20.c --- Example XMPP SASL SAML20 client.
+ * Copyright (C) 2004, 2005, 2007, 2009, 2010  Simon Josefsson
  *
  * This file is part of GNU SASL.
  *
@@ -25,34 +25,71 @@
 
 #include <gsasl.h>
 
+static char *
+xmltob64 (char *buf)
+{
+  while (*buf && *buf != '>')
+    buf++;
+  if (*buf)
+    buf++;
+  while (*buf && buf[strlen (buf) - 1] != '<')
+    buf[strlen (buf) - 1] = '\0';
+  if (*buf)
+    buf[strlen (buf) - 1] = '\0';
+  return buf;
+}
+
 static void
-client_authenticate (Gsasl_session * session)
+client_xmpp (Gsasl_session * session)
 {
   char buf[BUFSIZ] = "";
   char *p;
   int rc;
 
-  /* This loop mimics a protocol where the server send data first. */
+  /* This loop mimics a protocol where the client send data first,
+     which is something that XMPP supports.  For simplicity, it
+     requires that server send the XML blob on one line and XML parser
+     is not complete.  */
+
+  /* Generate client output. */
+  rc = gsasl_step64 (session, buf, &p);
+  if (rc != GSASL_NEEDS_MORE)
+    {
+      printf ("SAML20 init error (%d): %s\n", rc, gsasl_strerror (rc));
+      return;
+    }
+
+  printf ("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
+         "mechanism='SAML20'>%s</auth>\n", p);
 
   do
     {
-      printf ("Input base64 encoded data from server:\n");
-      fgets (buf, sizeof (buf) - 1, stdin);
-      if (buf[strlen (buf) - 1] == '\n')
-        buf[strlen (buf) - 1] = '\0';
-
-      rc = gsasl_step64 (session, buf, &p);
-
-      if (rc == GSASL_NEEDS_MORE || rc == GSASL_OK)
-        {
-          printf ("Output:\n%s\n", p);
-          free (p);
-        }
+      char *line = NULL;
+      size_t n;
+      ssize_t len;
+      char *b64;
+
+      len = getline (&line, &n, stdin);
+      if (len <= 0)
+       break;
+
+      b64 = xmltob64 (line);
+
+      printf ("parsed: '%s'\n", b64);
+
+      rc = gsasl_step64 (session, b64, &p);
+      if (rc != GSASL_NEEDS_MORE && rc != GSASL_OK)
+       {
+         printf ("SAML20 step error (%d): %s\n", rc, gsasl_strerror (rc));
+         return;
+       }
+
+      printf ("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
+             "%s</response>\n", p);
+
     }
   while (rc == GSASL_NEEDS_MORE);
 
-  printf ("\n");
-
   if (rc != GSASL_OK)
     {
       printf ("Authentication error (%d): %s\n", rc, gsasl_strerror (rc));
@@ -69,7 +106,7 @@ static void
 client (Gsasl * ctx)
 {
   Gsasl_session *session;
-  const char *mech = "SECURID";
+  const char *mech = "SAML20";
   int rc;
 
   /* Create new authentication session. */
@@ -80,7 +117,7 @@ client (Gsasl * ctx)
     }
 
   /* Do it. */
-  client_authenticate (session);
+  client_xmpp (session);
 
   /* Cleanup. */
   gsasl_finish (session);
@@ -89,35 +126,25 @@ client (Gsasl * ctx)
 static int
 callback (Gsasl * ctx, Gsasl_session * sctx, Gsasl_property prop)
 {
-  char buf[BUFSIZ] = "";
   int rc = GSASL_NO_CALLBACK;
 
   /* Get user info from user. */
 
-  printf ("Callback invoked, for property %d.\n", prop);
-
   switch (prop)
     {
-    case GSASL_PASSCODE:
-      printf ("Enter passcode:\n");
-      fgets (buf, sizeof (buf) - 1, stdin);
-      buf[strlen (buf) - 1] = '\0';
-
-      gsasl_property_set (sctx, GSASL_PASSCODE, buf);
+    case GSASL_SAML20_IDP_IDENTIFIER:
+      gsasl_property_set (sctx, prop, "https://saml.example.org/";);
       rc = GSASL_OK;
       break;
 
-    case GSASL_AUTHID:
-      printf ("Enter username:\n");
-      fgets (buf, sizeof (buf) - 1, stdin);
-      buf[strlen (buf) - 1] = '\0';
-
-      gsasl_property_set (sctx, GSASL_AUTHID, buf);
+    case GSASL_SAML20_AUTHENTICATE_IN_BROWSER:
+      printf ("client got redirect URL: %s\n",
+             gsasl_property_get (sctx, GSASL_SAML20_REDIRECT_URL));
       rc = GSASL_OK;
       break;
 
     default:
-      printf ("Unknown property!  Don't worry.\n");
+      printf ("Unknown property %d!  Don't worry.\n", prop);
       break;
     }
 
diff --git a/examples/server-xmpp-saml20.c b/examples/server-xmpp-saml20.c
new file mode 100644
index 0000000..5fd1e74
--- /dev/null
+++ b/examples/server-xmpp-saml20.c
@@ -0,0 +1,203 @@
+/* server-xmpp-saml20.c --- Example XMPP SASL SAML20 server.
+ * Copyright (C) 2004, 2005, 2007, 2009, 2010  Simon Josefsson
+ *
+ * This file is part of GNU SASL.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gsasl.h>
+
+static char *
+xmltob64 (char *buf)
+{
+  while (*buf && *buf != '>')
+    buf++;
+  if (*buf)
+    buf++;
+  while (*buf && buf[strlen (buf) - 1] != '<')
+    buf[strlen (buf) - 1] = '\0';
+  if (*buf)
+    buf[strlen (buf) - 1] = '\0';
+  return buf;
+}
+
+static void
+server_xmpp (Gsasl_session * session)
+{
+  char *b64, *p;
+  int rc;
+
+  do
+    {
+      char *line = NULL;
+      size_t n;
+      ssize_t len;
+
+      len = getline (&line, &n, stdin);
+      if (len <= 0)
+       break;
+
+      b64 = xmltob64 (line);
+
+      printf ("parsed: '%s'\n", b64);
+
+      rc = gsasl_step64 (session, b64, &p);
+      if (rc == GSASL_NEEDS_MORE)
+       {
+         printf ("<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
+                 "%s</challenge>\n", p);
+         free (p);
+       }
+    }
+  while (rc == GSASL_NEEDS_MORE);
+
+  if (rc == GSASL_OK)
+    puts ("<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>");
+  else
+    {
+      puts ("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
+           "<temporary-auth-failure/></failure></stream:stream>");
+      printf ("Authentication error (%d): %s\n", rc, gsasl_strerror (rc));
+    }
+}
+
+static void
+server (Gsasl * ctx)
+{
+  Gsasl_session *session;
+  const char *mech = "SAML20";
+  int rc;
+
+  /* Create new authentication session. */
+  if ((rc = gsasl_server_start (ctx, mech, &session)) != GSASL_OK)
+    {
+      printf ("Cannot initialize client (%d): %s\n", rc, gsasl_strerror (rc));
+      return;
+    }
+
+  /* Do it. */
+  server_xmpp (session);
+
+  /* Cleanup. */
+  gsasl_finish (session);
+}
+
+const char *samlchallenge =
+  "https://saml.example.org/SAML/Browser?SAMLRequest=PHNhbWxwOk";
+  "F1dGhuUmVxdWVzdCB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOl"
+  "NBTUw6Mi4wOnByb3RvY29sIg0KICAgIElEPSJfYmVjNDI0ZmE1MTAzNDI4OT"
+  "A5YTMwZmYxZTMxMTY4MzI3Zjc5NDc0OTg0IiBWZXJzaW9uPSIyLjAiDQogIC"
+  "AgSXNzdWVJbnN0YW50PSIyMDA3LTEyLTEwVDExOjM5OjM0WiIgRm9yY2VBdX"
+  "Robj0iZmFsc2UiDQogICAgSXNQYXNzaXZlPSJmYWxzZSINCiAgICBQcm90b2"
+  "NvbEJpbmRpbmc9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpiaW5kaW"
+  "5nczpIVFRQLVBPU1QiDQogICAgQXNzZXJ0aW9uQ29uc3VtZXJTZXJ2aWNlVV"
+  "JMPQ0KICAgICAgICAiaHR0cHM6Ly94bXBwLmV4YW1wbGUuY29tL1NBTUwvQX"
+  "NzZXJ0aW9uQ29uc3VtZXJTZXJ2aWNlIj4NCiA8c2FtbDpJc3N1ZXIgeG1sbn"
+  "M6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbi"
+  "I+DQogICAgIGh0dHBzOi8veG1wcC5leGFtcGxlLmNvbQ0KIDwvc2FtbDpJc3"
+  "N1ZXI+DQogPHNhbWxwOk5hbWVJRFBvbGljeSB4bWxuczpzYW1scD0idXJuOm"
+  "9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIg0KICAgICBGb3JtYX"
+  "Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0On"
+  "BlcnNpc3RlbnQiDQogICAgIFNQTmFtZVF1YWxpZmllcj0ieG1wcC5leGFtcG"
+  "xlLmNvbSIgQWxsb3dDcmVhdGU9InRydWUiIC8+DQogPHNhbWxwOlJlcXVlc3"
+  "RlZEF1dGhuQ29udGV4dA0KICAgICB4bWxuczpzYW1scD0idXJuOm9hc2lzOm"
+  "5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiANCiAgICAgICAgQ29tcGFyaX"
+  "Nvbj0iZXhhY3QiPg0KICA8c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZg0KIC"
+  "AgICAgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm"
+  "Fzc2VydGlvbiI+DQogICAgICAgICAgIHVybjpvYXNpczpuYW1lczp0YzpTQU"
+  "1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0DQ"
+  "ogIDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj4NCiA8L3NhbWxwOlJlcX"
+  "Vlc3RlZEF1dGhuQ29udGV4dD4gDQo8L3NhbWxwOkF1dGhuUmVxdWVzdD4=";
+
+static int
+callback (Gsasl * ctx, Gsasl_session * sctx, Gsasl_property prop)
+{
+  int rc = GSASL_NO_CALLBACK;
+
+  /* Get user info from user. */
+
+  switch (prop)
+    {
+    case GSASL_SAML20_REDIRECT_URL:
+      printf ("server got identity: %s\n",
+             gsasl_property_get (sctx, GSASL_SAML20_IDP_IDENTIFIER));
+      gsasl_property_set (sctx, prop, samlchallenge);
+      rc = GSASL_OK;
+      break;
+
+    case GSASL_VALIDATE_SAML20:
+      {
+       char *line = NULL;
+       size_t n;
+       ssize_t len;
+
+       puts ("Authorization decision time!");
+       printf ("User identity: %s\n",
+               gsasl_property_get (sctx, GSASL_SAML20_IDP_IDENTIFIER));
+       printf ("Accept user? (y/n) ");
+       fflush (stdout);
+
+       len = getline (&line, &n, stdin);
+       if (len <= 0)
+         break;
+       if (line[strlen (line) - 1] == '\n')
+         line[strlen (line) - 1] = '\0';
+
+       if (strcmp (line, "y") == 0 || strcmp (line, "Y") == 0)
+         rc = GSASL_OK;
+       else
+         rc = GSASL_AUTHENTICATION_ERROR;
+       free (line);
+      }
+      break;
+
+    default:
+      printf ("Unknown property %d!  Don't worry.\n", prop);
+      break;
+    }
+
+  return rc;
+}
+
+int
+main (int argc, char *argv[])
+{
+  Gsasl *ctx = NULL;
+  int rc;
+
+  /* Initialize library. */
+  if ((rc = gsasl_init (&ctx)) != GSASL_OK)
+    {
+      printf ("Cannot initialize libgsasl (%d): %s", rc, gsasl_strerror (rc));
+      return 1;
+    }
+
+  /* Set the callback handler for the library. */
+  gsasl_callback_set (ctx, callback);
+
+  /* Do it. */
+  server (ctx);
+
+  /* Cleanup. */
+  gsasl_done (ctx);
+
+  return 0;
+}
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 6355404..0702d7d 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,4 +1,4 @@
-# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Simon Josefsson
+# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Simon 
Josefsson
 #
 # This file is part of GNU SASL Library.
 #
@@ -62,6 +62,10 @@ if SCRAM
 SUBDIRS += scram
 endif
 
+if SAML20
+SUBDIRS += saml20
+endif
+
 if NTLM
 SUBDIRS += ntlm
 endif
diff --git a/lib/NEWS b/lib/NEWS
index f4043c5..1273846 100644
--- a/lib/NEWS
+++ b/lib/NEWS
@@ -2,9 +2,7 @@ GNU SASL LIBRARY NEWS -- History of user-visible changes.
 Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Simon 
Josefsson
 See the end for copying conditions.
 
-* Version 1.7.1 (unreleased) [beta]
-
-** SAML changes reverted, will be kept on a separate branch.
+* Version 1.7.1 (unreleased) [alpha]
 
 ** libgsasl.pc: Add a Libs.private.
 Reported by Volker Grabsch <address@hidden>.
diff --git a/lib/configure.ac b/lib/configure.ac
index 649f931..e1e1f27 100644
--- a/lib/configure.ac
+++ b/lib/configure.ac
@@ -166,6 +166,18 @@ AC_MSG_RESULT($scram_sha1)
 
 AM_CONDITIONAL(SCRAM, test "$scram_sha1" != "no")
 
+# SAML20
+AC_ARG_ENABLE(saml20,
+  AS_HELP_STRING([--disable-saml20], [don't use the SAML20 mechanism]),
+  saml20=$enableval)
+if test "$saml20" != "no" ; then
+       saml20=yes
+       AC_DEFINE(USE_SAML20, 1, [Define to 1 if you want SAML20.])
+fi
+AC_MSG_CHECKING([if SAML20 should be used])
+AC_MSG_RESULT($saml20)
+AM_CONDITIONAL(SAML20, test x$saml20 = xyes)
+
 # GS2, first part
 AC_ARG_ENABLE(gs2,
   AS_HELP_STRING([--disable-gs2], [don't use the GS2 mechanism]),
@@ -398,6 +410,7 @@ AC_CONFIG_FILES([
   cram-md5/Makefile
   digest-md5/Makefile
   scram/Makefile
+  saml20/Makefile \
   external/Makefile
   gl/Makefile
   gltests/Makefile
diff --git a/lib/gs2/server.c b/lib/gs2/server.c
index d586378..f866442 100644
--- a/lib/gs2/server.c
+++ b/lib/gs2/server.c
@@ -35,6 +35,7 @@
 
 #include "gss-extra.h"
 #include "gs2helper.h"
+#include "mechtools.h"
 
 struct _Gsasl_gs2_server_state
 {
@@ -158,93 +159,6 @@ _gsasl_gs2_server_start (Gsasl_session * sctx, void 
**mech_data)
   return GSASL_OK;
 }
 
-/* Create in AUTHZID a newly allocated copy of STR where =2C is
-   replaced with , and =3D is replaced with =.  Return GSASL_OK on
-   success, GSASL_MALLOC_ERROR on memory errors, GSASL_PARSE_ERRORS if
-   string contains any unencoded ',' or incorrectly encoded
-   sequence.  */
-static int
-unescape_authzid (const char *str, size_t len, char **authzid)
-{
-  char *p;
-
-  if (memchr (str, ',', len) != NULL)
-    return GSASL_MECHANISM_PARSE_ERROR;
-
-  p = *authzid = malloc (len + 1);
-  if (!p)
-    return GSASL_MALLOC_ERROR;
-
-  while (len > 0 && *str)
-    {
-      if (len >= 3 && str[0] == '=' && str[1] == '2' && str[2] == 'C')
-       {
-         *p++ = ',';
-         str += 3;
-         len -= 3;
-       }
-      else if (len >= 3 && str[0] == '=' && str[1] == '3' && str[2] == 'D')
-       {
-         *p++ = '=';
-         str += 3;
-         len -= 3;
-       }
-      else if (str[0] == '=')
-       {
-         free (*authzid);
-         *authzid = NULL;
-         return GSASL_MECHANISM_PARSE_ERROR;
-       }
-      else
-       {
-         *p++ = *str;
-         str++;
-         len--;
-       }
-    }
-  *p = '\0';
-
-  return GSASL_OK;
-}
-
-/* Parse the GS2 header containing flags and authorization identity.
-   Put authorization identity (or NULL) in AUTHZID and length of
-   header in HEADERLEN.  Return GSASL_OK on success or an error
-   code.*/
-static int
-parse_gs2_header (const char *data, size_t len,
-                 char **authzid, size_t * headerlen)
-{
-  char *authzid_endptr;
-
-  if (len < 3)
-    return GSASL_MECHANISM_PARSE_ERROR;
-
-  if (strncmp (data, "n,,", 3) == 0)
-    {
-      *headerlen = 3;
-      *authzid = NULL;
-    }
-  else if (strncmp (data, "n,a=", 4) == 0 &&
-          (authzid_endptr = memchr (data + 4, ',', len - 4)))
-    {
-      int res;
-
-      if (authzid_endptr == NULL)
-       return GSASL_MECHANISM_PARSE_ERROR;
-
-      res = unescape_authzid (data + 4, authzid_endptr - (data + 4), authzid);
-      if (res != GSASL_OK)
-       return res;
-
-      *headerlen = authzid_endptr - data + 1;
-    }
-  else
-    return GSASL_MECHANISM_PARSE_ERROR;
-
-  return GSASL_OK;
-}
-
 /* Perform one GS2 step.  GS2 state is in MECH_DATA.  Any data from
    client is provided in INPUT/INPUT_LEN and output from server is
    expected to be put in newly allocated OUTPUT/OUTPUT_LEN.  Return
@@ -285,7 +199,8 @@ _gsasl_gs2_server_step (Gsasl_session * sctx,
        char *authzid;
        size_t headerlen;
 
-       res = parse_gs2_header (input, input_len, &authzid, &headerlen);
+       res = _gsasl_parse_gs2_header (input, input_len,
+                                      &authzid, &headerlen);
        if (res != GSASL_OK)
          return res;
 
diff --git a/lib/plain/Makefile.am b/lib/saml20/Makefile.am
similarity index 80%
copy from lib/plain/Makefile.am
copy to lib/saml20/Makefile.am
index 97e7e83..ae3e27f 100644
--- a/lib/plain/Makefile.am
+++ b/lib/saml20/Makefile.am
@@ -1,4 +1,4 @@
-# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Simon Josefsson
+# Copyright (C) 2010  Simon Josefsson
 #
 # This file is part of GNU SASL Library.
 #
@@ -21,13 +21,13 @@ AM_CFLAGS = $(WERROR_CFLAGS) $(WSTACK_CFLAGS) $(WARN_CFLAGS)
 AM_CFLAGS += $(CFLAG_VISIBILITY)
 AM_CPPFLAGS = -I$(srcdir)/../src -I../src -I$(srcdir)/../gl -I../gl
 
-noinst_LTLIBRARIES = libgsasl-plain.la
-libgsasl_plain_la_SOURCES = plain.h mechinfo.c
+noinst_LTLIBRARIES = libgsasl-saml20.la
+libgsasl_saml20_la_SOURCES = saml20.h mechinfo.c
 
 if CLIENT
-libgsasl_plain_la_SOURCES += client.c
+libgsasl_saml20_la_SOURCES += client.c
 endif
 
 if SERVER
-libgsasl_plain_la_SOURCES += server.c
+libgsasl_saml20_la_SOURCES += server.c
 endif
diff --git a/lib/saml20/client.c b/lib/saml20/client.c
new file mode 100644
index 0000000..dccb1cd
--- /dev/null
+++ b/lib/saml20/client.c
@@ -0,0 +1,126 @@
+/* client.c --- SAML20 mechanism, client side.
+ * Copyright (C) 2010  Simon Josefsson
+ *
+ * This file is part of GNU SASL Library.
+ *
+ * GNU SASL Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GNU SASL Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GNU SASL Library; if not, write to the Free
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Get specification. */
+#include "saml20.h"
+
+/* Get strdup, strlen. */
+#include <string.h>
+
+/* Get free. */
+#include <stdlib.h>
+
+/* Get bool. */
+#include <stdbool.h>
+
+/* Get _gsasl_gs2_generate_header. */
+#include "mechtools.h"
+
+struct saml20_client_state
+{
+  int step;
+};
+
+int
+_gsasl_saml20_client_start (Gsasl_session * sctx, void **mech_data)
+{
+  struct saml20_client_state *state;
+
+  state = (struct saml20_client_state *) calloc (sizeof (*state), 1);
+  if (state == NULL)
+    return GSASL_MALLOC_ERROR;
+
+  *mech_data = state;
+
+  return GSASL_OK;
+}
+
+int
+_gsasl_saml20_client_step (Gsasl_session * sctx,
+                          void *mech_data,
+                          const char *input, size_t input_len,
+                          char **output, size_t * output_len)
+{
+  struct saml20_client_state *state = mech_data;
+  int res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES;
+
+  switch (state->step)
+    {
+    case 0:
+      {
+       const char *authzid = gsasl_property_get (sctx, GSASL_AUTHZID);
+       const char *p;
+       int len;
+
+       p = gsasl_property_get (sctx, GSASL_SAML20_IDP_IDENTIFIER);
+       if (!p || !*p)
+         return GSASL_NO_SAML20_IDP_IDENTIFIER;
+
+       res = _gsasl_gs2_generate_header (false, 'n', NULL, authzid,
+                                         strlen (p), p,
+                                         output, output_len);
+       if (res == GSASL_OK)
+         return res;
+
+       res = GSASL_NEEDS_MORE;
+       state->step++;
+       break;
+      }
+
+    case 1:
+      {
+       gsasl_property_set_raw (sctx, GSASL_SAML20_REDIRECT_URL,
+                               input, input_len);
+
+       res = gsasl_callback (NULL, sctx,
+                             GSASL_SAML20_AUTHENTICATE_IN_BROWSER);
+       if (res == GSASL_OK)
+         {
+           *output_len = 0;
+           *output = NULL;
+         }
+
+       state->step++;
+      }
+      break;
+
+    default:
+      break;
+    }
+
+  return res;
+}
+
+void
+_gsasl_saml20_client_finish (Gsasl_session * sctx, void *mech_data)
+{
+  struct saml20_client_state *state = mech_data;
+
+  if (!state)
+    return;
+
+  free (state);
+}
diff --git a/lib/external/mechinfo.c b/lib/saml20/mechinfo.c
similarity index 75%
copy from lib/external/mechinfo.c
copy to lib/saml20/mechinfo.c
index 847e982..ab6412f 100644
--- a/lib/external/mechinfo.c
+++ b/lib/saml20/mechinfo.c
@@ -1,5 +1,5 @@
-/* mechinfo.c --- Definition of EXTERNAL mechanism.
- * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009  Simon 
Josefsson
+/* mechinfo.c --- Definition of SAML20 mechanism.
+ * Copyright (C) 2010  Simon Josefsson
  *
  * This file is part of GNU SASL Library.
  *
@@ -25,33 +25,37 @@
 #endif
 
 /* Get specification. */
-#include "external.h"
+#include "saml20.h"
 
-Gsasl_mechanism gsasl_external_mechanism = {
-  GSASL_EXTERNAL_NAME,
+Gsasl_mechanism gsasl_saml20_mechanism = {
+  GSASL_SAML20_NAME,
   {
    NULL,
    NULL,
-   NULL,
 #ifdef USE_CLIENT
-   _gsasl_external_client_step,
+   _gsasl_saml20_client_start,
+   _gsasl_saml20_client_step,
+   _gsasl_saml20_client_finish,
 #else
    NULL,
-#endif
    NULL,
    NULL,
+#endif
+   NULL,
    NULL}
   ,
   {
    NULL,
    NULL,
-   NULL,
 #ifdef USE_SERVER
-   _gsasl_external_server_step,
+   _gsasl_saml20_server_start,
+   _gsasl_saml20_server_step,
+   _gsasl_saml20_server_finish,
 #else
    NULL,
-#endif
    NULL,
    NULL,
+#endif
+   NULL,
    NULL}
 };
diff --git a/lib/gssapi/x-gssapi.h b/lib/saml20/saml20.h
similarity index 53%
copy from lib/gssapi/x-gssapi.h
copy to lib/saml20/saml20.h
index d99362c..648b1a4 100644
--- a/lib/gssapi/x-gssapi.h
+++ b/lib/saml20/saml20.h
@@ -1,5 +1,5 @@
-/* x-gssapi.h --- Prototypes for SASL mechanism GSSAPI as defined in RFC 2222.
- * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009  Simon 
Josefsson
+/* saml20.h --- Prototypes for SAML20.
+ * Copyright (C) 2010  Simon Josefsson
  *
  * This file is part of GNU SASL Library.
  *
@@ -20,39 +20,35 @@
  *
  */
 
-#ifndef X_GSSAPI_H
-#define X_GSSAPI_H
+#ifndef SAML20_H
+# define SAML20_H
 
-#include <gsasl.h>
+# include <gsasl.h>
 
-#define GSASL_GSSAPI_NAME "GSSAPI"
+# define GSASL_SAML20_NAME "SAML20"
 
-extern Gsasl_mechanism gsasl_gssapi_mechanism;
+extern Gsasl_mechanism gsasl_saml20_mechanism;
 
-extern int _gsasl_gssapi_client_start (Gsasl_session * sctx,
+extern int _gsasl_saml20_client_start (Gsasl_session * sctx,
                                       void **mech_data);
-extern int _gsasl_gssapi_client_step (Gsasl_session * sctx,
+
+extern int _gsasl_saml20_client_step (Gsasl_session * sctx,
                                      void *mech_data,
                                      const char *input, size_t input_len,
                                      char **output, size_t * output_len);
-extern void _gsasl_gssapi_client_finish (Gsasl_session * sctx,
+
+extern void _gsasl_saml20_client_finish (Gsasl_session * sctx,
                                         void *mech_data);
-extern int _gsasl_gssapi_client_encode (Gsasl_session * sctx,
-                                       void *mech_data,
-                                       const char *input, size_t input_len,
-                                       char **output, size_t * output_len);
-extern int _gsasl_gssapi_client_decode (Gsasl_session * sctx,
-                                       void *mech_data,
-                                       const char *input, size_t input_len,
-                                       char **output, size_t * output_len);
-
-extern int _gsasl_gssapi_server_start (Gsasl_session * sctx,
+
+extern int _gsasl_saml20_server_start (Gsasl_session * sctx,
                                       void **mech_data);
-extern int _gsasl_gssapi_server_step (Gsasl_session * sctx,
+
+extern int _gsasl_saml20_server_step (Gsasl_session * sctx,
                                      void *mech_data,
                                      const char *input, size_t input_len,
                                      char **output, size_t * output_len);
-extern void _gsasl_gssapi_server_finish (Gsasl_session * sctx,
+
+extern void _gsasl_saml20_server_finish (Gsasl_session * sctx,
                                         void *mech_data);
 
-#endif /* X_GSSAPI_H */
+#endif /* SAML20_H */
diff --git a/lib/saml20/server.c b/lib/saml20/server.c
new file mode 100644
index 0000000..cc4fd6e
--- /dev/null
+++ b/lib/saml20/server.c
@@ -0,0 +1,137 @@
+/* server.c --- SAML20 mechanism, server side.
+ * Copyright (C) 2010  Simon Josefsson
+ *
+ * This file is part of GNU SASL Library.
+ *
+ * GNU SASL Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GNU SASL Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GNU SASL Library; if not, write to the Free
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Get specification. */
+#include "saml20.h"
+
+/* Get strdup, strlen. */
+#include <string.h>
+
+/* Get free. */
+#include <stdlib.h>
+
+/* Get _gsasl_parse_gs2_header. */
+#include "mechtools.h"
+
+struct saml20_server_state
+{
+  int step;
+};
+
+int
+_gsasl_saml20_server_start (Gsasl_session * sctx, void **mech_data)
+{
+  struct saml20_server_state *state;
+
+  state = (struct saml20_server_state *) calloc (sizeof (*state), 1);
+  if (state == NULL)
+    return GSASL_MALLOC_ERROR;
+
+  *mech_data = state;
+
+  return GSASL_OK;
+}
+
+int
+_gsasl_saml20_server_step (Gsasl_session * sctx,
+                          void *mech_data,
+                          const char *input, size_t input_len,
+                          char **output, size_t * output_len)
+{
+  struct saml20_server_state *state = mech_data;
+  int res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES;
+
+  *output_len = 0;
+  *output = NULL;
+
+  switch (state->step)
+    {
+    case 0:
+      {
+       const char *p;
+       char *authzid;
+       size_t headerlen;
+
+       if (input_len == 0)
+         return GSASL_NEEDS_MORE;
+
+       res = _gsasl_parse_gs2_header (input, input_len,
+                                      &authzid, &headerlen);
+       if (res != GSASL_OK)
+         return res;
+
+       if (authzid)
+         {
+           gsasl_property_set (sctx, GSASL_AUTHZID, authzid);
+           free (authzid);
+         }
+
+       input += headerlen;
+       input_len -= headerlen;
+
+       gsasl_property_set_raw (sctx, GSASL_SAML20_IDP_IDENTIFIER,
+                               input, input_len);
+
+       p = gsasl_property_get (sctx, GSASL_SAML20_REDIRECT_URL);
+       if (!p || !*p)
+         return GSASL_NO_SAML20_REDIRECT_URL;
+
+       *output_len = strlen (p);
+       *output = malloc (*output_len);
+       if (!*output)
+         return GSASL_MALLOC_ERROR;
+
+       memcpy (*output, p, *output_len);
+
+       res = GSASL_NEEDS_MORE;
+       state->step++;
+       break;
+      }
+
+    case 1:
+      {
+       res = gsasl_callback (NULL, sctx, GSASL_VALIDATE_SAML20);
+       state->step++;
+       break;
+      }
+
+    default:
+      break;
+    }
+
+  return res;
+}
+
+void
+_gsasl_saml20_server_finish (Gsasl_session * sctx, void *mech_data)
+{
+  struct saml20_server_state *state = mech_data;
+
+  if (!state)
+    return;
+
+  free (state);
+}
diff --git a/lib/src/Makefile.am b/lib/src/Makefile.am
index dcc7659..7be7245 100644
--- a/lib/src/Makefile.am
+++ b/lib/src/Makefile.am
@@ -1,4 +1,4 @@
-# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Simon Josefsson
+# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Simon 
Josefsson
 #
 # This file is part of GNU SASL Library.
 #
@@ -38,7 +38,8 @@ libgsasl_la_SOURCES = libgsasl.map \
        supportp.c suggest.c listmech.c \
        xstart.c xstep.c xfinish.c xcode.c mechname.c \
        base64.c md5pwd.c crypto.c \
-       saslprep.c free.c
+       saslprep.c free.c \
+       mechtools.c mechtools.h
 
 if HAVE_LD_VERSION_SCRIPT
 libgsasl_la_LDFLAGS += -Wl,--version-script=$(srcdir)/libgsasl.map
@@ -89,6 +90,10 @@ if SCRAM
 libgsasl_la_LIBADD += ../scram/libgsasl-scram.la
 endif
 
+if SAML20
+libgsasl_la_LIBADD += ../saml20/libgsasl-saml20.la
+endif
+
 if NTLM
 libgsasl_la_LIBADD += ../ntlm/libgsasl-ntlm.la
 endif
diff --git a/lib/src/error.c b/lib/src/error.c
index 6aeb4aa..aa68e10 100644
--- a/lib/src/error.c
+++ b/lib/src/error.c
@@ -1,5 +1,5 @@
 /* error.c --- Error handling functionality.
- * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010  Simon 
Josefsson
+ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011  
Simon Josefsson
  *
  * This file is part of GNU SASL Library.
  *
@@ -199,7 +199,11 @@ static struct
   ERR (GSASL_GSSAPI_RELEASE_OID_SET_ERROR,
        N_("GSSAPI error releasing OID set.")),
   ERR (GSASL_NO_CB_TLS_UNIQUE,
-       N_("Authentication failed because a tls-unique CB was not provided."))
+       N_("Authentication failed because a tls-unique CB was not provided.")),
+  ERR (GSASL_NO_SAML20_IDP_IDENTIFIER,
+       N_("Callback failed to provide SAML20 user identifier.")),
+  ERR (GSASL_NO_SAML20_REDIRECT_URL,
+       N_("Callback failed to provide SAML20 redirect URL."))
 };
 /* *INDENT-ON* */
 
diff --git a/lib/src/gsasl.h b/lib/src/gsasl.h
index 5631b8c..2a57f3b 100644
--- a/lib/src/gsasl.h
+++ b/lib/src/gsasl.h
@@ -1,5 +1,5 @@
 /* gsasl.h --- Header file for GNU SASL Library.
- * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010  Simon 
Josefsson
+ * Copyright (C) 2002-2011  Simon Josefsson
  *
  * This file is part of GNU SASL Library.
  *
@@ -136,6 +136,8 @@ extern "C"
    * @GSASL_NO_SERVICE: Could not get required service name.
    * @GSASL_NO_HOSTNAME: Could not get required hostname.
    * @GSASL_NO_CB_TLS_UNIQUE: Could not get required tls-unique CB.
+   * @GSASL_NO_SAML20_IDP_IDENTIFIER: Could not get required SAML IdP.
+   * @GSASL_NO_SAML20_REDIRECT_URL: Could not get required redirect URL.
    * @GSASL_GSSAPI_RELEASE_BUFFER_ERROR: GSS-API library call error.
    * @GSASL_GSSAPI_IMPORT_NAME_ERROR: GSS-API library call error.
    * @GSASL_GSSAPI_INIT_SEC_CONTEXT_ERROR: GSS-API library call error.
@@ -186,6 +188,8 @@ extern "C"
     GSASL_NO_SERVICE = 58,
     GSASL_NO_HOSTNAME = 59,
     GSASL_NO_CB_TLS_UNIQUE = 65,
+    GSASL_NO_SAML20_IDP_IDENTIFIER = 66,
+    GSASL_NO_SAML20_REDIRECT_URL = 67,
     /* Mechanism specific errors. */
     GSASL_GSSAPI_RELEASE_BUFFER_ERROR = 37,
     GSASL_GSSAPI_IMPORT_NAME_ERROR = 38,
@@ -300,11 +304,15 @@ extern "C"
    * @GSASL_SCRAM_SALTED_PASSWORD: Pre-computed salted SCRAM key,
    *   to avoid re-computation and storing passwords in the clear.
    * @GSASL_CB_TLS_UNIQUE: Base64 encoded tls-unique channel binding.
+   * @GSASL_SAML20_IDP_IDENTIFIER: SAML20 user IdP URL.
+   * @GSASL_SAML20_REDIRECT_URL: SAML20 challenge from server to client.
+   * @GSASL_SAML20_AUTHENTICATE_IN_BROWSER: Request to perform SAML20.
    * @GSASL_VALIDATE_SIMPLE: Request for simple validation.
    * @GSASL_VALIDATE_EXTERNAL: Request for validation of EXTERNAL.
    * @GSASL_VALIDATE_ANONYMOUS: Request for validation of ANONYMOUS.
    * @GSASL_VALIDATE_GSSAPI: Request for validation of GSSAPI/GS2.
    * @GSASL_VALIDATE_SECURID: Reqest for validation of SecurID.
+   * @GSASL_VALIDATE_SAML20: Reqest for validation of SAML20.
    *
    * Callback/property types.
    */
@@ -329,12 +337,17 @@ extern "C"
     GSASL_SCRAM_SALT = 16,
     GSASL_SCRAM_SALTED_PASSWORD = 17,
     GSASL_CB_TLS_UNIQUE = 18,
+    GSASL_SAML20_IDP_IDENTIFIER = 19,
+    GSASL_SAML20_REDIRECT_URL = 20,
+    /* Client callbacks. */
+    GSASL_SAML20_AUTHENTICATE_IN_BROWSER = 250,
     /* Server validation callback properties. */
     GSASL_VALIDATE_SIMPLE = 500,
     GSASL_VALIDATE_EXTERNAL = 501,
     GSASL_VALIDATE_ANONYMOUS = 502,
     GSASL_VALIDATE_GSSAPI = 503,
-    GSASL_VALIDATE_SECURID = 504
+    GSASL_VALIDATE_SECURID = 504,
+    GSASL_VALIDATE_SAML20 = 505
   } Gsasl_property;
 
   /**
diff --git a/lib/src/init.c b/lib/src/init.c
index 9889b31..eee4d9f 100644
--- a/lib/src/init.c
+++ b/lib/src/init.c
@@ -35,6 +35,7 @@
 #include "securid/securid.h"
 #include "digest-md5/digest-md5.h"
 #include "scram/scram.h"
+#include "saml20/saml20.h"
 
 #include "login/login.h"
 #include "ntlm/x-ntlm.h"
@@ -112,6 +113,12 @@ register_builtin_mechs (Gsasl * ctx)
     return rc;
 #endif /* USE_SCRAM_SHA1 */
 
+#ifdef USE_SAML20
+  rc = gsasl_register (ctx, &gsasl_saml20_mechanism);
+  if (rc != GSASL_OK)
+    return rc;
+#endif /* USE_SAML20 */
+
 #ifdef USE_GSSAPI
   rc = gsasl_register (ctx, &gsasl_gssapi_mechanism);
   if (rc != GSASL_OK)
diff --git a/lib/src/internal.h b/lib/src/internal.h
index d494222..eaf7e04 100644
--- a/lib/src/internal.h
+++ b/lib/src/internal.h
@@ -1,5 +1,5 @@
 /* internal.h --- Internal header with hidden library handle structures.
- * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010  Simon 
Josefsson
+ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011  
Simon Josefsson
  *
  * This file is part of GNU SASL Library.
  *
@@ -83,8 +83,7 @@ struct Gsasl_session
   void *mech_data;
   void *application_hook;
 
-  /* Properties.  If you add anything here, remember to change change
-     gsasl_finish() in xfinish.c and map() in property.c.  */
+  /* Properties. */
   char *anonymous_token;
   char *authid;
   char *authzid;
@@ -103,6 +102,10 @@ struct Gsasl_session
   char *scram_salt;
   char *scram_salted_password;
   char *cb_tls_unique;
+  char *saml20_idp_identifier;
+  char *saml20_redirect_url;
+  /* If you add anything here, remember to change change
+     gsasl_finish() in xfinish.c and map() in property.c.  */
 
 #ifndef GSASL_NO_OBSOLETE
   /* Obsolete stuff. */
diff --git a/lib/src/mechtools.c b/lib/src/mechtools.c
new file mode 100644
index 0000000..8c96810
--- /dev/null
+++ b/lib/src/mechtools.c
@@ -0,0 +1,216 @@
+/* mechtools.c --- Helper functions available for use by any mechanism.
+ * Copyright (C) 2010  Simon Josefsson
+ *
+ * This file is part of GNU SASL Library.
+ *
+ * GNU SASL Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GNU SASL Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GNU SASL Library; if not, write to the Free
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Get specification. */
+#include "mechtools.h"
+
+/* Get strcmp. */
+#include <string.h>
+
+/* Get malloc, free. */
+#include <stdlib.h>
+
+/* Get asprintf. */
+#include <stdio.h>
+
+/* Get error codes. */
+#include <gsasl.h>
+
+/* Create in AUTHZID a newly allocated copy of STR where =2C is
+   replaced with , and =3D is replaced with =.  Return GSASL_OK on
+   success, GSASL_MALLOC_ERROR on memory errors, GSASL_PARSE_ERRORS if
+   string contains any unencoded ',' or incorrectly encoded
+   sequence.  */
+static int
+unescape_authzid (const char *str, size_t len, char **authzid)
+{
+  char *p;
+
+  if (memchr (str, ',', len) != NULL)
+    return GSASL_MECHANISM_PARSE_ERROR;
+
+  p = *authzid = malloc (len + 1);
+  if (!p)
+    return GSASL_MALLOC_ERROR;
+
+  while (len > 0 && *str)
+    {
+      if (len >= 3 && str[0] == '=' && str[1] == '2' && str[2] == 'C')
+       {
+         *p++ = ',';
+         str += 3;
+         len -= 3;
+       }
+      else if (len >= 3 && str[0] == '=' && str[1] == '3' && str[2] == 'D')
+       {
+         *p++ = '=';
+         str += 3;
+         len -= 3;
+       }
+      else if (str[0] == '=')
+       {
+         free (*authzid);
+         *authzid = NULL;
+         return GSASL_MECHANISM_PARSE_ERROR;
+       }
+      else
+       {
+         *p++ = *str;
+         str++;
+         len--;
+       }
+    }
+  *p = '\0';
+
+  return GSASL_OK;
+}
+
+/* Parse the GS2 header containing flags and authorization identity.
+   Put authorization identity (or NULL) in AUTHZID and length of
+   header in HEADERLEN.  Return GSASL_OK on success or an error
+   code.*/
+int
+_gsasl_parse_gs2_header (const char *data, size_t len,
+                        char **authzid, size_t * headerlen)
+{
+  char *authzid_endptr;
+
+  if (len < 3)
+    return GSASL_MECHANISM_PARSE_ERROR;
+
+  if (strncmp (data, "n,,", 3) == 0)
+    {
+      *headerlen = 3;
+      *authzid = NULL;
+    }
+  else if (strncmp (data, "n,a=", 4) == 0 &&
+          (authzid_endptr = memchr (data + 4, ',', len - 4)))
+    {
+      int res;
+
+      if (authzid_endptr == NULL)
+       return GSASL_MECHANISM_PARSE_ERROR;
+
+      res = unescape_authzid (data + 4, authzid_endptr - (data + 4), authzid);
+      if (res != GSASL_OK)
+       return res;
+
+      *headerlen = authzid_endptr - data + 1;
+    }
+  else
+    return GSASL_MECHANISM_PARSE_ERROR;
+
+  return GSASL_OK;
+}
+
+/* Return newly allocated copy of STR with all occurrences of ','
+   replaced with =2C and '=' with '=3D', or return NULL on memory
+   allocation errors.  */
+static char *
+escape_authzid (const char *str)
+{
+  char *out = malloc (strlen (str) * 3 + 1);
+  char *p = out;
+
+  if (!out)
+    return NULL;
+
+  while (*str)
+    {
+      if (*str == ',')
+       {
+         memcpy (p, "=2C", 3);
+         p += 3;
+       }
+      else if (*str == '=')
+       {
+         memcpy (p, "=3D", 3);
+         p += 3;
+       }
+      else
+       {
+         *p = *str;
+         p++;
+       }
+      str++;
+    }
+  *p = '\0';
+
+  return out;
+}
+
+/* Generate a newly allocated GS2 header, escaping authzid
+   appropriately, and appending EXTRA. */
+int
+_gsasl_gs2_generate_header (bool nonstd, char cbflag,
+                           const char *cbname, const char *authzid,
+                           size_t extralen, const char *extra,
+                           char **gs2h, size_t *gs2hlen)
+{
+  int elen = extralen;
+  char *gs2cbflag;
+  int len;
+
+  if (cbflag == 'p')
+    len = asprintf (&gs2cbflag, "p=%s", cbname);
+  else if (cbflag == 'n')
+    len = asprintf (&gs2cbflag, "n");
+  else if (cbflag == 'y')
+    len = asprintf (&gs2cbflag, "y");
+  else
+    /* internal caller error */
+    return GSASL_MECHANISM_PARSE_ERROR;
+
+  if (len <= 0 || gs2cbflag == NULL)
+    return GSASL_MALLOC_ERROR;
+
+  if (authzid)
+    {
+      char *escaped_authzid = escape_authzid (authzid);
+
+      if (!escaped_authzid)
+       {
+         free (gs2cbflag);
+         return GSASL_MALLOC_ERROR;
+       }
+
+      len = asprintf (gs2h, "%s%s,a=%s,%.*s", nonstd ? "F," : "",
+                     gs2cbflag, escaped_authzid, elen, extra);
+
+      free (escaped_authzid);
+    }
+  else
+    len = asprintf (gs2h, "%s%s,,%.*s", nonstd ? "F," : "", gs2cbflag,
+                   elen, extra);
+
+  if (len <= 0 || gs2cbflag == NULL)
+    {
+      free (gs2cbflag);
+      return GSASL_MALLOC_ERROR;
+    }
+
+  *gs2hlen = len;
+}
diff --git a/lib/gs2/gs2helper.h b/lib/src/mechtools.h
similarity index 63%
copy from lib/gs2/gs2helper.h
copy to lib/src/mechtools.h
index 6adc75f..1c7fce9 100644
--- a/lib/gs2/gs2helper.h
+++ b/lib/src/mechtools.h
@@ -1,4 +1,4 @@
-/* gs2helper.h --- GS2 helper functions common to client and server.
+/* mechtools.h --- Helper functions available for use by any mechanism.
  * Copyright (C) 2010  Simon Josefsson
  *
  * This file is part of GNU SASL Library.
@@ -20,21 +20,21 @@
  *
  */
 
-#ifndef GS2_HELPER_H
-# define GS2_HELPER_H
+#ifndef MECHTOOLS_H
+# define MECHTOOLS_H
 
-/* Get GSS-API functions. */
-#ifdef HAVE_LIBGSS
-# include <gss.h>
-#elif HAVE_GSSAPI_H
-# include <gssapi.h>
-#elif HAVE_GSSAPI_GSSAPI_H
-# include <gssapi/gssapi.h>
-#endif
+/* Get size_t. */
+#include <stddef.h>
+
+/* Get bool. */
+#include <stdbool.h>
 
-/* Get gsasl functions and types. */
-#include <gsasl.h>
+extern int _gsasl_gs2_parse_header (const char *data, size_t len,
+                                   char **authzid, size_t * headerlen);
 
-extern int gs2_get_oid (Gsasl_session * sctx, gss_OID * mech_oid);
+extern int _gsasl_gs2_generate_header (bool nonstd, char cbflag,
+                                      const char *cbname, const char *authzid,
+                                      size_t extralen, const char *extra,
+                                      char **gs2h, size_t *gs2hlen);
 
-#endif /* GS2_HELPER_H */
+#endif
diff --git a/lib/src/property.c b/lib/src/property.c
index 6a31646..2ea5b7d 100644
--- a/lib/src/property.c
+++ b/lib/src/property.c
@@ -1,5 +1,5 @@
 /* property.c --- Callback property handling.
- * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010  Simon Josefsson
+ * Copyright (C) 2004-2011  Simon Josefsson
  *
  * This file is part of GNU SASL Library.
  *
@@ -104,6 +104,18 @@ map (Gsasl_session * sctx, Gsasl_property prop)
       p = &sctx->cb_tls_unique;
       break;
 
+    case GSASL_SAML20_IDP_IDENTIFIER:
+      p = &sctx->saml20_idp_identifier;
+      break;
+
+    case GSASL_SAML20_REDIRECT_URL:
+      p = &sctx->saml20_redirect_url;
+      break;
+
+      /* If you add anything here, remember to change change
+        gsasl_finish() in xfinish.c and Gsasl_session in
+        internal.h.  */
+
     default:
       break;
     }
diff --git a/lib/src/xfinish.c b/lib/src/xfinish.c
index 23e49e2..fc67c81 100644
--- a/lib/src/xfinish.c
+++ b/lib/src/xfinish.c
@@ -1,5 +1,5 @@
 /* xfinish.c --- Finish libgsasl session.
- * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010  Simon 
Josefsson
+ * Copyright (C) 2002-2011  Simon Josefsson
  *
  * This file is part of GNU SASL Library.
  *
@@ -64,6 +64,10 @@ gsasl_finish (Gsasl_session * sctx)
   free (sctx->scram_salt);
   free (sctx->scram_salted_password);
   free (sctx->cb_tls_unique);
+  free (sctx->saml20_idp_identifier);
+  free (sctx->saml20_redirect_url);
+  /* If you add anything here, remember to change change
+     gsasl_finish() in xfinish.c and Gsasl_session in internal.h.  */
 
   free (sctx);
 }
diff --git a/tests/Makefile.am b/tests/Makefile.am
index d34e184..cdf74db 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,5 +1,5 @@
 ## Process this file with automake to produce Makefile.in
-# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Simon 
Josefsson
+# Copyright (C) 2002-2011 Simon Josefsson
 #
 # This file is part of GNU SASL.
 #
@@ -42,7 +42,7 @@ TESTS_ENVIRONMENT = \
        $(VALGRIND)
 
 ctests = external cram-md5 digest-md5 md5file name errors suggest      \
-       simple crypto scram scramplus symbols readnz gssapi gs2-krb5
+       simple crypto scram scramplus symbols readnz gssapi gs2-krb5 saml20
 if OBSOLETE
 ctests += old-simple old-md5file old-cram-md5 old-digest-md5   \
        old-base64
diff --git a/tests/saml20.c b/tests/saml20.c
new file mode 100644
index 0000000..0222783
--- /dev/null
+++ b/tests/saml20.c
@@ -0,0 +1,230 @@
+/* saml20.c --- Test the SAML20 mechanism.
+ * Copyright (C) 2010  Simon Josefsson
+ *
+ * This file is part of GNU SASL.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "utils.h"
+
+const char *samlchallenge =
+  "https://saml.example.org/SAML/Browser?SAMLRequest=PHNhbWxwOk";
+  "F1dGhuUmVxdWVzdCB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOl"
+  "NBTUw6Mi4wOnByb3RvY29sIg0KICAgIElEPSJfYmVjNDI0ZmE1MTAzNDI4OT"
+  "A5YTMwZmYxZTMxMTY4MzI3Zjc5NDc0OTg0IiBWZXJzaW9uPSIyLjAiDQogIC"
+  "AgSXNzdWVJbnN0YW50PSIyMDA3LTEyLTEwVDExOjM5OjM0WiIgRm9yY2VBdX"
+  "Robj0iZmFsc2UiDQogICAgSXNQYXNzaXZlPSJmYWxzZSINCiAgICBQcm90b2"
+  "NvbEJpbmRpbmc9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpiaW5kaW"
+  "5nczpIVFRQLVBPU1QiDQogICAgQXNzZXJ0aW9uQ29uc3VtZXJTZXJ2aWNlVV"
+  "JMPQ0KICAgICAgICAiaHR0cHM6Ly94bXBwLmV4YW1wbGUuY29tL1NBTUwvQX"
+  "NzZXJ0aW9uQ29uc3VtZXJTZXJ2aWNlIj4NCiA8c2FtbDpJc3N1ZXIgeG1sbn"
+  "M6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbi"
+  "I+DQogICAgIGh0dHBzOi8veG1wcC5leGFtcGxlLmNvbQ0KIDwvc2FtbDpJc3"
+  "N1ZXI+DQogPHNhbWxwOk5hbWVJRFBvbGljeSB4bWxuczpzYW1scD0idXJuOm"
+  "9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIg0KICAgICBGb3JtYX"
+  "Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0On"
+  "BlcnNpc3RlbnQiDQogICAgIFNQTmFtZVF1YWxpZmllcj0ieG1wcC5leGFtcG"
+  "xlLmNvbSIgQWxsb3dDcmVhdGU9InRydWUiIC8+DQogPHNhbWxwOlJlcXVlc3"
+  "RlZEF1dGhuQ29udGV4dA0KICAgICB4bWxuczpzYW1scD0idXJuOm9hc2lzOm"
+  "5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiANCiAgICAgICAgQ29tcGFyaX"
+  "Nvbj0iZXhhY3QiPg0KICA8c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZg0KIC"
+  "AgICAgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm"
+  "Fzc2VydGlvbiI+DQogICAgICAgICAgIHVybjpvYXNpczpuYW1lczp0YzpTQU"
+  "1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0DQ"
+  "ogIDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj4NCiA8L3NhbWxwOlJlcX"
+  "Vlc3RlZEF1dGhuQ29udGV4dD4gDQo8L3NhbWxwOkF1dGhuUmVxdWVzdD4=";
+
+static int
+client_callback (Gsasl * ctx, Gsasl_session * sctx, Gsasl_property prop)
+{
+  int rc = GSASL_NO_CALLBACK;
+
+  /* The first round, the mechanism will need an authorization name
+     and a SAML IDP.  The next round it will request that the client
+     redirects the user (in the browser) using the data stored in the
+     GSASL_SAML20_REDIRECT_URL property.  */
+
+  switch (prop)
+    {
+    case GSASL_AUTHZID:
+      rc = GSASL_OK;
+      break;
+
+    case GSASL_SAML20_IDP_IDENTIFIER:
+      gsasl_property_set (sctx, prop, "https://saml.example.org/";);
+      rc = GSASL_OK;
+      break;
+
+    case GSASL_SAML20_AUTHENTICATE_IN_BROWSER:
+      printf ("client got redirect URL: %s\n",
+             gsasl_property_get (sctx, GSASL_SAML20_REDIRECT_URL));
+      rc = GSASL_OK;
+      break;
+
+    default:
+      fail ("Unknown client callback property %d\n", prop);
+      break;
+    }
+
+  return rc;
+}
+
+static int
+server_callback (Gsasl * ctx, Gsasl_session * sctx, Gsasl_property prop)
+{
+  int rc = GSASL_NO_CALLBACK;
+
+  /* The first round the mechanism will want the SAML challenge to
+     send to the client.  The next round it wants an authorization
+     decision. */
+
+  switch (prop)
+    {
+    case GSASL_SAML20_REDIRECT_URL:
+      printf ("server got identity: %s\n",
+             gsasl_property_get (sctx, GSASL_SAML20_IDP_IDENTIFIER));
+      gsasl_property_set (sctx, prop, samlchallenge);
+      rc = GSASL_OK;
+      break;
+
+    case GSASL_VALIDATE_SAML20:
+      printf ("server authenticating user OK\n");
+      rc = GSASL_OK;
+      break;
+
+    default:
+      fail ("Unknown server callback property %d\n", prop);
+      break;
+    }
+
+  return rc;
+}
+
+void
+doit (void)
+{
+  Gsasl *c = NULL, *s = NULL;
+  Gsasl_session *server = NULL, *client = NULL;
+  char *s1, *s2;
+  int res;
+
+  res = gsasl_init (&c);
+  if (res != GSASL_OK)
+    {
+      fail ("gsasl_init() failed (%d):\n%s\n", res, gsasl_strerror (res));
+      return;
+    }
+
+  res = gsasl_init (&s);
+  if (res != GSASL_OK)
+    {
+      fail ("gsasl_init() failed (%d):\n%s\n", res, gsasl_strerror (res));
+      return;
+    }
+
+  if (!gsasl_client_support_p (c, "SAML20"))
+    {
+      gsasl_done (c);
+      fail("No support for SAML20 clients.\n");
+      exit(77);
+    }
+
+  if (!gsasl_server_support_p (s, "SAML20"))
+    {
+      gsasl_done (s);
+      fail("No support for SAML20 servers.\n");
+      exit(77);
+    }
+
+  gsasl_callback_set (c, client_callback);
+  gsasl_callback_set (s, server_callback);
+
+  /* Simple client */
+
+  res = gsasl_client_start (c, "SAML20", &client);
+  if (res != GSASL_OK)
+    {
+      fail ("gsasl_client_start (%d):\n%s\n", res, gsasl_strerror (res));
+      return;
+    }
+
+  res = gsasl_server_start (s, "SAML20", &server);
+  if (res != GSASL_OK)
+    {
+      fail ("gsasl_server_start (%d):\n%s\n", res, gsasl_strerror (res));
+      return;
+    }
+
+  res = gsasl_step64 (client, NULL, &s1);
+  if (res != GSASL_NEEDS_MORE)
+    {
+      fail ("gsasl_step client1 (%d):\n%s\n", res, gsasl_strerror (res));
+      return;
+    }
+
+  if (debug)
+    printf ("C: `%s' (%d)\n", s1 ? s1 : "", strlen (s1));
+
+  res = gsasl_step64 (server, s1, &s2);
+  gsasl_free (s1);
+  if (res != GSASL_NEEDS_MORE)
+    {
+      fail ("gsasl_step server1 (%d):\n%s\n", res, gsasl_strerror (res));
+      return;
+    }
+
+  if (debug)
+    printf ("S: `%s' (%d)\n", s2 ? s2 : "", strlen (s2));
+
+  res = gsasl_step64 (client, s2, &s1);
+  gsasl_free (s2);
+  if (res != GSASL_OK)
+    {
+      fail ("gsasl_step client2 (%d):\n%s\n", res, gsasl_strerror (res));
+      return;
+    }
+
+  if (debug)
+    printf ("C: `%s' (%d)\n", s1 ? s1 : "", strlen (s1));
+
+  res = gsasl_step64 (server, s1, &s2);
+  gsasl_free (s1);
+  if (res != GSASL_OK)
+    {
+      fail ("gsasl_step server2 (%d):\n%s\n", res, gsasl_strerror (res));
+      return;
+    }
+
+  if (debug)
+    printf ("S: `%s' (%d)\n", s2 ? s2 : "", strlen (s2));
+
+  gsasl_free (s2);
+
+  gsasl_finish (client);
+  gsasl_finish (server);
+
+  gsasl_done (c);
+  gsasl_done (s);
+}


hooks/post-receive
-- 
GNU gsasl



reply via email to

[Prev in Thread] Current Thread [Next in Thread]