Allow remoting of unsafe strings, and protect encoding of RelayState URLs.
[shibboleth/cpp-sp.git] / shibsp / remoting / impl / ddf.cpp
index 463bb8c..c11a5e5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2001-2007 Internet2
+ *  Copyright 2001-2009 Internet2
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -32,6 +32,7 @@
 #include <xercesc/util/XMLUniDefs.hpp>
 #include <xmltooling/XMLToolingConfig.h>
 #include <xmltooling/util/ParserPool.h>
+#include <xmltooling/util/URLEncoder.h>
 #include <xmltooling/util/XMLHelper.h>
 
 using namespace shibsp;
@@ -101,7 +102,8 @@ struct shibsp::ddf_body_t {
         DDF_FLOAT,
            DDF_STRUCT,
         DDF_LIST,
-           DDF_POINTER
+           DDF_POINTER,
+        DDF_STRING_UNSAFE
     } type;                         // data type of node
 
     union {
@@ -126,11 +128,11 @@ DDF::DDF(const char* n)
     name(n);
 }
 
-DDF::DDF(const char* n, const char* val)
+DDF::DDF(const char* n, const char* val, bool safe)
 {
     m_handle=new(nothrow) ddf_body_t;
     name(n);
-    string(val);
+    string(const_cast<char*>(val), true, safe);
 }
 
 DDF::DDF(const char* n, long val)
@@ -171,7 +173,8 @@ DDF DDF::copy() const
         case ddf_body_t::DDF_EMPTY:
             return DDF(m_handle->name);
         case ddf_body_t::DDF_STRING:
-            return DDF(m_handle->name,m_handle->value.string);
+        case ddf_body_t::DDF_STRING_UNSAFE:
+            return DDF(m_handle->name,m_handle->value.string,(m_handle->type==ddf_body_t::DDF_STRING));
         case ddf_body_t::DDF_INT:
             return DDF(m_handle->name,m_handle->value.integer);
         case ddf_body_t::DDF_FLOAT:
@@ -240,7 +243,7 @@ bool DDF::isempty() const
 
 bool DDF::isstring() const
 {
-    return m_handle ? (m_handle->type==ddf_body_t::DDF_STRING) : false;
+    return m_handle ? (m_handle->type==ddf_body_t::DDF_STRING || m_handle->type==ddf_body_t::DDF_STRING_UNSAFE) : false;
 }
 
 bool DDF::isint() const
@@ -351,13 +354,13 @@ DDF& DDF::empty()
     return *this;
 }
 
-DDF& DDF::string(char* val, bool copyit)
+DDF& DDF::string(char* val, bool copyit, bool safe)
 {
     if (empty().m_handle) {
         m_handle->value.string = copyit ? ddf_strdup(val) : val;
         if (!m_handle->value.string && val && *val)
             return destroy();
-        m_handle->type=ddf_body_t::DDF_STRING;
+        m_handle->type=(safe ? ddf_body_t::DDF_STRING : ddf_body_t::DDF_STRING_UNSAFE);
     }
     return *this;
 }
@@ -680,6 +683,7 @@ void DDF::dump(FILE* f, int indent) const
                 break;
 
             case ddf_body_t::DDF_STRING:
+            case ddf_body_t::DDF_STRING_UNSAFE:
                 if (m_handle->name)
                     fprintf(f,"char* %s = ",m_handle->name);
                 else
@@ -798,6 +802,7 @@ void serialize(ddf_body_t* p, ostream& os, bool name_attr=true)
         switch (p->type) {
             
             case ddf_body_t::DDF_STRING:
+            case ddf_body_t::DDF_STRING_UNSAFE:
                 os << "<string";
                 if (name_attr && p->name) {
                     os << " name=\"";
@@ -805,8 +810,14 @@ void serialize(ddf_body_t* p, ostream& os, bool name_attr=true)
                     os << '"';
                 }
                 if (p->value.string) {
-                    os << '>';
-                    xml_encode(os,p->value.string);
+                    if (p->type == ddf_body_t::DDF_STRING) {
+                        os << '>';
+                        xml_encode(os,p->value.string);
+                    }
+                    else {
+                        os << " unsafe=\"1\">";
+                        xml_encode(os,XMLToolingConfig::getConfig().getURLEncoder()->encode(p->value.string).c_str());
+                    }
                     os << "</string>";
                 }
                 else
@@ -941,11 +952,12 @@ static const XMLCh _number[] =  UNICODE_LITERAL_6(n,u,m,b,e,r);
 static const XMLCh _array[] =   UNICODE_LITERAL_5(a,r,r,a,y);
 static const XMLCh _struct[] =  UNICODE_LITERAL_6(s,t,r,u,c,t);
 static const XMLCh _lowercase[] = UNICODE_LITERAL_9(l,o,w,e,r,c,a,s,e);
+static const XMLCh _unsafe[] =  UNICODE_LITERAL_6(u,n,s,a,f,e);
 
 DDF deserialize(DOMElement* root, bool lowercase)
 {
     DDF obj(NULL);
-    auto_ptr_char name_val(root->getAttribute(_name));
+    auto_ptr_char name_val(root->getAttributeNS(NULL, _name));
     if (name_val.get() && *name_val.get()) {
         if (lowercase)
             for (char* pch=const_cast<char*>(name_val.get()); *pch=tolower(*pch); pch++);
@@ -961,9 +973,19 @@ DDF deserialize(DOMElement* root, bool lowercase)
     if (XMLString::equals(tag,_string)) {
         DOMNode* child=root->getFirstChild();
         if (child && child->getNodeType()==DOMNode::TEXT_NODE) {
-            char* val = toUTF8(child->getNodeValue(), true);    // use malloc
-            if (val)
-                obj.string(val, false); // don't re-copy the string
+            const XMLCh* unsafe = root->getAttributeNS(NULL, _unsafe);
+            if (unsafe && *unsafe==chDigit_1) {
+                // If it's unsafe, it's not UTF-8 data, so we have to convert to ASCII and decode it.
+                char* encoded = XMLString::transcode(child->getNodeValue());
+                XMLToolingConfig::getConfig().getURLEncoder()->decode(encoded);
+                obj.string(encoded, true, false); // re-copy into free-able buffer, plus mark unsafe
+                XMLString::release(&encoded);
+            }
+            else {
+                char* val = toUTF8(child->getNodeValue(), true);    // use malloc
+                if (val)
+                    obj.string(val, false); // don't re-copy the string
+            }
         }
     }
     else if (XMLString::equals(tag,_number)) {