176959301e183fa91942fb41f5d22ec7dd9f8665
[shibboleth/sp.git] / shib-target / shib-target.h
1 /*
2  *  Copyright 2001-2005 Internet2
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * shib-target.h -- top-level header file for the SHIB Common Target Library
19  *
20  * Created by:  Derek Atkins <derek@ihtfp.com>
21  *
22  * $Id$
23  */
24
25 #ifndef SHIB_TARGET_H
26 #define SHIB_TARGET_H
27
28 // New headers
29 #include <shibsp/base.h>
30 #include <shibsp/ListenerService.h>
31 #include <shibsp/PropertySet.h>
32 #include <saml/saml2/metadata/MetadataProvider.h>
33 #include <xmltooling/security/TrustEngine.h>
34 #include <xmltooling/signature/CredentialResolver.h>
35
36 // Old headers
37 #include <saml/saml.h>
38 #include <shib/shib.h>
39
40 #ifdef WIN32
41 # ifndef SHIBTARGET_EXPORTS
42 #  define SHIBTARGET_EXPORTS __declspec(dllimport)
43 # endif
44 # define SHIB_SCHEMAS "/opt/shibboleth-sp/share/xml/shibboleth"
45 # define SHIB_CONFIG "/opt/shibboleth-sp/etc/shibboleth/shibboleth.xml"
46 #else
47 # include <shib-target/shib-paths.h>
48 # define SHIBTARGET_EXPORTS
49 #endif
50
51 namespace shibtarget {
52   
53     // Abstract APIs for access to configuration information
54     
55     // Forward declaration
56     class SHIBTARGET_EXPORTS ShibTarget;
57
58     /**
59      * Interface to a protocol handler
60      * 
61      * Protocol handlers perform system functions such as processing SAML protocol
62      * messages to create and logout sessions or creating protocol requests.
63      */
64     struct SHIBTARGET_EXPORTS IHandler : public virtual saml::IPlugIn
65     {
66         IHandler() : m_props(NULL) {}
67         virtual ~IHandler() {}
68         virtual const shibsp::PropertySet* getProperties() const { return m_props; }
69         virtual void setProperties(const shibsp::PropertySet* properties) { m_props=properties; }
70         virtual std::pair<bool,void*> run(ShibTarget* st, bool isHandler=true) const=0;
71     private:
72         const shibsp::PropertySet* m_props;
73     };
74     
75     /**
76      * Interface to Shibboleth Applications, which exposes most of the functionality
77      * required to process web requests or security protocol messages for resources
78      * associated with them.
79      * 
80      * Applications are implementation-specific, but generally correspond to collections
81      * of resources related to one another in logical ways, such as a virtual host or
82      * a Java servlet context. Most complex configuration data is associated with an
83      * Application. Implementations should always expose an application named "default"
84      * as a last resort.
85      */
86     struct SHIBTARGET_EXPORTS IApplication : public virtual shibsp::PropertySet,
87         public virtual shibboleth::ShibBrowserProfile::ITokenValidator
88     {
89         virtual const char* getId() const=0;
90         virtual const char* getHash() const=0;
91         
92         virtual saml::Iterator<saml::SAMLAttributeDesignator*> getAttributeDesignators() const=0;
93         virtual saml::Iterator<shibboleth::IAAP*> getAAPProviders() const=0;
94         virtual opensaml::saml2md::MetadataProvider* getMetadataProvider() const=0;
95         virtual xmltooling::TrustEngine* getTrustEngine() const=0;
96         virtual saml::Iterator<const XMLCh*> getAudiences() const=0;
97         virtual const shibsp::PropertySet* getCredentialUse(const opensaml::saml2md::EntityDescriptor* provider) const=0;
98
99         // caller is borrowing object, must use within scope of config lock
100         virtual const saml::SAMLBrowserProfile* getBrowserProfile() const=0;
101         virtual const saml::SAMLBinding* getBinding(const XMLCh* binding) const=0;
102
103         // caller is given ownership of object, must use and delete within scope of config lock
104         virtual saml::SAMLBrowserProfile::ArtifactMapper* getArtifactMapper() const=0;
105
106         // general token validation based on conditions, signatures, etc.
107         virtual void validateToken(
108             saml::SAMLAssertion* token,
109             time_t t=0,
110             const opensaml::saml2md::RoleDescriptor* role=NULL,
111             const xmltooling::TrustEngine* trust=NULL
112             ) const=0;
113
114         // Used to locate a default or designated session initiator for automatic sessions
115         virtual const IHandler* getDefaultSessionInitiator() const=0;
116         virtual const IHandler* getSessionInitiatorById(const char* id) const=0;
117         
118         // Used by session initiators to get endpoint to forward to IdP/WAYF
119         virtual const IHandler* getDefaultAssertionConsumerService() const=0;
120         virtual const IHandler* getAssertionConsumerServiceByIndex(unsigned short index) const=0;
121         virtual saml::Iterator<const IHandler*> getAssertionConsumerServicesByBinding(const XMLCh* binding) const=0;
122         
123         // Used by dispatcher to locate the handler for a request
124         virtual const IHandler* getHandler(const char* path) const=0;
125
126         virtual ~IApplication() {}
127     };
128
129     /**
130      * OpenSAML binding hook
131      *
132      * Instead of wrapping the binding to deal with mutual authentication, we
133      * just use the HTTP hook functionality offered by OpenSAML. The hook will
134      * register "itself" as a globalCtx pointer with the SAML binding and the caller
135      * will declare and pass the embedded struct as callCtx for use by the hook.
136      */
137     class ShibHTTPHook : virtual public saml::SAMLSOAPHTTPBinding::HTTPHook
138     {
139     public:
140         ShibHTTPHook(const xmltooling::TrustEngine* trust) : m_trust(trust) {}
141         virtual ~ShibHTTPHook() {}
142         
143         // Only hook we need here is for outgoing connection to server.
144         virtual bool outgoing(saml::HTTPClient* conn, void* globalCtx=NULL, void* callCtx=NULL);
145
146         // Client declares a context object and pass as callCtx to send() method.
147         class ShibHTTPHookCallContext {
148         public:
149             ShibHTTPHookCallContext(const shibsp::PropertySet* credUse, const opensaml::saml2md::RoleDescriptor* role)
150                 : m_credUse(credUse), m_role(role), m_hook(NULL), m_authenticated(false) {}
151             const ShibHTTPHook* getHook() {return m_hook;}
152             const shibsp::PropertySet* getCredentialUse() {return m_credUse;}
153             const opensaml::saml2md::RoleDescriptor* getRoleDescriptor() {return m_role;}
154             bool isAuthenticated() const {return m_authenticated;}
155             void setAuthenticated() {m_authenticated=true;}
156             
157         private:
158             const shibsp::PropertySet* m_credUse;
159             const opensaml::saml2md::RoleDescriptor* m_role;
160             ShibHTTPHook* m_hook;
161             bool m_authenticated;
162             friend class ShibHTTPHook;
163         };
164         
165         const xmltooling::TrustEngine* getTrustEngine() const {return m_trust;}
166     private:
167         const xmltooling::TrustEngine* m_trust;
168     };
169
170     /**
171      * Interface to a cached user session.
172      * 
173      * Cache entries provide implementations with access to the raw SAML information they
174      * need to publish or provide access to the data for applications to use. All creation
175      * or access to entries is through the ISessionCache interface, and callers must unlock
176      * the entry when finished using it, rather than explicitly freeing them.
177      */
178     struct SHIBTARGET_EXPORTS ISessionCacheEntry : public virtual saml::ILockable
179     {
180         virtual const char* getClientAddress() const=0;
181         virtual const char* getProviderId() const=0;
182         virtual std::pair<const char*,const saml::SAMLSubject*> getSubject(bool xml=true, bool obj=false) const=0;
183         virtual const char* getAuthnContext() const=0;
184         virtual std::pair<const char*,const saml::SAMLResponse*> getTokens(bool xml=true, bool obj=false) const=0;
185         virtual std::pair<const char*,const saml::SAMLResponse*> getFilteredTokens(bool xml=true, bool obj=false) const=0;
186         virtual ~ISessionCacheEntry() {}
187     };
188
189     /**
190      * Interface to a sink for session cache events.
191      *
192      * All caches support registration of a backing store that can be informed
193      * of significant events in the lifecycle of a cache entry.
194      */
195     struct SHIBTARGET_EXPORTS ISessionCacheStore
196     {
197         virtual HRESULT onCreate(
198             const char* key,
199             const IApplication* application,
200             const ISessionCacheEntry* entry,
201             int majorVersion,
202             int minorVersion,
203             time_t created
204             )=0;
205         virtual HRESULT onRead(
206             const char* key,
207             std::string& applicationId,
208             std::string& clientAddress,
209             std::string& providerId,
210             std::string& subject,
211             std::string& authnContext,
212             std::string& tokens,
213             int& majorVersion,
214             int& minorVersion,
215             time_t& created,
216             time_t& accessed
217             )=0;
218         virtual HRESULT onRead(const char* key, time_t& accessed)=0;
219         virtual HRESULT onRead(const char* key, std::string& tokens)=0;
220         virtual HRESULT onUpdate(const char* key, const char* tokens=NULL, time_t lastAccess=0)=0;
221         virtual HRESULT onDelete(const char* key)=0;
222         virtual ~ISessionCacheStore() {}
223     };
224
225     /**
226      * Interface to the session cache.
227      * 
228      * The session cache abstracts a persistent (meaning across requests) cache of
229      * instances of the ISessionCacheEntry interface. Creation of new entries and entry
230      * lookup are confined to this interface to enable implementations to flexibly
231      * remote and/or optimize calls by implementing custom versions of the
232      * ISessionCacheEntry interface as required.
233      */
234     struct SHIBTARGET_EXPORTS ISessionCache : public virtual saml::IPlugIn
235     {
236         virtual std::string insert(
237             const IApplication* application,
238             const opensaml::saml2md::RoleDescriptor* source,
239             const char* client_addr,
240             const saml::SAMLSubject* subject,
241             const char* authnContext,
242             const saml::SAMLResponse* tokens
243             )=0;
244         virtual ISessionCacheEntry* find(
245             const char* key, const IApplication* application, const char* client_addr
246             )=0;
247         virtual void remove(
248             const char* key, const IApplication* application, const char* client_addr
249             )=0;
250
251         virtual bool setBackingStore(ISessionCacheStore* store)=0;
252         virtual ~ISessionCache() {}
253     };
254
255     #define MEMORY_SESSIONCACHE "edu.internet2.middleware.shibboleth.sp.provider.MemorySessionCacheProvider"
256     #define MYSQL_SESSIONCACHE  "edu.internet2.middleware.shibboleth.sp.provider.MySQLSessionCacheProvider"
257     #define ODBC_SESSIONCACHE   "edu.internet2.middleware.shibboleth.sp.provider.ODBCSessionCacheProvider"
258
259     #define MYSQL_REPLAYCACHE   "edu.internet2.middleware.shibboleth.sp.provider.MySQLReplayCacheProvider"
260     #define ODBC_REPLAYCACHE    "edu.internet2.middleware.shibboleth.sp.provider.ODBCReplayCacheProvider"
261
262
263     /**
264      * Interface to an access control plugin
265      * 
266      * Access control plugins return authorization decisions based on the intersection
267      * of the resource request and the active session. They can be implemented through
268      * cross-platform or platform-specific mechanisms.
269      */
270     struct SHIBTARGET_EXPORTS IAccessControl : public virtual xmltooling::Lockable, public virtual saml::IPlugIn
271     {
272         virtual bool authorized(ShibTarget* st, ISessionCacheEntry* entry) const=0;
273         virtual ~IAccessControl() {}
274     };
275
276     #define HTACCESS_ACCESSCONTROL  "edu.internet2.middleware.shibboleth.sp.apache.provider.htAccessControl"
277     #define XML_ACCESSCONTROL       "edu.internet2.middleware.shibboleth.sp.provider.XMLAccessControl"
278
279     /**
280      * Interface to a request mapping plugin
281      * 
282      * Request mapping plugins return configuration settings that apply to resource requests.
283      * They can be implemented through cross-platform or platform-specific mechanisms.
284      */
285     struct SHIBTARGET_EXPORTS IRequestMapper : public virtual xmltooling::Lockable, public virtual saml::IPlugIn
286     {
287         typedef std::pair<const shibsp::PropertySet*,IAccessControl*> Settings;
288         virtual Settings getSettings(ShibTarget* st) const=0;
289         virtual ~IRequestMapper() {}
290     };
291
292     #define XML_REQUESTMAP_PROVIDER     "edu.internet2.middleware.shibboleth.sp.provider.XMLRequestMapProvider"
293     #define NATIVE_REQUESTMAP_PROVIDER  "edu.internet2.middleware.shibboleth.sp.provider.NativeRequestMapProvider"
294     #define LEGACY_REQUESTMAP_PROVIDER  "edu.internet2.middleware.shibboleth.target.provider.XMLRequestMap"
295
296     struct SHIBTARGET_EXPORTS IConfig : public virtual xmltooling::Lockable, public virtual shibsp::PropertySet, public virtual saml::IPlugIn
297     {
298         virtual void init()=0;
299         virtual shibsp::ListenerService* getListener() const=0;
300         virtual ISessionCache* getSessionCache() const=0;
301         virtual saml::IReplayCache* getReplayCache() const=0;
302         virtual IRequestMapper* getRequestMapper() const=0;
303         virtual const IApplication* getApplication(const char* applicationId) const=0;
304         virtual xmlsignature::CredentialResolver* getCredentialResolver(const char* id) const=0;
305         virtual ~IConfig() {}
306     };
307
308     class SHIBTARGET_EXPORTS ShibTargetConfig
309     {
310     public:
311         ShibTargetConfig() : m_ini(NULL) {}
312         virtual ~ShibTargetConfig() {}
313         
314         virtual bool init(const char* schemadir) = 0;
315         virtual bool load(const char* config) = 0;
316         virtual void shutdown() = 0;
317
318         virtual IConfig* getINI() const {return m_ini;}
319
320         static ShibTargetConfig& getConfig();
321
322     protected:
323         IConfig* m_ini;
324     };
325
326     class ShibTargetPriv;
327     class SHIBTARGET_EXPORTS ShibTarget {
328     public:
329         ShibTarget(const IApplication* app);
330         virtual ~ShibTarget(void);
331
332         // These are defined here so the subclass does not need to specifically
333         // depend on log4cpp.  We could use log4cpp::Priority::PriorityLevel
334         // but this is just as easy, IMHO.  It's just a case statement in the
335         // implementation to handle the event level.
336         enum ShibLogLevel {
337           LogLevelDebug,
338           LogLevelInfo,
339           LogLevelWarn,
340           LogLevelError
341         };
342
343         //
344         // Note: subclasses MUST implement ALL of these virtual methods
345         //
346         
347         // Send a message to the Webserver log
348         virtual void log(ShibLogLevel level, const std::string &msg)=0;
349
350         void log(ShibLogLevel level, const char* msg) {
351           std::string s = msg;
352           log(level, s);
353         }
354
355         // Get/Set a cookie for this request
356         virtual std::string getCookies() const=0;
357         virtual void setCookie(const std::string& name, const std::string& value)=0;
358         virtual const char* getCookie(const std::string& name) const;
359         void setCookie(const char* name, const char* value) {
360           std::string ns = name;
361           std::string vs = value;
362           setCookie(ns, vs);
363         }
364         void setCookie(const char* name, const std::string& value) {
365           std::string ns = name;
366           setCookie(ns, value);
367         }
368
369         // Get any URL-encoded arguments or the raw POST body from the server
370         virtual const char* getQueryString() const=0;
371         virtual const char* getRequestBody() const=0;
372         virtual const char* getRequestParameter(const char* param, size_t index=0) const;
373
374         // Clear a header, set a header
375         // These APIs are used for exporting the Assertions into the
376         // Headers.  It will clear some well-known headers first to make
377         // sure none remain.  Then it will process the set of assertions
378         // and export them via setHeader().
379         virtual void clearHeader(const std::string& name)=0;
380         virtual void setHeader(const std::string& name, const std::string& value)=0;
381         virtual std::string getHeader(const std::string& name)=0;
382         virtual void setRemoteUser(const std::string& user)=0;
383         virtual std::string getRemoteUser()=0;
384
385         void clearHeader(const char* n) {
386           std::string s = n;
387           clearHeader(s);
388         }
389         void setHeader(const char* n, const char* v) {
390           std::string ns = n;
391           std::string vs = v;
392           setHeader(ns, vs);
393         }
394         void setHeader(const std::string& n, const char* v) {
395           std::string vs = v;
396           setHeader(n, vs);
397         }
398         void setHeader(const char* n, const std::string& v) {
399           std::string ns = n;
400           setHeader(ns, v);
401         }
402         std::string getHeader(const char* n) {
403           std::string s = n;
404           return getHeader(s);
405         }
406         void setRemoteUser(const char* n) {
407           std::string s = n;
408           setRemoteUser(s);
409         }
410
411         // We're done.  Finish up.  Send specific result content or a redirect.
412         // If there are no headers supplied assume the content-type is text/html
413         typedef std::pair<std::string, std::string> header_t;
414         virtual void* sendPage(
415             const std::string& msg,
416             int code = 200,
417             const std::string& content_type = "text/html",
418             const saml::Iterator<header_t>& headers = EMPTY(header_t)
419             )=0;
420         void* sendPage(const char* msg) {
421           std::string m = msg;
422           return sendPage(m);
423         }
424         virtual void* sendRedirect(const std::string& url)=0;
425         
426         // These next two APIs are used to obtain the module-specific "OK"
427         // and "Decline" results.  OK means "we believe that this request
428         // should be accepted".  Declined means "we believe that this is
429         // not a shibbolized request so we have no comment".
430
431         virtual void* returnDecline();
432         virtual void* returnOK();
433
434         //
435         // Note:  Subclasses need not implement anything below this line
436         //
437
438         // These functions implement the server-agnostic shibboleth engine
439         // The web server modules implement a subclass and then call into 
440         // these methods once they instantiate their request object.
441         // 
442         // Return value:
443         //   these APIs will always return the result of sendPage(), sendRedirect(),
444         //   returnDecline(), or returnOK() in the void* portion of the return code.
445         //   Exactly what those values are is module- (subclass-) implementation
446         //   specific.  The 'bool' part of the return value declares whether the
447         //   void* is valid or not.  If the bool is true then the void* is valid.
448         //   If the bool is false then the API did not call any callback, the void*
449         //   is not valid, and the caller should continue processing (the API Call
450         //   finished successfully).
451         //
452         //   The handleProfile argument declares whether doCheckAuthN() should
453         //   automatically call doHandlePOST() when it encounters a request for
454         //   the ShireURL;  if false it will call returnOK() instead.
455         //
456         std::pair<bool,void*> doCheckAuthN(bool handler = false);
457         std::pair<bool,void*> doHandler();
458         std::pair<bool,void*> doCheckAuthZ();
459         std::pair<bool,void*> doExportAssertions(bool requireSession = true);
460
461         // Basic request access in case any plugins need the info
462         virtual const IConfig* getConfig() const;
463         virtual const IApplication* getApplication() const;
464         const char* getRequestMethod() const {return m_method.c_str();}
465         const char* getProtocol() const {return m_protocol.c_str();}
466         const char* getHostname() const {return m_hostname.c_str();}
467         int getPort() const {return m_port;}
468         const char* getRequestURI() const {return m_uri.c_str();}
469         const char* getContentType() const {return m_content_type.c_str();}
470         const char* getRemoteAddr() const {return m_remote_addr.c_str();}
471         const char* getRequestURL() const {return m_url.c_str();}
472         
473         // Advanced methods useful to profile handlers implemented outside core
474         
475         // Get per-application session and state cookie name and properties
476         virtual std::pair<std::string,const char*> getCookieNameProps(const char* prefix) const;
477         
478         // Determine the effective handler URL based on the resource URL
479         virtual std::string getHandlerURL(const char* resource) const;
480
481     protected:
482         ShibTarget();
483
484         // Internal APIs
485
486         // Initialize the request from the parsed URL
487         // protocol == http, https, etc
488         // hostname == server name
489         // port == server port
490         // uri == resource path
491         // method == GET, POST, etc.
492         void init(
493             const char* protocol,
494             const char* hostname,
495             int port,
496             const char* uri,
497             const char* content_type,
498             const char* remote_addr,
499             const char* method
500             );
501
502         std::string m_url, m_method, m_protocol, m_hostname, m_uri, m_content_type, m_remote_addr;
503         int m_port;
504
505     private:
506         mutable ShibTargetPriv* m_priv;
507         friend class ShibTargetPriv;
508     };
509
510 }
511
512 #endif /* SHIB_TARGET_H */