ZXID PHP Interface

Sampo Kellomäki (sampo@iki.fi)

ZXID.org Identity Management toolkit implements standalone SAML 2.0 and Liberty ID-WSF 2.0 stacks. This document describes the PHP glue.

1 Introduction

The PHP glue for ZXID was generated using swig(1), however, the swig interface is not a retrofit: the whole ZXID API was designed to be easily swiggifiable.

The main aim of the glue is supporting the easy and simple API, see zxid_simple() for general reference. Only differences and language specifics are covered in this document.

1.1 Other documents

2 PHP extension php_zxid.so

The PHP integration is incomplete due to incomplete support in SWIG for php5. However, enough interface exists to get most high level API working and thus successfully run an SP.

2.1 Installing Binaries or from Package

TBD

2.2 Building and Installing ZXID PHP extension

After building main zxid distribution, say

  make phpzxid

You MUST have php-config(1) in path. If not, try

  make phpzxid PHP_CONFIG=/path/to/php-config

If the extension built successfully, you can use it by copying it to a suitable place, e.g.

  make phpzxid_install

The install again uses the php-config(1) to figure out where php(1) can find the module.

Next you need to decide whether to run under Apache mod_php setup (apache), or as CGI (any web server).

We have tested ZXID with php-5.1.6 and php-5.3.6 (current development target as of 20110707). For mod_php operation, the Apache httpd 2.2.3 and 2.2.19 (current development target as of 20110707) have been tested.

2.2.1 Running PHP as Apache mod_php

See Apache recipe for how to compile Apache to support mod_php.

2.2.2 Running PHP as CGI (any web server)

In the CGI case you generally make sure your CGI script starts like

  #!/usr/bin/php
  <?
  dl("php_zxid.so");  # These three lines can go to initialization: they only need to run once
  $conf = "PATH=/var/zxid/&URL=https://sp1.zxidsp.org:8443/zxidhlo.php";
  $cf = zxid_new_conf_to_cf($conf);
  ?>

The first line makes sure the file is executed with php interpreter. You should change it to match the path where your php is installed. You also need to make your CGI script executable, e.g:

  chmod a+x mycgi.php

Then you place the CGI script in a directory in the document tree of the web site and make sure your http server is configured (permitted) to execute CGI scripts in that directory.

One tricky thing that can go wrong is dynamic linking. When you compiled the php_zxid.so module, some linking dependencies are usually created. Problem arises if some of the dependencies are not in the paths allowed for dynamic linking by your web server. The paths allowed by web server can easily be different than in your shell and some web servers even ignore LD_LIBRARY_PATH environment variable. Sometimes you just have to copy the dependency libraries to one of the allowed directories. This is "dirty", but works. See ldconfig(8) and section for further information.

You can easily see the dependencies using ldd(1)

  ldd /apps/php/5.1.6/lib/php/extensions/no-debug-non-zts-20050922/php_zxid.so
        linux-gate.so.1 =>  (0xffffe000)
        libpthread.so.0 => /lib/libpthread.so.0 (0xb7796000)
        libdl.so.2 => /lib/libdl.so.2 (0xb7792000)
        libcurl.so.3 => /apps/lib/libcurl.so.3 (0xb7611000)
        libz.so.1 => /apps/lib/libz.so.1 (0xb75fe000)
        libc.so.6 => /lib/libc.so.6 (0xb74dd000)
        /lib/ld-linux.so.2 (0x80000000)

In this example the suspect library dependencies are /apps/lib/libcurl.so.3 and /apps/lib/libz.so.1 because they are outside normal places, i.e. /lib and /usr/lib.

Another thing to remember is that CGI specification requires that the Content-Type header and an empty line (to separate headers from content) is emitted before the actual page. If this fails to happen, the page will mysteriously not appear although your script ran successfully.

Sometimes the debug output can end up in stdout (e.g. somewhere stderr was redirected to stdout) and this will garble the returned web page. Easy fix is to disable debug output by not supplying ZXID_AUTO_DEBUG. See section .

2.3 Programming with ZXID PHP Extension

To use the ZXID PHP extension you must add near beginning of your script

  dl("php_zxid.so");   // Load the module

You may need to tweak the paths, or LD_LIBRARY_PATH, to get this to work.

After this, you can use the PHP interface much the same way as you would use the C interface. See the distributed zxid.php and zxidhlo.php for further usage examples.

2.4 Simple API Using PHP

