1#include <map>
2#include <cstring>
3#include <algorithm>
4#include <Poco/String.h>
5#include <common/find_symbols.h>
6#include <Common/Exception.h>
7#include <Common/StringUtils/StringUtils.h>
8#include "validateODBCConnectionString.h"
9
10
11namespace DB
12{
13
14namespace ErrorCodes
15{
16 extern const int BAD_ODBC_CONNECTION_STRING;
17}
18
19
20std::string validateODBCConnectionString(const std::string & connection_string)
21{
22 /// Connection string is a list of name, value pairs.
23 /// name and value are separated by '='.
24 /// names are case insensitive.
25 /// name=value pairs are separated by ';'.
26 /// ASCII whitespace characters are skipped before and after delimiters.
27 /// value may be optionally enclosed by {}
28 /// in enclosed value, } is escaped as }}.
29 ///
30 /// Example: PWD={a}}b} means that password is a}b
31 ///
32 /// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqldriverconnect-function?view=sql-server-2017#comments
33
34 /// unixODBC has fixed size buffers on stack and has buffer overflow bugs.
35 /// We will limit string sizes to small values.
36
37 static constexpr size_t MAX_ELEMENT_SIZE = 100;
38 static constexpr size_t MAX_CONNECTION_STRING_SIZE = 1000;
39
40 if (connection_string.empty())
41 throw Exception("ODBC connection string cannot be empty", ErrorCodes::BAD_ODBC_CONNECTION_STRING);
42
43 if (connection_string.size() >= MAX_CONNECTION_STRING_SIZE)
44 throw Exception("ODBC connection string is too long", ErrorCodes::BAD_ODBC_CONNECTION_STRING);
45
46 const char * pos = connection_string.data();
47 const char * end = pos + connection_string.size();
48
49 auto skip_whitespaces = [&]
50 {
51 while (pos < end && isWhitespaceASCII(*pos))
52 {
53 if (*pos != ' ')
54 throw Exception("ODBC connection string parameter contains unusual whitespace character", ErrorCodes::BAD_ODBC_CONNECTION_STRING);
55 ++pos;
56 }
57 };
58
59 auto read_name = [&]
60 {
61 const char * begin = pos;
62
63 if (pos < end && isValidIdentifierBegin(*pos))
64 ++pos;
65 else
66 throw Exception("ODBC connection string parameter name doesn't begin with valid identifier character", ErrorCodes::BAD_ODBC_CONNECTION_STRING);
67
68 while (pos < end && isWordCharASCII(*pos))
69 ++pos;
70
71 return std::string(begin, pos);
72 };
73
74 auto read_plain_value = [&]
75 {
76 const char * begin = pos;
77
78 while (pos < end && *pos != ';' && !isWhitespaceASCII(*pos))
79 {
80 signed char c = *pos;
81 if (c < 32 || strchr("[]{}(),;?*=!@'\"", c) != nullptr)
82 throw Exception("ODBC connection string parameter value is unescaped and contains illegal character", ErrorCodes::BAD_ODBC_CONNECTION_STRING);
83 ++pos;
84 }
85
86 return std::string(begin, pos);
87 };
88
89 auto read_escaped_value = [&]
90 {
91 std::string res;
92
93 if (pos < end && *pos == '{')
94 ++pos;
95 else
96 throw Exception("ODBC connection string parameter value doesn't begin with opening curly brace", ErrorCodes::BAD_ODBC_CONNECTION_STRING);
97
98 while (pos < end)
99 {
100 if (*pos == '}')
101 {
102 ++pos;
103 if (pos >= end || *pos != '}')
104 return res;
105 }
106
107 if (*pos == 0)
108 throw Exception("ODBC connection string parameter value contains ASCII NUL character", ErrorCodes::BAD_ODBC_CONNECTION_STRING);
109
110 res += *pos;
111 ++pos;
112 }
113
114 throw Exception("ODBC connection string parameter is escaped but there is no closing curly brace", ErrorCodes::BAD_ODBC_CONNECTION_STRING);
115 };
116
117 auto read_value = [&]
118 {
119 if (pos >= end)
120 return std::string{};
121
122 if (*pos == '{')
123 return read_escaped_value();
124 else
125 return read_plain_value();
126 };
127
128 std::map<std::string, std::string> parameters;
129
130 while (pos < end)
131 {
132 skip_whitespaces();
133 std::string name = read_name();
134 skip_whitespaces();
135
136 Poco::toUpperInPlace(name);
137 if (name == "FILEDSN" || name == "SAVEFILE" || name == "DRIVER")
138 throw Exception("ODBC connection string has forbidden parameter", ErrorCodes::BAD_ODBC_CONNECTION_STRING);
139
140 if (pos >= end)
141 throw Exception("ODBC connection string parameter doesn't have value", ErrorCodes::BAD_ODBC_CONNECTION_STRING);
142
143 if (*pos == '=')
144 ++pos;
145 else
146 throw Exception("ODBC connection string parameter doesn't have value", ErrorCodes::BAD_ODBC_CONNECTION_STRING);
147
148 skip_whitespaces();
149 std::string value = read_value();
150 skip_whitespaces();
151
152 if (name.size() > MAX_ELEMENT_SIZE || value.size() > MAX_ELEMENT_SIZE)
153 throw Exception("ODBC connection string has too long keyword or value", ErrorCodes::BAD_ODBC_CONNECTION_STRING);
154
155 if (!parameters.emplace(name, value).second)
156 throw Exception("Duplicate parameter found in ODBC connection string", ErrorCodes::BAD_ODBC_CONNECTION_STRING);
157
158 if (pos >= end)
159 break;
160
161 if (*pos == ';')
162 ++pos;
163 else
164 throw Exception("Unexpected character found after parameter value in ODBC connection string", ErrorCodes::BAD_ODBC_CONNECTION_STRING);
165 }
166
167 /// Reconstruct the connection string.
168
169 auto it = parameters.find("DSN");
170
171 if (parameters.end() == it)
172 throw Exception("DSN parameter is mandatory for ODBC connection string", ErrorCodes::BAD_ODBC_CONNECTION_STRING);
173
174 std::string dsn = it->second;
175
176 if (dsn.empty())
177 throw Exception("DSN parameter cannot be empty in ODBC connection string", ErrorCodes::BAD_ODBC_CONNECTION_STRING);
178
179 parameters.erase(it);
180
181 std::string reconstructed_connection_string;
182
183 auto write_plain_value = [&](const std::string & value)
184 {
185 reconstructed_connection_string += value;
186 };
187
188 auto write_escaped_value = [&](const std::string & value)
189 {
190 reconstructed_connection_string += '{';
191
192 const char * value_pos = value.data();
193 const char * value_end = value_pos + value.size();
194 while (true)
195 {
196 const char * next_pos = find_first_symbols<'}'>(value_pos, value_end);
197
198 if (next_pos == value_end)
199 {
200 reconstructed_connection_string.append(value_pos, next_pos - value_pos);
201 break;
202 }
203 else
204 {
205 reconstructed_connection_string.append(value_pos, next_pos - value_pos);
206 reconstructed_connection_string.append("}}");
207 value_pos = next_pos + 1;
208 }
209 }
210
211 reconstructed_connection_string += '}';
212 };
213
214 auto write_value = [&](const std::string & value)
215 {
216 if (std::all_of(value.begin(), value.end(), isWordCharASCII))
217 write_plain_value(value);
218 else
219 write_escaped_value(value);
220 };
221
222 auto write_element = [&](const std::string & name, const std::string & value)
223 {
224 reconstructed_connection_string.append(name);
225 reconstructed_connection_string += '=';
226 write_value(value);
227 reconstructed_connection_string += ';';
228 };
229
230 /// Place DSN first because that's more safe.
231 write_element("DSN", dsn);
232 for (const auto & elem : parameters)
233 write_element(elem.first, elem.second);
234
235 if (reconstructed_connection_string.size() >= MAX_CONNECTION_STRING_SIZE)
236 throw Exception("ODBC connection string is too long", ErrorCodes::BAD_ODBC_CONNECTION_STRING);
237
238 return reconstructed_connection_string;
239}
240
241}
242