1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.security.visibility;
19
20 import static org.apache.hadoop.hbase.TagType.VISIBILITY_TAG_TYPE;
21 import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_FAMILY;
22 import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_NAME;
23 import static org.apache.hadoop.hbase.security.visibility.VisibilityUtils.SYSTEM_LABEL;
24
25 import java.io.ByteArrayOutputStream;
26 import java.io.DataOutputStream;
27 import java.io.IOException;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.HashSet;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Set;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.apache.hadoop.conf.Configuration;
38 import org.apache.hadoop.hbase.Cell;
39 import org.apache.hadoop.hbase.CellUtil;
40 import org.apache.hadoop.hbase.HConstants.OperationStatusCode;
41 import org.apache.hadoop.hbase.Tag;
42 import org.apache.hadoop.hbase.TagType;
43 import org.apache.hadoop.hbase.classification.InterfaceAudience;
44 import org.apache.hadoop.hbase.client.Delete;
45 import org.apache.hadoop.hbase.client.Get;
46 import org.apache.hadoop.hbase.client.HTable;
47 import org.apache.hadoop.hbase.client.Put;
48 import org.apache.hadoop.hbase.client.Result;
49 import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
50 import org.apache.hadoop.hbase.regionserver.HRegion;
51 import org.apache.hadoop.hbase.regionserver.OperationStatus;
52 import org.apache.hadoop.hbase.security.User;
53 import org.apache.hadoop.hbase.security.access.AccessControlLists;
54 import org.apache.hadoop.hbase.security.visibility.expression.ExpressionNode;
55 import org.apache.hadoop.hbase.security.visibility.expression.LeafExpressionNode;
56 import org.apache.hadoop.hbase.security.visibility.expression.NonLeafExpressionNode;
57 import org.apache.hadoop.hbase.security.visibility.expression.Operator;
58 import org.apache.hadoop.hbase.util.Bytes;
59
60 import com.google.common.collect.Lists;
61
62
63
64
65
66
67
68 @InterfaceAudience.Private
69 public class ExpAsStringVisibilityLabelServiceImpl implements VisibilityLabelService {
70
71 private static final Log LOG = LogFactory.getLog(ExpAsStringVisibilityLabelServiceImpl.class);
72
73 private static final byte[] DUMMY_VALUE = new byte[0];
74 private static final byte STRING_SERIALIZATION_FORMAT = 2;
75 private static final Tag STRING_SERIALIZATION_FORMAT_TAG = new Tag(
76 TagType.VISIBILITY_EXP_SERIALIZATION_FORMAT_TAG_TYPE,
77 new byte[] { STRING_SERIALIZATION_FORMAT });
78 private final ExpressionParser expressionParser = new ExpressionParser();
79 private final ExpressionExpander expressionExpander = new ExpressionExpander();
80 private Configuration conf;
81 private HRegion labelsRegion;
82 private List<ScanLabelGenerator> scanLabelGenerators;
83 private List<String> superUsers;
84 private List<String> superGroups;
85
86 @Override
87 public OperationStatus[] addLabels(List<byte[]> labels) throws IOException {
88
89
90
91 OperationStatus[] status = new OperationStatus[labels.size()];
92 for (int i = 0; i < labels.size(); i++) {
93 status[i] = new OperationStatus(OperationStatusCode.SUCCESS);
94 }
95 return status;
96 }
97
98 @Override
99 public OperationStatus[] setAuths(byte[] user, List<byte[]> authLabels) throws IOException {
100 assert labelsRegion != null;
101 OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()];
102 Put p = new Put(user);
103 for (byte[] auth : authLabels) {
104 p.addImmutable(LABELS_TABLE_FAMILY, auth, DUMMY_VALUE);
105 }
106 this.labelsRegion.put(p);
107
108 for (int i = 0; i < authLabels.size(); i++) {
109 finalOpStatus[i] = new OperationStatus(OperationStatusCode.SUCCESS);
110 }
111 return finalOpStatus;
112 }
113
114 @Override
115 public OperationStatus[] clearAuths(byte[] user, List<byte[]> authLabels) throws IOException {
116 assert labelsRegion != null;
117 OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()];
118 List<String> currentAuths;
119 if (AccessControlLists.isGroupPrincipal(Bytes.toString(user))) {
120 String group = AccessControlLists.getGroupName(Bytes.toString(user));
121 currentAuths = this.getGroupAuths(new String[]{group}, true);
122 }
123 else {
124 currentAuths = this.getUserAuths(user, true);
125 }
126 Delete d = new Delete(user);
127 int i = 0;
128 for (byte[] authLabel : authLabels) {
129 String authLabelStr = Bytes.toString(authLabel);
130 if (currentAuths.contains(authLabelStr)) {
131 d.deleteColumns(LABELS_TABLE_FAMILY, authLabel);
132 } else {
133
134 finalOpStatus[i] = new OperationStatus(OperationStatusCode.FAILURE,
135 new InvalidLabelException("Label '" + authLabelStr + "' is not set for the user "
136 + Bytes.toString(user)));
137 }
138 i++;
139 }
140 this.labelsRegion.delete(d);
141
142 for (i = 0; i < authLabels.size(); i++) {
143 if (finalOpStatus[i] == null) {
144 finalOpStatus[i] = new OperationStatus(OperationStatusCode.SUCCESS);
145 }
146 }
147 return finalOpStatus;
148 }
149
150 @Override
151 @Deprecated
152 public List<String> getAuths(byte[] user, boolean systemCall) throws IOException {
153 return getUserAuths(user, systemCall);
154 }
155
156 @Override
157 public List<String> getUserAuths(byte[] user, boolean systemCall) throws IOException {
158 assert (labelsRegion != null || systemCall);
159 List<String> auths = new ArrayList<String>();
160 Get get = new Get(user);
161 List<Cell> cells = null;
162 if (labelsRegion == null) {
163 HTable table = null;
164 try {
165 table = new HTable(conf, VisibilityConstants.LABELS_TABLE_NAME);
166 Result result = table.get(get);
167 cells = result.listCells();
168 } finally {
169 if (table != null) {
170 table.close();
171 }
172 }
173 } else {
174 cells = this.labelsRegion.get(get, false);
175 }
176 if (cells != null) {
177 for (Cell cell : cells) {
178 String auth = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(),
179 cell.getQualifierLength());
180 auths.add(auth);
181 }
182 }
183 return auths;
184 }
185
186 @Override
187 public List<String> getGroupAuths(String[] groups, boolean systemCall) throws IOException {
188 assert (labelsRegion != null || systemCall);
189 List<String> auths = new ArrayList<String>();
190 if (groups != null && groups.length > 0) {
191 for (String group : groups) {
192 Get get = new Get(Bytes.toBytes(AccessControlLists.toGroupEntry(group)));
193 List<Cell> cells = null;
194 if (labelsRegion == null) {
195 HTable table = null;
196 try {
197 table = new HTable(conf, VisibilityConstants.LABELS_TABLE_NAME);
198 Result result = table.get(get);
199 cells = result.listCells();
200 } finally {
201 if (table != null) {
202 table.close();
203 }
204 }
205 } else {
206 cells = this.labelsRegion.get(get, false);
207 }
208 if (cells != null) {
209 for (Cell cell : cells) {
210 String auth = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(),
211 cell.getQualifierLength());
212 auths.add(auth);
213 }
214 }
215 }
216 }
217 return auths;
218 }
219
220 @Override
221 public List<String> listLabels(String regex) throws IOException {
222
223 return new ArrayList<String>();
224 }
225
226 @Override
227 public List<Tag> createVisibilityExpTags(String visExpression, boolean withSerializationFormat,
228 boolean checkAuths) throws IOException {
229 ExpressionNode node = null;
230 try {
231 node = this.expressionParser.parse(visExpression);
232 } catch (ParseException e) {
233 throw new IOException(e);
234 }
235 node = this.expressionExpander.expand(node);
236 List<Tag> tags = new ArrayList<Tag>();
237 if (withSerializationFormat) {
238 tags.add(STRING_SERIALIZATION_FORMAT_TAG);
239 }
240 if (node instanceof NonLeafExpressionNode
241 && ((NonLeafExpressionNode) node).getOperator() == Operator.OR) {
242 for (ExpressionNode child : ((NonLeafExpressionNode) node).getChildExps()) {
243 tags.add(createTag(child));
244 }
245 } else {
246 tags.add(createTag(node));
247 }
248 return tags;
249 }
250
251 @Override
252 public VisibilityExpEvaluator getVisibilityExpEvaluator(Authorizations authorizations)
253 throws IOException {
254
255
256 if (isReadFromSystemAuthUser()) {
257 return new VisibilityExpEvaluator() {
258 @Override
259 public boolean evaluate(Cell cell) throws IOException {
260 return true;
261 }
262 };
263 }
264 List<String> authLabels = null;
265 for (ScanLabelGenerator scanLabelGenerator : scanLabelGenerators) {
266 try {
267
268 authLabels = scanLabelGenerator.getLabels(VisibilityUtils.getActiveUser(), authorizations);
269 authLabels = (authLabels == null) ? new ArrayList<String>() : authLabels;
270 authorizations = new Authorizations(authLabels);
271 } catch (Throwable t) {
272 LOG.error(t);
273 throw new IOException(t);
274 }
275 }
276 final List<String> authLabelsFinal = authLabels;
277 return new VisibilityExpEvaluator() {
278 @Override
279 public boolean evaluate(Cell cell) throws IOException {
280 boolean visibilityTagPresent = false;
281
282 if (cell.getTagsLengthUnsigned() > 0) {
283 Iterator<Tag> tagsItr = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(),
284 cell.getTagsLengthUnsigned());
285 while (tagsItr.hasNext()) {
286 boolean includeKV = true;
287 Tag tag = tagsItr.next();
288 if (tag.getType() == VISIBILITY_TAG_TYPE) {
289 visibilityTagPresent = true;
290 int offset = tag.getTagOffset();
291 int endOffset = offset + tag.getTagLength();
292 while (offset < endOffset) {
293 short len = Bytes.toShort(tag.getBuffer(), offset);
294 offset += 2;
295 if (len < 0) {
296
297 len = (short) (-1 * len);
298 String label = Bytes.toString(tag.getBuffer(), offset, len);
299 if (authLabelsFinal.contains(label)) {
300 includeKV = false;
301 break;
302 }
303 } else {
304 String label = Bytes.toString(tag.getBuffer(), offset, len);
305 if (!authLabelsFinal.contains(label)) {
306 includeKV = false;
307 break;
308 }
309 }
310 offset += len;
311 }
312 if (includeKV) {
313
314
315
316 return true;
317 }
318 }
319 }
320 }
321 return !(visibilityTagPresent);
322 }
323 };
324 }
325
326 protected boolean isReadFromSystemAuthUser() throws IOException {
327 User user = VisibilityUtils.getActiveUser();
328 return havingSystemAuth(user);
329 }
330
331 private Tag createTag(ExpressionNode node) throws IOException {
332 ByteArrayOutputStream baos = new ByteArrayOutputStream();
333 DataOutputStream dos = new DataOutputStream(baos);
334 List<String> labels = new ArrayList<String>();
335 List<String> notLabels = new ArrayList<String>();
336 extractLabels(node, labels, notLabels);
337 Collections.sort(labels);
338 Collections.sort(notLabels);
339
340
341
342
343 for (String label : notLabels) {
344 byte[] bLabel = Bytes.toBytes(label);
345 short length = (short) bLabel.length;
346 length = (short) (-1 * length);
347 dos.writeShort(length);
348 dos.write(bLabel);
349 }
350 for (String label : labels) {
351 byte[] bLabel = Bytes.toBytes(label);
352 dos.writeShort(bLabel.length);
353 dos.write(bLabel);
354 }
355 return new Tag(VISIBILITY_TAG_TYPE, baos.toByteArray());
356 }
357
358 private void extractLabels(ExpressionNode node, List<String> labels, List<String> notLabels) {
359 if (node.isSingleNode()) {
360 if (node instanceof NonLeafExpressionNode) {
361
362 LeafExpressionNode lNode = (LeafExpressionNode) ((NonLeafExpressionNode) node)
363 .getChildExps().get(0);
364 notLabels.add(lNode.getIdentifier());
365 } else {
366 labels.add(((LeafExpressionNode) node).getIdentifier());
367 }
368 } else {
369
370 NonLeafExpressionNode nlNode = (NonLeafExpressionNode) node;
371 assert nlNode.getOperator() == Operator.AND;
372 List<ExpressionNode> childExps = nlNode.getChildExps();
373 for (ExpressionNode child : childExps) {
374 extractLabels(child, labels, notLabels);
375 }
376 }
377 }
378
379 @Override
380 public Configuration getConf() {
381 return this.conf;
382 }
383
384 @Override
385 public void setConf(Configuration conf) {
386 this.conf = conf;
387 }
388
389 @Override
390 public void init(RegionCoprocessorEnvironment e) throws IOException {
391 this.scanLabelGenerators = VisibilityUtils.getScanLabelGenerators(this.conf);
392 initSystemAndSuperUsers();
393 if (e.getRegion().getRegionInfo().getTable().equals(LABELS_TABLE_NAME)) {
394 this.labelsRegion = e.getRegion();
395 }
396 }
397
398 private void initSystemAndSuperUsers() throws IOException {
399 this.superUsers = new ArrayList<String>();
400 this.superGroups = new ArrayList<String>();
401 User user = User.getCurrent();
402 if (user == null) {
403 throw new IOException("Unable to obtain the current user, "
404 + "authorization checks for internal operations will not work correctly!");
405 }
406 if (LOG.isTraceEnabled()) {
407 LOG.trace("Current user name is " + user.getShortName());
408 }
409 String currentUser = user.getShortName();
410 List<String> superUserList = Lists.asList(currentUser,
411 this.conf.getStrings(AccessControlLists.SUPERUSER_CONF_KEY, new String[0]));
412 if (superUserList != null) {
413 for (String name : superUserList) {
414 if (AccessControlLists.isGroupPrincipal(name)) {
415 this.superGroups.add(AccessControlLists.getGroupName(name));
416 } else {
417 this.superUsers.add(name);
418 }
419 }
420 };
421 }
422
423 protected boolean isSystemOrSuperUser(User user) throws IOException {
424 if (this.superUsers.contains(user.getShortName())) {
425 return true;
426 }
427 String[] groups = user.getGroupNames();
428 if (groups != null) {
429 for (String group : groups) {
430 if (this.superGroups.contains(group)) {
431 return true;
432 }
433 }
434 }
435 return false;
436 }
437
438 @Override
439 @Deprecated
440 public boolean havingSystemAuth(byte[] user) throws IOException {
441
442 if (this.superUsers.contains(Bytes.toString(user))) {
443 return true;
444 }
445 List<String> auths = this.getUserAuths(user, true);
446 if (LOG.isTraceEnabled()) {
447 LOG.trace("The auths for user " + Bytes.toString(user) + " are " + auths);
448 }
449 return auths.contains(SYSTEM_LABEL);
450 }
451
452 @Override
453 public boolean havingSystemAuth(User user) throws IOException {
454 if (isSystemOrSuperUser(user)) {
455 return true;
456 }
457 Set<String> auths = new HashSet<String>();
458 auths.addAll(this.getUserAuths(Bytes.toBytes(user.getShortName()), true));
459 auths.addAll(this.getGroupAuths(user.getGroupNames(), true));
460 return auths.contains(SYSTEM_LABEL);
461 }
462
463 @Override
464 public boolean matchVisibility(List<Tag> putTags, Byte putTagsFormat, List<Tag> deleteTags,
465 Byte deleteTagsFormat) throws IOException {
466 assert putTagsFormat == STRING_SERIALIZATION_FORMAT;
467 assert deleteTagsFormat == STRING_SERIALIZATION_FORMAT;
468 return checkForMatchingVisibilityTagsWithSortedOrder(putTags, deleteTags);
469 }
470
471 private static boolean checkForMatchingVisibilityTagsWithSortedOrder(List<Tag> putVisTags,
472 List<Tag> deleteVisTags) {
473 boolean matchFound = false;
474
475
476 if ((deleteVisTags.size()) == putVisTags.size()) {
477 for (Tag tag : deleteVisTags) {
478 matchFound = false;
479 for (Tag givenTag : putVisTags) {
480 if (Bytes.equals(tag.getBuffer(), tag.getTagOffset(), tag.getTagLength(),
481 givenTag.getBuffer(), givenTag.getTagOffset(), givenTag.getTagLength())) {
482 matchFound = true;
483 break;
484 }
485 }
486 if (!matchFound)
487 break;
488 }
489 }
490 return matchFound;
491 }
492
493 @Override
494 public byte[] encodeVisibilityForReplication(final List<Tag> tags, final Byte serializationFormat)
495 throws IOException {
496 if (tags.size() > 0 && (serializationFormat == null
497 || serializationFormat == STRING_SERIALIZATION_FORMAT)) {
498 return createModifiedVisExpression(tags);
499 }
500 return null;
501 }
502
503
504
505
506
507 private byte[] createModifiedVisExpression(final List<Tag> tags)
508 throws IOException {
509 StringBuilder visibilityString = new StringBuilder();
510 for (Tag tag : tags) {
511 if (tag.getType() == TagType.VISIBILITY_TAG_TYPE) {
512 if (visibilityString.length() != 0) {
513 visibilityString.append(VisibilityConstants.CLOSED_PARAN
514 + VisibilityConstants.OR_OPERATOR);
515 }
516 int offset = tag.getTagOffset();
517 int endOffset = offset + tag.getTagLength();
518 boolean expressionStart = true;
519 while (offset < endOffset) {
520 short len = Bytes.toShort(tag.getBuffer(), offset);
521 offset += 2;
522 if (len < 0) {
523 len = (short) (-1 * len);
524 String label = Bytes.toString(tag.getBuffer(), offset, len);
525 if (expressionStart) {
526 visibilityString.append(VisibilityConstants.OPEN_PARAN
527 + VisibilityConstants.NOT_OPERATOR + CellVisibility.quote(label));
528 } else {
529 visibilityString.append(VisibilityConstants.AND_OPERATOR
530 + VisibilityConstants.NOT_OPERATOR + CellVisibility.quote(label));
531 }
532 } else {
533 String label = Bytes.toString(tag.getBuffer(), offset, len);
534 if (expressionStart) {
535 visibilityString.append(VisibilityConstants.OPEN_PARAN + CellVisibility.quote(label));
536 } else {
537 visibilityString.append(VisibilityConstants.AND_OPERATOR
538 + CellVisibility.quote(label));
539 }
540 }
541 expressionStart = false;
542 offset += len;
543 }
544 }
545 }
546 if (visibilityString.length() != 0) {
547 visibilityString.append(VisibilityConstants.CLOSED_PARAN);
548
549 return Bytes.toBytes(visibilityString.toString());
550 }
551 return null;
552 }
553 }