2 * Copyright 2001-2005 Internet2
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 /* adfs.cpp - bootstraps the ADFS extension library
24 # define ADFS_EXPORTS __declspec(dllexport)
31 #include <xercesc/util/Base64.hpp>
36 using namespace shibboleth;
37 using namespace shibtarget;
39 using namespace adfs::logging;
43 PlugManager::Factory ADFSListenerFactory;
44 PlugManager::Factory ADFSSessionInitiatorFactory;
45 PlugManager::Factory ADFSHandlerFactory;
47 IListener* adfs::g_MemoryListener = NULL;
49 extern "C" int ADFS_EXPORTS saml_extension_init(void*)
51 SAMLConfig& conf=SAMLConfig::getConfig();
53 if (ShibTargetConfig::getConfig().isEnabled(ShibTargetConfig::Caching)) {
54 // Build an internal "listener" to handle the work.
55 IPlugIn* plugin=conf.getPlugMgr().newPlugin(shibtarget::XML::MemoryListenerType,NULL);
56 g_MemoryListener=dynamic_cast<IListener*>(plugin);
57 if (!g_MemoryListener) {
59 fprintf(stderr, "Basic MemoryListener plugin failed to load");
64 // Register extension schema.
65 saml::XML::registerSchema(adfs::XML::WSTRUST_NS,adfs::XML::WSTRUST_SCHEMA_ID);
67 // Register plugin factories (some override existing Shib functionality).
68 conf.getPlugMgr().regFactory(shibtarget::XML::MemoryListenerType,&ADFSListenerFactory);
70 auto_ptr_char temp1(Constants::SHIB_SESSIONINIT_PROFILE_URI);
71 conf.getPlugMgr().regFactory(temp1.get(),&ADFSSessionInitiatorFactory);
73 auto_ptr_char temp2(adfs::XML::WSFED_NS);
74 conf.getPlugMgr().regFactory(temp2.get(),&ADFSHandlerFactory);
79 extern "C" void ADFS_EXPORTS saml_extension_term()
81 // Unregister metadata factories
82 SAMLConfig& conf=SAMLConfig::getConfig();
83 conf.getPlugMgr().unregFactory(shibtarget::XML::MemoryListenerType);
85 auto_ptr_char temp1(Constants::SHIB_SESSIONINIT_PROFILE_URI);
86 conf.getPlugMgr().unregFactory(temp1.get());
88 auto_ptr_char temp2(adfs::XML::WSFED_NS);
89 conf.getPlugMgr().unregFactory(temp2.get());
91 delete g_MemoryListener;
92 g_MemoryListener=NULL;
95 // For now, we'll just put the meat of the profile here.
97 SAMLAuthenticationStatement* adfs::checkAssertionProfile(const SAMLAssertion* a)
101 throw FatalProfileException("rejected unsigned ADFS assertion");
104 time_t now=time(NULL);
105 SAMLConfig& config=SAMLConfig::getConfig();
106 if (a->getIssueInstant()->getEpoch() < now-(2*config.clock_skew_secs))
107 throw ExpiredAssertionException("rejected expired ADFS assertion");
109 const SAMLDateTime* notBefore=a->getNotBefore();
110 const SAMLDateTime* notOnOrAfter=a->getNotOnOrAfter();
111 if (!notBefore || !notOnOrAfter)
112 throw ExpiredAssertionException("rejected ADFS assertion without time conditions");
113 if (now+config.clock_skew_secs < notBefore->getEpoch())
114 throw ExpiredAssertionException("rejected ADFS assertion that is not yet valid");
115 if (notOnOrAfter->getEpoch() <= now-config.clock_skew_secs)
116 throw ExpiredAssertionException("rejected expired ADFS assertion");
118 // Look for an authentication statement.
119 SAMLAuthenticationStatement* as=NULL;
120 for (Iterator<SAMLStatement*> statements=a->getStatements(); !as && statements.hasNext();)
121 as=dynamic_cast<SAMLAuthenticationStatement*>(statements.next());
123 throw FatalProfileException("rejecting ADFS assertion without authentication statement");
128 /*************************************************************************
129 * CGI Parser implementation
132 CgiParse::CgiParse(const char* data, unsigned int len)
134 const char* pch = data;
135 unsigned int cl = len;
140 value=fmakeword('&',&cl,&pch);
143 name=makeword(value,'=');
149 CgiParse::~CgiParse()
151 for (map<string,char*>::iterator i=kvp_map.begin(); i!=kvp_map.end(); i++)
156 CgiParse::get_value(const char* name) const
158 map<string,char*>::const_iterator i=kvp_map.find(name);
159 if (i==kvp_map.end())
164 /* Parsing routines modified from NCSA source. */
166 CgiParse::makeword(char *line, char stop)
169 char *word = (char *) malloc(sizeof(char) * (strlen(line) + 1));
171 for(x=0;((line[x]) && (line[x] != stop));x++)
180 line[y++] = line[x++];
186 CgiParse::fmakeword(char stop, unsigned int *cl, const char** ppch)
194 word = (char *) malloc(sizeof(char) * (wsize + 1));
198 word[ll] = *((*ppch)++);
203 word = (char *)realloc(word,sizeof(char)*(wsize+1));
206 if((word[ll] == stop) || word[ll] == EOF || (!(*cl)))
218 CgiParse::plustospace(char *str)
223 if(str[x] == '+') str[x] = ' ';
227 CgiParse::x2c(char *what)
231 digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
233 digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
238 CgiParse::url_decode(char *url)
242 for(x=0,y=0;url[y];++x,++y)
244 if((url[x] = url[y]) == '%')
246 url[x] = x2c(&url[y+1]);
253 static inline char hexchar(unsigned short s)
255 return (s<=9) ? ('0' + s) : ('A' + s - 10);
258 string CgiParse::url_encode(const char* s)
260 static char badchars[]="\"\\+<>#%{}|^~[]`;/?:@=&";
264 if (strchr(badchars,*s) || *s<=0x20 || *s>=0x7F) {
266 ret+=hexchar(*s >> 4);
267 ret+=hexchar(*s & 0x0F);
275 // CDC implementation
277 const char CommonDomainCookie::CDCName[] = "_saml_idp";
279 CommonDomainCookie::CommonDomainCookie(const char* cookie)
284 Category& log=Category::getInstance(ADFS_LOGCAT".CommonDomainCookie");
286 // Copy it so we can URL-decode it.
287 char* b64=strdup(cookie);
288 CgiParse::url_decode(b64);
290 // Chop it up and save off elements.
291 vector<string> templist;
294 while (*ptr && isspace(*ptr)) ptr++;
296 while (*end && !isspace(*end)) end++;
297 templist.push_back(string(ptr,end-ptr));
302 // Now Base64 decode the list.
303 for (vector<string>::iterator i=templist.begin(); i!=templist.end(); i++) {
305 XMLByte* decoded=Base64::decode(reinterpret_cast<const XMLByte*>(i->c_str()),&len);
306 if (decoded && *decoded) {
307 m_list.push_back(reinterpret_cast<char*>(decoded));
308 XMLString::release(&decoded);
311 log.warn("cookie element does not appear to be base64-encoded");
315 const char* CommonDomainCookie::set(const char* providerId)
317 // First scan the list for this IdP.
318 for (vector<string>::iterator i=m_list.begin(); i!=m_list.end(); i++) {
319 if (*i == providerId) {
325 // Append it to the end.
326 m_list.push_back(providerId);
328 // Now rebuild the delimited list.
330 for (vector<string>::const_iterator j=m_list.begin(); j!=m_list.end(); j++) {
331 if (!delimited.empty()) delimited += ' ';
334 XMLByte* b64=Base64::encode(reinterpret_cast<const XMLByte*>(j->c_str()),j->length(),&len);
336 for (pos=b64, pos2=b64; *pos2; pos2++)
341 delimited += reinterpret_cast<char*>(b64);
342 XMLString::release(&b64);
345 m_encoded=CgiParse::url_encode(delimited.c_str());
346 return m_encoded.c_str();