1 /* zxidsrvlet.java  -  SAML SSO Java/Tomcat servlet script that calls libzxid using JNI
2  * Copyright (c) 2012 Synergetics (sampo@synergetics.be), All Rights Reserved.
3  * Copyright (c) 2010-2011 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
4  * Copyright (c) 2007-2009 Symlabs (symlabs@symlabs.com), All Rights Reserved.
5  * Author: Sampo Kellomaki (sampo@iki.fi)
6  * This is confidential unpublished proprietary source code of the author.
7  * NO WARRANTY, not even implied warranties. Contains trade secrets.
8  * Distribution prohibited unless authorized in writing.
9  * Licensed under Apache License 2.0, see file COPYING.
10  * $Id: zxidsrvlet.java,v 1.3 2009-11-20 20:27:13 sampo Exp $
11  * 12.1.2007,  created --Sampo
12  * 16.10.2009, refined from zxidhlo example to truly useful servlet that populates session --Sampo
13  * 6.2.2012,   added use of ZXIDConf <init-param> --Sampo
14  * 17.10.2012, added passing ZXID cookies --Sampo
15  *
16  * See also: README-zxid section 10 "zxid_simple() API"
17  * http://www.easywms.com/easywms/?q=en/read-parameters-web-xml-servlet-release-work-programmer
18  */
19 
20 import zxidjava.*;
21 import java.io.*;
22 import java.net.*;
23 import javax.servlet.*;
24 import javax.servlet.http.*;
25 
26 public class zxidsrvlet extends HttpServlet {
27     //static final String conf = "URL=http://sp1.zxidsp.org:8080/sso&PATH=/var/zxid/";
28     static zxidjava.zxid_conf cf;
29     static { System.loadLibrary("zxidjni"); }
30 
31     // setptmcookie=SYNPTM%3Dhttps%3A%2F%2Fpds%2Epersonaldata%2Eeu%3A4444%2Fptm%2Fptm%2Ecgi%3Fl0https%3A%2F%2Fidp%2Ei%2Ddent%2Eeu%2Fsynidp%3D1%3B%20path%3D%2F%3B%20secure
32     // setcookie=SYNSES%3DS8M3juzHFe2NbZE%2DTJC10qw1W%3B%20path%3D%2F%3B%20secure
33     // SYNPTM=https://pds.personaldata.eu:4444/ptm/ptm.cgi?l0https://idp.i-dent.eu/synidp=1; path=/; secure
34     // SYNSES=S8M3juzHFe2NbZE-TJC10qw1W; path=/; secure
35 
pass_cookie_from_str(String setcookie, HttpServletResponse res)36     private void pass_cookie_from_str(String setcookie, HttpServletResponse res)
37     {
38 	if (setcookie == null)
39 	    return;
40 	System.err.print("setcookie("+setcookie+")\n");
41 	int eq = setcookie.indexOf('=');
42 	int semi = setcookie.indexOf(';', eq+1);
43 	if (eq != -1 && semi != -1) {
44 	    Cookie cookie = new Cookie(setcookie.substring(0, eq),
45 				       setcookie.substring(eq+1, semi));
46 	    eq = setcookie.indexOf("path=", semi);
47 	    if (eq == -1) {
48 		cookie.setPath("/");
49 		cookie.setSecure(setcookie.indexOf("secure", semi) != -1);
50 	    } else {
51 		semi = setcookie.indexOf(';', eq+5);
52 		cookie.setPath(setcookie.substring(eq+5, semi));
53 		cookie.setSecure(setcookie.indexOf("secure", semi) != -1);
54 	    }
55 	    res.addCookie(cookie);
56 	}
57     }
58 
59     //public static void main(String argv[]) throws java.io.IOException  {  }
do_zxid(HttpServletRequest req, HttpServletResponse res, String qs)60     public void do_zxid(HttpServletRequest req, HttpServletResponse res, String qs)
61 	throws ServletException, IOException
62     {
63 	// CONFIG: You must have created /var/zxid directory hierarchy. See `make dir'
64 	// CONFIG: To set config string, edit web.xml (hope you know where it is) and
65 	// add to your servlets sections like
66         //  <servlet>
67 	//    <servlet-name>zxidsrvlet</servlet-name><servlet-class>zxidsrvlet</servlet-class>
68 	//    <init-param>
69 	//      <param-name>ZXIDConf</param-name><param-value>PATH=/var/zxid/</param-value>
70 	//    </init-param>
71 	//  </servlet>
72 	// CONFIG: You must edit the URL to match your domain name and port, usually you
73 	// CONFIG: would create and edit /var/zxid/zxid.conf and override the URL there.
74 	if (cf == null) {
75 	    String conf = getServletConfig().getInitParameter("ZXIDConf");
76 	    System.err.print("SSO servlet conf("+conf+")\n");
77 	    cf = zxidjni.new_conf_to_cf(conf);
78 	}
79 	if (req.getParameter("gr") != null || req.getParameter("gl") != null)
80 	    req.getSession(true).invalidate();  // Invalidate local ses in case of SLO
81 	System.err.print("----- Calling zxid_simple\n");
82 	String ret = zxidjni.simple_cf(cf, -1, qs, null, 0x3d54);  // QS response requested
83 	System.err.print("----- ret(" + ret + ")\n");
84 	switch (ret.charAt(0)) {
85 	case 'L':  /* Redirect: ret == "LOCATION: urlCRLF2" */
86 	    res.sendRedirect(ret.substring(10, ret.length() - 4));
87 	    System.err.print("^^^^^^^^^^^^ SSO Servlet Redirect ^^^^^^^^^^^^\n");
88 	    return;
89 	case '<':
90 	    switch (ret.charAt(1)) {
91 	    case 's':  /* <se:  SOAP envelope */
92 	    case 'm':  /* <m20: metadata */
93 		res.setContentType("text/xml");
94 		break;
95 	    default:
96 		res.setContentType("text/html");
97 		break;
98 	    }
99 	    res.setContentLength(ret.length());
100 	    res.getOutputStream().print(ret);
101 	    break;
102         case 'z': /* Authorization denied case (if PDP_URL was configured) */
103 	    System.err.print("Deny (z)\n");
104 	    res.sendError(403, "Denied. Authorization to rs("+req.getParameter("RelayState")+") was refused by a PDP.");
105 	    System.err.print("^^^^^^^^^^^^ SSO Servlet DENIED ^^^^^^^^^^^^\n");
106 	    return;
107 	case 'd': /* Logged in case (both LDIF and QS will start by "dn") */
108 	    HttpSession ses = req.getSession(true);
109 	    String[] avs = ret.split("&");
110 	    for (int i = 0; i < avs.length; ++i) {
111 		String av[] = avs[i].split("=", 2);
112 		ses.setAttribute(av[0], URLDecoder.decode(av.length > 1 ? av[1] : "", "UTF-8"));
113 	    }
114 
115 	    /* Make sure cookie is visible to other servlets on the same server.
116 	     * Alternately you could add emptySessionPath="true" to tomcat conf/server.xml */
117 	    Cookie[] cookies = req.getCookies();
118 	    if (cookies != null) {
119 		for (int i = 0; i < cookies.length; i++) {
120 		    if (cookies[i].getName().equals("JSESSIONID")) {  // MUST match cookie name
121 			cookies[i].setPath("/");
122 			break;
123 		    }
124 		}
125 	    }
126 
127 	    pass_cookie_from_str(ses.getAttribute("setcookie").toString(), res);
128 	    pass_cookie_from_str(ses.getAttribute("setptmcookie").toString(), res);
129 
130 	    System.err.print("Logged in. jses("+ses.getId()+") rs("+ses.getAttribute("rs")+")\n");
131 	    //String rs = URLDecoder.decode(ses.getAttribute("rs").toString(), "UTF-8");
132 	    /* N.B. RelayState was set by app servlet by setting fr query string
133 	     * parameter when it redirected to SSO servlet. The fr qs param was
134 	     * then deflated and safe base64 encoded and sent to IdP as RelayState.
135 	     * It then came back from IdP and was decoded as one of the SSO attributes.
136 	     * The decoding is controlled by <<tt: rsrc$rs$unsb64-inf$$ >>  rule in OUTMAP. */
137 	    String rs = ses.getAttribute("rs").toString();
138 	    if (rs != null && rs.length() > 0 && rs.charAt(rs.length()-1) != '-')
139 		res.sendRedirect(rs);
140 
141 	    /* Redirect was not viable. Just show the management screen. */
142 
143 	    ret = zxidjni.fed_mgmt_cf(cf, null, -1, ses.getAttribute("sesid").toString(), 0x3d54);
144 	    res.setContentType("text/html");
145 	    res.setContentLength(ret.length());
146 	    res.getOutputStream().print(ret);
147 	    break;
148 	default:
149 	    System.err.print("Unknown zxid_simple() response("+ret+").\n");
150 	}
151 	System.err.print("^^^^^^^^^^^^ SSO Servlet Done ^^^^^^^^^^^^\n");
152     }
153 
doGet(HttpServletRequest req, HttpServletResponse res)154     public void doGet(HttpServletRequest req, HttpServletResponse res)
155 	throws ServletException, IOException
156     {
157 	System.err.print("\n============ SSO Servlet GET ============\n");
158 	// LECP/ECP PAOS header checks
159 	do_zxid(req, res, req.getQueryString());
160     }
161 
doPost(HttpServletRequest req, HttpServletResponse res)162     public void doPost(HttpServletRequest req, HttpServletResponse res)
163 	throws ServletException, IOException
164     {
165 	System.err.print("\n============ SSO Servlet POST ============\n");
166 	String qs;
167 	int len = req.getContentLength();
168 	//System.err.print("Got Content-Length="+len+"\n");
169 	byte[] b = new byte[len];
170 	int here, got;
171 	for (here = 0; here < len; here += got)
172 	    got = req.getInputStream().read(b, here, len - here);
173 	qs = new String(b, 0, len);
174 	//System.err.print("Got "+len+" bytes qs("+qs+")\n");
175 	do_zxid(req, res, qs);
176     }
177 }
178 
179 /* EOF - zxidsrvlet.java */
180