README.zxid ########### <> <> <> <> <> 1 Who needs this? ================= ZXID project has currently (Jan 2007) five outputs libzxid:: A C library for supporting SAML 2.0, including federated Single Sign-On (SSO) zxid:: A C program that implements a SAML Service Provider (SP) as a CGI script Net::SAML:: A Perl module wrapping libzxid. Also zxid.pl, that implements SP in mod_perl environment, is supplied. php_zxid:: A PHP extension that wraps libzxid. Also supplied: zxid.php that implements SP in mod_php environment. libzxidjni.so:: A Java JNI extension that wraps libzxid. Also supplied: zxid.java that implements SP as a CGI script. *You need this if you are* Web Master:: You want to enable SAML based Single Sign-On (SSO) to your web site. In this case you would use the zxid SP CGI script directly, only configuring it slightly or you can go the zxid_simple() route. Otherwise you can hint your PHP or perl developer that this functionality is available and your want it. Perl Developer:: You can use the Net::SAML module to integrate SSO to your application and web site. Given the direct perl support, this is easier than fully understanding the C interface. Both mod_perl and perl as CGI are supported. PHP Developer:: You can use ~dl("php_zxid.so")~ to load the module and access the high level functionality, such as SAML 2.0 SSO. We support functionality roughly equivalent to perl Net::SAML. The PHP module is fully ready to use for SSO, but we expect to add a lot more, such as WSC, in future. Both mod_php5 and php as CGI are supported. php4 should also work. Java Developer:: You can use ~System.loadLibrary("zxidjni")~ to pull into your Java proram the full power of the ZXID. The functionality supported is roughly equal to Net::SAML. Web Developer:: You want to integrate SAML based SSO to your web site tool or product so that your customers can enjoy SSO enabled web sites. In this case you would study zxid.c for examples and use libzxid.a to implement the functionality in your own program. Identity Management hacker:: You need some building blocks: you will study libzxid and add to it, contributing to the project. ZXID Project has vastly more ambitious goals. See the ZXID Project chapter later in this document. Conor Cahill of Intel (formerly AOL) said back in 2006: > IMNSHO, better go Liberty up front and have the confidence that you > do not need to upgrade later - or run two parallel systems. The Liberty > (or SAML 2.0) system is comprehensive and addresses every use case > anyone has thought so far. The percieved complexity is really an > implementation issue and not underlying propery of the spec. Since > we provide an implementation, the "complexity" is not customer problem. 2 Installing ============ If you want to try ZXID out immediately, we recommend compiling the library and examples and installing one of the examples as a CGI script in an existing web server. See later chapters for more details. tar xvzf zxid-0.15.tgz cd zxid-0.15 # N.B. There is no configure script. The Makefile works for all # supported platforms by provision of correct TARGET option. # N.B2: We distribute some generated files. If they are missing, you need # to regenerate them: make cleaner; make dep ENA_GEN=1 # Standard place is /var/zxid. You can change this with # make ZXID_PATH=/usr/local/var make # default Linux. Do `make TARGET=sol8' for Solaris make dir # Creates /var/zxid hierarchy (may need to be root) make samlmod # optional make samlmod_install # optional: install Net::SAML perl module make phpzxid # optional make phpzxid_install # optional: install php_zxid.so PHP extension make javazxid # optional cp zxid / # configure your web server to recognize zxid a CGI, e.g. mini_httpd -p 8443 -c 'zxid*' -S -E zxid.pem # Edit your /etc/hosts to contain 127.0.0.1 localhost sp1.zxidcommon.org sp1.zxidsp.org # Point your browser to (zxid_simple() API version) https://sp1.zxidsp.org:8443/zxidhlo?o=E https://sp1.zxidsp.org:8443/zxidhlo.pl?o=E # Perl version https://sp1.zxidsp.org:8443/zxidhlo.php?o=E # PHP version http://sp1.zxidsp.org:8080/zxidservlet/zxidHLO?o=E # Java version # Point your browser to (full API version) https://sp1.zxidsp.org:8443/zxid?o=E https://sp1.zxidsp.org:8443/zxid.pl?o=E # Perl version https://sp1.zxidsp.org:8443/zxid.php?o=E # PHP version https://sp1.zxidsp.org:8443/zxid-java.sh?o=E # Java version # Find an IdP to test with and configure it... 2.1 Prerequisites ----------------- This software depends on the following packages: 1. zlib from zlib.net. Generally whatever comes with your distro is sufficient. 2. openssl-0.9.8d or later. See www.openssl.org. Generally openssl libraries distributed with most Linux distros are sufficient.<> 3. libcurl from http://curl.haxx.se/. I used version 7.15.5, but probably whatever ships with your distribution is fine. libcurl is needed for SOAP bindings and for fetching metadata. It needs to be compiled to support HTTPS.<> 4. HTTPS capable web server. For most trivial testing CGI support is needed. We recommend mini_httpd(8) available from http://www.acme.com/software/mini_httpd/ 5. Perl, PHP, and Java interfaces depend on the respective development tools but should not need any additional modules or tools. Following additional packages are needed by developers who wish to build from scratch, including the code generation (the standard distribution includes the output of the code generation, so most people do not need these). A. gperf from gnu.org (only for build process when generating code) B. swig from swig.org (only for build process and only if you want scripting interfaces) C. perl from cpan.org (only for build process and only if you want to generate code from .sg) D. plaindoc from http://mercnet.pt/plaindoc/pd.html (only for build process, for code generation from .sg, and for documentation) Although technically not needed to build zxid, you will need an IdP to test against. We do not, at the time, supply one, so you will need to find a third party, perhaps a free download of one of the commercial ones like * http://symlabs.com/Products/SFIAM.html * Lasso: http://lasso.entrouvert.org/ * The IDP: http://authentic.labs.libre-entreprise.org/ 2.2 Canned Tutorial: Running ZXID as CGI under mini_httpd --------------------------------------------------------- While zxid will run easily under Apache httpd (see <>), for sake of simplicity we first illustrate running it with mini_httpd(8), a very simple SSL capable web server by Jef Poskanzer. 2.2.1 Getting and installing mini_httpd ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can download the source for mini_httpd from http://www.acme.com/software/mini_httpd/ You should already have installed OpenSSL, or quite probably OpenSSL shipped with your distribution. If it is not located at /usr/local/ssl, the you need to edit the mini_httpd ~Makefile~ to indicate where it is. At any rate you need to uncomment all lines that start by SSL_ in the ~Makefile~. Then say make Now copy the mini_httpd binary somewhere in your path. 2.2.2 Running mini_httpd ~~~~~~~~~~~~~~~~~~~~~~~~ After building zxid, cd to zxid directory and run mini_httpd -p 8443 -c 'zxid*' -S -E zxid.pem where -p 8443 specifies the port to listen to -c 'zxid*' specifies that URL paths with "zxid" are CGI scripts -S specifies that https is to be used -E zxid.pem specifies the SSL certificate to use See <> for alternative that avoids mini_httpd, but is more complicated otherwise. > N.B. The zxid.pem certificate and private key combo is shipped with zxid > for demonstration purposes. Obviously everybody who downloads zxid > has that private key, so there is no real security what-so-ever. For > production use, you must generate, or acquire, your own private > key-certificate pair (and keep the private key secret). See Certificates > chapter for further info. 2.2.3 Accessing ZXID ~~~~~~~~~~~~~~~~~~~~ Edit your /etc/hosts file so that the definition of localhost also includes sp1.zxidcommon.org and sp1.zxidsp.org domain names, e.g: 127.0.0.1 localhost sp1.zxidcommon.org sp1.zxidsp.org Point your browser to > https://sp1.zxidsp.org:8443/zxid or if you do not want the common domain cookie check > https://sp1.zxidsp.org:8443/zxid?o=E *Dynamic linking problems* If accessing the URL (while running mini_httpd) you get no error message and no content - everything just mysteriously fails - you may be hitting a dynamic linking problem. If mini_httpd(8) fails to launch CGI script it will silently fail. This is unfortunate, but I guess that is what the "mini" in the name implies. To make matters even worse, mini_httpd(8), probably in the interest of security, will ignore ~LD_LIBRARY_PATH~ variable. Apparently it has its fixed notion of the library paths that is set at compile time. If you suspect this problem, try following: 1. Create shell script called test.sh: #!/bin/sh echo Content-Type: text/plain echo echo Test $$ echo lib_path is --$LD_LIBRARY_PATH-- ldd zxid ./zxid -h 2>&1 echo Exit value --$?-- 2. Restart mini_httpd(8) like this chmod a+x test.sh mini_httpd -p 8443 -c test.sh -S -E zxid.pem -l mini.out 3. Access https://sp1.zxidsp.org:8443/test.sh - you may see something like Test 1655 lib_path is --/usr/local/lib:/usr/lib-- ./zxid: error while loading shared libraries: libcurl.so.3: cannot open shared object file: No such file or directory Now you at least see why it's failing (in this case the directory where libcurl was installed is not in mini_httpd's notion of LD_LIBRARY_PATH). If the zxid binary runs fine from comman line, try `ldd zxid' to see where it is finding its libraries. Easiest dirty fix is to copy the missing libraries to one of the hardwired directories of mini_httpd(8) (e.g. /usr/lib). More sophisticated fixes include using ldconfig(8), recompiling your mini_httpd(8), or statically linking the offending library into zxid binary. 2.2.4 Setting up an IdP ~~~~~~~~~~~~~~~~~~~~~~~ Currently zxid does not ship with an IdP (though the necessary protocol encoders and decoders are latently available in libzxid, should anyone wish to make an attempt to hack an IdP together). For you to test zxid, you will need to acquire an IdP from somewhere - any vendor whose product is SAML 2.0 certified will do. Possible sources are * http://symlabs.com/Products/SFIAM.html who have a free download of commercial product * Lasso: http://lasso.entrouvert.org/ * The IDP: http://authentic.labs.libre-entreprise.org/ If you do not want to install an IdP yourself (even for testing), find someone who already runs one and ask if they would be willing to load the metadata of your zxid SP. If you do this, you will need to get externally visible domain names. This canned tutorial uses /etc/hosts (see previous step) which is only visible on your own machine. Once you get your IdP up and running, you need to make sure it accepts the zxid SP in its Circle of Trust (CoT). This is done by placing the metadata of the SP in right place in the IdP product configuration. If your IdP supports automatic CoT management, just turn it on and chances are you are done.<> If not, you can obtain the zxid SP metadata (which is slightly different for each install so you can't just copy it from existing install) from > https://sp1.zxidsp.org:8443/zxid?o=B This URL is the +well known location+ method metadata URL. It is also the SP +Entity ID+ or Provider ID, should the IdP product ask for this in its configuration. If the IdP product needs you to supply the metadata manually as an xml file, just point your web browser to the above URL and save to file, or use curl(1) or wget(1). zxid SP, by default, has automatic fetching of IdP metadata enabled so there is no manual configuration step needed, provided that the IdP supports the well known location method. All SAML 2.0 certified IdP implementations must support it (but you may still need to enable it in configuration). See [SAML2meta] section 4.1 "Publication and Resolution via Well-Known Location", p.29, for normative description of this method. However, you will need the Entity ID (Provider ID) of the IdP. This is the URL that the IdP uses for well known location method of metadata sharing. You may need to dig the IdP documentation or GUI for a while to find it. If you already have the IdP metadata as an xml file, open it and look for EntityDescriptor/entityID. If you already have the file, you can also import it manually by running the following command ./zxid -import file:///path/to/idp-meta.xml But the preferred method still is: just let the automatic method do its job. 2.2.5 Your first SSO ~~~~~~~~~~~~~~~~~~~~ 1. Start at > https://sp1.zxidsp.org:8443/zxid or > https://sp1.zxidsp.org:8443/zxid?o=E If you had common domain cookie already in place, and you are already logged in the IdP, the SSO may happen automatically (go to step 3). The automatic experience will be typical when you use SSO regularly for more than one web site (i.e. several SPs). However, if you get a screen titled "ZXID SP SSO", you need to paste the IdP's Entity ID to the supplied field and click "Login". If zxid SP already obtained the metadata for the IdP, you may also see a button specific for your IdP (and in this case there is no need to know the Entity ID anymore or paste anything). 2. Next step depends on the IdP product you are using. Usually a login screen will appear asking for user name and password. Supply these and login. You will need an account at the IdP. 3. For more slick IdPs, that's all you need to do and you will land right back at the zxid SP page titled "ZXID SP Management". > Congratulations, you have made your first SSO! However, some IdPs will pester you with additional questions and you will have to jump through their hoops. A typical question is whether you want to accept a federation. You do. Sometimes the federation question does not appear automatically and you need to figure out a way to create a federation in their user interface and how to get them to send you back to the SP. Sometimes the word used is "account linking" instead of federation.<> 3 Configuring and Running ========================= ZXID ships with working demo configuration so you can run it right away and once you are familiar with the concepts, you can return to this chapter. ZXID uses a configuration file in default path<> /var/zxid/zxid.conf for figuring out its parameters. If this file is not present, built-in default configuration is used (see zxidconf.h).<> The built-in configuration will allow you to test features of ZXID, but should not be used in production because it uses default certificates and private keys. Obviously the demo private key is of public knowledge since it is distributed with the ZXID package, and as such it provides no privacy protection what-so-ever. For production use you MUST generate your own certificate and private key. Usually configuring a system involves following tasks 1. Configure web server (see your web server documentation) a. HTTPS operation and TLS certificate. In the minimum you need the main site, but you may want to configure the Common Domain Cookie virtual host as well. b. Arrange for ZXID to be invoked. This could mean configuring zxid, zxid-java.sh, or zxid.pl to be recognized as a CGI script, or it could mean setting up your ~mod_perl~ or ~mod_php~ system to call ZXID at the appropriate place. 2. Configure ZXID, including signing certificate and CoT with peer metadata a. generate or acquire certificate b. Obtain peer metadata (from their well known location) or enable +Auto CoT+ feature. 3. Configure CoT peers with your metadata. They can download your metadata from your well known location (which is the URL that is your entity ID). For this to happen you need to have web server and ZXID up and running. 3.1 Configuration Parameters ---------------------------- 3.1.1 zxidroot (PATH configuration parameter) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The root directory of ZXID configuration files and directories. By default this is /var/zxid and has following directories and files in it /var/zxid/ | +-- zxid.conf Main configuration file +-- pem/ Our certificates +-- cot/ Metadata of CoT partners (metadata cache) +-- ses/ Sessions `-- log/ Log files, pid files, and the like 3.1.2 pem ~~~~~~~~~ Directory that holds various certificates. The certificates have hardwired names that are not configurable. ca.pem:: Certification Authority certificates. These are used for validating any certificates received from peers (other sites on the CoT). The CA certificates may also be shipped to the peers to facilitate them validating our signatures. This is especially relevant if the certificate is issued by multilayer CA hierarchy where the peer may not have the intermediate CA certificates. sign-nopw-cert.pem:: The signing certificate AND private key (concatenated in one file). The private key MUST NOT be encrypted (there will not be any opportunity to supply decryption password). enc-nopw-cert.pem:: The encryption certificate AND private key (concatenated in one file). The private key MUST NOT be encrypted (there will not be any opportunity to supply decryption password). The signing certificate can be used as the encryption certificate. If encryption certificate is not specified it will default to signing certificate. In addition to the above certificates and private keys, you will need to configure your web server to use TLS or SSL certificates for the main site and the Common Domain site. We suggest the following naming ssl-nopw-cert.pem:: SSL or TLS certificate for main site. In order to avoid browser warnings, the CN field of this certificate should match the domain name of the site. The SSL certificate can be same as signing or encryption certificate. cdc-nopw-cert.pem:: SSL or TLS certificate for Common Domain Cookie introduction site. In order to avoid browser warnings, the CN field of this certificate should match the domain name of the site. The SSL certificate can be same as signing or encryption certificate. 3.1.3 cot ~~~~~~~~~ Directory that holds metadata of the Circle of Trust (CoT) partners. If +Auto CoT+ is enabled, this directory needs to be writable at run time. Typical metadata file path would be /var/zxid/cot/Inkl5fOnhVNa0LbWjHem2Y2UphY If the metadata file appears in this directory, then the entity is in the CoT. The file name component of the path is safe base64 encoded SHA1 hash of the Entity ID, with last character stripped (that character would always be an equals sign). 3.2 Configuration File Format ----------------------------- During zxid project development phase (ongoing as of Jan 2007), most configuration related documentation will be kept as comments in zxidconf.h, which you should see. Configuration file is line oriented. Comments can be introduced with cardinal (#) and empty lines are ignored. End of line comments are NOT supported at this time. Each configuration option is a name=value pair. The name is the same as in zxidconf.h except that ZXID_ prefix does not appear. Only single line values are supported, but you can embed characters using URI encoding, e.g. %0a for newline. In fact, the configuration lines are treated as CGI variables, thus & can also be used as separator instead of newline (and needs to be encoded if not intended as separator). When option is not specified, the default from zxidconf.h prevails. Thus in the following the PATH specification is redundant. To give some idea consider following /var/zxid/zxid.conf example: # Demo /var/zxid/zxid.conf file PATH=/var/zxid URL=https://sp.mydomain.com:8443/zxid NICE_NAME=My SP's human%0areadable name. #EOF 4 Logging and Audit =================== N.B. zxidconf.h contains a wealth of logging related config options. > Tip: Your web server also has logging options. You may want > to correlate the web server logs with zxid audit logs. In serious use of SSO it is fundamental that the relying party, the SP, WSC, or WSP, archives the digitally signed evidence that justifies its actions. Generally this means that at least the SSO or credential assertions have to be archived. Quite often, especially in the WSC world, the entire SOAP response (which may be partially signed) needs to be preserved as a proof of an authorized action or attested attributes. To lesser extent, it is also important that the issuing party, the IdP (or sometimes the DS, PS, WSC, or WSP), keeps records so that it can confirm or refute the claims of the relying party -- in the minimum it should be able to refute any obviously false claim and it should be able to detect breaches of its own security arrangements, e.g. situations where somebody is signing messages in its name although internal audit trail demonstrates this to be impossible. The IdP audit trail consists of preserving any (signed) request made by anyone as well as preserving every (signed) response it makes. Generally every assertion, request, and response will have its unique ID that can be used as the primary key, or filename, for storing it in a database. Unfortunately these namespaces<> are not disjoint (it is not very well specified in any of the standards how they interact or how wide their uniqueness properties are).<> The only safe assumption is the pessimistic one: each type of object observes a unique namespace only towards its issuer and type and hence we need to map such namespaces to subdirectories. 4.1 Filesystem Layout for Logs ------------------------------ Please consider following layout of the log directory: /var/zxid/ | +-- zxid.conf Main configuration file +-- pem/ Our certificates +-- cot/ Metadata of CoT partners (metadata cache) +-- ses/ Sessions `-- log/ Log files, pid files, and the like | +-- issue/ | | | +-- SHA1NAME/ Evidence given to an entity ID is kept in this directory | | | | | +-- a7n/ Assertions issued to the given 3rd party, named by AssertionID | | `-- msg/ Messages of any type issued to the given 3rd party, named by MessageID | ... | `-- SHA1NAME2/ Evidence relating to another entity ID | +-- rely/ | | | +-- SHA1NAME/ Evidence relating to given entity ID is kept in this directory | | | | | +-- a7n/ Assertions from 3rd parties, named by AssertionID | | `-- msg/ Messages of any type from 3rd parties, named by MessageID | ... | `-- SHA1NAME2/ Evidence relating to another entity ID | +-- tmp/ Subdirectory used for atomic operations à la Maildir +-- act Global activity log +-- err Global error log `-- debug Global debugging log 4.2 Log Line Format ------------------- The log file is line oriented, one record per line irrespective of line length, and plain text: binary data is generally omitted or represented as (safe) base64. Fields are separated by exactly one space character (0x20), except for the last free format field. Records are separated by exactly one new line (0x0a) character (never by CRLF sequence). The log file format supports 1. Plain text logging 2. Signed plain text logging using either RSA-SHA1 or DSA-SHA1 3. Symmetrically encrypted logging using either 3DES or AES 4. Asymmetrically encrypted logging using RSA (or DSA?) 5. Signed and symmetrically encrypted logging 6. Signed and Asymmetrically encrypted logging All activity and error log file lines have the following format (any one of the 3): # comment SE CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SE SIG OURTS SRCTS IP:PORT SUCCEID MID A7NID NID VVV RES OP PPP FMT <> where SE:: Log signing and encryption designator. In all cases the actual signing or encryption key is not identified on the log line. This will need to be determined out-of-band. PP:: PlainPlain: not signed and not encrypted Rx:: RSA-SHA1 signed (x = any encryption) Dx:: DSA-SHA1 signed Sx:: SHA1 check-summed, but not signed (SSSS is the checksum) xA:: Asymmetrically AES encrypted (x = any signing method) xT:: Asymmetrically 3DES encrypted xB:: Symmetrically AES encrypted (theoretical: how to safeguard the key?) xU:: Symmetrically 3DES encrypted (theoretical: how to safeguard the key?) xZ:: [RFC1951] zipped (not really encryption) Xxx:: Experimental arrangements. CCCC:: Safe base64 encoded log encryption blob. In case of encryption blob, the rest of the log fields will not appear. Decrypted logline will contain fields starting from SSSS. SIG:: Safe base64 encoded log line signature blob. If no signature, this is a dash ("-"). OURTS:: Our time stamp, format YYYYMMDD-HHMMSS.TTT where TTT are the milliseconds. The time is always in GMT (UTC, Zulutime). SRCTS:: Source time stamp, format YYYYMMDD-HHMMSS.TTT. If TTT was not originally specified it is represented as "501". The time is always in GMT (UTC, Zulutime). IP:PORT:: The IP address and the port number of the other end point (usually client, but could be spoofed, caveat emptor). SUCCEID:: The SHA1 name of the entity (succinct entity ID without the equals sign). MID:: Message ID relating to the log line. Allows message to be fetched from the database or the file system. Any relates-to or similar ID is only available by fetching the original message. Dash ("-") if none. A7NID:: Assertion ID relating to the log line. Allows assertion to be fetched from the database or the file system. If message benefits from multiple assertions, this is the one relating to the outermost one. Other A7NIDs are only available by fetching the original assertion. Dash ("-") if none. If the assertion is encrypted and can not be decrypted, then placehoder "-enca7n-" is used. NID:: IdP assigned NameID relating to the message, if any. If the NameID is encrypted and can not be decrypted, then placeholder "-encnid-" is used. VVV:: Signature validation codes (*** still avail HJLMQUYZ) O:: Capital Oh (not zero). All relevant signatures validate (generally assertion) A:: Unsupported or bad signature or message digest algorithm G:: Checksum of XML DSIG does not validate R:: The RSA layer of the signature does not validate N:: No signature detected. I:: Issuer metadata not found (or not in CoT, or corrupt metadata). V:: Assertion validity error (e.g. not in time range or wrong audience) F:: Operation failed or faulted by error code (low level protocol ok) Exx:: Extended signature validation code (generally failure) Xxx:: Experimental signature validation code (generally failure) RES:: Result of the operation. K:: Operation was success C:: Operation failed because client did not provide valid input S:: Operation failed due to server side error P:: Operation failed due to policy or permissions issue T:: Temporary error, client was encouraged to retry B:: Metadata related error (no metadata or parse error in metadata) D:: Redirect or recredential. Client was encouraged to retry. W:: Way point message. Neither success nor failure. Exx:: Extended result (generally failure) Xxx:: Experimental result (generally failure) OP:: The documented operation FEDNEW:: Federation and SSO request succeeded, new federation was created. FEDSSO:: SSO using federated ID was performed TMPSSO:: SSO using temporary NameID was performed SLO:: Single Logout was completed DEFED:: Defederation was performed BADCF:: Server configuration (/var/zxid/zxid.conf) is bad NOMD:: No metadata found after options exhausted (cache, fetch from net) BADMD:: Metadata parsing error BADXML:: XML parsing error in protocol SAMLFAIL:: SAML call failed (often SOAP call) ERR:: Other error For WSP the OP is the command verb that was exercised. For WSC the OP is the command verb preceded by capital C, e.g. "CQuery". Additional OP verbs may need to be specified for protocol substeps like artifact resolution (ART) and direct authentication (AUTH). ART:: Artifact resolution request sent with SOAP (1) ANREDIR:: Redirection with Authentication Request LOCLO:: Local Logout (1) SLOREDIR:: Redirection with Single Logout Request MNIREDIR:: Redirection with Manage NameID Request for changing NameID DEFEDREDIR:: Redirection with Manage NameID Request for defederation SLOSOAP:: Single Logout Request SOAP call made MNISOAP:: Manage NameID Request for changing NameID SOAP call DEFEDSOAP:: Manage NameID Request for defederation SOAP call SAMLOK:: SAML call OK (often SOAP call) Additional OP verbs may need to be specified for other logging operations like regular web access logs (HEAD, GET, POST). IDPSEL:: IdP Selection screen is shown (2) MGMT:: Management screen is shown (2) SHOWPC:: Logged in (by SSO or session). Show protected content. arg is sid. (1) SPDISP:: SP Command Dispatch (received POST or redir) (2) MYMD:: My metadata was served to requester on the net (1) GETMD:: Getting metadata from net (2) GOTMD:: Got metadata from net (1) BADCGI:: Unknown CGI options (0, but not implemented yet) PPP:: Operation dependent one most relevant parameter. Dash ("-") if none. FMT:: Operation dependent free-form data. May contain spaces. Dash ("-") if none. 4.3 Log Signing and Encryption ------------------------------ Logs are enabled in the config file zxidconf.h (compile time) by ZXLOG macros which provide default values for the log flags in ~struct zxid_conf~. Each log flag is a bitmask of signing and encryption options. Zero value means no logging. "1" can be used to enable plain text logging. Log signing may help you to argue that log evidence was (not) tampered with. You can configure the signing level in the config file zxidconf.h (compile time): 0:: no signing (Px) 2:: sha1 MD only (Sx) 4:: RSA-SHA1 (Rx) 6:: DSA-SHA1 (Dx) For actual signing (options 2 and 3), the private key for signing must be available in /var/zxid/pem/logsign-nopw-cert.pem. Note that this file need not contain the actual certificate (but it may, it just will not be used). The weak point of log signing is that if the private key is stolen, then someone can create falsified logs and the private key needs to be available on the point where the logs are generated - thus it is actually quite vulnerable. Log encryption may help to keep the logs confidential. You can configure the configuration level in the config file zxidconf.h (compile time): 0x00:: no encryption (xP) 0x10:: [RFC1951] zip - safe-base64 [RFC3548] (xZ) 0x20:: RSA-AES (xA) 0x30:: RSA-3DES (xT) 0x40:: Symmetric AES (xB) 0x50:: Symmetric 3DES (xU) For RSA modes the public key for encryption must be available in /var/zxid/pem/logenc-nopw-cert.pem. Note that the +private key+ should *NOT* be kept in this file: the whole point of public key encryption is that even if your server machine is stolen, the bad guys can't access the logs - if the private key was anywhere in the stolen machine, they will find it. For symmetric encryption the key is the SHA1 hash of file /var/zxid/pem/logenc.key. Obviously this key must be kept secret, but see the caveat about stolen machine in the previous paragraph. All encryption modes, except for 0, [RFC1951] zip compress the log line before encryption and safe-base64 encode the result of the encryption. All encryption modes, except 0 and 1, prefix the zipped log line with 128 bit nonce before encrypting. The algorithm is roughly 1. If encrypt, zip the raw log line 2. If sign, compute the signature (over zipped version if applicable) 3. Prepend signature blob to log line. If encrypting, the signature is embedded in binary form, otherwise it is embedded in safe-base64 form. 4. If encrypt, perform the encryption. 5. If encrypt, apply safe-base64. The supplied tool zxlogview(1) allows the logs to be decrypted and the signatures verified. ./zxlogview logsign-nopw-cert.pem logenc-nopw-cert.pem N.B. While encrypted logs are cool, you should evaluate the > gain against the incovenience: if you encrypt them, the lesser > mortal sysadmins may not be able to debug your installation > because they do not know how to decrypt logs or you are not > willing to trust them with the keys. For this reason, you > can configure the encryption of error log separately. 4.4 Internal Crypto Formats --------------------------- For [RFC1951] zipped safe-base64 [RFC3548] output the input to base64 encoding is LLSSSSZZZZZZZZZZZZZZ -- RFC1951 zipped safe-base64 For encrypted modes the input to AES (or other symmetric cipher) is NNNNLLSSSSZZZZZZZZZZ -- Note how nonce is prepended The NNNN is used as initialization vector and actual encryption encompasses LL, SSSS, and ZZZZ. In RSA-AES the session key is encrypted using RSA and prepended to the input for base64 encoding. KKEEEECCCCCCCCCCCCCC -- RSA-AES: note prepended session key NNNN:: 16 bytes of nonce. This is used as initialization vector for AES or 3DES cipher operated in CBC mode. LL:: Bigendian integer representing signature length in bytes. 0 means none. Negative values reserved for future use. SSSS:: The signature in binary ZZZZ:: [RFC1951] zipped safe-base64 [RFC3548] of the payload KK:: Bigendian integer representing encrypted session key length in bytes. Negative values are reserved for future use. EEEE:: RSA encrypted session key in binary CCCC:: Ciphertext from the symmetric cipher, including nonce. In RSA operations RSA_PKCS1_OAEP_PADDING padding is used (PKCS #1 v2.0). 4.5 Logging Assertions ---------------------- Logging of assertions is controlled by configuration options ZXLOG_ISSUE_A7N and ZXLOG_RELY_A7N. At least ZXLOG_RELY_A7N should be turned on for ID-WSF web services to work correctly. Logging relied assertions also allows detection of duplicate assertion IDs. Logging, or not, of issued assertions does not have any operational effect and is only for audit trail purposes. Assertions are logged in directories depending on issuer's sha1 name. /var/zxid/log/rely/ISSUER-SHA1-NAME/a7n/A7N-ID-AS-SHA1 Sha1 names are used to avoid any attack through issuer entity ID or the assertion ID being evilly crafted to contain shell metacharacters or filesystem significant characters. Assertions issued by ourselves follow similar pattern /var/zxid/log/issue/DEST-SHA1-NAME/a7n/A7N-ID-AS-SHA1 If the logfile starts by less-than character ("<") then it is in plain text. Encrypted or signed formats will start in another way, but are not specified at this time. N.B. The relied-on assertions may be referenced from session objects and used in construction of credentials for ID-WSF based web services calls. Therefore the rely directory should not be cleaned too aggressively: the assertions must remain there until the referencing session expires. 4.6 Logging Requests and Responses ---------------------------------- Logging of requests and responses is controlled by ZXLOG_ISSUE_MSG and ZXLOG_RELY_MSG. Logging, or not, messages has no operational effect and is only for audit trail purposes. If logging of relied messages is turned on, then it is possible to detect duplicate message IDs. Request messages are logged in directories depending on issuer's sha1 name. /var/zxid/log/rely/ISSUER-SHA1-NAME/msg/REQ-ID-AS-SHA1 Sha1 names are used to avoid any attack through issuer entity ID or the assertion ID being evilly crafted to contain shell metacharacters or filesystem significant characters. Responses issued by ourselves follow similar pattern /var/zxid/log/issue/DEST-SHA1-NAME/msg/RESP-ID-AS-SHA1 If the logfile starts by less-than character ("<") then it is in plain text. Encrypted or signed formats will start in another way, but are not specified at this time. 4.7 Session Storage and Bootstraps ---------------------------------- The ZXID session system serves three purposes: 1. Remember whether user has logged in. The session ID is carried either in a cookie or as part of the URL. 2. Make it possible to perform Single Logout (SLO) and certain federation management tasks. 3. Remember the service end points (EPRs) that were either a. supplied as bootstrap attributes in the SSO assertion, or b. later discovered The biggest complication is the requirement to remember the EPRs and the solution currently used is to keep them as files in a per session directory under the /var/zxid/ses tree. /var/zxid/ | +-- zxid.conf Main configuration file +-- pem/ Our certificates +-- cot/ Metadata of CoT partners (metadata cache) +-- ses/ Sessions | | | +-- SESID/ Each session has its own directory | | | +-- .ses The session file | +-- SVC,SHA1 Each bootstrap is kept in its own file | +-- user/ Local user accounts (if enabled) | | | +-- SHA1/ Each local user has a directory whose name is SHA1 | | of the user's NameID (idpnid) | +-- .mni Information needed by Name ID management | `-- log/ Log files, pid files, and the like 4.7.1 Session directory ~~~~~~~~~~~~~~~~~~~~~~~ The session ID is an unguessable (but see ID_BITS configuration options) safe base64 encoded pseudorandom number. Unguessability ensures that the session can only be crated via SSO. The service EPRs are XML documents whose name is composed from two components SVC,SHA1 SVC:: The service type URI, with file system unsafe characters (e.g. "/" and ",") folded to underscore ("_"). Purpose of the SVC is to allow quick identification, without opening, of the files that contain EPRs for a given service type. Only first 200 bytes of the service type are used. SHA1:: safe base64 encoded SHA1 hash of the content of the EPR. The purpose of the SHA1 hash is to produce a unique identifier so that two distinct EPRs for same service will have different file names. The session directory also contains .ses file. The first line is as follows (still subject to change, Oct 2007): NameID|a7n-ref The pipey symbol (|) is a field separator. Future versions may define further fields beyound these original two. All other lines are reserved for future expansion. Fields: NameID:: NameID, extracted during SSO a7n-ref:: Filesystem path to the SSO assertion. 4.7.2 User directory ~~~~~~~~~~~~~~~~~~~~ User directories are used for storage of +local account+ information. Since many web applications, to which ZXID may be integrated, already have their own local user storage, the ZXID user directory is optional, see USER_LOCAL configuration option. IdP initiated ManageNameID requests depend on local user accounts, so if you want this to work you need to enable them. Local user account management may be useful on its own right if your application does not yet have such system. If it has, you probably want to continue to use the application's own system. Each user is represented by a file whose filename is safe base64 of the SHA1 hash of the user's NameID. Inside the directory, a file called .mni captures the information needed for NameID Management. It is expected that other files about the user may be populated to capture other aspects. Your own applications could even create files here. The first line of the .mni file is as follows FMT|IDPEnt|SPqual|NameID|MNIptr The pipey symbol (|) is a field separator. Future versions may define further fields beyound these original two. All other lines are reserved for future expansion. Fields: FMT:: NameID Format IDPent:: IdP entity ID that qualifies the NameID (namespace if you like). This usually corresponds to the NameQualifier of SPqual:: SP entity or affilitation ID (optionally) sent by IdP. This further qualifies the namespace of the Name ID. NameID:: NameID of the account MNIptr:: If NameID Management has been used to change the IdP assigned NameID, then the new NameID. There will be a local user account directory for the new NameID. Consider this as a sort of symlink functionality. 5 Compilation for Experts ========================= make cleaner make dep ENA_GEN=1 make make all ENA_GEN=1 5.1 Build Process ----------------- The build process of ZXID relies heavily on code generation techniques that are not for the faint of heart. Some of these techniques, like xsd2sg.pl were innovated for this project, while others like SWIG and gperf are existing software. Here and there some additional perl(1) and sed(1) scripts are run to fix a thing or two. < hc [label="xsd2sg.pl"]; sg -> gperf [label="xsd2sg.pl"]; gperf -> hc [label="gperf"]; hc -> hc [label="gen-consts-from-gperf-output.pl"]; hc -> libzxid [label="gcc"]; hc -> pm [label="swig"]; i -> pm [label="swig"]; hc -> php [label="swig"]; phpi -> php [label="swig"]; libzxid -> zxid [label="ld"]; libzxid -> netsaml [label="ld"]; pm -> netsaml [label="perl, gcc, ld"]; libzxid -> php_zxid [label="ld"]; php -> php_zxid [label="gcc, ld"]; >> Carefully study the Makefile, and this should all start to make sense. Please note that there is no configuration script and GNU Auto-tools (GNU Autohell) are not used. This is because they have been evaluated to be unsuitable to this project (or to author's tastes). If the Makefile does not do what you want, you should first study if you can change necessary variables using localconf.mk, or if that does not work, then just edit the Makefile itself. You can temporarily customize the variables in the Makefile by providing new value on command line, e.g. make CC=cc # override the default of CC=gcc 5.2 Special or embedded compile (reduced functionality) ------------------------------------------------------- libzxid contains thousands of functions and any given application is unlikely to use them all. Thus the easiest, safest, no loss of functionality, way to reduce the footprint is to simply enable compiler and linker flags that support dead function elimination.<> If you need to squeeze zxid into as minimal space as possible, some functionality trade-offs are supported. I stress that you should only attempt these trade-offs once you are familiar with zxid and know what you are doing. The canned install instructions and tutorial walk thrus stop working if you omit significant functionality. 5.2.1 Compilation without OpenSSL ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Comment out the -DUSE_OPENSSL flag from CFLAGS in Makefile and recompile. This will cripple zxid from security perspective because it will no longer be able to verify or generate digital signatures. Unless your environment does not need trust and security, or you understand thoroughly how to provide trust and security by other means, it is a very bad idea to compile without OpenSSL. N.B. Compiling, or not, zxid with OpenSSL does not affect whether your web server will use SSL or TLS. Unless you know what you are doing, you should be using SSL at web server layer. Given that SSL is used at web server layer, the memory footprint savings you would gain from compiling zxid without OpenSSL may be negligible if you use dynamic linking. 5.2.2 Compilation without libcurl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Comment out the -DUSE_CURL flag from CFLAGS in Makefile and recompile. Disabling libcurl does not have adverse security implications: you only loose some functionality and depending on your situation you may well be able to live without it. 1. Without libcurl, zxid can not act as a SOAP client. This has a few consequences a. Artifact profile for SSO is not supported because it needs SOAP to resolve the artifact. In most cases a perfectly viable alternative is to use POST profile for SSO. b. SOAP profiles for Single Logout and NameID management (aka defederation) are not supported. You can use the redirect profiles and get mostly the same functionality. 2. Automatic CoT metadata fetching using well known location method is not supported without libcurl. You can fetch the metadata manually, e.g. using web browser, and place it in /var/zxid/cot directory. If you want to manually control your Circle of Trust relationships, you probably want to do this anyway so loss of automatic functionality may be a non-issue.<> 3. Web Services Client (WSC) functionality is not supported without libcurl. Effectively this is just another case of "SOAP needed". If you have your own SOAP implementation, you may, at lesser automation, achieve much of the same functionality by calling the encoder and decoder functions manually. 5.2.3 Compiling without zlib (not supported) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ zlib is used mainly in redirect profiles. Since zlib is widely avvailable and its foot print is small, we have made no supported provision to compile without it. If you hack something together, let us know. 5.3 Choosing Which Standards to Compile in (default: all) --------------------------------------------------------- WARNING: Only regularly tested configuration is to compile all standards in. On space constrained systems you may shed additional weight by only compiling in the IdM standards you actually use. Of course, if you do not use them, the dead function elimination should take care of them, but sometimes you can gain additional savings in space and especially compile time. Another reason could be, in the land of the free, if some modules are covered by a software patent, you may want to compile a binary without the contested functionality.<> You can tweak the flags, shown in accompanying table, in the Makefile or by supplying new values in localconf.mk or on commend line. For example make TARGET=sol8 ENA_SAML2=0 would disable SAML 2.0 (and trigger build for Sparc Solaris 8). <> 5.4 localconf.mk ---------------- You can use localconf.mk to remember your own make options, such as TARGET and different ENA flags, without editing the distributed Makefile. One useful option to put in localconf.mk is ENA_GEN which will turn on the dependencies that will trigger generation of the files in zxid/c directory. For example echo 'ENA_GEN=1' >>localconf.mk make > WARNING: If you are confused about compilation flags > appearing "out of nowhere", despite not being mentioned > in the Makefile, be sure you have not inadvertently created > a localconf.mk (perhaps you rsynced the sources from another > machine and forgot to remove localconf.mk). A similar > problem can occur if you accidentally copied deps.dep > from another machine. It will reference the dependencies > with the paths of that machine. Just remove the deps.dep > to solve this issue. 5.5 Tough Compilation Errors ---------------------------- ZXID was mostly developed on gcc 3.4.6 and explicitly has not been tested on 4.x series of gcc (due to deprecation of commonly useful features in that gcc series). If you hit compile time problems with 4.x gcc, try with 3.4.x series gcc first, before reporting bugs. *** 5.6 Tough Linking Errors ------------------------ * There are some, yet unspecified, compilation or linking errors that manifest on 64 bit or mixed 32 and 64 bit Linux platforms. Current suggested workaround is to compile 32 bit only. * Linking error that has been reported a lot on the Google for other software packages: /usr/lib/libc_nonshared.a(elf-init.oS): In function `__libc_csu_init': (.text+0x2a): undefined reference to `__init_array_start' /usr/bin/ld: zxidjava/libzxidjni.so: hidden symbol `__fini_array_end' isn't defined /usr/bin/ld: final link failed: Nonrepresentable section on output This seems to be either glibc version related or linker configuration related (or compiler version?). See http://www-gatago.com/gnu/gcc/help/43379636.html http://lists.debian.org/debian-68k/2006/07/msg00017.html The verdict seems to be that somewhere in glibc-2.3 development it was broken by design and nobody cared to provide any viable fix short of upgrading your binutils. Seems to be Ulrich Drepper at it again, turning symbols hidden in teeny version number upgrade. A symptom of this is if this command gives the follwing output ld --verbose | egrep '__init_array_(start|end)' PROVIDE_HIDDEN (__init_array_start = .); PROVIDE_HIDDEN (__init_array_end = .); You can manually attempt to link with gcc -nostdlib -o zxidjava/libzxidjni.so -shared --export-all-symbols -Wl,-whole-archive -Wl,--allow-multiple-definition zxidjava/zxid_wrap.o -L. -lzxid -lpthread -L/usr/local/lib -L/usr/local/ssl/lib -lcrypto -lcurl -lz The trick is to supply the -nostdlib flag. ZXID project considers this to be glibc or binutils bug and will not investigate further. Either use the workaround link line or upgrade binutils, glibc, or both and complain to glibc authors or binutils authors. 5.7 Overview of the xsd2sg.pl Tool ---------------------------------- The xsd2sg.pl tool, distributed as part of Plaindoc (http://mercnet.pt/plaindoc/pd.html) system, is at the heart of the ZXID code generation. Unfortunately the tool is still in flux. What follows is general outline valid as of March 2007. xsd2sg.pl can be invoked in three ways (see xsd2sg.pl --help), but the invocation of interest to use is the -S SG to XSD with Code generation mode. This mode takes as inputs all .sg files and names of decoding root elelment. It will generate one mega parser that is capable of decoding any root element (and since root elements contain other elements, then really all elements). The decoder works by recursive descent parser principle so there will be smaller decoder functions to call. Similarily the encoder is generated by creating many unit encoders for different elements. Then the parent elements will call the child element encoders. 5.7.1 Process .sg to create %dt hash (throw away XSD output) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ xsd2sg.pl first processes the .sg files to XSD. As a side effect it populates %dt multidimentsional hash $dt{'element'}{elemname}[array-of-sublemens-and-attrs] $dt{'attribute'}{attrname} = ""; $dt{'complexType'}{typename}[array-of-sublemens-and-attrs] $dt{'group'}{groupname}[array-of-sublemens-and-attrs] $dt{'attributeGroup'}{agname}[array-of-attrs] Where the array-of-subelems-and-attrs can contain tag or reference to fundamental or derived type, e.g. "tag$sa:Assertion$min$max" "ref$xs:anyURI$" "ref$sa:Assertion$" "base$$0$1$base-type" "enum$n1$n2$n3$n4$n5" "any$$min$max$pc$ns" "anyAttribute$$min$1" "_d$$0$1$simplecontent" 5.7.2 The expanding element definitions in %dt ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Since the concrete goal is to generate data structures for elements irrespective of how types were used to describe the data, we need to expand any type reference into element definition where it appears. This is done by expand_element() which will in its turn call expand_complex_type(), expand_group() and expand_attribute_group() to do the job. 5.7.3 Generation Phase ~~~~~~~~~~~~~~~~~~~~~~ Once %dt is in expanded form, the get_element() is called for each XML namespace. It will generate the code for encoders and decoders for the elements in the XML namespace. Finally the root decoder is gnerated using gen_element(). Furing generation following template files are used aux-templ.c - Code generation template for auxiliary functions dec-templ.c - Code generation template for decoders enc-templ.c - Code generation template for encoders getput-templ.c - Code generation template for accessor functions A lot of the generated headers are simply hard coded in the xsd2sg.pl (i.e. no templates). 6 Net::SAML Perl Module ======================= * perl CGI example: zxid.pl * using with mod_perl After building the main zxid tree, you can cd Net perl Makefile.PL make make test # Tests are extremely sparse at the moment make install This assumes you use the pregenerated Net/SAML_wrap.c and Net/SAML.pm files that we distribute. If you wish to generate these files from origin, you need to have SWIG installed and then say in main zxid directory make perlmod ENA_GEN=1 # Makes all available perl modules (including heavy low level ones) make samlmod ENA_GEN=1 # Only makes Net::SAML (much faster) make wsfmod ENA_GEN=1 # Only makes Net::WSF (much faster) > WARNING: Low level interface is baroque, and consequently, it > will take a lot of disk space, RAM and CPU to build it: 100 MB > would not be exaggeration and over an hour (on 1GHz CPU). Build > time memory consumption of single cc1 process will be over > 256 MB of RAM. You have been warned. 6.1 Current major modules are ----------------------------- * Net::SAML - The high level interfaces for Single Sign-On (SSO) * Net::SAML::Raw - Low level assertion and protocol manipulation interfaces * Net::SAML::Metadata - Low level metadata manipulation interfaces 6.2 Planned modules ------------------- * Net::WSF - The high level interfaces for Web Services Frameworks (WSF) * Net::WSF::Raw - The low level interfaces for WSF variants * Net::WSF::WSC - The high level interfaces for Web Services Clients * Net::WSF::WSC:Raw 6.3 Perl API Adaptations ------------------------ The perl APIs were generated from the C .h files using SWIG. Generally any C functions and constants that start by zxid_, ZXID_, SAML2_, or SAML_ have that prefix changed to <>. Note, however, that the zx_ prefix is not stripped. Since ZXID wants to keep strings in many places in length + data representation, namely as ~struct zx_str~, SWIG typemaps were used to make this happen automatically. Thus any C function that takes as an argument <> can take a perl string directly. Similarly any C function that returns such a pointer, will return a perl string instead. As a final goodie, any C function, such as struct zx_str* zx_ref_len_str(struct zx_ctx* c, int len, char* s); that takes ~length~ and <> as explicit arguments, takes only single argument that is a perl string (the one argument automatically satisfies two C arguments, thanks to a type map). The above could be called like $a = Net::SAML::zx_ref_len_str(c, "foo"); First the "foo" satisfies both ~len~ and ~s~, and then the return value is converted back to perl string. 6.4 Testing Net::SAML and zxid.pl as CGI script ----------------------------------------------- To test the perl module, you must restart the mini_httpd(8) so that it recognizes zxid.pl as CGI script: mini_httpd -p 8443 -c zxid.pl -S -E zxid.pem Then start browsing from https://sp1.zxidsp.org:8443/zxid.pl or if you want to avoid the common domain cookie check https://sp1.zxidsp.org:8443/zxid.pl?o=E 6.5 Testing Net::SAML and zxid.pl under mod_perl ------------------------------------------------ You can run zxid.pl under mod_perl using the Apache::Registry module. See <> for how to compile Apache to support mod_perl. After configuration it should work the same as the CGI approach. 6.6 Debugging Net::SAML with GDB -------------------------------- As bizarre as it may sound, it is actually quite feasible to debug libzxid and the SAML_wrap.c using GDB while in perl. For example cd zxid gdb /usr/local/bin/perl set env QUERY_STRING=o=E r ./zxid.pl If the script crashes inside the C code, GDB will perfectly reasonably take control, allowing you to see stack back-trace (bt) and examine variables. Of course it helps if openssl and perl were compiled with debug symbols (libzxid is compiled with debug symbols by default), but even if they weren't you can usually at least get some clue. When preparing a perl module, generally Makefile.PL mechanism causes the same compilation flags to be used as were used to compile the perl itself. Generally this is good, but if libzxid was compiled with different flags, mysterious errors can crop up. For example, I compile my libzxid against openssl that I have also compiled myself. However, I once had a bug where the perl had been compiled such that the Linux distribution's incompatible openssl would be picked by perl compile flags, resulting in mystery crashes deep inside openssl ASN.1 decoder routines (c2i_ASN1_INTEGER() while in d2i_X509() to be exact). When I issued `info files' in GDB I finally realized that I was using the wrong openssl library. 7 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. 7.1 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). 7.1.1 Running PHP as Apache mod_php ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ See <> for how to compile Apache to support mod_php. 7.1.2 Running PHP as CGI (any web server) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In the CGI case you generally make sure your CGI script starts like #!/usr/bin/php 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 <>. 7.2 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. 8 Python Extension ================== *** TBD using SWIG. Very preliminary support in Makefile exists, but *** this has not been tested in any real world set up. THe Python extension is built into subdirectory ~py~ with command make pyzxid The result should be py/py_zxid.so 9 Java Native API (JNI): zxidjava package and zxidjni class =========================================================== 9.1 Building the JNI -------------------- After building main zxid distribution, say make javazxid You must have set ~JNI_INC~ variable correctly in the Makefile (or in localconf.mk) and ~javac~ must be in path (or you must set ~JAVAC~ variable). For the servlet or Tomcat support, you must make sure ~SERVLET_PATH~ points to your servlet-api.jar file. The ZXID Java interface has been mainly tested with ~j2sdk1.4.2~ and some versions of Java SDK 1.5. If you have done changes that require regeneration of the zxidjava/zxid_wrap.c file you should build with make javazxid ENA_GEN=1 but this requires that you have swig(1) installed. Depending on the changes it may also require xsd2sg.pl and gperf(1), see "Compilation for Experts" section, above, for full explanation. As of January 2007, all of the Java JNI interface is swig(1) generated - there are no human authored files. However, we anticipate building a helper Java library to facilitate use of the JNI - contributions welcome. After compilation, just copy the class files and the libzxidjni.so to suitable locations in your system (Makefile lacks any specific Java installation target because the author has not yet made up his mind about what makes sense). When you run Java programs that use the zxidjni class, you must makes sure the libzxidjni.so is found by the dynamic linker - usually this means setting ~LD_LIBRARY_PATH~ environment variable. The zxid-java.sh shell script demonstrates how to do this for the example CGI program zxid.java. 9.1.1 MacOS X: JNI Notes ~~~~~~~~~~~~~~~~~~~~~~~~ * The resulting library is called libzxidjni.jnilib * You may need to supply additional arguments to java command: java -classpath .:zxidjava -Djava.library.path=zxidjava zxid * On Mac, it seems ~export LD_LIBRARY_PATH=zxidjava~ is not needed. 9.2 Programming with ZXID Java API ---------------------------------- For detailed usage examples of the Java interface you should study the zxid.java file. The Java interface is contained in a package called ~zxidjava~. This package contains the main wrapper class ~zxidjni~ as well as a number of data type specific classes. The ~zxidjni~ class is just a container for procedural zxid API - all methods of this class are static. To start using the ZXID Java interface you need to do two things: import zxidjava.*; somewhere near top of your program pulls in the zxidjava package, including the zxidjni class. Then you need to have a static initializer smewhere in your program to pull in the libzxidjni.so: public class myprog { static { System.loadLibrary("zxidjni"); } public static void main(String argv[]) throws java.io.IOException { // ... } } From here on you can call the C API procedures as static methods of the zxidjni class, e.g: cf = zxidjni.new_conf("/var/zxid/"); Note that the ~zxid_~ prefix is omitted in favour of the ~zxidjni~ class name qualifier. 9.3 Known Problems and Limitations ---------------------------------- The zx_str type is generally NOT nul terminated. We try to map these in the SWIG type maps, but any function returning char* currently maps to Java String type, yet there is no way of knowing how long the string type is. Therefore it's not safe to call functions returning char*. For example, consider int zx_LEN_SO_sa_Action(struct zx_ctx* c, struct zx_sa_Action_s* x); char* zx_ENC_SO_sa_Action(struct zx_ctx* c, struct zx_sa_Action_s* x, char* p); struct zx_str* zx_EASY_ENC_SO_sa_Action(struct zx_ctx* c, struct zx_sa_Action_s* x); The intent of the LEN_SO plus ENC_SO pair is that you first compute length, allocate sufficient buffer, and then render the encoding into the buffer. The ENC_SO in fact returns char* one past the end of the string. It is NOT safe to cal ENC_SO from Java because the SWIG generated interface would make Java believe that the char* one past end of string is a C string in its own right. Thus the only safe one to call is the EASY_ENC_SO variant. 9.4 Running as servlet under Tomcat ----------------------------------- ZXID distribution contains subdirectory called ~servlet~. You should link this into webapps directory of Tomcat servlet container cd ~/apache-tomcat-5.5.20/webapps ln -s ~/zxid-0.17/servlet zxidservlet You also need to set ~allowLinking~ flag in apache-tomcat-5.5.20/conf/context.xml (the ~reloadable~ flag avoids having to restart Tomcat if you recompile the .class file): ... The file servlet/WEB-INF/web.xml describes the example zxid application. The actual application lives in servlet/WEB-INF/classes which is actually just a symlink back to the top level of the ZXID distribution. Therefore the zxidhello.class file appears on the top level and the wrapper classes, which are scoped in ~zxidjava~ package, appear in zxidjava/ subdirectory. From the servlet container's perspective the directory appears to be apache-tomcat-5.5.20/webapps/zxidservlet/WEB-INF/classes/zxidjava After ~make javazxid~ and restart of Tomcat (~killall java; apache-tomcat-5.5.20/bin/startup.sh~), you can access the application using URL (defined in servlet/WEB-INF/web.xml) http://sp1.zxidsp.org:8080/zxidservlet/zxidHLO?o=E 9.5 Troubleshooting class loader -------------------------------- If you get java.lang.ClassNotFoundException: zxidhlo org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1355) org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1201) org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105) org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148) org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:869) org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:664) org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527) org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:80) org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:684) java.lang.Thread.run(Thread.java:595) Then you forgot to turn on the symlinks (see ~allowLinking~, above). If you get java.lang.UnsatisfiedLinkError: no zxidjni in java.library.path java.lang.ClassLoader.loadLibrary(ClassLoader.java:1517) java.lang.Runtime.loadLibrary0(Runtime.java:788) java.lang.System.loadLibrary(System.java:834) zxidhlo.(zxidhlo.java:20) sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39) sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27) java.lang.reflect.Constructor.newInstance(Constructor.java:274) java.lang.Class.newInstance0(Class.java:308) java.lang.Class.newInstance(Class.java:261) org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105) org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148) org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:869) org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:664) org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527) org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:80) org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:684) java.lang.Thread.run(Thread.java:534) Then it is not finding zxidjava/libzxidjni.so. Either say export LD_LIBRARY_PATH=~/zxid-0.14/zxidjava:$LD_LIBRARY_PATH or place libzxidjni.so in $CATALINA_HOME/shared/lib. Note that the library name really is libzxidjni.so despite the misleading exception suggesting just "zxidjni". If you get java.lang.UnsatisfiedLinkError: Native Library /home/sampo/zxid/zxidjava/libzxidjni.so already loaded in another classloader java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1551) java.lang.ClassLoader.loadLibrary(ClassLoader.java:1511) java.lang.Runtime.loadLibrary0(Runtime.java:788) java.lang.System.loadLibrary(System.java:834) zxidhlo.(zxidhlo.java:20) sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39) sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27) java.lang.reflect.Constructor.newInstance(Constructor.java:274) java.lang.Class.newInstance0(Class.java:308) java.lang.Class.newInstance(Class.java:261) org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105) org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148) org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:869) org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:664) org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527) org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:80) org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:684) java.lang.Thread.run(Thread.java:534) Then ... ? Currently it seems you have to restart Tomcat. If you get java.lang.NoClassDefFoundError: javax/servlet/http/HttpServlet java.lang.ClassLoader.defineClass1(Native Method) java.lang.ClassLoader.defineClass(ClassLoader.java:620) java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124) java.net.URLClassLoader.defineClass(URLClassLoader.java:260) java.net.URLClassLoader.access$100(URLClassLoader.java:56) java.net.URLClassLoader$1.run(URLClassLoader.java:195) java.security.AccessController.doPrivileged(Native Method) java.net.URLClassLoader.findClass(URLClassLoader.java:188) java.lang.ClassLoader.loadClass(ClassLoader.java:306) sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268) java.lang.ClassLoader.loadClass(ClassLoader.java:251) org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1270) org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1201) org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105) org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148) org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:869) org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:664) org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527) org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:80) org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:684) java.lang.Thread.run(Thread.java:595) Then the problem is with class path. Apparently running the startup.sh script from anywhere else than top level of Tomcat distribution produces the above error. 9.6 Logging and Debugging Tips ------------------------------ In case you add debug prints, the stderr (System.err) output appears to go by default to apache-tomcat-5.5.20/logs/catalina.out 9.7 Debugging libzxidjni.so under jdb and gdb --------------------------------------------- Debugging the JNI C code would appear to require running java or jdb under gdb and setting break points in the C code. Unfortunately this appears to be particularly tricky. A possible approach is to introduce a sleep(1) in the C code and then use gdb to attach to the java process. Unfortunately even this method does not seem to allow us to set break points. export LD_LIBRARY_PATH=zxidjava:$LD_LIBRARY_PATH export QUERY_STRING='e=https%3A%2F%2Fidp.symdemo.com%3A8880%2Fidp.xml&l2=+Login+%28SAML20%3APOST%29+&fc=1&fn=prstnt&fq=&fy=&fa=&fm=exact' N.B. In following "$" means Unix shell prompt, "%" gdb prompt, and ">" jdb prompt. $ gdb jdb % set env LD_LIBRARY_PATH=zxidjava % set env QUERY_STRING=e=https%3A%2F%2Fidp.symdemo.com%3A8880%2Fidp.xml&l2=+Login+%28SAML20%3APOST%29+&fc=1&fn=prstnt&fq=&fy=&fa=&fm=exact % r zxid > stop at zxid:24 > run > next # or step > print cf > cont 10 C# (csharp) Support ====================== *** TBD: SWIG based, try make csharpzxid (*** see also cvsdoc:fiam-man/sdk-dotnet.pd) 11 Ruby Support =============== *** TBD: SWIG based, try make rubyzxid 12 zxid_simple() API ==================== The ZXID library provides two main APIs: the simple and the full. The simple API is meant to get you going with minimal understanding about SAML or SSO. It tries to encapsulate as much control flow as possible. 12.1 Hello World ---------------- Consider the follwing "Hello World" CGI example in C<> (the zxid_simple() API is available in all language bindings): 01 #include 02 #define CONF "PATH=/var/zxid/&URL=https://sp1.zxidsp.org:8443/zxid" 03 void main() { 04 char* res = zxid_simple(CONF, 0, 255); 05 switch (res[0]) { 06 case 'd': /* Logged in case */ 07 my_parse_ldif(res); 08 my_render_content(); 09 exit(0); 10 default: 11 ERR("Unknown zxid_simple() response(%s)", res); 12 } 13 } What happens here: 1. The CGI script calls zxid_simple() to handle SAML protocol according to the configuration 2. The last argument with value 255 tells zxid_simple() to automatically handle redirections, login screen and any other protocol interaction needed to make SSO happen. 3. If zxid_simple() returns, we have either succeeded in SSO or we have failed (all other cases are handled internally by zxid_simple() which calls exit(2) so it never returns). 4. In the success case, zxid_simple() returns an LDIF entry (as a nul terminated C string) describing the SSO and the attributes received. For example dn: idpnid=Pa45XAs2332SDS2asFs,affid=https://idp.demo.com/idp.xml objectclass: zxidsession affid: https://idp.demo.com/idp.xml idpnid: Pa45XAs2332SDS2asFs authnctxlevel: password sesid: S12aF3Xi4A cn: Joe Doe where dn:: LDAP distinguished name (part of LDIF format). Always first. objectclass:: Part of LDIF format. affid:: Specifies which IdP was used for SSO idpnid:: The federated ID, or pseudonym (IdP assigned NameID) authnctxlevel:: Rough indication of how IdP authenticated user sesid:: Session ID, as may be stored in cookie or used for file name in the session cache (/var/zxid/ses) setcookie:: If this field is not empty and not equal to '-' (single dash), then the controlling appication should take the necessary actions to set the cookie as specified. Namely, the controlling application should create 'Set-Cookie' HTTP response header whose value is the value of the +setcookie+ field. How this is done is environment dependent. cookie:: The ZXIDSES (or other as configured) cookie value. For reference. cn:: Common Name. This attribute just exemplifies how any additional attributes the IdP may have set will appear. Typically the LDAP attribute names are used. The ~dn~ line will always be the first. All other lines may appear in any order. String representation of LDIF was chosen as it is easy to parse in most programming languages.<> 12.1.1 Configuration Options ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The zxid_simple() can be configured in following ways (later ways can override earlier ones). 1. Built in default configuration 2. Configuration file, usually /var/zxid/zxid.conf, if any 3. Configuration string passed as first argument. While configuration string can override all other options (i.e. it is processed last), the PATH specification is parsed before the configuration file is looked up. Turns out that often the default configuration modified by the configurations string is all you need - you do not need to prepare configuration file. See section "Configuring and Running" for complete list of configuration options, but generally it is sufficient to specify only a handful: PATH:: Where files are kept and configuration file is found. URL:: The URL of the SP CDC_URL:: The Common Domain URL (optional, if omitted the Common Domain Cookie processing is disabled) 12.1.2 AUTO options (auto flags) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ~auto_flags~ argument allows you to control which operations should be performed automatically and which should be passed to the calling application, see "Gaining More Control" section, below, for full description of this case. The auto options can be added together. < tag. 4095 0xfff ZXID_AUTO_ALL Enable all automatic CGI behaviour. 4096 0x1000 ZXID_AUTO_DEBUG Enable debugging output to stderr. >> If the AUTO_REDIR flag is true, the LOCATION header is sent to stdout and exit(0) may be called, depending on the AUTO_NOEXIT flag. The SOAP, META, LOGIN, and MGMT flags follow convention: HC 00 No automation. Only action letter is returned ("e"=login, "b"=meta, etc.) 01 Content, not wrapped in headers, is returned as a string 10 Headers and content is returned as a string 11 Headers and content are sent to stdout, CGI style and exit(0) may be called, depending on AUTO_EXIT. Otherwise "n" is returned. Whether exit(0) is called in 11 case depends on ZXID_AUTO_NOEXIT flag. How much HTML is generated for Login page and Mgmt page in 01 (content only) mode depends on AUTO_PAGE and AUTO_FORM flags TF 00 reserved / nothing is generated 01 Only form fields (but not form tag itself) are generated. 10 Complete form is generated 11 Whole page is generated (best support) 12.1.3 Configuration options for customizing HTML ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When whole page is generated, some templating information is taken from the configuration. IDP_SEL_START:: All the HTML before
tag. This can include HTML headers and the tag, as well as beginning of the page, allowing for complete color selection, stylesheet embedding, and general branding of the page. IDP_SEL_NEW_IDP:: The HTML fragment to allow login using a new IdP (Auto CoT using IdP URL). Set to empty to hide this possibility. IDP_SEL_OUR_EID:: Message displaying SP Entity ID, in case (technically minded) user needs to know this to establish relationship with an IdP. IDP_TECH_USER:: Technical parameters that user might want to set, and typically would be allowed to set. May be hidden (not user controllable) or visible. fc:: Create federation (AllowCreate flag) fn:: Name ID format prstnt:: Persistent (pseudonym) trnsnt:: Transient, temporary pseudonym IDP_TECH_SITE:: Technical parameters that the site administrator should decide and set. Usually hidden fields: fq:: Affiliation ID (usually empty) fy:: Consent obtained by SP for the federation or SSO empty:: No statement about consent urn:liberty:consent:obtained:: Has been obtained (unspecified way) urn:liberty:consent:obtained:prior:: Obtained prior to present transaction, e.g. user signed terms and conditions of service urn:liberty:consent:obtained:current:implicit:: Consent is implicit in the situation where user came to invoke service urn:liberty:consent:obtained:current:explicit:: Obtained explicitly urn:liberty:consent:unavailable:: Consent can not be obtained urn:liberty:consent:inapplicable:: Obtaining consent is not relevant for the SP or service. fa:: Authentication Context (strength of authentication) needed by the SP fm:: Matching rule for authentication strength (usually empty, IdP decides) fp:: Forbid IdP from interacting with the user (IsPassive flag) ff:: Request reauthentication of user (ForceAuthn flag) 12.2 Gaining More Control ------------------------- The fully automated interface works well for CGI deployment but probably is not appropriate in more complex situations. You can use zxid_simple() without automation and prevent it from accessing the system layer too directly. Consider 01 #define CONF "PATH=/var/zxid/&URL=https://sp1.zxidsp.org:8443/zxid" 02 int main() { 03 char* res; 04 res = zxid_simple(CONF, getenv("QUERY_STRING"), 0); 05 switch (res[0]) { 06 case 'L': printf("%s", res); exit(0); /* Redirect */ 07 case 'C': printf("%s", res); exit(0); /* Content-type + content */ 08 case '<': printf("Content-Type: text/html\r\n\r\n%s", res); exit(0); 09 case 'n': exit(0); 10 case 'b': my_send_metadata(); 11 case 'e': my_render_login_screen(); 12 case 'd': /* Logged in case */ 13 my_parse_ldif(res); 14 my_render_content(); 15 exit(1); 16 default: 17 ERR("Unknown zxid_simple() response(%s)", res); 18 } 19 } Here we specify zero as ~auto_flags~, thus all automation is disabled. This means that the res can take more varied shape and the calling application has to be prepared to handle them. The different cases are distinguished by the first character of the res string: L:: Redirection request (L as in Location header). The full contents of the res is the redirection request, ready to be printed to stdout of a CGI. If you want to handle the redirection some other way, you can parse the string to extract the URL and do your thing. This res is only returned if you did not set ZXID_AUTO_REDIR. Example: Location: https://sp1.zxidsp.org:8443/zxid?o=C C:: Content with Content-type header. The res is ready to be printed to the stdout of a CGI, but if you want to handle it some other way, you can parse the res to extract the header and the actual body. Example: CONTENT-TYPE: text/html Login page ... Example (metadata): CONTENT-TYPE: text/xml ... Less than ("<"):: Content without headers. This could be HTML content for login page or metadata XML. To know which (and set content type correctly), you would have to parse the content. This res format is only applicable if you did not specify ZXID_AUTO_CTYPE (but did specify ZXID_AUTO_CONTENT). n:: Do nothing. The operation was somehow handled internally but the exit(2) was not called (e.g. ZXID_AUTO_SOAP was NOT specified). The application should NOT attempt generating any output. b:: Indication that the application should send SP metadata to the client. This res is only returned if you did not set ZXID_AUTO_META. e:: Indication that the application should display the login page. Application may use zxid_idp_list() to render the IdP selection area. This res is only returned if you did not set ZXID_AUTO_CONTENT. d:: Indication that SSO has been completed or that there was an existing valid session in place. The res is an LDIF entry containing attributes that describe the SSO or session. See "Hello World" section for description of the LDIF data. Usually your application would parse the attributes and then render its application specific content. If you want to render the SSO management form (with logout and defederate buttones), you can call zxid_fed_mgmt(). Asterisk ("*"):: Although any unknown letter should be interpreted as an error, we follow convention of prefixing errors with an asterisk ("*"). 12.3 Some Generalization and Optimizations ------------------------------------------ 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 21 Protected content, logged in as <$=$attr['cn']$> 22 ... 23 24 28 Please Login Using IdP 29 ... 30 31 ... 32 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. 12.4 Java Servlet Example Using Tomcat -------------------------------------- Consider 01 import zxidjava.*; 02 import java.io.*; 03 import javax.servlet.*; 04 import javax.servlet.http.*; 05 public class zxidhlo extends HttpServlet { 06 static { System.loadLibrary("zxidjni"); } 07 static final String conf 08 = "PATH=/var/zxid/&URL=http://sp1.zxidsp.org:8080/zxidservlet/zxidHLO"; 09 public void do_zxid(HttpServletRequest req, HttpServletResponse res, String qs) 10 throws ServletException, IOException { 11 String ret = zxidjni.simple(conf, qs, 0xd54); 12 switch (ret.charAt(0)) { 13 case 'L': /* Redirect: ret == "LOCATION: urlCRLF2" */ 14 res.sendRedirect(ret.substring(10, ret.length() - 4)); 15 return; 16 case '<': 17 switch (ret.charAt(1)) { 18 case 's': /* >/tmp/hlo.err; exit ;; esac cat << EOF Content-Type: text/html ZXID HELLO SP Mgmt

