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