The simplest APIs are easy to use and suitable for CGIs where the program is restarted anew every time. However in situations where the script engine stays alive persistently, it is wasteful to reparse (and reallocate) the configuration every time. Consider following PHP snippet designed to be used with mod_php:

  01 # Put this in the PHP initialization (it only needs to run once)
  02 dl("php_zxid.so");
  03 $conf = "PATH=/var/zxid/&URL=https://sp1.zxidsp.org:8443/zxiddemo.php";
  04 $cf = zxid_new_conf_to_cf($conf);
  05 <?   # For every page that is accessed
  06 $qs = $_SERVER['REQUEST_METHOD'] == 'GET'
  07       ? $_SERVER['QUERY_STRING']
  08       : file_get_contents('php://input');
  09 $res = zxid_simple_cf($cf, -1, $qs, null, 0x1800);
  10 switch (substr($res, 0, 1)) {
  11 case 'L': header($res); exit;  # Redirect
  12 case 'n': exit;   # already handled
  13 case 'b': my_send_metadata();
  14 case 'e': my_render_login_screen();
  15 case 'd': break;  # Logged in case -- fall through
  16 default:  error_log("Unknown zxid_simple() res(%s)", res); exit;
  17 }
  18 # *** Parse the LDIF in $res into a hash of attributes (see zxidhlo.php)
  19 
  20 ?>
  21 <html><title>Protected content, logged in as <$=$attr['cn']$></title>
  22 ...
  23 </html>
  24 <?
  25 function my_render_login_screen()
  26 {
  27 ?>
  28 <html><title>Please Login Using IdP</title>
  29 ...
  30 <?=zxid_idp_select_cf($cf, 0, 0x1800)?>
  31 ...</html>
  32 <? exit; }?>

Notes

  1. Line 4 creates a system-wide configuration object that is later used by the other API calls

  2. On line 9 we call zxid_simple_cf() with the created object. The second and third argument specify a buffer or string that contains the CGI form data to parse. This may come from QUERY_STRING of a GET request or from HTTP body of a POST request, as determined on line 8. The -1 means the length of the data should be determined using strlen(3), i.e. C string nul termination. The auto_flags == 0x1800 enables form tag wrapping and debug prints, but otherwise automation is disabled.

  3. Since automation was disabled, we need to handle several cases of possible outcomes from zxid_simple_cf(), on lines 10-17.

  4. From line 18 onwards we handle the login successful or already logged in case. First we split the LDIF entry into a hash so that we can access the attributes easily (e.g. cn on line 20).

  5. On line 30 we call zxid_idp_list_cf() to create the form for choosing which IdP to use for login (remember that auto_flags == 0xc0 enabled the form wrapper). As can be seen the same configuration object, $cf, is used through out.

3 Integration of Other Libraries with ZXID PHP

3.1 Pat Patterson's php module

Pat Patterson of Sun distributes a pure PHP module (not to be confused with Sun's OpenSSO open source effort, with which Pat has some contact) that implements some aspects of SAML 2.0. As of May 2007, his library provides functionality that, by and large, parallels that of the php_zxid module. A major advatage of his module is that it does not have C shared library dependency, but beware that he still depends on XML parsing and popular crypto libraries (openssl) to be available. These assumptions are not onerous, but you should be aware of them in case your system differs from main stream deployments.

Overall, Pat's PHP implementation, as of May 2007, is still lacking in metadata generation and loading (it does not implement Auto-CoT or Well Known Location) and has some rough edges around less frequently used parts of the SAML specification. No doubt matters will improve over the time.

Pat's library handles only SSO and not ID Web Services. It would be possible to extract the discovery bootstrap from SSO using his library after which you can use ZXID WSC API to actually call the services.

3.2 Sun OpenSSO

Sun Microsystems distributes an open source implementation of SAML 2.0. Their implementation is of primary interest as it provides a freely available IdP implementation (as of May 2007 IMNSHO the ZXID SP interface is superior to the OpenSSO SP - and since both implement an open standard, you can mix ZXID SP with OpenSSO IdP).

Thus, the ZXID to OpenSSO integration reduces to each one acting in its role using standard wire protocol - SAML 2.0.

4 FAQ

4.1 Build and Linking Problems

4.1.1 Dry run test

A good way to test whether php is able to run at all is to simulate running of a CGI script by setting few environment variables and then invoking the script from command line:

 QUERY_STRING=o=E REQUEST_METHOD=GET php ./zxidhlo.php 

The result should be the IdP selection page as HTML. Another test is to run o=B to obtain the metadata.

4.1.2 Duplicate symbols

Following types of error messages

 Warning: Function registration failed - duplicate name - hexdec in /a/d/sampo/zxid/zxidhlo.php on line 16
 
 Warning: zxid:  Unable to register functions, unable to load in Unknown on line 0
 
 Fatal error: Call to undefined function zxid_new_conf_to_cf() in /a/d/sampo/zxid/zxidhlo.php on line 22

Theory: the zxid library has already been loaded in some other way, such as part of mod_auth_saml or Net::SAML loading.

5 License

Copyright (c) 2006-2009 Symlabs (symlabs@symlabs.com), All Rights Reserved. Author: Sampo Kellomäki (sampo@iki.fi)

See file COPYING for complete information.

References

[SAML2core]
"Assertions and Protocols for the OASIS Security Assertion Markup Language (SAML) V2.0", Oasis Standard, 15.3.2005, saml-core-2.0-os