ZXID HELLO SP Management (user $CN logged in, session active)

EOF The zxidsimple(1) utility will return exit value 1 if it handled a SAML protocol operation (by outputting to stdout whatever was appropriate). The shell script should not do any further processing and just exit. If the exit value is 0 (success) then SSO has been done. Since the attributes from the SAML assertion are usually interesting, you can capture them to a temporary file using the -o option. First we split the result of the backtick into a list on (literal) newline. Then we process the list with for loop and look with case for the interesting attributes and capture them into local variables. Finally the protected content page is output. 12.5 Form Field Naming ---------------------- The ZXID cgi interface assumes certain hardwired form field names. These are not configurable (and there is no intent to make them configurable). The cgi fields may appear either in query string (GET method) or as POST content (though depending on your programming environment and language, you may need to read the POST data in yourself prior to calling zxid_simple()). 12.5.1 Common Fields ~~~~~~~~~~~~~~~~~~~~ o:: Operation. In particular o=P means that form uses POST method. s:: Session ID RelayState:: SAML 2.0 mandated field name for relay state SAMLart:: SAML 2.0 mandated field name for SAML artifact SAMLResponse:: SAML 2.0 mandated field name for SAML response, especially in POST profile SAMLRequest:: SAML 2.0 mandated field name for SAML request SigAlg:: SAML 2.0 mandated field name for signature algorithm in redirect binding Signature:: SAML 2.0 mandated field name for signature in redirect binding 12.5.2 IdP Selection (Login) Screen ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ u:: User (local login) p:: Password (local login) c:: Common Domain Cookie e:: Entity ID (manual entry field) d:: Entity ID (from popup or radio box) i:: Protocol index l1:: Login using artifact profile (same as i=1) l2:: Login using POST profile (same as i=2) l1EID:: Login using specified IdP (artifact profile), same as e=EID&i=1 l2EID:: Login using specified IdP (POST profile), same as e=EID&i=2 fc:: Allow Create flag fp:: IsPassive flag ff:: Force Authentication flag fn:: NameID format fq:: Affiliation ID fy:: Consent field fm:: Matching rule fa:: Authentication Context Class fr:: Relay State The IdP selection form (aka Login) screen can be implemented, using the above documented form interface, in many ways as following examples illustrate. *Example IdP Selection Form: Popup menu method* *** *Example IdP Selection Form: Separate IdP buttons method*

