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 | |
11 | namespace DB |
12 | { |
13 | |
14 | namespace ErrorCodes |
15 | { |
16 | extern const int BAD_ODBC_CONNECTION_STRING; |
17 | } |
18 | |
19 | |
20 | std::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 | |