1 /*
2  * Copyright (C) 2011 Emweb bv, Herent, Belgium.
3  *
4  * See the LICENSE file for terms of use.
5  */
6 
7 #include <vector>
8 #include <iterator>
9 #include <cstring>
10 #include <memory>
11 
12 #include "web/WebUtils.h"
13 
14 #include "Wt/Utils.h"
15 
16 #include "AuthUtils.h"
17 #include "base64.h"
18 
19 #include "Wt/WException.h"
20 #include "Wt/WRandom.h"
21 #include "Wt/WServer.h"
22 
23 namespace Wt {
24   namespace Auth {
25     namespace Utils {
26 
createSalt(unsigned int length)27 std::string createSalt(unsigned int length)
28 {
29   auto saltBuf = std::unique_ptr<unsigned char[]>(new unsigned char[length]);
30   for (unsigned i = 0; i < length; i += 3) {
31     unsigned r = WRandom::get();
32     std::memcpy(saltBuf.get() + i, &r, 3);
33   }
34 
35   std::string s(saltBuf.get(), saltBuf.get() + length);
36 
37   return s;
38 }
39 
40 /*
41  * This is like base64 encoding except that we use [a-zA-Z0-9./]
42  * instead of [a-zA-Z0-9+/]
43  */
encodeAscii(const std::string & a)44 std::string encodeAscii(const std::string& a)
45 {
46   std::vector<char> v;
47 
48   base64::encode(a.begin(), a.end(), std::back_inserter(v));
49 
50   std::string result(v.begin(), v.end());
51 
52   for (unsigned i = 0; i < result.length(); ++i)
53     if (result[i] == '+')
54       result[i] = '.';
55 
56   return result;
57 }
58 
decodeAscii(const std::string & a)59 std::string decodeAscii(const std::string& a)
60 {
61   std::string msg = a;
62 
63   for (unsigned i = 0; i < msg.length(); ++i)
64     if (msg[i] == '.')
65       msg[i] = '+';
66 
67   std::vector<char> v;
68   base64::decode(msg.begin(), msg.end(), std::back_inserter(v));
69 
70   return std::string(v.begin(), v.end());
71 }
72 
encodeState(const std::string & secret,const std::string & url)73 std::string encodeState(const std::string &secret, const std::string &url)
74 {
75   std::string hash(Wt::Utils::base64Encode(Wt::Utils::hmac_sha1(url, secret)));
76 
77   std::string b = Wt::Utils::base64Encode(hash + "|" + url, false);
78 
79   /* Variant of base64 encoding which is resistant to broken OAuth2 peers
80    * that do not properly re-encode the state */
81   b = Wt::Utils::replace(b, "+", "-");
82   b = Wt::Utils::replace(b, "/", "_");
83   b = Wt::Utils::replace(b, "=", ".");
84 
85   return b;
86 }
87 
decodeState(const std::string & secret,const std::string & state)88 std::string decodeState(const std::string &secret, const std::string &state)
89 {
90   std::string s = state;
91   s = Wt::Utils::replace(s, "-", "+");
92   s = Wt::Utils::replace(s, "_", "/");
93   s = Wt::Utils::replace(s, ".", "=");
94 
95 #ifndef WT_TARGET_JAVA
96   s = Wt::Utils::base64Decode(s);
97 #else
98   s = Wt::Utils::base64DecodeS(s);
99 #endif
100 
101   std::size_t i = s.find('|');
102   if (i != std::string::npos) {
103     std::string url = s.substr(i + 1);
104 
105     std::string check = encodeState(secret, url);
106     if (check == state)
107       return url;
108     else
109       return std::string();
110   } else
111     return std::string();
112 }
113 
configurationProperty(const std::string & prefix,const std::string & property)114 std::string configurationProperty(const std::string &prefix,
115                                   const std::string &property)
116 {
117   WServer *instance = WServer::instance();
118 
119   if (instance) {
120     std::string result;
121 
122     bool error;
123 #ifndef WT_TARGET_JAVA
124     error = !instance->readConfigurationProperty(property, result);
125 #else
126     std::string* v = instance->readConfigurationProperty(property, result);
127       if (v != &result) {
128         error = false;
129         result = *v;
130       } else {
131         error = true;
132       }
133 #endif
134 
135     if (error)
136       throw WException(prefix + ": no '" + property + "' property configured");
137 
138     return result;
139   } else
140     throw WException(prefix + ": could not find a WServer instance");
141 }
142 
143     }
144   }
145 }
146