Login Using New IdP

IdP URL

Login Using Known IdP

Technical options

Create federation, NID Format:
*Example IdP Selection Form: IdP links method* *** 12.5.3 Single Logout and Federation Management ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gl:: Local Logout gr:: Single Logout using redirection gs:: Single Logout using SOAP gt:: NameID Managment (redirect) gu:: NameID Management (SOAP) *Example Management Form*
11 Integration with Existing Web Sites ====================================== Single Sign-On is used to protect some useful resources. ZXID does not have any means of serving these resources, rather a normal web server or application server should do it. ZXID should just concentrate on verifying that a user has valid session, and if not, establishing the session by way of SSO. 11.1 Brief Overview of Control Flow ----------------------------------- The SAML 2.0 specifications mandate a wire protocol, and in order to speak the wire protocol, the SP application typically has to follow certain standard sequence of control flow. <> First a user<> tries to access a web site that acts in SP role. This triggers following sequence of events 1. User is redirected to URL in a common domain. This is so that we can read the Common Domain Cookie that indicates which IdP the user uses. Alternatively, if you started at https://sp1.zxidsp.org:8443/zxid?o=E, the CDC check is by-passed and flow 2b. happens. 2. After the CDC check, a Authentication Request (AuthnReq) is generated. The IdP may have been chosen automatically using CDC (2a), or there may have been some user interface interaction (not show in the diagram) to choose the IdP. 3. User is redirected to the IdP. The redirection carries as a query string a compressed and encoded form of the SAML 2.0 AuthnReq. 4. Once the IdP has authenticated the user, or observed that there already is a valid IdP session (perhaps from a cookie), the IdP redirects the user back to the SP. The AuthnResponse may be carried in this redirection in a number of alternate ways a. The redirect contains a special token called +artifact+. The artifact is a reference to the AuthnResponse and the SP needs to get the actual AuthnResponse by using a SOAP call (the 4bis step). b. The "redirect" is actually a HTML page with a form and little JavaScript that causes the form to be automatically posted to the SP. The AuthnResponse is carried as a form field. 5. After verifying that AuthnResponse indicated a success, the SP establishes a local session for the user (perhaps setting a cookie to indicate this). Depending on how the SP to web site integration is done the user is taken to the web site in one of the two ways a. Redirect to the content. This time the session is there, therefore the flow passes directly from check session to the web content. b. It is also possible to show the content directly without any intervening redirection. 11.2 Redirect Approach to Integration ------------------------------------- 11.3 Pass-thru Approach to Integration -------------------------------------- 11.3.1 mod_perl pass-thru ~~~~~~~~~~~~~~~~~~~~~~~~~ 11.3.2 PHP pass-thru ~~~~~~~~~~~~~~~~~~~~ 11.3.3 mod_zxid pass-thru ~~~~~~~~~~~~~~~~~~~~~~~~~ 11.4 Proxy Approach to Integration ---------------------------------- 12 Full Native C API ==================== The generated aspects of the native C API are in c/*-data.h, for example c/zx-sa-data.h Studying this file is very instructive.<> 12.1 C Data Structures ---------------------- From .sg a header (NN-data.h) is generated. This header contains structs that represent the data of the elements. Each element and attribute generates its own node. Even trivial nodes like strings have to be kept this way because the nodes form basis of remembering the ordering of data. This ordering is needed for exclusive XML canonicalization, and thus for signature verification.<> Any missing data is represented by NULL pointer. Any repeating data is kept as a linked list, in reverse order of being seen in the data stream.<> Simple elements and all attributes are represented by simple string node (even if they are booleans or integers). *Example* Consider following XML lNIzVMrp8CwTE= GeMp7LS...vnjn8= Decoding would produce the data structure in Fig-<>. You should also look at c/zx-sa-data.h to see the structs involved in this example. <gg.kids|SignedInfo|SignatureValue|KeyInfo (0)|Object (0)|Id (0)}}"]; siginfo [shape=record,label="zx_ds_SignedInfo_s|{|{gg.kids|gg.g.wo|CanonicalizationMethod|SignatureMethod|Reference|Id (0)}}"]; canonmeth [shape=record,label="zx_ds_CanonicalizationMethod_s|{|{gg.g.wo|Algorithm\n\"http://w3.org/xml-exc-c14n#\"}}"]; sigmeth [shape=record,label="zx_ds_SignatureMethod_s|{|{gg.g.wo|Algorithm\n\"http://w3.org/xmldsig#rsa-sha1\"}}"]; ref [shape=record,label="zx_ds_Reference_s|{|{gg.kids|gg.g.wo (0)|Transforms|DigestMethod|DigestValue|Id (0)|Type (0)|URI\n\"#RrcrNwFIw6n\"}}"]; xforms [shape=record,label="zx_ds_Transforms_s|{|{gg.kids|gg.g.wo|gg.g.n (0)|Transform}}"]; xform_c14n [shape=record,label="zx_ds_Transform_s|{|{gg.g.wo|gg.g.n (0)|XPath (0)|Algorithm\n\"http://w3.org/xml-exc-c14n#\"}}"]; xform_env [shape=record,label="zx_ds_Transform_s|{|{gg.g.wo (0)|gg.g.n|XPath (0)|Algorithm\n\"http://w3.org/xmldsig#env-sig\"}}"]; xforms:f_xform -> xform_env xform_env:f_n -> xform_c14n digmeth [shape=record,label="zx_ds_DigestMethod_s|{|{gg.g.wo|Algorithm\n\"http://w3.org/xmldsig#sha1\"}}"]; digval [shape=record,label="zx_elem_s|{|{gg.g.wo (0)|content\n\"lNIzVMrp8CwTE=\"}}"]; sigval [shape=record,label="zx_ds_SignatureValue_s|{|{gg.g.wo (0)|gg.content\n\"GeMp7LS...vnjn8=\"|Id (0)}}"]; sig:f_siginfo -> siginfo sig:f_sigval -> sigval siginfo:f_canonmeth -> canonmeth siginfo:f_sigmeth -> sigmeth siginfo:f_ref -> ref ref:f_xforms -> xforms ref:f_digmeth -> digmeth ref:f_digval -> digval sig:f_kids ->siginfo [weight=0,style=dashed,color=red] siginfo:f_wo ->sigval [weight=0,style=dashed,color=red] siginfo:f_kids -> canonmeth [weight=0,style=dashed,color=red] canonmeth:f_wo -> sigmeth [weight=0,style=dashed,color=red] sigmeth:f_wo -> ref [weight=0,style=dashed,color=red] ref:f_kids -> xforms [weight=0,style=dashed,color=red] xforms:f_wo -> digmeth [weight=0,style=dashed,color=red] digmeth:f_wo -> digval [weight=0,style=dashed,color=red] xforms:f_kids -> xform_c14n [weight=0,style=dashed,color=red] xform_c14n:f_wo -> xform_env [weight=0,style=dashed,color=red] >> There are two pointer systems at play here. The black solid arrows depict the logical structure of the XML document. For each child element there is a struct field that simply points to the child. If there are multiple occurrences of the child, as in ~sig->SignedInfo->Reference->Transforms->Transform~, the children are kept in a linked list connected by gg.g.n (next) fields.<> The +wide order+ structure, depicted by red dashed arrows, is maintained using gg.kids and gg.g.wo fields. For example ~sig->SignedInfo->Reference->Transforms~ keeps its kids, the ~zx_ds_Transform~ objects, in the original order hanging from the kids and linked with the wo field. As can be seen, the order kept with wo fields can be different than the one kept using n (next) fields. What's more, the kids list can contain dissimilar objects, witness ~sig->SignedInfo->Reference->gg.kids~. The wire order representation is only captured when decoding the document and is mainly useful for correctly canonicalizing the document for signature verification. If you are building a data structure in your own program, you typically will not set the gg.kids and gg.g.wo fields. In the diagram, the objects of type ~zx_str~ were collapsed to double quoted strings. Superfluous gg.kids, gg.g.wo, and gg.g.n fields were omitted: they exist in all structures, but are not shown when they are ~NULL~. The ~NULL~ is depicted as zero (0).<> <> 12.1.1 Handling XML Namespaces ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ An annoying feature of XML documents is that they have variable namespace prefixes. The namespace prefix for the unqualified elements is taken to be the one specified in target() directive of the .sg input. Name of an element in C code is formed by prefixing the element by the namespace prefix and an underscore. Attributes will only have namespace prefix if such was expressly specified in .sg input. When decoding, the actual namespace prefixes are recorded. The wire order encoder knows to use these recorded prefixes so that accurate canonicalization for XMLDSIG can be produced. If the message on wire uses wrong namespaces, the wrong ones are remembered so that canonicalization for signature validation will work irrespective. The ability to accept wrong namespaces only works as long as there is no ambiguity as to which tag was meant - there are some tags that need namespace information to distinguish. If you hit one of these then either you get lucky and the one that is arbitrarily picked by the decoder happens to be the correct one, or you are stuck with no easy way to make it right. Of course the XML document was wrong to start with so theoretically this is not a concern. Generally the more schemata that are simultaneously generated to one package, the greater the risk of collisions between tags. The schema order encoder always uses the prefixes defined using target() directives in .sg files. The runtime notion of namespaces is handled by ~ns_tab~ field of the decoding and encoding context. It is initialized to contain all namespaces known by virtue of .sg declarations. The runtime assigned prefixes are held in a linked list hanging from <> (next) field of ~struct zx_ns_s~. (*** more work needed here) The code generation creates a file, such as c/zx-ns.c, which contains initialization for the table. The main program should point the ~ns_tab~ field of context as follows: main { struct zx_ctx* ctx; ... ctx->ns_tab = zx_ns_tab; /* Here zx_ is the chosen prefix */ } Consider the following evil contortion Assuming the ~ns_tab~ assigns prefix <> to the namespace URI, we would have following data structure as a result of a decode <}|{z|iru|}}"] e [shape=record,label="e|uri|"] h [shape=record,label="h|uri|"] b [shape=record,label="b|uri|0"] i [shape=record,label="e|iru|0"] ns_tab:uri_n -> e ns_tab:iru_n -> i e:n -> h h:n -> b E -> H [style=bold] E -> B [style=bold] B -> C [style=bold] B -> D [style=bold] D -> F [style=bold] E -> e [color=red] H -> h [color=red] B -> b [color=red] C -> e [color=red] D -> i [color=red] F -> e [color=red] >> The red thin arrows indicate how the elements reference the namespaces. Since none of the elements used the prefix originally specified in the schema grammar target() directive, we ended up allocating "alias" nodes for the uri. However, since E and C use the same prefix, they share the alias node. Things get interesting with D: it redefines the prefix e to mean different namespace URI, "iru", which happens to be an alias of prefix z. Later, when wire order canonical encode is done, the red thin arrows are chased to determine the namespaces. However, we need to keep a separate "seen" stack to track whether parent has already declared the prefix and URI. E would declare xmlns:e="uri", but C would not because it had already been "seen". However, F would have to declare it again because the xmlns:e="iru" in D masks the declaration. The ~zx_ctx~ structure is used to track the namespaces and "seen" status through out decoders and encoders. <}|{z|iru|0|0|}}"] e [shape=record,label="e|uri|0|0|"] ee [shape=record,label="e|uri||0|"] h [shape=record,label="h|uri|0||"] b [shape=record,label="b|uri|0||0"] i [shape=record,label="e|iru||0|0"] ctx [shape=record,label="{ctx|{|{ns_tab|seen_n}}}"] ns_tab:uri_n -> ee ns_tab:iru_n -> i ee:n -> e e:n -> h h:n -> b E -> H [style=bold] E -> B [style=bold] B -> C [style=bold] B -> D [style=bold] D -> F [style=bold] E -> e [color=red] H -> h [color=red] B -> b [color=red] C -> e [color=red] D -> i [color=red] F -> ee [color=red] ns_tab -> ctx:ns [arrowhead=none,arrowtail=normal] b -> ctx:sn [color=blue,style=dotted,arrowhead=none,arrowtail=normal] b:sn -> h [color=blue,style=dotted] h:sn -> ee [color=blue,style=dotted] ee:s -> i [color=green,style=dashed] i:s -> e [color=green,style=dashed] >> Here we can see how the ~seen_n~ list, represented by the blue dotted arrows, was built: at the head of the list, ~ctx->seen_n~, is the last seen prefix, namely b (because, although the meaning of e at F was different, e as a prefix had already been seen earlier at E), followed by other prefixes in inverse order of first occurrence.<> The green dashed arrows from e:uri to e:iru and then on to second e:uri reflect the fact that e:uri (second) was put to the list first (when we were at E), but later, at D, a different meaning, iru, was given to prefix e. Finally at F we give again a different meaning for e, thus pushing to the "seen stack" another node. Although e at E and at F have namespace URI, "uri", we are not able to use the same node because we need to keep the stack order. Thus we are forced to allocate two identical nodes. 12.1.2 Handling any and anyAttribute ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Since our aim is to be lax in what we accept, every element can handle unexpected additional attributes as well as unexpected elements. Thus whether the schema specifies any or anyAttribute or not, we handle everything as if they were there. However, when attributes and elements are received outside of their expected context, they are simply treated as strings with string names. This is true even for those attributes and elements that would be recognizable in their proper context. The any extension points, as well as some bookkeeping data are hidden inside ~ZX_ELEM_EXT~ macro. If you tinker with this macro, be sure you know what you are doing. If you want to add your own specific fields to all structs, redefining ~ZX_ELEM_EXT~ may be appropriate, but if you want to add more fields only to some specific structures, you can define a macro of form TPF_EEE_EXT and put in it whatever fields you want. These fields will be initialized to zero when the structure is created, but are not touched in any other way by the generated code. In particular, if some of your fields are pointers, it will be your responsibility to free them. The standard free functions will not understand to free them. See the data structure walking functions, below for one way to accomplish this. 12.1.3 Root data structure ~~~~~~~~~~~~~~~~~~~~~~~~~~ The root data structure struct zx_root_s; is a special structure that has a field for every top level recognizable element. 12.1.4 Per element data structures ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *** TBW 12.1.5 Memory Allocation ~~~~~~~~~~~~~~~~~~~~~~~~ After decoding all string data points directly into the input buffer, i.e. strings are NOT copied. Be sure to not free the input buffer until you are done processing the data structure. If you need to take a copy of the strings, you will need to walk the data structure as a post processing step and do your copies. This can be done using void TPF_dup_strs_len_NS_EEE(struct zx_dec_ctx* c, struct TPF_NS_EEE_s* x); The structures are allocated via ZX_ZALLOC() macro, which by default calls zx_zalloc() function, which in turn uses system malloc(3). However, you can redefine the macro to use whatever other allocation scheme you desire. The generated libraries never free(3) memory. In many programming patterns, this is actually desirable: for example a CGI program can count on dying - the process exit(2) will free all the memory. If you need to free(3) the data structure, you will need to walk it using void TPF_free_len_NS_EEE(struct zx_dec_ctx* c, struct TPF_NS_EEE_s* x, int free_strings); void zx_free_any(struct zx_dec_ctx* c, struct zx_note_s* n, int free_strs); The zx_free_any() works by having a gigantic switch statement that calls the appropriate specific free function. You can deep clone the data structure with void TPF_deep_clone_NS_EEE(struct zx_dec_ctx* c, struct TPF_NS_EEE_s* x, int dup_strings); struct zx_note_s* zx_clone_any(struct zx_dec_ctx* c, struct zx_note_s* n, int dup_strs); The zx_clone_any() works by having a gigantic switch statement that calls the appropriate specific free function. 12.2 Decoder as Recursive Descent Parser ---------------------------------------- The entry point to the decoder is struct zx_root_s* zx_DEC_root(struct zx_dec_ctx* c, struct zx_ns_s* dummy, int n_decode); The decoding context holds pointer to the raw data and must be initialized prior to calling the decoder. The third argument specifies how many recognized elements are decoded before returning. Usually you would specify 1 to consume one top level element from the stream.<> The returned data structure, ~struct zx_root_s~, contains one pointer for each type of top level element that can be recognized. The ~tok~ field of the returned value identifies the last top level element recognized and can be used to dispatch to correct request handler: zx_prepare_dec_ctx(c, TPF_ns_tab, start_ptr, end_ptr); struct TPF_root_s* x = TPF_DEC_root(c, 0, 1); switch (x->gg.g.tok) { case TPF_NS_EEE_ELEM: return process_EEE_req(x->NN_EEE); } When processing responses, it is generally already known which type of response you are expecting, so you can simply check for NULLness of the respective pointer in the returned data structure. Internally zx_DEC_root() works much the same way: it scans a beginning of an element from the stream, looks up the token number corresponding to the element name, and switches on that, calling element specific decoder functions (see next section) to do the detailed processing. In the above code fragment, you should note the call to zx_prepare_dec_ctx() which initializes the decoder machinery. It takes +ns_tab+ argument, which specifies which namespaces will be recognized. This table MUST match the TPF_DEC_root() function you call (i.e. both must have been generated as part of the same xsd2sg.pl invocation). The other arguments are the start of the buffer to decode and pointer one past the end of the buffer to decode. 12.2.1 Element Decoders ~~~~~~~~~~~~~~~~~~~~~~~ For each recognizable element there is a function of form struct TPF_NS_EEE_s* zx_DEC_NS_EEE(struct zx_dec_ctx* c); where TPF is the prefix, NS is the namespace prefix, and EEE is the element name. For example: struct zx_se_Envelope_s* zx_DEC_se_Envelope(struct zx_ctx* c); These functions work much the same way as the root decoder. You should consult dec-templ.c for the skeleton of the decoder. Generally you should not be calling element specific decoders: they exist so that zx_DEC_root() can call them. They have somewhat nonintuitive requirements, for example the opening <, the namespace prefix, and the element name must have already been scanned from the input stream by the time you call element specific decoder. 12.2.2 Decoder Extension Points ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The generated code is instrumented with following macros ZX_ATTR_DEC_EXT(ss):: Extension point called just after decoding known attribute ZX_XMLNS_DEC_EXT(ss):: Extension point called just after decoding xmlns attribute ZX_UNKNOWN_ATTR_DEC_EXT(ss):: Extension point called just after decoding unknown attr ZX_START_DEC_EXT(x):: Extension point called just after decoding element name and allocating struct, but before decoding any of the attributes. ZX_END_DEC_EXT(x):: Extension point called just after decoding the entire element. ZX_START_BODY_DEC_EXT(x):: Extension point called just after decoding element tag, including attributes, but before decoding the body of the element. ZX_PI_DEC_EXT(pi):: Extension point called just after decoding processing instruction ZX_COMMENT_DEC_EXT(comment):: Extension point called just after decoding comment ZX_CONTENT_DEC(ss):: Extension point called just after decoding string content ZX_UNKNOWN_ELEM_DEC_EXT(elem):: Extension point called just after decoding unknown element Following macros are available to the extension points TPF:: Type prefix (as specified by -p during code generation) EL_NAME:: Namespaceful element name (NS_EEE) EL_STRUCT:: Name of the struct that describes the element EL_NS:: Namespace prefix of the element (as seen in input schema) EL_TAG:: Name of the element without any namespace qualification. 12.3 Exclusive Canonical Encoder -------------------------------- The encoder receives a C data structure and generates a gigantic string containing an XML document corresponding to the data structure and the input schemata. The XML document conforms to the rules of exclusive XML canonicalization and hence is useful as input to XMLDSIG. One encoder is generated for each root node specified at the code generation. Often these encoders share code for interior nodes. The encoders allow two pass rendering. You can first use the length computation method to calculate the amount of storage needed and then call one of the rendering functions to actually render. Or if you simply have large enough buffer, you can just render directly. The encoders take as argument next free position in buffer and return a char pointer one past the last byte used. Thus you can discover the length after rendering by subtracting the pointers. This is guaranteed to result same length as returned by the length computation method.<> You can also call the next encoder with the return value of the previous encoder to render back-to-back elements. The XML namespace and XML attribute handling of the encoders is novel in that the specified sort is done already at code generation time, i.e. the renderers are already in the order that the sort mandates. For attributes we know the sort order directly from the schema because [XML-C14N], sec 2.2, p.7, specifies that they sort first by namespace URI and then by name, bot of which we know from the schema. For ~xmlns~ specifications the situation is similarly easy in the schema order encoder case because we know the namespace prefixes already at code generation time. However, for the wire order encoder we actually need a runtime sort because we can not control which namespace prefixes get used. However, for both cases we can make a pretty good guess about which namespaces might need to be declared at any given element: the element's own namespace and namespaces of each of its attributes. That's all, and it's all known at code generation time. At runtime we only need to check if the namespace has already been seen at outer layer. 12.3.1 Length computation ~~~~~~~~~~~~~~~~~~~~~~~~~ Compute length of an element (and its subelements). The XML attributes and elements are processed in schema order. int TPF_LEN_SO_NS_EEE(struct zx_ctx* c, struct TPF_NS_EEE_s* x); For example: int zx_LEN_SO_se_Envelope(struct zx_ctx* c, struct zx_se_Envelope_s* x); Compute length of an element (and its subelements). The XML namespaces and elements are processed in wire order. int TPF_LEN_WO_NS_EEE(struct zx_ctx* c, struct TPF_NS_EEE_s* x); For example: int zx_LEN_WO_se_Envelope(struct zx_ctx* c, struct zx_se_Envelope_s* x); 12.3.2 Encoding in schema order ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Render an element into string. The XML elements are processed in schema order. The xmlns declarations and XML attributes are always sorted per [XML-EXC-C14N] rules.<> This is what you generally want for rendering new data structure to a string. The wo pointers are not used. char* TPF_ENC_SO_NS_EEE(struct zx_ctx* c, struct TPF_NS_EEE_s* x, char* p); For example: char* zx_ENC_SO_se_Envelope(struct zx_ctx* c, struct zx_se_Envelope_s* x, char* p); Since it is a very common requirement to allocate correct sized buffer and then render an element, a helper function is provided to do this in one step. struct zx_str* zx_EASY_ENC_SO_se_Envelope(struct zx_ctx* c, struct zx_se_Envelope_s* x); The returned string is allocated from allocation arena described by ~zx_ctx~. 12.3.3 Encoding in wire order ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Render element into string. The XML elements are processed in wire order by chasing wo pointers. This is what you want for validating signatures on other people's XML documents. If the wire representation was schema invalid, e.g. elements were in wrong order, the wire representation is still respected, except for xmlns declarations and XML attributes, which are always sorted, per exc-c14n rules. For each element a function is generated as follows char* TPF_ENC_WO_NS_EEE(struct zx_ctx* c, struct TPF_NS_EEE_s* x, char* p); For example char* zx_ENC_WO_se_Envelope(struct zx_ctx* c, struct zx_se_Envelope_s* x, char* p); A helper function is also available struct zx_str* zx_EASY_ENC_WO_se_Envelope(struct zx_ctx* c, struct zx_se_Envelope_s* x); 12.4 Signatures (XMLDSIG) ------------------------- 12.4.1 Signature Generation ~~~~~~~~~~~~~~~~~~~~~~~~~~~ *** TBW 12.4.2 Signature Validation ~~~~~~~~~~~~~~~~~~~~~~~~~~~ For signature validation you need to walk the decoded data structure to locate the signature as well as the references and pass them to zxsig_validate(). The validation involves wire order exclusive canonical encoding of the referenced XML blobs, computation of SHA1 or MD5 checksums over them, and finally computation of SHA1 check sum over the element and validation of the actual against that. The validation involves public key decryption using the signer's certificate. A nasty problem in exclusive canonicalization is that the namespaces that are needed in the blob may actually appear in the containing XML structures, thus in order to know the correct meaning of a namespace prefix, we need to perform the +seen+ computation for all elements outside and above the blob of interest.<> To verify signature, you have to do certain amount of preparatory work to locate the signature and the data that was signed. Generally what should be signed will be evident from protocol specifications or from the security requirements of your application environment. Conversely, if there is a signature, but it does not reference the appropriate elements, its worthless and you might as well reject the document without even verifying the signature. *Example* struct zxsig_ref refs[1]; cf = zxid_new_conf("/var/zxid/"); ent = zxid_get_ent_from_file(cf, "YV7HPtu3bfqW3I4W_DZr-_DKMP4."); refs[0].ref = r->Envelope->Body->ArtifactResolve ->Signature->SignedInfo->Reference; refs[0].blob = (struct zx_elem_s*)r->Envelope->Body->ArtifactResolve; res = zxsig_validate(cf->ctx, ent->sign_cert, r->Envelope->Body->ArtifactResolve->Signature, 1, refs); if (res == ZXSIG_OK) { D("sig vfy ok %d", res); } else { ERR("sig vfy failed due to(%d)", res); } This code illustrates 1. You have to determine who signed and provide the entity object that corresponds to the signer. Often you would determine the entity from element somewhere inside the message. The entity is used for retrieving the signing certificate. Another alternative is that the signature itself contains a element and you extract the certificate from there. You would still need to have a way to know if you trust the certificate. 2. You have to prepare the refs array. It contains pairs of specifications combined with the actual elements that are signed. Generally the URI XML attribute of the element points to the data that was signed. However, it is application dependent what type of ID XML attribute the URI actually references or the URI could even reference something outside the document. It would be way too unreliable for the zxsig_validate() to attempt guessing how to locate the signed data: therefore we push the responsibility to you. Your code will have to walk the data to locate all referenced bits and pieces. In the above example, locating the one signed bit was very easy: the specification says where it is (and this location is fixed so there really is no need to check the URI either). You pass the length of the refs array and the array itself as two last arguments to zxsig_validate(). 3. You need to locate the element in the document and pass it as argument to zxsig_validate(). Usually a protocol specification will say where the element is to be found, so locating it is not difficult. 4. The return value will indicate validation status. ZXSIG_OK, which has numerical value of 0, indicates success. Other nonzero values indicate various kinds of failure. 12.4.3 Certificate Validation and Trust Model ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Trust models for TLS and signature validation are separate. TLS layer is handled mainly by libcurl or in case of ClientTLS, by the https web server (which is not part of zxid). In signature validation the primary trust mechanism is that entity's metadata specifies the signing certificate and there is no Certification Authority check at all.<> This model works well if you control the admission to your CoT. However, ZXID ships by default with the automatic CoT feature turned on, thus anyone can get added to the CoT and therefore signature with any certificate they declare is "valid". This hardly is acceptable for anything involving money. 12.5 Data Accessor Functions ---------------------------- Simple read access to data should, in C, be done by simply referencing the fields of the struct, e.g. if (!r->EntitiesDescriptor->EntityDescriptor) goto bad_md; *** TBW 12.6 Memory Allocation and Free ------------------------------- *** TBW 12.7 Walking the data structure ------------------------------- *** TBW 12.9 Thread Safety ------------------ All generated libraries are designed to be thread safe, provided that the underlying libc APIs, such as malloc(3) are thread safe. 13 ID-WSF Features of ZXID ========================== 13.1 EPR Cache -------------- Calling an ID Web Service requires an Endpoint Reference (EPR) that indicates not only the URL of the web service, but also the security mechanism and any tokens or credentials required by the security mechanism, collectively called the +metadata+ of the EPR.<> An EPR can be obtained from two sources 1. From the SSO assertion containing an attribute statement that has the EPR. This is called the +bootstrap+ method. 2. By calling the discovery service. Either way, the EPRs are cached in the SSO session as files with paths like /var/zxid/ses/SESID/SVCTYPE,SHA1 where +SESID+ is the SSO session ID (safe base64 of a random number). +SVCTYPE+ and +SHA1+ form a unique file name inside the session directory. +SVCTYPE+ component allows easy identification of the EPRs that are relevant for calling a given service. The +SHA1+ component is a SHA1 hash over the canonical XML representation of the EPR. When bootstrap EPRs are present in the SSO assertion, they are automatically extracted to the EPR cache (internally zxid_snarf_eprs_from_ses() is called). Generally this will yield at least bootstrap EPR for discovery service. Later, when calling web services, discovery is automatically performed as needed and the results are automatically populated to the EPR cache (internally zxid_cache_epr() is called). When calling higher level WSC APIs, the discovery will happen automatically, but if you work in terms of lower level APIs, you can obtain an EPR by calling zxid_get_epr() which will first consult the cache, and if there is a miss, then try discovering the EPR using discovery service EPR, if any (usually one was obtained as bootstrap during the SSO). 13.2 High level WSC API ----------------------- The high level API is based on the idea of constructing the SOAB body (the payload) as a string and passing that to function that performs the SOAP call and returns response. 01 cf = zxid_new_conf_to_cf(CONF); 02 03 res = zxid_simple_cf(cf, cl, qs2, 0, 0x1fff); 04 switch (res[0]) { 05 default: 06 ERR("Unknown zxid_simple() response(%s)", res); 07 case 'd': break; /* Logged in case */ 08 } 09 sid = strstr(res, "sesid: "); 10 zxid_get_ses(cf, &sess, sid); 11 env = zxid_callf(cf, &sess, zx_xmlns_idhrxml, 12 "" 13 "" 14 "%s" 15 "%s" 16 "" 17 "", cgi.select, cgi.data); 18 ZXID_CHK_STATUS(env, idhrxml_ModifyResponse, 19 hrxml_resp = "Modify failed"; break); 20 hrxml_resp = "Modify OK"; On lines 1-10 a single sign on is done to prepare the session and EPR cache. 13.2.1 zxid_callf() - Make SOAP call with specified body ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ env = zxid_callf(cf, ses, svctype, fmt, ...) zxid_callf() first creates the body of the SOAP call by expanding the format string (which may involve additional arguments). Next it parses the string into XML data structure. If the format string uses unresolved namespaces, they are resolved using the svctype argument. Next zxid_callf() will attempt to locate an EPR for the service type. This may already be in cache, or discovery step may be performed. Then zxid_callf() augments the XML data structure with Liberty ID-WSF mandated headers. It will look at the security mechanism and token specified in the EPR and perform appropriate steps to create WS-Security header and apply signature as needed. Finally a SOAP call is made. The result is XML parsed and returned as SOAP envelope object. cf:: Configuration object, see zxid_new_conf_to_cf() ses:: Session object, used to locate EPRs, see zxid_get_ses() svctype:: Service type and namespace that is applicable to the body. This is one of the constants from c/zx-ns.h fmt:: printf style format string that is used to describe the body of the call as a string. If fmt contains format specifiers, then additional arguments are used to expand these. Return value:: SOAP envelope structure. > N.B. Although the request body is formulated as a string, > you can only use XML constructs whose schema is known to > ZXID. The resulting XML must be validly parseable by ZXID. 13.2.2 ZXID_CHK_STATUS() - Macro for checking OK status ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ZXID_CHK_STATUS(env, resp_tag, abort-action); ZXID_CHK_STATUS(env, idhrxml_ModifyResponse, break); In ID-WSF web services it is very common that the overall outcome of the operation is expressed by element at top level. This macro makes it easy to check the status. If status is NOT "OK", then +abort-action+ is invoked. This would be whatever needed in your program to report error and abort further processing of the response. It could be a ~break~, ~return~, or even a ~goto~ statement. env:: SOAP envelope representing the response resp_tag:: The response tag as it appears as a child of env->Body abort-action:: Statement, or statements, used to report error and abort processing. There is no return value. This macro is "procedural". 13.3 Low Level WSC API ---------------------- Typical code for calling a web service at low level 01 #include 02 #include 03 #include 04 #include 05 #include 06 #include 07 08 struct zx_e_Envelope_s* env; 09 struct zx_a_EndpointReference_s* epr; 10 epr = zxid_get_epr(cf, ses, zx_xmlns_dap, 1); 11 if (epr) { 12 env = zx_NEW_e_Envelope(cf->ctx); 13 env->Header = zx_NEW_e_Header(cf->ctx); 14 env->Body = zx_NEW_e_Body(cf->ctx); 15 env->Body->Query = zxid_mk_dap_query(cf, ...); /* See ID-DAP inteface */ 16 env = zxid_wsc_call(cf, ses, epr, env); /* The beef */ 17 if (env->dap_QueryResponse) 18 D("Result is LDIF(%.*s)", 19 env->Body->dap_QueryResponse->Data->LDIF->gg.content->len, 20 env->Body->dap_QueryResponse->Data->LDIF->gg.content->s); 21 } 1. On line 10 zxid_get_epr() is used with following arguments cf:: Configuration object ses:: Session object (generallly obtained from SSO) zx_xmlns_dap:: The +service type+ of the service that is to be called. Generally this is same as the namespace URI. The include contains macros, such as ~zx_xmlns_dap~, for the namespaces supported by zxid. Alternatively you could simply supply the string. 1:: The last argument ("1", one) is an iterator index that, in case of multiple EPRs allows you to pick which one to use. Most common usage is to simply supply 1 which picks the first one.<> 13.3.1 zxid_get_epr() - Obtain EPR fron cache or by discovery ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ epr = zxid_get_epr(cf, ses, svctype, n); See if an EPR for service +svctype+ is available in current session EPR cache. If not, see if discovery EPR is available and try to discover the EPR for the +svctype+. Note that the discovery is transparent to the programmer - indeed programmer can not know whether EPR was served from cache (where it may have appeared by virtue of a bootstrap) or whether it was discovered. cf:: Configuration object, see zxid_new_conf_to_cf() ses:: Session object, used to locate EPRs in cache, see zxid_get_ses() svctype:: Service type (usually namespace of the service). This is one of the constants from c/zx-ns.h n:: Ordinal. If more than one EPR is available, specifies which one is desired. Usually 1 is supplied, to pick the first EPR. Return Value:: An EPR object (URL of the service, security mechanism, and possibly security token (SAML assertion)). 13.4 ID-DAP Interface --------------------- The ID-DAP search filters and data model are very similar to LDAP, see [RFC2251] for further explanation. 13.4.1 Short example of using low level API ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ env->Body->dap_Query = zxid_mk_dap_query(cf, 0, /* No tests */ zxid_mk_dap_query_item(cf, zxid_mk_dap_select(cf, 0, /* DN from ID-WSF */ "objecttype=svcprofile", 0, /* all attributes */ 1, /* chase symlinks */ ZXID_DAP_SCOPE_SUBTREE, 0, /* no size limit */ 0, /* no time limit */ 0), /* return data */ 0, /* regular data entries */ 0, /* No predefined operation */ 0, /* No sorting. */ 0, /* No changed since specification. */ 0, /* Do not include LDAP common attributes. */ 0, /* Start from first result (offset == 0) */ 0, /* Return all results (count == 0) */ 0, /* Do not request snapshot */ 0, /* Do not refer to snapshot */ 0), /* No contingent item ID reference */ 0); /* No subscriptions */ As can be seen, it is common to specify nearly all arguments as 0, relying on default values. Thus the code typically appears without comments as env->Body->dap_Query = zxid_mk_dap_query(cf, 0, zxid_mk_dap_query_item(cf, zxid_mk_dap_select(cf, 0, "objecttype=svcprofile", 0, 1, ZXID_DAP_SCOPE_SUBTREE, 0, 0, 0), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), /* 10 zeroes here */ 0); /* No subscriptions */ 13.4.2 Fully winded example of using low level API ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As a query can also have test and subscription clauses, the fully winded example becomes quite onerous. Of course for many daily tasks you will not need all the frills, or you can just use the simple API instead. env->Body->dap_Query = zxid_mk_dap_query(cf, zxid_mk_dap_test_item(cf, zxid_mk_dap_testop(cf, 0, /* DN from ID-WSF */ "objecttype=svcprofile", 0, /* all attributes */ 1, /* chase symlinks */ ZXID_DAP_SCOPE_SUBTREE, 0, /* no size limit */ 0, /* no time limit */ 0), /* return data */ 0, /* regular data entries */ 0), /* No predefined operation */ zxid_mk_dap_query_item(cf, zxid_mk_dap_select(cf, 0, /* DN from ID-WSF */ "objecttype=svcprofile", 0, /* all attributes */ 1, /* chase symlinks */ ZXID_DAP_SCOPE_SUBTREE, 0, /* no size limit */ 0, /* no time limit */ 0), /* return data */ 0, /* regular data entries */ 0, /* No predefined operation */ 0, /* No sorting. */ 0, /* No changed since specification. */ 0, /* Do not include LDAP common attributes. */ 0, /* Start from first result (offset == 0) */ 0, /* Return all results (count == 0) */ 0, /* Do not request snapshot */ 0, /* Do not refer to snapshot */ 0), /* No contingent item ID reference */ zxid_mk_dap_subscription(cf, "subsID", 0, /* No item ID reference */ zxid_mk_dap_resquery(cf, zxid_mk_dap_select(cf, 0, /* DN from ID-WSF */ "objecttype=svcprofile", 0, /* all attributes */ 1, /* chase symlinks */ ZXID_DAP_SCOPE_SUBTREE, 0, /* no size limit */ 0, /* no time limit */ 0), /* return data */ 0, /* regular data entries */ 0, /* No predefined operation */ 0, /* No sorting. */ 0, /* No changed since specification. */ 0, /* Do not include LDAP common attributes. */ 0), /* No contingent item ID reference */ 0, /* No notification aggregation spec. */ 0, /* No notification trigger spec. */ 0, /* Subscription starts immediately. */ 0, /* Subscription never expires. */ 1, /* Include changed data in the notifications. */ 0, /* Use notification reference for administrative notifications. */ "http://host/notif_sink") ); 13.4.3 zxid_mk_dap_query() ~~~~~~~~~~~~~~~~~~~~~~~~~~ struct zx_dap_Query_s* zxid_mk_dap_query(struct zxid_conf* cf, struct zx_dap_TestItem_s* tis, struct zx_dap_QueryItem_s* qis, struct zx_dap_Subscription_s* subs); 13.4.4 zxid_mk_dap_query_item() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ struct zx_dap_QueryItem_s* zxid_mk_dap_query_item(struct zxid_conf* cf, struct zx_dap_Select_s* sel, char* objtype, char* predef, char* sort, char* changed_since, int incl_common_attrs, int offset, int count, char* setreq, char* setid, char* contingent_itemidref); sel:: Selection expression, see zxid_mk_dap_select(). objtype:: Either "entry", or "_Subsciption". The former is used for searching and manipulating the normal attribute data while the latter is used for manipulating subscription objects. Specifying NULL selects "entry". predef:: Predefined serverside operation identifier. This is server dependent, but you can think of it as a stored procedure. If you pass predef, it is common to leave sel and objtype as NULL and vice versa, i.e. you should pass NULL as predef unless you know your server to expect otherwise. sort:: String specifying sorting of the result set. See ID-DAP specification for further details. N.B. Not all servers support sorting. Pass NULL if you do not need sorting. changed_since:: LDAP date time string specifying that only entries that have changed since specified moment should be returned. incl_common_attrs:: If 1 (true), "common" LDAP attributes such as modificationtime are included. If you do not need these attributes you can save the server some work and also some network transmission overhead by not requesting these attributes, i.e. pass 0 (false). offset:: If search matches multiple entries, the zero based index of the first entry to return. Pass 0 to get the beginning of the result set. count:: Maximum number of entries to return in the response. 0 means return all. +offset+ and +count+ allow pagination through large result set. N.B. +count+ is very different from +sizelimit+ that you may specify in zxid_mk_dap_select(). The latter causes the actual backend search operation to abort or return partial result set if the result would be too large and is unsuitable for pagination. setreq:: Request creation of a result set snapshot for pagination. N.B. It is possible to paginate through large result set even without creating a snapshot, but the results may be inconsistent because the underlying data may change between queries that paginate through it. Creating a snapshot avoids this problem. Whether pagination with or without snapshot is cheaper depends on the backend implementation. +setreq+ is specified on the first query that referes to the snapshot. The subsequent queries referring to the same snapshot will specify +setid+. The two are mutually exclusive: if +setid+ is specified the +setreq+ must be NULL. setid:: If you are paginating through a result set snapshot created using +setreq+, then you must specify +setid+ in subsequent queries that refer to same snapshot. When specifying +setreq+, you muse leave +setid+ as NULL. contingent_itemidref:: A query item can be made contingent on a test item, i.e. the query will only be made if the test succeeded. If you want this, you must pass the item ID of the test item here. Passing NULL means that no such dependency exists. N.B. The server side implementation of the tests may actually require a query to be made anyway. 13.4.5 zxid_mk_dap_select() ~~~~~~~~~~~~~~~~~~~~~~~~~~~ struct zx_dap_Select_s* zxid_mk_dap_select(struct zxid_conf* cf, char* dn, char* filter, char* attributes, int deref_aliases, int scope, int sizelimit, int timelimit, int typesonly); dn:: Distinguished or relative distinguished name. Since ID-DAP usually uses the identity conveyed using ID-WSF headers to determine the distinguished name, it is common to pass simply a NULL. filter:: LDAP filter to apply. NULL if none. attributes:: List of attributes to return. NULL means return all attributes. deref_aliases:: Boolean: whether the server should chase any "symlinks", i.e. an entry may appear at some location as an alias that is just a pointer to the real location of the entry. This is usually what you want so pass 1 (true). scope:: The scope of the ID-DAP search. ZXID_DAP_SCOPE_BASE (0):: Only what is pointed to by DN, e.g. one entry. The default. ZXID_DAP_SCOPE_SINGLE (1):: Single level of directory right under DN. ZXID_DAP_SCOPE_SUBTREE (2):: Full subtree search under the DN. sizelimit:: Maximum number of entries to return. 0 means no limit. This is intended to stop the server from accidentally performing expensive queries. N.B. +sizelimit+ is different from +count+, see zxid_mk_dap_query_item(), the latter is meant for pagination of a large result set without aborting it. timelimit:: Maximum number of seconds to spend in the search. 0 means no limit. typesonly:: If true, only attribute names are returned, without their values. This allows an existence test to be performed without passing the values over the network. Usually you want the values so you would pass 0 (false). 13.4.6 zxid_mk_dap_test_item() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ struct zx_dap_TestItem_s* zxid_mk_dap_test_item(struct zxid_conf* cf, struct zx_dap_TestOp_s* tstop, char* objtype, char* predef); tstop:: See zxid_mk_dap_testop(). objtype:: See +objtype+ in zxid_mk_dap_query_item(). predef:: See +predef+ in zxid_mk_dap_query_item(). 13.4.7 zxid_mk_dap_testop() ~~~~~~~~~~~~~~~~~~~~~~~~~~~ struct zx_dap_TestOp_s* zxid_mk_dap_testop(struct zxid_conf* cf, char* dn, char* filter, char* attributes, int deref_aliases, int scope, int sizelimit, int timelimit, int typesonly); See description of zxid_mk_dap_select(). For ID-DAP protocol the Select and TestOp are defined to be the same. However, this need not be the case for Data Services Template (DST) based services in general. Hence, the data types for Select and TestOp are different (although very similar) and two separate constructors are needed. 13.4.8 zxid_mk_dap_subscription() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ struct zx_dap_Subscription_s* zxid_mk_dap_subscription(struct zxid_conf* cf, char* subsID, char* itemidref, struct zx_dap_ResultQuery_s* rq, char* aggreg, char* trig, char* starts, char* expires, int incl_data, char* admin_notif, char* notify_ref); subsID:: Subscription ID itemidref:: When subscribing to data described by a query item, create item, or modify item, the reference to the relevant item. NULL if no such item exists, in which case +rq+ is usually specified. rq:: Result query that identifies the data of interest for the subscription. See zxid_mk_dap_resquery(). Pass NULL if the data is identified otherwise, e.g. via +itemidref+. aggreg:: Notification aggregation mode. Implementation dependent. Pass NULL. Notification aggregation is an optimization where some notification may be delayed a little so that it can be sent more optimally in same message with other notifications that may happen a little later. This functionality need not be supported by the backend implementations. trig:: Implementation dependent notification triggers. Pass NULL. starts:: Start date time of the subscription. NULL means subscription starts immediately. expires:: End data time of the subscription. NULL means the subscription will not expire. incl_data:: If 1 (true), the notifications resulting from the subscription will contain the changed data (push model). If 0 (false), the notification will just say that something changed, but interested party will need to perform a separate query to retrieve the data (pull model). admin_notif:: Administrative notification address. If NULL, the +notify_ref+ will be used for administrative notifications as well. notify_ref:: Notification address. 13.4.9 zxid_mk_dap_resquery() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ struct zx_dap_ResultQuery_s* zxid_mk_dap_resquery(struct zxid_conf* cf, struct zx_dap_Select_s* sel, char* objtype, char* predef, char* sort, int incl_common_attr, char* changed_since, char* contingent_itemidref); See description of zxid_mk_dap_query_item(). 13.5 ID Messaging Interface --------------------------- 13.6 ID Geo Location Interface ------------------------------ 13.7 Contact Book Interface --------------------------- 13.8 People Service Interface ----------------------------- 13.9 Interface to Conor's Demo Media Service -------------------------------------------- 13.10 ID-SIS Data Service for HR-XML ------------------------------------ HR-XML defines a XML document format that can be used for data interchange in the Human Resources world. The essence of HR-XML is to represent CV (Curriculum Vitae, a.k.a. Resume) in structured form. At the moment (June 2007) HR-XML does not define any standard way to pass these documents around. In CV 2007 conference organized by EIfE-L in Paris, an idea was canvassed: use Liberty Data Service to exchange HR-XML documents. I tried to implement such service during the conference, but only got it working at the air port after the conference. This service is a good example of how to apply ZXID to implement your own services. *Files* sg/hr-xml-sampo.sg - A slightly modified version of HR-XML schema sg/id-hrxml.sg - Liberty DST 2.1 based data service for HR-XML zxidhrxmlwsc.c - Web Services Client for HR-XML zxidhrxmlwsp.c - Web Services Provider for HR-XML *Running demo* 1. Set up your /etc/hosts. At least you need sp1.zxidsp.org and you may also need your IdPs domain name (e.g. idp.symdemo.com). 2. Start your IdP and DS (not supplied with ZXID) cd /opt/SYMfiam/std conf/test-idp3/start.sh start log Next you need to cause the id-hr-xml services to be registered in the discovery service. This depends on product. (*** fix) Then you need to create an association for id-hr-xml service for some test user. 3. Start the web services client mini_httpd -p 8443 -c 'zxid*' -S -E zxid.pem -l tmp/mini.stderr & tail -f tmp/zxid.stderr& 4. Start the web services provider mini_httpd -p 8444 -c 'zxid*' -S -E zxid.pem -l tmp/mini2.stderr & tail -f tmp/zxid2.stderr& 5. Start browsing from https://sp1.zxidsp.org:8443/zxidhrxmlwsc?o=E 6. Login using the test user that has the association 7. Paste element in HR-XML Data form field and click Create. This creates a Candidate record in the WSP. 8. Click Query. This queries the WSP for the record we saved in previous step. You should see the record appear in the HR-XML Response field. http://s-idp.liberty-iop.org:8881/N 14 Integration of Other Libraries with ZXID =========================================== 14.1 Conor Cahill's C++ Library for ID-WSF ------------------------------------------ Conor P. Cahill, of AOL and Intel fame, has developed and maintains a C++ library for ID-WSF 2.0 Web Service Client functionality for selected application protocols, including the ID-WSF 2.0 Discovery and some application protcols. Conor also provides a server side package that implements the corresponding WSP roles in Java. These libraries are valuable resources and come with extensive test suites - in fact, passing Conor's test suites has become the gold standard for validity and interoperability of any ID-WSF implmentations (this is not to detract from formal IOP events and the Liberty certification program, but passing Conor's test suite is a good predictor of getting certified). *Install Recipe* Conor's libraries have certain dependencies. Following is my best understanding of how to get them installed.<> mkdir conor cd conor tar xvf /t/LibertyIDWSFServices-v0.8.2.tgz cd .. mkdir conor-cli cd conor-cli/ tar xvf /t/LibertyClientToolkit-v1.0.1.tgz 14.2 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. 14.3 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. 15 Creating New Interfaces Using ZXID Methodology ================================================= The ZXID code generation methodology can be used to create interfaces to any XML document or protocol that can be described as a Schema Grammar (which includes any document that can be expressed as XML Schema - XSD). The general steps are 1. Convert .xsd file to .sg, or write the .sg directly. For conversion, you would typically use a command like ~/pd/xsd2sg.pl foo.sg 2. Tweak and rationalize the resulting .sg file. In ideal world any construct expressible as .xsd should be nicely representable, but in practise some work better than others, thus you can create a much nicer interface if you invest in some manual tweaking. Note that the tweaked .sg still is able to represent the same document as the original .xsd described, though often the tweaking causes some relaxation. Most common tweaks a. If the .xsd is written so that the targeted namespace is also the default namespace, you should introduce a namespace prefix because this is needed during code generation to keep different C identifiers from clashing with each other. Ideally you should coordinate the namespace prefixes globally so that even two different projects will not clash. b. Where the choice construct is used, indicated by pipey symbol (|) in the .sg file, you should refactor these into sequences of zero-or-one occurrence (?) instances of the alternatives of the choice. This is needed because for the foreseeable future xsd2sg.pl has a limitation in code generation feature. If the choice has maxOccurs="unbounded" you should use (*) instead. c. xml:lang and other similar attributes may need to be factored open to be just of type %xs:string. This is a bug in xsd2sg.pl 3. "Connect" the schema to bigger framework. Usually this means adding your schema grammar to the ZX_SG variable in zxid/Makefile and supplying additional -r flags in ZX_ROOT variable. This allows your new schema to be visible at top level. If your schema is meant to extend leafs or interior nodes of the parse tree, such as SOAP Body, you would edit the SOAP schema to accept your new protocol elements in the Body. Or that the generic SOAP header can accept your specific header schemata, or that the SAML attribute definitions accept your kind of attributes - whatever makes sense in your context. Alternative to this is to create an entirely new monolithic encoder decoder, i.e. instead of extending the existing ZXID project to accommodate your new protocol, you just start a new project that uses the same methodology. You should see how the SAML protocol part is separated from the SAML metadata parsing and from the WSF parsing in the existing project. 16 ZXID Project =============== Immediate goal: build a SAML 2.0 SP and ID-WSF 2.0 WSC Goals of ZXID project include * SOAP 1.1 support (done) * SAML 2.0 compliance - SP role (done) - IdP role * Liberty ID-FF 1.2 support - SP - IdP - SAML 1.1 * Liberty ID-WSF 1.1 support - Discovery bootstrap - Discovery WSC - ID-DAP WSC - ID-DAP WSP * Liberty ID-WSF 2.0 support - Discovery bootstrap (done) - Discovery WSC (done) - ID-DAP WSC (done) - ID-DAP WSP <> <> <> <> 16.1 Project Layout ------------------- Following directory layout is used by the project. Many of the specified directories are used by intermediate outputs that are not distributed in tarball releases, but may or may no be present in CVS checkouts. zxid-0.xx | +-- Net The Net::SAML perl module (also mod_perl) +-- php PHP / mod_php integration +-- zxidjava The Java JNI interface to ZXID +-- servlet Apache Tomcat integration +-- c C code generated from the Schema Grammar descriptions +-- sg Schema Grammar (.sg) descriptions of protocols +-- xsd XML schema descriptions of protocols (not distributed) +-- tex Temporary files for document generation using PlainDoc (not distributed) +-- html HTML documentation generated using PlainDoc +-- review Publicly released announcements and documents (not distributed) +-- t Test scripts and expected test outputs `-- tmp Temporary files, such as actual test outputs The Manifest file, which follows, explains each file in more detail. <> >> 16.2 Protocol Encoders and Decoders ----------------------------------- The protocol encoders and decoders are generated automatically from the schema grammar (.sg) descriptions. This ensures accurate protocol implementation. While the output is strictly schema driven and correct, the decoders have some provisions to accept some deviations from strict spec (e.g. out of order elements are tolerated). However, one should note that XMLDSIG does not tolerate very much deviation, thus even if decoder accepts a slightly illformed message, it is likely to fail in signature verification. There are three outputs from generation 1. Data structures describing the data (xx.h) 2. Encoder that linearizes the data structure to wire protocol (xx-enc.c) 3. Decoder that converts wire protocol byte stream to a data structure (xx-dec.c) 16.3 Standards and Namespaces ----------------------------- ZXID uses consistently the same namespace prefixes throughout the project. The generated encoders and decoders support following schemata <> 17 Code Generation Tools ======================== Main work horse of code generation is xsd2sg.pl, which serves multiple purposes 1. Build hashes of all declarations in .sg input. Each hash element consists of array of elements and attributes, as well as groups and attribute groups. The type of array element sis determined from prefix, per .sg rules. 2. Expand groups and attribute groups 3. Evaluate each element wrt its type and generate a. C data structures b. Decoder grammar c. Token descriptions for perfect hash and lexical analyzer d. Encoder C code The code to build hashes is interwoven in the code that generates .xsd from .sg. The rest of the generation happens in a function called generate(). Typical command line (to generate SAML 2.0 protocol engine) ~/plaindoc/xsd2sg.pl -d -gen saml2 -p zx_ \ -r saml:Assertion -r se:Envelope \ -S \ sg/saml-schema-assertion-2.0.sg \ sg/saml-schema-protocol-2.0.sg \ sg/xmldsig-core.sg \ sg/xenc-schema.sg \ sg/soap11.sg \ >/dev/null </dev/null >> To generate SAML 2.0 Metadata engine you would issue ~/plaindoc/xsd2sg.pl -d -gen saml2md -p zx_ \ -r md:EntityDescriptor -r md:EntitiesDescriptor \ -S \ sg/saml-schema-assertion-2.0.sg \ sg/saml-schema-metadata-2.0.sg \ sg/xmldsig-core.sg \ sg/xenc-schema.sg \ >/dev/null </dev/null >> 17.1 Special Support for Specific Programming Languages ------------------------------------------------------- While C code generation is the main output, and this can always be converted to other languages using SWIG, sometimes a more natural language interface can be built by directly generating it. We plan to enhance the code generation to do something like this. At least direct hash-of-hashes-of-arrays-of-hashes type data-structure generation for benefit of some scripting languages is planned. 18 ZXID SP ========== *** warning: not checked lately, may be wrong! <> *** add description of CGI fields 19 Certificates =============== *** TBD - This chapter should be elaborated to be a certificate tutorial with following contents: * Intro to certs and private keys * Generating self signed cert * Generating certificate signing request and using it to obtain commercially issued cert * Installing root certs so you can recognize other people's certs * Client TLS considerations For the time being, the short answer is that ZXID uses OpenSSL and PEM format certificates. You can use same techniques as you would use for Apache / mod_ssl for acquiring certificates. You should NEVER password protect your private key. There will not be any opportunity to supply the password. You should instead protect your private key using Unix filesystem permissions. See OpenSSL.org or modssl.org FAQs for further information, including how to remove a password if you accidentally enabled it. 20 Testing ========== ZXID test suite is still in tatters. Some things that should be tested 1. Will generated HTTP redirect sig validate at IdP? 2. Does IdP issued A7N validate? 3. Validation of EncryptedAssertion? 4. Will generated SOAP binding sig validate at IdP? 5. Does IdP issued SOAP sig validate? Metadata related 1. IBM metadata (can we parse) 2. Sun metadata (can we parse) XML related 1. Fully qualified XML parses? 2. Unknown ns prefix that refers to known namespace URI 3. Known ns prefix, referring to wrong URI 4. Known prefix refers to aliased URI 5. Use of default namespaces working? 6. Unknown prefix and URI as long as it is never used 7. Unknown prefix and URI, used 8. Known NS (prefix or URI), unknown element 96 License ========== Copyright (c) 2006-2008 Symlabs (symlabs@symlabs.com), All Rights Reserved. Author: Sampo Kellomäki (sampo@iki.fi) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. While the source distribution of ZXID does not contain SSLeay or OpenSSL code, if you use this code you will use OpenSSL library. Please give Eric Young and OpenSSL team credit (as required by their licenses). Binary distribution of this product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/). See LICENSE.openssl for further information. Binary distribution of this product includes cryptographic software written by Eric Young (eay@cryptsoft.com). Binary distribution of this product includes software written by Tim Hudson (tjh@cryptsoft.com). See LICENSE.ssleay for further information. And remember, you, and nobody else but you, are responsible for auditing ZXID and OpenSSL library for security problems, back-doors, and general suitability for your application. 96.1 Dependency Library Licenses -------------------------------- ZXID strives to maintain IPR hygiene and avoid both non-free and GPL license contamination. All the dependency libraries have BSD style licenses * OpenSSL under BSDish (with "advertising" clause) * libcurl under BSDish * zlib under BSDish * libc available as part of the operating system Please see each library package for the exact details of their licenses. 96.2 Specification IPR ---------------------- ZXID is based on open SAML and Liberty specifications. The parties that have developed these specifications, including Symlabs, have made Royalty Free (RF) licensing commitment. Please ask OASIS and Liberty Alliance for the specifics of their IPR policies and IPR disclosures. Some protocols, such as WS-Trust and WS-Federation enjoy Microsoft's pledge<> that they will not sue you even if you implement these specifications. You should evaluate yourself whether this is good enough for your situation. 96.3 Further Warranties ----------------------- If you need the author or Symlabs to further disclaim IPR interest or make warranties of non-infringement, such declarations are available for a fee. Please contact sales@symlabs.com Legal queries and clarifications will be answered at then-current Symlabs Professional Services rate, please contact sales@symlabs.com. 97 FAQ ====== 97.1 Compilation Problems ------------------------- 97.1.1 OpenSSL not found: you need to create localconf.mk ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ZXID does NOT have a configure script. It ships with a notion of "standard" locations for the three dependency libraries, but if these libraries are not where it expects to find them, then typically you see (n.b. lines were folded for presentation): make If you get compilation errors, try: make help gcc -g -fpic -fmessage-length=0 -Wno-unused-label -Wno-unknown-pragmas -fno-strict-aliasing -D_REENTRANT -DDEBUG -DUSE_CURL -DUSE_OPENSSL -DLINUX -I/tmp/zxid-0.20 -I/usr/local/ssl/include -I/usr/local/include -c -o zxid.o zxid.c zxid.c:34:23: curl/curl.h: No such file or directory In file included from zxid.c:38: zx.h:26:25: openssl/rsa.h: No such file or directory ... What happened is that OpenSSL for some reason is not in the location where standard OpenSSL distribution would install it (as indicated by -I/usr/local/ssl/include flag that ships with ZXID Makefile). You need to determine where OpenSSL is installed in your case. You can use find / -name rsa.h -ls to locate candidates. For example, if it turns out that OpenSSL is installed in /opt/ssl, then you need to create a localconf.mk file that indicates this location: echo OPENSSL_ROOT=/opt/ssl >localconf.mk There are several other make variables you may need to tweak. In the above example, we also notice that libcurl was not found where expected. This would be fixed like this echo CURL_ROOT=/opt/curl >>localconf.mk Net result? ZXID does not try to guess where the libraries are. It makes you do the foot work of locating the correct libraries (some people have more than one instance installed) and prepare the localconf.mk. This may seem like a lot of work, but in my experience, fixing GNU autohell configure scripts that guess wrong is thousand times more frustrating. The system is dumb by design so you, as a human, do not have to try to second guess it - you are in control. 97.1.2 Missing gperf ~~~~~~~~~~~~~~~~~~~~ gcc -g -fpic -fmessage-length=0 -Wno-unused-label -Wno-unknown-pragmas -fno-strict-aliasing -D_REENTRANT -DDEBUG -DUSE_CURL -DUSE_OPENSSL -DLINUX -I/c/cvs/zxid_cvs -I/usr/local/ssl/include -I/usr//include -c -o c/zx-a-aux.o c/zx-a-aux.c c/zx-a-aux.c: In function "zx_NEW_a_Action": c/zx-a-aux.c:80: error: "zx_a_Action_ELEM" undeclared (first use in this function) This happens because c/zx-const.h was misgenerated (it should not happen at all if you do not supply ENA_GEN=1) and does not include the necessary defines. c/zx-const.h should have more than 1900 lines and look something like /* generated file, do not edit! zx_ _ATTR */ #ifndef _zx__ATTR #define _zx__ATTR #define zx_use_ATTR 0 #define zx_used_ATTR 1 #define zx_sequence_ATTR 2 ... #define zx_wantDSEPR_ATTR 347 #define zx_ZX_TOK_NOT_FOUND_ATTR 348 #define zx__ATTR_MAX 349 #endif /* generated file, do not edit! zx_ _ELEM */ #ifndef _zx__ELEM #define _zx__ELEM #define zx_ds_Y_ELEM 0 #define zx_gl_Y_ELEM 1 #define zx_gl_esrd_ELEM 2 ... #define zx_wst_OnBehalfOf_ELEM 1629 #define zx_ZX_TOK_NOT_FOUND_ELEM 1630 #define zx__ELEM_MAX 1631 #endif 97.1.3 make samlmod gives "incompatible types in assignment" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Should not happen with version 0.21 or later. See zxidnoswig.h for explanation of the problem. 97.1.4 Perl compiled with different compiler than zxid ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Perl modules generally want to be compiled with the same C compiler and options as were used to compile perl itself (see perl -V). If this happens to be different than the compiler you have defined in CC variable (gcc by default, near top of Makefile or in localconf.mk), you may get an error like: cd Net; perl Makefile.PL && make Warning: -L.. changed to -L/home/sampo/zxid/Net/.. Writing Makefile for Net::SAML make[1]: Entering directory `/home/sampo/zxid/Net' cc -c -I.. -I/apps/openssl/std/include -I/apps/include -D_REENTRANT -D_GNU_SOURCE -DTHREADS_HAVE_PIDS -DDEBUGGING -fno-strict-aliasing -pipe -Wdeclaration-after-statement -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -O2 -march=i586 -mtune=i686 -fmessage-length=0 -Wall -D_FORTIFY_SOURCE=2 -g -Wall -pipe -DVERSION=\"\" -DXS_VERSION=\"\" -fPIC "-I/usr/lib/perl5/5.8.8/i586-linux-thread-multi/CORE" SAML_wrap.c /bin/sh: cc: command not found make[1]: *** [SAML_wrap.o] Error 127 make[1]: Leaving directory `/zxid/Net' make: *** [samlmod] Error 2 *Solutions* 1. Compile zxid with compiler that was used for perl, e.g. make CC=the-compiler-that-perl-wants 2. Recompile perl using the compiler that you want to use for zxid 3. Tinker with PATH environment variable so that both C compilers are found. However, using two different compilers is not really supported. In general these types of problems happen when you use perl installed by your distribution, but have later compiled a gcc of your own. It may even be that you never installed the distribution cc - in that case consider installing it and then trying approaches 1 or 3. A similar situation can arise with incompatibility of the compiler and options used for dependency libraries, such as OpenSSL or libcurl, and those used for compiling zxid itself. 97.1.5 All files under zx missing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You need to symlink zx to zxid source directory, thus ln -s . zx If you do not have it, then you will get a lot of file inclusion errors for headers that are supposed to be in path starting by zx/ The symlink is there to keep all hand written source files on top level of directory for ease of development, yet allow inclusions to go through ~zx~ subdirectory. When zxid is installed, it goes to /usr/include/zx. Hence the symlink keeps the includes the same whether developing or using installed version. 97.1.6 Compiler Warnings ~~~~~~~~~~~~~~~~~~~~~~~~ If you compile zxid with compiler warnings turned on (CFLAGS += -Wall), you will see quite a number of warnings, most of which are unwarranted. Since the warnings are unwarranted, I ship zxid Makefile with warnings turned off. If this bothers you, feel free to investigate the warnings and report to me any issues you uncover. Following warnings in partuclar are unwarranted: 1. Any unusued variable warnings, especially in generated code. Most common of these is ~se~ variable (see enc-templ.c). 2. "Suggest parenthesis around assignment when used as truth value." I rely on C language operator precedence. Also, in most cases the assignment is the only expression in the truth test - there simply is no opportunity for ambiguity -- and no justified case for gcc to warn about this. 3. "Suggest parenthesis around && when used in ||". I rely on C language operator precedence, hence the suggestion is redundant. Some warnings you may want to worry about A. "int format, long int arg". On 32 bit platforms int and long are both 32 bits so this warning is not an issue. On 64 bit platforms, however, there may be cause for worry. 97.1.7 SWIG and Java Problems ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ javac -J-Xmx128m -g zxid.java zxidjava/*.java zxidjava/zxidjni.java:159: cannot find symbol symbol : class SWIGTYPE_p_p_void location: class zxidjava.zxidjni public static zx_str zx_rsa_pub_enc(zx_ctx c, zx_str plain, SWIGTYPE_p_p_void rsa_pkey, int pad) { ^ zxidjava/zxidjni.java:164: cannot find symbol symbol : class SWIGTYPE_p_p_void location: class zxidjava.zxidjni public static zx_str zx_rsa_pub_dec(zx_ctx c, zx_str ciphered, SWIGTYPE_p_p_void rsa_pkey, int pad) { ^ zxidjava/zxidjni.java:169: cannot find symbol symbol : class SWIGTYPE_p_p_void location: class zxidjava.zxidjni public static zx_str zx_rsa_priv_dec(zx_ctx c, zx_str ciphered, SWIGTYPE_p_p_void rsa_pkey, int pad) { ^ zxidjava/zxidjni.java:174: cannot find symbol symbol : class SWIGTYPE_p_p_void location: class zxidjava.zxidjni public static zx_str zx_rsa_priv_enc(zx_ctx c, zx_str plain, SWIGTYPE_p_p_void rsa_pkey, int pad) { ^ This was due to missing SWIG generated classes. Probably interrupted file transfer. javac -J-Xmx128m -g zxid.java zxidjava/*.java zxid.java:24: cannot find symbol symbol : method new_conf(java.lang.String) location: class zxidjava.zxidjni cf = zxidjni.new_conf("/var/zxid/"); ^ zxid.java:27: cannot find symbol symbol : method url_set(zxidjava.zxid_conf,java.lang.String) location: class zxidjava.zxidjni zxidjni.url_set(cf, url); ^ zxid.java:28: cannot find symbol jar cf zxidjava.jar *.class jar cf /tmp/zxidjava.jar zxidjava/*.class javac -J-Xmx128m -g zxid.java zxid.java:187: cannot access zxid_conf bad class file: /Library/Java/Extensions/zxidjava.jar(zxid_conf.class) class file contains wrong class: zxidjava.zxid_conf Please remove or make sure it appears in the correct subdirectory of the classpath. public static int mgmt_screen(zxid_conf cf, zxid_cgi cgi, zxid_ses ses, char op) ^ 1 error Underscore in linking error ./zxid-java.sh Start... Exception in thread "main" java.lang.NoSuchMethodError: zxidjava.zxidjni.new_conf(Ljava/lang/String;)Lzxidjava/zxid_conf; at zxid.main(zxid.java:24) This was due to finding some old copies from system paths. java -classpath .:zxidjava -Djava.library.path=zxidjava zxid Start... Exception in thread "main" java.lang.UnsatisfiedLinkError: _zxid_new_conf at zxidjava.zxidjniJNI._zxid_new_conf(Native Method) at zxidjava.zxidjni.new_conf(zxidjni.java:586) at zxid.main(zxid.java:24) 97.2 Platform Specifics ----------------------- If your Unix platform is not mentioned, you should try saying just make which will compile with Linux options. These options actually are pretty close to pure POSIX compile so you should get very close to working configuration. 97.2.1 Linux ~~~~~~~~~~~~ Native development platform. Just say make Seems there are some "improvements" that distributions have made. ZXID adopts the policy of expecting dependency modules where the module author meant it to be installed by default - for example OpenSSL by default installs in /usr/local/ssl (naming is historic, but has stuck). Many distros tinker with these paths. This means you need to create a localconf.mk. Redhat used to have an issue with Net::SAML (make samlmod). This has since been fixed, please see zxidnoswig.h for explanation. No doubt, distros will eventually pick up ZXID and provide it as a package. Once that happens they will solve any path issues accoring to their disto policy and that is fine, just do not ask me to comply with any such policy. 97.2.2 FreeBSD ~~~~~~~~~~~~~~ No target available on Makefile, but a port is available from http://www.freshports.org/security/zxid/ 97.2.3 Solaris (Sparc) ~~~~~~~~~~~~~~~~~~~~~~ make TARGET=sol8 make TARGET=xsol8 # Cross compile for Solaris (e.g. on Linux host) 97.2.4 MacOS X (PowerPC?) ~~~~~~~~~~~~~~~~~~~~~~~~~ make TARGET=macosx 97.2.5 Windows Using MinGW ~~~~~~~~~~~~~~~~~~~~~~~~~~ make zxid.dll TARGET=xmingw # Cross compile on Linux host (best supported) make zxid.dll TARGET=mingw # Native compile for mingw target in Cygwin environment Eitherway, the net result is native Windows DLL that does not have Cygwin library dependencies or GPL encumberation. See Makefile for further mingw notes. 97.2.6 Windows Using Cygwin ~~~~~~~~~~~~~~~~~~~~~~~~~~~ make TARGET=cygwin Very experimental (as of Oct 2007) native build for Cygwin. Cygwin appears to not have neither flock(2) nor lockf(2). This is strange because at least one of these is implemented on MinGW. Current workaround is to define flock() to be empty macro. This of course means there is no file locking. There are 3 known races where things can go wrong 1. Audit logs can get garbled. This does not stop ZXID from working, but may make log analysis more complicated. 2. Auto-CoT metadata writes can get garbled. This is very unprobable, but if it happens, the ZXID deployment will not work towards affected IdP. Nothing to worry about really. 3. Locking is used to protect against updates of zxid.conf while zxid is running. Again any corruption is very unlikely. Nothing to worry about. The results of Cygwin compile may be GPL encumbered due to libraries. 97.2.7 Windows Using MSVC ~~~~~~~~~~~~~~~~~~~~~~~~~ Never been done (as of Oct 2007), but probably this is not very difficult given that MinGW port already has addressed many Windows platform issues. Please send any success reports, and receipes, my way. 97.3 Configuration Questions ---------------------------- 1. Q: In mod_auth_saml, what is the relation between ZXIDConf and httpd.conf? A: httpd.conf can contain ZXIDConf directives. Those directives are processed as if they came from /var/zxid/zxid.conf file (which is processed first, before and ZXIDConf directives), except that if you specify ZXIDConf "PATH=/your/path", this triggers reporcessing of the zxid.conf (from the new path). 2. Q: In mod_auth_saml, what is the relation between the +port+ in ZXIDConf and the +port+ in the httpd.conf? A: The ports must agree. ZXID configuration must match the way the Apache layer is configured. 3. Q: Multiple roles of same entity, acting as SP, WSC, and WSP for different services Asa: > Part of what you are saying is that the service > registration is WSC. This is rather confusing since the case is a WSP > acting as a WSC of the Discovery Service. For the ClientLib thus far, > I have chosen to think of service registration as a WSP to WSP. What is > the downside to this approach? Conor: > Service registrations can't be done WSP to WSP with any Liberty protocol > (in fact, we don't define any such method of invocation as the invoking > party is always a WSC for the intent of that message - there's no > problem with a WSP in turn being a WSC of another service instance, just Right. You can don WSC role whenever convenient. There is nothing confusing about WSP of one service being WSC of another service. Perhaps the confusion would be avoided if everybody fully qualified their descriptions until common convention about less than fully qualified roles emerges. Entity E1, an ID-DAP WSP (primary role), will act as Discovery WSC (secondary role) to perform metadata registration. This same entity E1 will also have SP interface (another secondary role) which allows the user to trigger discovery association, again E1 acting in secondary role of Discovery WSC. No confusion as far as I can see. 97.3 Common Mistakes -------------------- 1. When I try accessing https://sp1.zxidsp.org:8443/zxidtest.sh nothing happens! Assuming you have the web server correctly running, the most common gotcha is that zxidhlo has dynamic linking problem. See <> subsection "Dynamic Linking Problems", for explanation and resolution. 2. Single Logout does not end the IdP session (i.e. IdP does not force you to supply password when you do SSO next time). Usual cause is that the management form (the one with the SLO buttons) does not have correct or any session ID. Do a view source on the the page and look for field called "s". The session ID is supposed to be extracted from the Single Sign-On result. For zxid_simple() you need to parse the returned LDIF and take the sesid. Pass that to zxid_fed_mgmt() as second argument. 3. Login buttons do nothing. A possible cause is that the entity ID is not passed from the IdP selection form. If the form is using POST method, you must make sure you actually read the HTTP body and pass its contents to the zxid_simple() as the ~qs~ argument. 4. The SP Login, a.k.a. IdP selection, page shows, but SSO does not work a. Your configuration does not match actual URL used to access the zxid system. For the zxidhlo family of examples you MUST edit the configuration string to match your situation. Watch out for domain name and port number. b. Connectivity issue prevents IdP from fetching metadata. Make sure your domain name is resolvable at IdP (e.g. add it to /etc/hosts). See also next point. c. IdP is not configured to get your metadata automatically. You have to configure your metadata to the IdP manually. How to do this depends on IdP product. Do not ask us. d. You supplied IdP URL that, in fact, is not the well known location for fetching IdP metadata. Or the IdP does not have well known location enabled. In the latter case you will need to install the IdP metadata manually (*** procedure to be documented). See [SAML2meta] section 4.1 "Publication and Resolution via Well-Known Location", p.29, for normative description of this method. e. Connectivity issue at web browser level. Make sure your web browser can resolve both SP and IdP domain names. Edit /etc/hosts as needed on the machine where the browser runs. f. Personal firewall blocks access. Check firewall set up on * browser machine * SP machine * IdP machine 5. The SP Login, a.k.a. IdP selection, page does not show at all a. Connectivity issue at web browser level. Make sure your web browser can resolve both SP and IdP domain names. Edit /etc/hosts as needed. b. Personal firewall blocks access. Check firewall set up on * browser machine * SP machine c. You deployed the zxid in some other URL than you thought. Double check your webserver or servlet container configuration and be sure you understand where zxid is supposed to appear. Be sure you are editing the right configuration - some people run multiple web servers in their machine and get confused about which one actually is active on which port and where the configuration files are located. d. ZXID lacks execute permissions, dynamic link libraries are missing (use "ldd zxid" to check), or CGI permission setup prevents it from running. See previous bullet. 6. Mystery configuration problems. Double check /var/zxid/zxid.conf or consider removing it if you do not understand what it does. Double check the conf string if using zxid_simple() interface. 7. Writes a user... > Once it has been compiled, I copied the files zxidhlo.php and zxid.php > to /var/www/zxid (my webroot). I accessed zxidhlo.php?o=E with my browser > and I saw a page asking for IDP metadata. But when I looked at > the /var/log/apache2/error.log, I found these: > > tb77f96c0 zxidmeta.c:352 zxid_get_ent_by_sha1_name zxid d Trying > sha1_name(cot) open (vopen_fd_from_path): No such file or directory Did you create the /var/zxid hierarchy (make dir) and make sure your web user (nobody?) has write permission to the ~log~ directory? Or did you configure it to use some other directory than /var/zxid? 8. What is this /var/zxidcot directory? It is supposed to be /var/zxid/cot When configuring PATH, did you forget trailing slash? E.g. "PATH=/var/zxid&URL=..." # WRONG! "PATH=/var/zxid/&URL=..." # Right 9. If configuration appears to be prematurely truncated, then see if you need to adjust ZXID_MAX_CONF (default 4KB) in zxidconf.h and recompile. 97.3.2 Doubts ~~~~~~~~~~~~~ How to decode auto_flags 0x1d54 1 = debug; d = FORMT + FORMF + MGMTC; 5 = METAC + LOGINC; 4 = SOAPC 97.4 Consent ------------ A frequent concern among the business people and lawyer types is whether the architecture provides for consent by the user. Usually this is related to (avoidance of) liability. If the system can be said to have gathered the consent of the user, we are safe. Unfortunately the standards do not mandate an uniform user interface, thus there is no single specific way how the consent is gathered or determined: it depends from business situation and application to another. Fortunately the Liberty and SAML 2.0 architectures provide plenty of ways and hooks to gather and convey the consent. Consider the following: 1. When arriving to SP, user chooses IdP for SSO. This act of course manifests user's intent to perform SSO. 2. IdP can ask the user whether he wants to perform SSO to the SP (IdP can make this question even if user is already logged in to the IdP, though most demos omit the question in the already logged in case). At this point the IdP may also ask whether the user wants to create a federation so that the SP can track the user. Creating federation is consenting to be tracked by the SP. If the federation already exists, the IdP can still offer a choice: should the federation be used this time, i.e. does the user consent to be tracked this time specifically. If user does not consent to federation and use of federation this time, but still consents to SSO, the SSO will be made using a temporary name ID. 3. If user gives any Personally Identifying Information to the SP (beyond the federated pseudonym), then the SP may be able to "connect the dots" and correlate user's actions on the SP with his actions in some other systems (technically this is called collusion). In a very technical sense users should be aware of this risk or the implication and therefore by providing such information they are effectively consenting to be correlated across systems. However, lawyers would probably say that if the SP intends to correlate, it should state so to the user at the time the information is asked so that the user can make an informed decision. If, after being informed, the user still supplies the information, then user is clearly consenting to the information being used for the stated purpose, i.e. correlation. 4. When user starts to use an ID web service, the user is consenting to this service being visible to at least some parties (why use the service if you did not intend this). To make this consent explicit, the user interface of the ID Web Service can ask. Also, the Discovery Service can ask consent using the Liberty Interaction Service. It is quite appropriate for the DS to ask this consent because it allows the ACL to be set correctly right from the beginning, when the service is registered. 5. When the user later accesses an SP that needs to contact an ID Web Service, it could be construed that the user, by using the SP at all, is effectively consenting that the SP may access the ID Web Services of the user. If this is not enough, the Discovery Service can use the Interaction Service on per service invocation basis to ask if the user consents to the specific request. Finally, the actual ID Web Service can also invoke the Interaction Service to ask the user to consent to the specific request, or otherwise enforce its policies. 6. When using People Service, the inviter (Alice) consents to the access by the invitee (Bob) by requesting an invitation string from the system. Once the invitation has been sent (and accepted by invitee) there is no easy way to collect consent from inviter on per request basis. For example Alice may not be online at the time when Bob accesses her resource. Alice can later revoke Bob's invitation, but in the window between Alice sending the invite and revoking it, Bob can access Alice's resource without Alice actively consenting to every access. Of course the resource can implement ACL policies, like only allowing Bob to access the resource a limited number of times, such as once. 7. When the invitee (Bob) uses inviter's (Alice's) ID Web Services (resources), Bob has consented to some form of tracking by Alice's resources by accepting the invite. Further consent may be obtained by Bob's own IdP, see bullet 2. 97.5 Deployment Planning ------------------------ Here is a rudimentary decision tree for deployment planning 1. List your applications a. Any provided by external partner? b. Non web apps 2. Document your existing identity stores and approaches to a. User provisioning (when someone is employed) b. Application provisioning (when someone starts using app) c. Authorization: how do you know who is supposed to be doing what? d. Deprovisioning: what happens when someone is fired? e. Login? Per app? Harmonized user names? Enterprise SSO? 3. Document your goal: federated SAML SSO über alles :-) a. Do you want to run IdP? b. Could you out-source IdP? c. Will your partners / customers be running their own IdPs? d. Will you participate (or run) single CoT or do you need to consider cross CoT inter-operation (e.g. IdP proxying) To be continued... 97.6 Use of Signing and Crypto, Security Concerns ------------------------------------------------- 97.7 Vendor products -------------------- 97.7.1 Symlabs Federated Identity Suite (SFIS) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Metadata import to IdP? What I usually do is cd /opt/SYMfiam/3.0.x/conf/symdemo-idpa echo 'sp: zxid-sp1$https://sp1.zxidsp.org:8443/zxid?o=B$$' >>cot.ldif Double check with text editor that the file is sensible. Note that the single quotes are essential as the dollars are to be interpretted literally, as separators. cd pem wget https://sp1.zxidsp.org:8443/zxid?o=B >zxid-sp1.xml Here the intent is to fetch the metadata from the SP and store it in a file whose name (without .xml extension) matches the first component of the sp: line. I am not 100% on the wget syntax. You can also use browser to fetch the metadata and simply Save as under the correct name. cd /opt/SYMfiam/3.0.x/conf/symdemo-idpa/start.sh restart This should restart the IdP server process and cause a refresh of the metadata it may have cached. You may want to tail -f /opt/SYMfiam/3.0.x/conf/symdemo-idpa/log/debug.log to see if its getting indigestion. N.B. FIAM seems to have NameID encryption on by default, Turn this off by editing slimidp.ldif: encnids: 0 If this is not done, the SSO will fail (with what appears like signature error).1x 97.8 Known Bugs --------------- Following are known limitations. We document them here because we do not plan to fix them in near-to-medium future. 1. Unknown XML attributes are not sorted according to rules of exc-c14n. Instead they appear always +after+ known XML attributes and in the order they happen to be in the linked list. *Work around:* Add the attribute to schema (.sg) and regenerate and rebuild. 2. XML canonicalization a. CRLF to LF b. Single quote to double quote c. Special character handling, entity escapes? 97.9 Mysterious Error Messages ------------------------------ "Random number generator not seeded!!!" This warning indicates that randomize() was not able to read /dev/random or /dev/urandom, possibly because your system does not have them or they are differently named. You can still use SSL, but the encryption will not be as strong. Investigate setting up EGD (entropy gathering daemon) or PRNG (Pseudo Random Number Generator). Both are available on the net. "msg 123: 1 - error:140770F8:SSL routines:SSL23_GET_SERVER_HELLO:unknown proto" SSLeay error string. First number (123) is PID, second number (1) indicates the position of the error message in SSLeay error stack. You often see a pile of these messages as errors cascade. "msg 123: 1 - error:02001002::lib(2) :func(1) :reason(2)" The same as above, but you didn't call load_error_strings() so SSLeay couldn't verbosely explain the error. You can still find out what it means with this command: /usr/local/ssl/bin/ssleay errstr 02001002 97.9.1 Password is being asked for private key ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is normal behaviour if your private key is encrypted. Either you have to supply the password or you have to use unencrypted private key. One way to remove password is openssl rsa -in key.pem -out keyout-nopw.pem For this to work, key.pem must contain only the private key. On the other hand, for ZXID to work, the file must have both certificate and private key. You will need to use your favorite text editor to split them for password removal and then to join them for use. Scan OpenSSL.org for the FAQ for full explanation on how to remove password from the private key. 97.9.2 Quick command for looking at certificate ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sometimes you get warning messages (in browser) or signature validation errors (in IdP end) because the Subject field of the certificate does not match your actual domain name. You can check this with openssl x509 -text also results in error: "SN1_get_object:header too long". Curiously, > these certificates still seem to work for encryption and decryption, > as witnessed using the procedure for checking whether certificate > and private key belong together (see below). 97.9.3 Self signed certificate ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ZXID ships with zxid.pem which gets by default copied to /var/zxid/pem under various different names. This is fine for testing, but disastrous for production or security sentitive use as the private key corresponding to zxid.pem certificate is of public knowledge (it is distributed with every copy of ZXID) - it offers no security and no non-repudiation what-so-ever. For production or security sensitive install you need to either 1. Obtain certificates from an official certification authority, usually a commercial one. ZXID uses same certificate format as Apache (i.e. the pem format), so aquiring certificates is easy. Or, 2. Generate your own certificate. The simplest case is a self signed certificate: openssl req -new -x509 -nodes -keyout pkey.pem -out cert.pem cat cert.pem pkey.pem >/var/zxid/pem/ssl-nopw-cert.pem The cat step is there because you need to supply both certificate and the private key in same file for ZXID to understand it. > Warning: Although ZXID wants to see the private key in the same > file as the certificate, you MUST NOT give this concatenated > file to any outsider. Others have legitimate need to know your > certificate, but they MUST NOT know your private key. If they > ask, you should take special care to delete the private key from > the file prior to giving it to them. Often those who need to > get your certificate, actually need your metadata: just tell them > to fetch it from the Well Known Location URL (i.e. the Entity ID > of your SP). ZXID will never leak the private key to the metadata. 97.9.4 Checking that cert and private key belong together ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can use openssl(1) tool to test if a keypair is good echo boo >foo openssl rsautl -encrypt -pkcs -in foo -out foo.enc -inkey enc-nopw-cert.pem openssl rsautl -decrypt -pkcs -in foo.enc -out foo2 -inkey enc-nopw-cert.pem diff foo foo2 97.9.4 snprintf() multibyte character related errors in log ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is due to locale setting. Try export LANG=C This will disable any UTF-8 processing in sprintf(). 97.9.5 My own messages are redirected back to me ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In several SAML profiles a HTTP redirect is performed to send the user to other party, usually with a request or response in the query string. A mysterious error is when you see yourself receiving as input the stuff that was supposed to be sent to the other. The way this happens is if for some reason the other party's URL can not be determined, then the ~Location~ header will only consist of the query string that you are trying to send. Without domain name part of the URL, the browser will redirect back to the web site where the redirection came from. This is called "local redirect" and is usually the cause of you receiving your own output as input. To fix this, make sure you have the other site's metadata and make sure it parses and loads correctly. If that does not resolve the problem, see if the metadata has any binding for the operation you are trying. No binding will result in no URL. 97.9.6 SSL Handshake Fails ~~~~~~~~~~~~~~~~~~~~~~~~~~ Possible causes: 1. No common ground in terms of cipher suites. While venerable SSL2 cipher suite ~SSL_RSA_WITH_RC4_128_MD5~ works with legacy browsers, some "modern" software do not support this out of box. [SAML2conf], sections 4 "XML Digital Signature and XML Encryption" and 5 "Use of SSL 3.0 and TLS 1.0" mandate some algorithms, but unfortunately these do not overlap with SSL2 legacy use. The latter section mandates ~TLS_RSA_WITH_3DES_EDE_CBC_SHA~ and ~SSL_RSA_WITH_3DES_EDE_CBC_SHA~. 2. To make matters more complex, enabling all cipher suites at once has historically caused some vendor implementations of SAML2 to fail. As bugs get fixed, this may improve. (Information as of late 2007.) 3. OpenSSL SSLv23 autonegotiation mode (which can also autonegotiate to TLSv1) works mostly great, but some vendor implementations of SSL have trouble with this. Apparently the only way to work around them is to disable the autonegotiation and hardwire the protocol to TLSv1 or SSL3. (Information as of OpenSSL 0.9.8g, Feb, 2008) 4. On some platforms wireshark(1) network sniffer reports TCP layer checksum errors even when communications work fine. This is probably a bug in those wireshark(1) installations (as of early 2008, unknown version, difficult to reproduce). Please note that getting TCP checksums wrong at user land networking code is (nearly?) impossible. It would have to be a kernel bug - and that is unlikely. > wireshark(1) hint: Right click on SSL packet and choose "Decode as..." > and then select SSL, to see the details of SSL/TLS handshake > that can cause the handshake failures. 5. Some implementations of SSL report handshake failure when they are unable to establish trust in certificate. Technically this is a trust failure and the handshake happened OK - they just report it in a misleading way. The fix is to either supply appropriate CA certificate (chain for multilevel) or relax the trust check. 6. One sure fire way to break SSL or signing is to mismatch certificate and private key. Sometimes this happens due to practise of storing certificate, its CA certificates, and the private key in the same file. If you suspect this, just remove the CA certificates so that the PEM file only contains certificate and its private key. Also any manual key exchanges are much more prone to error than ones effectuated via automatically generated metadata. Finally, sometimes the OpenSSL PEM parsing code can not digest line endings that are not natural for the platform. On Unix machines, running dos2unix(1) on the PEM file may help. To adjust cipher suite and SSL version parameters, first see http://www.openssl.org/docs/apps/ciphers.html and the man page for SSL_CTX_set_cipher_list(3) at http://www.openssl.org/docs/ssl/SSL_CTX_set_cipher_list.html Then diagnose whether adjustment is needed in SSL server or client role: a. For SSL server role, you need to consult the documentation of the web server under which you run ZXID. b. For SSL client role, you need to consult the documentation for the libcurl(3) package, which uses OpenSSL in its guts. > Note: ZXID and Apache (the web server you are likely to use) > use main stream OpenSSL package for SSL and crypto. While > bugs are always possible, OpenSSL is considered stable. Many > newer implementations of SSL or crypto, e.g. for Java, are not > nearly as well tested and are more likely to contain bugs or > limitations. Many vendors do not disclose what SSL or crypto > code base they use - you should suspect the worst. 97.10 Author's Pet Peeves ------------------------- 1. What is Schema Grammar (.sg) and why are you using it? * Schema Grammar is a compact formal description of XML documents. It is mostly bidirectionally convertible to XML Schema (XSD) and captures the useful essence of most XML schemata. * Schema Grammars are intuitive and compact, often allowing the essence to be understood at glance, and even most complex cases being only about 50% of the volume of the corresponding XSD. * We use Schema Grammar descriptions because they are more human readable than XSD and still equally amenable to automated code generation. * Schema Grammar descriptions are usually converted using xsd2sg.pl, which is part of the PlainDoc distribution. * See http://mercnet.pt/plaindoc * N.B. You do not need xsd2sg.pl or PlainDoc if you just want to compile and use ZXID. 2. What is PlainDoc (.pd)? * PlainDoc is a document preparation system that uses intuitive plain text files with minimal markup to generate PDF and HTML outputs. * We use PlainDoc because it makes it easy to maintain documentation. * See http://mercnet.pt/plaindoc * N.B. You do not need PlainDoc if you just want to compile and use ZXID. 3. How come zxid is so heavy to compile? * SAML 2.0 and related specs have a lot of functionality and detail, even if you really only need 1% of it. We do not wish to arbitrate which functionality is best or most needed, so we simply provide it all. * A lot of the code is generated, thus the input for C compiler is well in excess of half a million lines of code (of which only about 6k were written by a human). * Some of the generated files are gigantic, e.g. Net/SAML/zxid_wrap.c is over 380k lines. Compiler has to process all of this as a single compilation unit. * gcc and gnu ld were, perhaps, not designed to process this large inputs efficiently. Often the implementation strategy of keeping everything in memory will cause a smaller machines to swap. * My 1GHz CPU, 256 MB RAM machine definitely swaps and thus takes about 45 minutes to compile all this stuff. * I recommend at least 1GB RAM and 3GHz CPU for development machine. On such machine, you should be able to build in about 10 min. 4. Why do you not use ./configure and GNU autoconf? * ~autoconf~ is not for everyone. World does not stop without ~autoconf~. Or indeed need ~autoconf~. It is Yet Another Dependency I Do Not Need (YADIDNN). * I find the GNU ~autoconf~ stuff much more difficult to understand than my own ~Makefile~. Why should I debug ~autoconf~ when I could spend the time debugging my ~Makefile~ or the actual code? * I find resolving problems much easier at source code and ~Makefile~ level than trying to debug a million line script generated by some system I do not understand (perhaps some hardcore ~autoconf~ advocate could try to convince me and educate me, but I doubt). * My policy is to only support systems I have first hand experience with, or I have trustworthy friends to rely on. It does not help me to have a system that tries to guess +gazillion irrelevant variables+ to an unpredictable state. It's much easier to stick to standards like POSIX and make sure you have predictable results from predictable inputs. * If the deterministic and predictable results are wrong, they can at least be debugged and fixed with a finite amount of work. * Supporting all relevant systems manually is not that much of work. The inhabitants of the irrelevant systems can support themselves, probably learning a great deal on the side. 97.11 What does ZXID aim at - an answer --------------------------------------- A recent (Sept 2006) conversation that touched on the aims of ZXID project: > So just generally, what are your goals for it, are you interested in making > it work well with what other people are producing (e.g. SAML -> WSF > cross-over), etc? I'm certainly assuming the answer's yes to that. I aim at full stack client side implementation. ID-FF, SAML 2.0, WSF (both versions). The generation technique I use will yield the encoders and decoders for both WSP and WSC, but the hand written higher level logic will at first be only written for SP and WSC. Some WSP support has now been written as well (complete WSP support was completed as of July 2007). It is Apache licensed project, of course, so if someone contributes the IdP and WSP capabilities, I'll merge them into the distribution. I am interested to have it working with other people's code at 3 levels: 1. Over-the-wire interoperability 2. I have split the functionality of the SP from the WSC such that zxid SP could probably be used with someone else's WSC and someone else's SP would reasonably easily be able to use zxid WSC. 3. Interfaces to non IdM parts of the complete system, typically used to implement the application layer, shall be plentiful: C/C++ API, Net::SAML/mod_perl, php - whatever you can SWIGify. One thing I am NOT interested in is "layered" stack. I strongly believe it's better each vertically integrated slice is implemented by one mind. Thus, except for lowest HTTP, TLS, and TCP/IP layers, my SP, or WSC, or WSP, handles the whole depth of the stack - SOAP, signature, and app interface layers (of course the actual app should be its own layer and probably user written). That is by design. I have found in practise that if you attempt a layered stack, you have impedance mismatches between the modules at different layers because they were designed and written by different minds. By having vertical integration I avoid impedance mismatches. This is the reason why monolithic TCP/IP implementations tend to be better than explicitly layered, such as the streams approach. Now, if someone else wanted to take my generated encoders and decoders and use them as a "layer" in their layered stack, I guess I would not have any issue. If you do that, please let me know because I would have to commit to API stability at that layer. I am willing to do that once there are real projects that depend on it, but until then I still may redesign those APIs, after all, I am at revision 0.4 :-) In the end, it seems that ZXID is actually somewhat layered approach - what I mean by "vertical integration" is that all the layers are designed and controlled by the same mind. > BTW, I gather that it's SAML 2.0 at the moment, which I can't offer any test > capability for, but if you get to SAML 1.1, I'm happy to set up some kind of > IdP test capability for that. In SSO world SAML 1.1 and ID-FF 1.2 capabilities are definitely on the road map. In ID-WSF world, I'll probably start with 2.0 DS-WSC (don't we all) followed by ID-DAP WSC and then tackle 1.1 after that.<> 97.12 Annoyances and improvement ideas -------------------------------------- There is a lot of commonality that is not leveraged, especially in the way service end points are chose given the metadata. The descriptors are nearly identical so casting them to one should work. Many of the SAML2 responses are nearly identical. Rather than construct them fully formally, we could have just one "SAML any response" function. Perhaps this could be supported by some schema grammar level aliasing feature: if an element derives from base type without adding anything at all of its own, we might as well only generate code for the base type. Namespace aliasing scheme would allow us to consider two versions of schema the same. It seems to be fairly common that the schema changes are so minor that there is no justification for two different decoding engines. 97.13 Non-obvious SAML ---------------------- 1. Destination XML attribute is needed in redirect and POST bindings. 2. Assertion//SubjectConfirmationData/@InResponseTo XML attribute is needed in SSO assertions, unless the SSO was unsolicited. SAML is not very explicit about this, [SAML2core], ll.729-732 describes it as optional, but [SAML2prof], ll.580-582 and ll.559-560 seem to imply this requirement. 3. Some deployments use POST binding for many more things than officially sanctioned by SAML [SAML2conf], Table 1 "Possible Implementations", p.6. None of the offical profiles, see [SAML2conf], Table 2 "Feature Matrix", p.9, require support for POST for sending or receiving Single Logout or Manage NameID requests. Nor is sending AuthnRequest using POST officially sanctioned. Using artifact profile for anything else than fetching the SSO assertion is not official. Never-the-less, some of these bindings are perfectly implementable and some deployments actually use them. ZXID may support some of them, especially the POST bindings, if it is easy to do so, but we make no commitment beyound official SAML conformance. 4. In SAML SOAP bindings it is bit unclear if the caller needs to be authenticated. Currently ZXID solves this by signing the SOAP requests (see SSO_SOAP_SIGN configuration options). Other approaches are using HTTP Basic authentication, using Client-TLS, or simply not authenticating the peer. 5. Interpretation of metadata KeyDescriptor/EncryptionMethod Algos on [SAML2conf], section 4.2 "XML Encryption Algorithms", ll.252-253. The interpretation in [SAML2meta], section 2.4.1.1 "Element ", ll.621-624, p.16, and the example on l.1117. Since the can appear several times, it would seem reasonable to specify it once for assymmetric crypto and once for symmetric crypto. If specified, then for each of the cases, only one of the allowed algos may be used. If not specified, then any algo authorized in [SAML2conf] is allowed. If specified, but the algo is not authorized by [SAML2conf], then implementation is nonconformant. 6. The selection of protocol binding for return path of SSO is non-trivial. The Authentication Request may specify any number of parameters like ProtocolBinding or Index. 7. When passing around Name IDs or storing them in database, remember to store all components, including NameQualifier and SPNameQualifier. 8. Single Logout: IdP should not call originator of SLO when it is logging out everybody. 97.14 Best Practises -------------------- 1. Each entity chooses its own Entity ID. When you are setting up a SP, you choose your Entity ID and the IdP(s) MUST be able to adapt to your choice. Similarily, an IdP decides its own Entity ID and all SPs MUST be able to adapt to it. 2. Entity IDs MUST be unique within a Circle of Trust (CoT). Given that CoT relationships may change from time to time, its best to choose Entity ID so that it is globally unique. If Entity ID contains a domain name as a component, then the +globally unique+ property tends to be enforced by the domain name allocation system. 3. Entity ID SHOULD be the Well Known Location (WKL), i.e. the URL from which the metadata can be fetched. 4. Providing metadata by URL, ideally by the Entity ID, SHOULD always be enabled. This greatly facilitates configuration. 5. elements should have ~use~ XML attribute 97.15 Cardspace / Infocard / DigitalMe Tutorial ----------------------------------------------- N.B. zxid.org does not yet support Infocard, but since we are starting the investigation, we thought to share some of it 97.15.1 Installing DigitalMe and Firefox plugin ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DigitalMe by Bandit project is an open source Infocard implementation, providing functionality roughly similar to CardSpace. You can download it from http://www.bandit-project.org/index.php/Digital_Me rpm2cpio digitalme-0.4.1238-2.1.i586.rpm | cpio -di 97.15.2 Setting up IdP account ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For one InfoCard aware IdP, please see: http://www.cdatazone.org/index.php?/archives/27-Managed-Infocard-Demo.html 1. Register at the IdP site (e.g. https://www.ctindustries.net/icard/index.php) 2. Download the card ("Retrieve Managed Card" link (savea as "cdatamanaged.crd" by default). 3. Install the card to DigitalMe 97.15.9 Legal ~~~~~~~~~~~~~ Microsoft promises to not sue you: http://www.microsoft.com/interop/osp/default.mspx 97.16 Attributes ---------------- Q:: I want to read the attributes that come in the assertion. how do I do that? A:: You get attributes back as an LDIF entry as return value of zxid_simple() The attributes are also available by reparsing the assertion, which gets stored in /var/zxid/rely hierarchy. /var/zxid/ses/SuzZQS5Ub/.ses file contains the path to the assertion file. Q:: In the zxid directory you store some users. what does the extension .mni stand for? Why is the info stored? I assume it is some sort of local cache. I would like to store the attributes there too. how do I do that? A:: The .mni file is used to support Manage NameID requests. In normal operation of ZXID it really is not needed, but to support some of the SAML conformance test requests it is needed. Rather than store attributes in that directory, I'd suggest reparsing the assertion when you need them. But if you must, you could create a file of your own in that directory. We of course need a naming convention that prevents naming conflicts with future versions of ZXID: Your file extension should start by ".x-", for example: "attributes.x-attr" Q:: The ldif returned by zxid_simple() is perfect for my needs. but nothing is being stored in log\rely directory. could be some configuration issue? also, can I have zxid automatically store the ldif file returned zxid_simple()? A:: The log/rely should be populated by default, but if the directory structure itself is missing, may be it does not work. Try make dirs. Or check that web server user's permissions allow writing there. A:: Re ldif cached: the logic is supposed to be that the zxid_simple() will be called to protect every page, therefore its return value is available on every page. If you do not call it every time, but instead bootstrap some sort of app specific session, then you would strore the LDIF (or the attributes parsed out of it) to that app specific session. 98 Support ========== 98.1 Mailing list and forums ---------------------------- Mail the author until we get the list set up. Or volunteer a list :-) 98.2 Bugs --------- Mail the author until we get bug tracking set up. Or volunteer. 98.3 Developer access --------------------- We use CVS, but access needs to be manually configured and is not anonymous. If you contribute significantly, I will bother. Others can send patches (good way to show you are worthy of CVS access) to me. I've heard some mixed experiences about open source sites like sourceforge. If you run such site and want to host ZXID Project, please contact me. If you just always want the latest source: get the tar ball from the downloads section. Trust me, this is still so much in flux that only the tar ball snapshots are in any usable state. CVS access just to get latest source would be pointless. 98.9 Commercial Support ----------------------- Following companies provide consultancy and support contracts for ZXID: * symlabs.com 99 Appendix: Schema Grammars ============================ Large parts of ZXID code are generated from +schema grammars+ which are a convenient notation for describing XML schmata. This chapter gives a sampling of some schema grammars that are currently implemented and distributed in the ZXID package. For fuller list, see sg subdirectory of the distribution or schemata.pd file. < %tt Arrow (->) signifies reference to type that defines element or attribute xx: ... ; Colon (:) means that the definition of type follows immediately ee An element or attribute by itself means exactly one occurance is expected ee? Question mark (?) means the element or attribute is optional ee* Asterisk (*) means the element may appear from zero to infinite number of times (same as * in regular expressions) ee+ Plus (+) means the element must appear at least once, but may appear an infinite number of times (same as + in regular expressions) ee{x,y} The element must appear between x and y times (same as in regex) ee | ee The pipey symbol (|) means elements are mutually exclusive choices. ee ee Concatenation of elements or attributes means sequence base( t ) Introduce Extension base type (derive a type) redef( .. ) Redefine a type (using construct) mixed(1) Mark a complex type as having mixed content type, i.e. strings and elements alternate enum( ... ) Introduce enumeration of xs:strings any xs:any, the XML arbitrary element extension mechanism @any xs:anyAttribute, the XML arbitrary attribute extension mechanism target( ... ) Define target namespace described by the schema import( ... ) Bring in other schemata and namespaces ns( ... ) Declare existence of another namespace (without importing it) >> <> 99.1 SAML 2.0 ------------- 99.1.1 saml-schema-assertion-2.0 (sa) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <> >> 99.1.2 saml-schema-protocol-2.0 (sp) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <> >> 99.1.4 saml-schema-metadata-2.0 (md) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <> >> 99.5 Liberty ID-WSF 2.0 ----------------------- 99.5.1 liberty-idwsf-utility-v2.0 (lu) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <> >> 99.5.3 liberty-idwsf-soap-binding-v2.0 (b) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <> >> 99.5.4 liberty-idwsf-security-mechanisms-v2.0 (sec) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <> >> 99.5.5 liberty-idwsf-disco-svc-v2.0 (di) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <> >> 99.5.7 id-dap (dap) ~~~~~~~~~~~~~~~~~~~ <> >> 99.5.8 liberty-idwsf-subs-v1.0 (subs) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <> >> 99.5.9 liberty-idwsf-dst-v2.1 (dst) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <> >> 99.6 SOAP 1.1 Processor wsf-soap11 (e) -------------------------------------- <> >> 99.7 XML and Web Services Infrastructure ---------------------------------------- 99.7.1 xmldsig-core (ds) ~~~~~~~~~~~~~~~~~~~~~~~~ <> >> 99.7.2 xenc-schema (xenc) ~~~~~~~~~~~~~~~~~~~~~~~~~ <> >> 99.7.3 ws-addr-1.0 (a) ~~~~~~~~~~~~~~~~~~~~~~ <> >> 100 Appendix: Some Example XML Blobs ==================================== These XML blobs are for reference. They have been pretty printed. Indentation indicates nesting level and closing tags have been abbreviated as "". The actual XML on wire generally does not have any whitespace. 100.1 SAML 2.0 Artifact Response with SAML 2.0 SSO Assertion and Two Bootstraps ------------------------------------------------------------------------------- This example corresponds to t/sso-w-bootstraps.xml in the distribution. Both bootstraps illustrate SAML assertion as bearer token. https://a-idp.liberty-iop.org:8881/idp.xml https://a-idp.liberty-iop.org:8881/idp.xml https://a-idp.liberty-iop.org:8881/idp.xml r8OvtNmq5LkYwCNg6bsRZAdT4NE= GtWVZzHYW54ioHk/C7zjDRThohrpwC4= PB5fLIA4lRU2bH4HkQsn9 https://sp1.zxidsp.org:8443/zxidhlo?o=B https://a-idp.liberty-iop.org:8881/idp.xml dqq/28hw5eEv+ceFyiLImeJ1P8w= UKlEgHKQwuoCE= https://sp1.zxidsp.org:8443/zxidhlo?o=B urn:oasis:names:tc:SAML:2.0:ac:classes:Password Sue https://a-idp.liberty-iop.org/profiles/WSF1.1/RID-DISCO-sue urn:liberty:disco:2003-08 https://a-idp.liberty-iop.org:8881/idp.xml urn:liberty:security:2005-02:TLS:Bearer CREDOTGAkvhNoP1aiTq4bXBg https://a-idp.liberty-iop.org:8881/DISCO-S Symlabs Discovery Service Team G https://a-idp.liberty-iop.org:8881/DISCO-S SYMfiam Discovery Service https://a-idp.liberty-iop.org:8881/idp.xml urn:liberty:disco:2006-08 urn:liberty:security:2005-02:TLS:Bearer https://a-idp.liberty-iop.org:8881/idp.xml o2SgbuKIBzl4e0dQoTwiyqXr/8Y= hHdUKaZ//cZ8UYJxvTReNU= 9my93VkP3tSxEOIb3ckvjLpn0pa6aV3yFXioWX-TzZI= https://a-idp.liberty-iop.org:8881/idp.xml urn:oasis:names:tc:SAML:2.0:ac:classes:Password N.B. The AttributeStatement/Attribute/AttributeValue/ EndpointReference/Metadata/SecurityContext/ Token/Assertion/Conditions/AudienceRestriction/Audience is the same as the IdP because in many products the IdP and Discovery Service roles are implemented by the same entity. Note also that the audience of the inner assertion is the discovery service where as the audience of the outer assertion is the SP that will eventually call the Discovery Service. 100.2 ID-WSF 2.0 Call with X509v3 Sec Mech ------------------------------------------ 123 ... urn:xx:Query 2005-06-17T04:49:17Z MIIB9zCCAWSgAwIBAgIQ... ... ... ... ... Ru4cAfeBAB YgGfS0pi56p HJJWbvqW9E84vJVQkjDElgscSXZ5Ekw== The salient features of the above XML blob are * Signature that covers relevant SOAP headers and Body * Absence of any explicit identity token. Absence of identity token means that from the headers it is not possible to identify the taget identity. The signature generally coveys the Invoker identity (the WSC that is calling the service). Since one WSC typically serves many principals, knowing which principal is impossible. For this reason X509 security mechanism is seldom used in ID-WSF 2.0 world (with ID-WSF 1.1 the ResourceID provides an alternative way of identifying the principal, thus making X509 a viable option). 100.3 ID-WSF 2.0 Call with Bearer (Binary) Sec Mech --------------------------------------------------- ... ... urn:xx:Query 2005-06-17T04:49:17Z mQEMAzRniWkAAAEH9RWir0eKDkyFAB7PoFazx3ftp0vWwbbzqXdgcX8fpEqSr1v4 YqUc7OMiJcBtKBp3+jlD4HPUaurIqHA0vrdmMpM+sF2BnpND118f/mXCv3XbWhiL VT4r9ytfpXBluelOV93X8RUz4ecZcDm9e+IEG+pQjnvgrSgac1NrW5K/CJEOUUjh oGTrym0Ziutezhrw/gOeLVtkywsMgDr77gWZxRvw01w1ogtUdTceuRBIDANj+KVZ vLKlTCaGAUNIjkiDDgti= ... ... ... ... ... YgGfS0pi56pu ... 100.4 ID-WSF 2.0 Call with Bearer (SAML) Sec Mech ------------------------------------------------- ... ... urn:xx:Query 2005-06-17T04:49:17Z http://idp.symdemo.com/idp.xml ... U2XTCNvRX7Bl1NK182nmY00TEk== ... http://wsp.zxidsp.org urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport mQEMAzRniWkAAAEH9RbzqXdgcX8fpEqSr1v4= ... A7N123 ... ... ... ... ... *** is the reference above to wsse11:TokenType really correct? Note how the and the attributes are encrypted such that only the WSP can open them. This protects against WSC gaining knowledge of the NameID at the WSP. <> <README ZXID

