1 | #!/usr/bin/env python |
---|
2 | """NDG Security Session Manager .tac file |
---|
3 | |
---|
4 | This file enables the Session Manager web service to be |
---|
5 | called under the Twisted framework |
---|
6 | |
---|
7 | NERC Data Grid Project |
---|
8 | """ |
---|
9 | __author__ = "P J Kershaw" |
---|
10 | __date__ = "23/11/06" |
---|
11 | __copyright__ = "(C) 2007 STFC & NERC" |
---|
12 | __license__ = \ |
---|
13 | """This software may be distributed under the terms of the Q Public |
---|
14 | License, version 1.0 or later.""" |
---|
15 | __contact__ = "P.J.Kershaw@rl.ac.uk" |
---|
16 | __revision__ = '$Id$' |
---|
17 | |
---|
18 | import os, base64 |
---|
19 | |
---|
20 | from logging.config import fileConfig |
---|
21 | try: |
---|
22 | _logConfig = os.path.join(os.environ["NDGSEC_DIR"], |
---|
23 | 'conf', |
---|
24 | 'sessionMgrLog.cfg') |
---|
25 | fileConfig(_logConfig) |
---|
26 | |
---|
27 | except KeyError: |
---|
28 | from warnings import warn |
---|
29 | warn(\ |
---|
30 | '"NDGSEC_DIR" environment variable must be set to enable logging config', |
---|
31 | RuntimeWarning) |
---|
32 | |
---|
33 | import logging |
---|
34 | log = logging.getLogger(__name__) |
---|
35 | |
---|
36 | from ZSI.twisted.WSresource import WSResource |
---|
37 | from twisted.application import service, internet |
---|
38 | from twisted.web.server import Site |
---|
39 | from twisted.web.resource import Resource |
---|
40 | |
---|
41 | from ndg.security.server.SessionMgr.SessionMgr_services_server import \ |
---|
42 | SessionMgrService |
---|
43 | from ndg.security.server.SessionMgr import SessionMgr |
---|
44 | from ndg.security.common.wsSecurity import SignatureHandler |
---|
45 | from ndg.security.server.twisted import WSSecurityHandler, \ |
---|
46 | WSSecurityHandlerChainFactory |
---|
47 | |
---|
48 | |
---|
49 | from ndg.security.common.X509 import X509CertRead |
---|
50 | |
---|
51 | |
---|
52 | class SessionMgrServiceSub(SessionMgrService, WSResource): |
---|
53 | |
---|
54 | # Add WS-Security handlers |
---|
55 | factory = WSSecurityHandlerChainFactory |
---|
56 | |
---|
57 | def __init__(self): |
---|
58 | '''Initialize Session Manager class - encapsulates inner workings |
---|
59 | including session management and proxy delegation |
---|
60 | |
---|
61 | @type ps: ZSI ParsedSoap |
---|
62 | @param ps: client SOAP message |
---|
63 | @rtype: tuple |
---|
64 | @return: request and response objects''' |
---|
65 | |
---|
66 | # Stop in debugger at beginning of SOAP stub if environment variable |
---|
67 | # is set |
---|
68 | self.__debug = bool(os.environ.get('NDGSEC_INT_DEBUG')) |
---|
69 | if self.__debug: |
---|
70 | import pdb |
---|
71 | pdb.set_trace() |
---|
72 | |
---|
73 | WSResource.__init__(self) |
---|
74 | self.sm = SessionMgr() |
---|
75 | |
---|
76 | |
---|
77 | def soap_connect(self, ps, **kw): |
---|
78 | '''Connect to Session Manager and create a user session |
---|
79 | |
---|
80 | @type ps: ZSI ParsedSoap |
---|
81 | @param ps: client SOAP message |
---|
82 | @rtype: tuple |
---|
83 | @return: request and response objects''' |
---|
84 | |
---|
85 | if self.__debug: |
---|
86 | import pdb |
---|
87 | pdb.set_trace() |
---|
88 | |
---|
89 | request, response = SessionMgrService.soap_connect(self, ps) |
---|
90 | |
---|
91 | result = self.sm.connect(username=request.Username, |
---|
92 | passphrase=request.Passphrase, |
---|
93 | createServerSess=request.CreateServerSess) |
---|
94 | |
---|
95 | response.ProxyCert, response.ProxyPriKey, response.UserCert, \ |
---|
96 | response.SessID = result |
---|
97 | |
---|
98 | return request, response |
---|
99 | |
---|
100 | |
---|
101 | def soap_disconnect(self, ps, **kw): |
---|
102 | '''Disconnect and remove user's session |
---|
103 | |
---|
104 | @type ps: ZSI ParsedSoap |
---|
105 | @param ps: client SOAP message |
---|
106 | @rtype: tuple |
---|
107 | @return: request and response objects''' |
---|
108 | if self.__debug: |
---|
109 | import pdb |
---|
110 | pdb.set_trace() |
---|
111 | |
---|
112 | request, response = SessionMgrService.soap_disconnect(self, ps) |
---|
113 | |
---|
114 | # Derive designated user ID differently according to whether |
---|
115 | # a session ID was passed and the message was signed |
---|
116 | sessID = request.SessID or None |
---|
117 | |
---|
118 | if srv.sm['useSignatureHandler']: |
---|
119 | # Get certificate corresponding to private key that signed the |
---|
120 | # message - i.e. the user's proxy |
---|
121 | userCert = WSSecurityHandler.signatureHandler.verifyingCert |
---|
122 | else: |
---|
123 | userCert = request.UserCert |
---|
124 | |
---|
125 | self.sm.deleteUserSession(sessID=sessID, proxyCert=userCert) |
---|
126 | return request, response |
---|
127 | |
---|
128 | |
---|
129 | def soap_getSessionStatus(self, ps, **kw): |
---|
130 | '''Check for existence of a session with given session ID or user |
---|
131 | Distinguished Name |
---|
132 | |
---|
133 | @type ps: ZSI ParsedSoap |
---|
134 | @param ps: client SOAP message |
---|
135 | @rtype: tuple |
---|
136 | @return: request and response objects''' |
---|
137 | |
---|
138 | if self.__debug: |
---|
139 | import pdb |
---|
140 | pdb.set_trace() |
---|
141 | |
---|
142 | request, response = SessionMgrService.soap_getSessionStatus(self, ps) |
---|
143 | |
---|
144 | response.IsAlive = self.sm.getSessionStatus(userDN=request.UserDN, |
---|
145 | sessID=request.SessID) |
---|
146 | |
---|
147 | return request, response |
---|
148 | |
---|
149 | |
---|
150 | def soap_getAttCert(self, ps, **kw): |
---|
151 | '''Get Attribute Certificate from a given Attribute Authority |
---|
152 | and cache it in user's Credential Wallet |
---|
153 | |
---|
154 | @type ps: ZSI ParsedSoap |
---|
155 | @param ps: client SOAP message |
---|
156 | @rtype: tuple |
---|
157 | @return: request and response objects''' |
---|
158 | if self.__debug: |
---|
159 | import pdb |
---|
160 | pdb.set_trace() |
---|
161 | |
---|
162 | request, response = SessionMgrService.soap_getAttCert(self, ps) |
---|
163 | |
---|
164 | # Get certificate corresponding to private key that signed the |
---|
165 | # message - i.e. the user's |
---|
166 | if srv.sm['useSignatureHandler']: |
---|
167 | # Get certificate corresponding to private key that signed the |
---|
168 | # message - i.e. the user's proxy |
---|
169 | userCert = WSSecurityHandler.signatureHandler.verifyingCert |
---|
170 | else: |
---|
171 | userCert = None |
---|
172 | |
---|
173 | # Cert used in signature is prefered over userCert input element - |
---|
174 | # userCert may have been omitted. |
---|
175 | result = self.sm.getAttCert(\ |
---|
176 | userCert=userCert or request.UserCert, |
---|
177 | sessID=request.SessID, |
---|
178 | aaURI=request.AttAuthorityURI, |
---|
179 | reqRole=request.ReqRole, |
---|
180 | mapFromTrustedHosts=request.MapFromTrustedHosts, |
---|
181 | rtnExtAttCertList=request.RtnExtAttCertList, |
---|
182 | extAttCertList=request.ExtAttCert, |
---|
183 | extTrustedHostList=request.ExtTrustedHost) |
---|
184 | |
---|
185 | |
---|
186 | if result[0]: |
---|
187 | response.AttCert = result[0].toString() |
---|
188 | |
---|
189 | response.Msg, response.ExtAttCertOut = result[1:] |
---|
190 | |
---|
191 | return request, response |
---|
192 | |
---|
193 | |
---|
194 | def soap_getX509Cert(self, ps, **kw): |
---|
195 | '''Return Session Manager's X.509 certificate |
---|
196 | |
---|
197 | @type ps: ZSI ParsedSoap |
---|
198 | @param ps: client SOAP message |
---|
199 | @rtype: tuple |
---|
200 | @return: request and response objects''' |
---|
201 | if self.__debug: |
---|
202 | import pdb |
---|
203 | pdb.set_trace() |
---|
204 | |
---|
205 | request, response = SessionMgrService.soap_getX509Cert(self, ps) |
---|
206 | |
---|
207 | x509Cert = X509CertRead(srv.sm['certFile']) |
---|
208 | response.X509Cert = base64.encodestring(x509Cert.asDER()) |
---|
209 | return request, response |
---|
210 | |
---|
211 | |
---|
212 | # Create Service |
---|
213 | srv = SessionMgrServiceSub() |
---|
214 | |
---|
215 | if srv.sm['useSignatureHandler']: |
---|
216 | # Initialise WS-Security signature handler passing Session Manager |
---|
217 | # public and private keys |
---|
218 | WSSecurityHandler.signatureHandler = SignatureHandler(\ |
---|
219 | verifyingCertFilePath=srv.sm['clntCertFile'], |
---|
220 | signingCertFilePath=srv.sm['certFile'], |
---|
221 | signingPriKeyFilePath=srv.sm['keyFile'], |
---|
222 | signingPriKeyPwd=srv.sm['keyPwd'], |
---|
223 | caCertFilePathList=srv.aa.get('caCertFileList')) |
---|
224 | |
---|
225 | # Add Service to Session Manager branch |
---|
226 | root = Resource() |
---|
227 | root.putChild('SessionManager', srv) |
---|
228 | siteFactory = Site(root) |
---|
229 | |
---|
230 | if srv.sm['useSSL']: |
---|
231 | # Use SSL connection |
---|
232 | log.info("Running over https ...") |
---|
233 | |
---|
234 | # Using M2Crypto ... |
---|
235 | import os |
---|
236 | os.putenv("OPENSSL_ALLOW_PROXY_CERTS", "1") |
---|
237 | |
---|
238 | import twisted.protocols.policies as policies |
---|
239 | from M2Crypto import SSL |
---|
240 | from M2Crypto.SSL import TwistedProtocolWrapper |
---|
241 | from M2Crypto.SSL.TwistedProtocolWrapper import TLSProtocolWrapper |
---|
242 | |
---|
243 | siteFactory.startTLS = True |
---|
244 | siteFactory.sslChecker = SSL.Checker.Checker() |
---|
245 | |
---|
246 | # TODO: Python ssl client seems to require SSL vers 2 is this a security |
---|
247 | # risk? |
---|
248 | ctx = SSL.Context(protocol='sslv23') |
---|
249 | ctx.set_cipher_list("NULL-MD5:ALL:!ADH:!EXP:@STRENGTH") |
---|
250 | ctx.load_cert(srv.sm['sslCertFile'], |
---|
251 | srv.sm['sslKeyFile'], |
---|
252 | callback=lambda *args, **kw: srv.aa['sslKeyPwd']) |
---|
253 | |
---|
254 | ctx.set_allow_unknown_ca(False) |
---|
255 | |
---|
256 | # TODO: resolve check - verify_peer setting fails with |
---|
257 | # BIOError: 'no certificate returned' error 18 |
---|
258 | # ctx.set_verify(SSL.verify_peer, 10) |
---|
259 | ctx.set_verify(SSL.verify_client_once, 1) |
---|
260 | |
---|
261 | ctx.load_verify_locations(capath=srv.sm['sslCACertDir']) |
---|
262 | |
---|
263 | class ContextFactory: |
---|
264 | def getContext(self): |
---|
265 | return ctx |
---|
266 | |
---|
267 | factory = policies.WrappingFactory(siteFactory) |
---|
268 | factory.protocol.TLS = True |
---|
269 | factory.protocol = lambda factory, wrappedProtocol: \ |
---|
270 | TLSProtocolWrapper(factory, |
---|
271 | wrappedProtocol, |
---|
272 | startPassThrough=0, |
---|
273 | client=0, |
---|
274 | contextFactory=ContextFactory(), |
---|
275 | postConnectionCheck=None) |
---|
276 | |
---|
277 | siteFactory = factory |
---|
278 | |
---|
279 | port = internet.TCPServer(srv.sm['portNum'], siteFactory) |
---|
280 | port.CERTFILE = srv.sm['sslCertFile'] |
---|
281 | port.KEYFILE = srv.sm['sslKeyFile'] |
---|
282 | root.__class__.server = port |
---|
283 | else: |
---|
284 | # Non-SSL |
---|
285 | log.info("Running over http ...") |
---|
286 | port = internet.TCPServer(srv.sm['portNum'], siteFactory) |
---|
287 | |
---|
288 | application = service.Application("SessionManagerContainer") |
---|
289 | port.setServiceParent(application) |
---|