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