README ZXID

>> <> <> SAML Open Source catalogs http://saml.xml.org/saml-open-source-implementations http://openliberty.org/wiki/index.php/Existing_Identity_Systems#Open_Source_ http://docs.safehaus.org/display/HAUS/Id+OSS+Map Suspicious: when decrypting elements and plugging their plain text variants into original data structure, the wo pointers are not updated. Thus the "old" encrypted data may remain accessible for some purposes. Pointers from Pat http://rnd.feide.no/2007/04/13/light-bulb-update-request-for-testing/ https://opensso.dev.java.net/public/extensions/index.html Add macros for OK response. http://wiki.oasis-open.org/security/SstcSamlX509AuthnAttribProfile http://wiki.oasis-open.org/security/SimpleSignBinding On CYGWIN lockf() and flock() apparently are not defined. On mingw they are. Way to pass RelayState through zxid_simple() AuditExplorer elgg.org is very relevant for e-Learning / HR-XML market https://imb.phil.uni-augsburg.de/elgg/ FEDORA Moodle (Open Source, Open University) MyStuff (Open Source, Open University) Privacy features of SAML/Liberty User centric features of SAML/Liberty - User control (not necessarily interaction every steps of the way) ECP + IS plugin for Firefox ================== In general, wild card cert is one whose cn field is of form *.cellmail.com The openssl command for creating CSR is 'openssl req', for example > openssl req -new -nodes -keyout pkey.pem -out req.pem Generating a 1024 bit RSA private key ......................++++++ .................................................................................++++++ writing new private key to 'pkey.pem' ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:FI State or Province Name (full name) [Some-State]: Locality Name (eg, city) []:Helsinki Organization Name (eg, company) [Internet Widgits Pty Ltd]:Tietosampo Organizational Unit Name (eg, section) []: Common Name (eg, YOUR name) []:*.tietosampo.fi Email Address []:sampo@iki.fi Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []: In the example above I left the challenge password and company name empty, but it could be that Thawte insists that you fill in something there. They may also have specific requirements about the company name (and possibly the Organization Name and Oraganization Unit Name) matching the registered name of your company. Anyway, the output from the above should be > cat req.pem -----BEGIN CERTIFICATE REQUEST----- MIIBwjCCASsCAQAwgYExCzAJBgNVBAYTAkZJMRMwEQYDVQQIEwpTb21lLVN0YXRl MREwDwYDVQQHEwhIZWxzaW5raTETMBEGA1UEChMKVGlldG9zYW1wbzEYMBYGA1UE AxQPKi50aWV0b3NhbXBvLmZpMRswGQYJKoZIhvcNAQkBFgxzYW1wb0Bpa2kuZmkw gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALudDsX0ZU13ajartg4IECD0+5Lo xSThKu47vQ6GfIeh1+5QO0PCytmrUAI+w0mai9gIp4MssBGqvLs5e2No09ih1KmM 7s8tgXnnexRQ7FsTEVnaZlZ2dgMNO4DYYtRgX+Kxks6hpHLEY0R3VmCVe1BPlkPs 0Y4gP1yDNMXMAO+bAgMBAAGgADANBgkqhkiG9w0BAQUFAAOBgQBSWviTot4mScAi xGlky+UqkYtih0dmqhBBTiiSaVHBerUATKG0p8NkM0NGXuPt8Wozx6t53f8VeXDo BML4SzkoYSrmOkEqk8np8O3IWSG4+HRwhetG/THOvNwRz9shvadPec+VQxJEL2FC vxz/z/oQ8oFxyCwVUtTb4zKhT9rFEw== -----END CERTIFICATE REQUEST----- Or if you want to convince yourself that the wild card is really in there, you can check with > openssl asn1parse On another train of thought, if I was to have a local CA here, could I use the > commercial certificate I get to sign the x509 certificates I would make? The > x509 would be used to sign emails via smart cards. This is not a commercial > project but rather one to learn more about smart cards. Sun has made code > available to manage smart cards so it may be interesting to learn more. The regular SSL certificate usually will not work as CA certificate due to certificate usage indicators. Technically it is possible to ignore such indicators and use the certificate anyway, but a lot of widely distributed software does not ignore them so you would have a lot of interoperability problems or at least confirmation questions. Commercial CAs do issue CA certificates, but they tend to be expensive. Even if you get commercial CA certificate, you should know that some (older) software only supports one level of certificate hierarchy. This problem has surfaced when some commercial CAs tried to structure themselves internally as multi layer CA. If you want to run your own CA, all you really have to do is configure the CA cert of yours to be trusted by all the software. For browsers this is easy enough within the GUI itself. For servers (such as apache or dsproxy), there is a way to do this at config file level. Configuring direct trust to your CA cert tends to be easier than trying to get commercial CA cert and playing multilayer CA games. Re Thunderbird, I am bit surprised that it does not accept self signed certs. It seems more probable to me that it actually can be configured to accept them, but does not ship with that turned on to protect naive users. The most basic way to use self signed cert would be to import the self signed cert as one of the trusted CA certs. Was your problem with Thunderbird not accepting the IMAPS connection? In that case the Thunderbird client software needs to start trusting the self signed cert as CA cert. There is probably a GUI way to do this - probably something very similar to the Firefox GUI for configuring certs. If you were trying to configure a ClientTLS certificate and the IMAPS server refused it, then you need to adjust configuration in the server end, probably in a config file. ----- ZXID CARML stack * frontend API bindings * middle layer routing and mapping engine * backend connectors --Sampo ----- http://saml.xml.org/products http://saml.xml.org/zxid ZXID.org Identity Management toolkit implements standalone SAML 2.0 and Liberty ID-WSF 2.0 stacks. It is a C implementation with minimal external dependencies - OpenSSL, CURL, and zlib - ensuring easy deployment (no DLLhell). Due to its small footprint and efficient and accurate schema driven implementation, it is suitable for embedded and high volume applications. Language bindings to all popular highlevel languages such as PHP, Perl, and Java, are provided via SWIG. ZXID implements, as of July 07, SP, WSC, and WSP roles.