Skip to content

Commit c606713

Browse files
committed
RANGER-5567: allow validateConfig API available only for users with Ranger admin role
1 parent 3fd46db commit c606713

6 files changed

Lines changed: 564 additions & 2 deletions

File tree

hive-agent/src/main/java/org/apache/ranger/services/hive/client/HiveClient.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,7 @@ private void initConnection(String userName, String password) throws HadoopExcep
673673
String driverClassName = prop.getProperty("jdbc.driverClassName");
674674
String url = prop.getProperty("jdbc.url");
675675

676+
JdbcUrlValidator.validate(url);
676677
if (driverClassName != null) {
677678
try {
678679
Driver driver = (Driver) Class.forName(driverClassName).newInstance();
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.ranger.services.hive.client;
19+
20+
import org.apache.ranger.plugin.client.HadoopException;
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
23+
24+
import java.net.URLDecoder;
25+
import java.nio.charset.StandardCharsets;
26+
import java.util.Arrays;
27+
import java.util.Collections;
28+
import java.util.HashSet;
29+
import java.util.Set;
30+
31+
public final class JdbcUrlValidator {
32+
private static final Logger LOG = LoggerFactory.getLogger(JdbcUrlValidator.class);
33+
private static final Set<String> BLOCKED_PARAMS = Collections.unmodifiableSet(
34+
new HashSet<>(Arrays.asList(
35+
"socketfactory", "socketfactoryarg", "sslfactory", "sslfactoryarg",
36+
"sslhostnameverifier", "authenticationpluginclassname", "loggerclassname",
37+
"kerberosservername", "gssdelegatecred", "sslpasswordcallback")));
38+
private static final String[] DANGEROUS_PATTERNS = {"socketfactory", "sslfactory", "autodeserialize"};
39+
40+
private JdbcUrlValidator() {
41+
}
42+
43+
public static void validate(String jdbcUrl) throws HadoopException {
44+
if (jdbcUrl == null || jdbcUrl.trim().isEmpty()) {
45+
HadoopException e = new HadoopException("jdbc.url must not be null or empty");
46+
e.generateResponseDataMap(false, "Validation failed", "jdbc.url is required",
47+
null, "jdbc.url");
48+
throw e;
49+
}
50+
String trimmed = jdbcUrl.trim();
51+
int queryStart = findQueryStart(trimmed);
52+
if (queryStart != -1) {
53+
String queryString = trimmed.substring(queryStart + 1);
54+
validateQueryString(queryString, trimmed);
55+
}
56+
LOG.debug("jdbc.url passed validation: {}", sanitizeForLog(trimmed));
57+
}
58+
59+
private static void validateQueryString(String queryString, String fullUrl) throws HadoopException {
60+
String[] tokens = queryString.split("[&;?]");
61+
for (String token : tokens) {
62+
if (token.trim().isEmpty()) {
63+
continue;
64+
}
65+
int eqIdx = token.indexOf('=');
66+
String paramName = (eqIdx >= 0 ? token.substring(0, eqIdx) : token).trim();
67+
String decodedParamName = paramName;
68+
try {
69+
decodedParamName = URLDecoder.decode(paramName, StandardCharsets.UTF_8);
70+
} catch (Exception e) {
71+
LOG.warn("Failed to decode parameter name: {}", paramName);
72+
}
73+
74+
String normalized = decodedParamName.toLowerCase().trim().replaceAll("[._-]", "");
75+
if (BLOCKED_PARAMS.contains(normalized)) {
76+
logAndThrow("blocked parameter", normalized, paramName, fullUrl);
77+
}
78+
for (String danger : DANGEROUS_PATTERNS) {
79+
if (normalized.contains(danger)) {
80+
logAndThrow("dangerous pattern '" + danger + "'", normalized, paramName, fullUrl);
81+
}
82+
}
83+
if (normalized.contains("factory") && (normalized.contains("socket") || normalized.contains("ssl") ||
84+
normalized.contains("connection") || normalized.contains("auth") ||
85+
normalized.contains("driver") || normalized.contains("datasource"))) {
86+
logAndThrow("potentially dangerous factory parameter", normalized, paramName, fullUrl);
87+
}
88+
}
89+
}
90+
91+
static String sanitizeForLog(String url) {
92+
if (url == null) {
93+
return "<null>";
94+
}
95+
int idx = findQueryStart(url);
96+
return idx >= 0 ? url.substring(0, idx) + "?<params_redacted>" : url;
97+
}
98+
99+
private static int findQueryStart(String url) {
100+
int qIdx = url.indexOf('?');
101+
int sIdx = url.indexOf(';');
102+
if (qIdx >= 0 && sIdx >= 0) {
103+
return Math.min(qIdx, sIdx);
104+
} else if (qIdx >= 0) {
105+
return qIdx;
106+
} else if (sIdx >= 0) {
107+
return sIdx;
108+
}
109+
return -1;
110+
}
111+
112+
private static void logAndThrow(String reason, String normalized, String originalParam, String fullUrl) {
113+
LOG.warn("Rejected jdbc.url containing {} '{}' (param='{}'): {}", reason, normalized, originalParam, sanitizeForLog(fullUrl));
114+
HadoopException e = new HadoopException("jdbc.url contains a prohibited parameter: '" + originalParam +
115+
"'. This parameter is not permitted for security reasons.");
116+
e.generateResponseDataMap(false, "Invalid jdbc.url parameter", "Parameter '" +
117+
originalParam + "' is blocked", null, "jdbc.url");
118+
throw e;
119+
}
120+
}

0 commit comments

Comments
 (0)