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.VisibilityConstants.LABEL_QUALIFIER;
24 import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.SORTED_ORDINAL_SERIALIZATION_FORMAT;
25 import static org.apache.hadoop.hbase.security.visibility.VisibilityUtils.SYSTEM_LABEL;
26
27 import java.io.ByteArrayOutputStream;
28 import java.io.DataOutputStream;
29 import java.io.IOException;
30 import java.util.ArrayList;
31 import java.util.BitSet;
32 import java.util.Collections;
33 import java.util.HashMap;
34 import java.util.Iterator;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38
39 import org.apache.commons.logging.Log;
40 import org.apache.commons.logging.LogFactory;
41 import org.apache.hadoop.classification.InterfaceAudience;
42 import org.apache.hadoop.conf.Configuration;
43 import org.apache.hadoop.hbase.Cell;
44 import org.apache.hadoop.hbase.CellUtil;
45 import org.apache.hadoop.hbase.HConstants.OperationStatusCode;
46 import org.apache.hadoop.hbase.Tag;
47 import org.apache.hadoop.hbase.client.Delete;
48 import org.apache.hadoop.hbase.client.Mutation;
49 import org.apache.hadoop.hbase.client.Put;
50 import org.apache.hadoop.hbase.client.Scan;
51 import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
52 import org.apache.hadoop.hbase.filter.Filter;
53 import org.apache.hadoop.hbase.io.util.StreamUtils;
54 import org.apache.hadoop.hbase.regionserver.HRegion;
55 import org.apache.hadoop.hbase.regionserver.OperationStatus;
56 import org.apache.hadoop.hbase.regionserver.RegionScanner;
57 import org.apache.hadoop.hbase.security.User;
58 import org.apache.hadoop.hbase.security.access.AccessControlLists;
59 import org.apache.hadoop.hbase.util.Bytes;
60 import org.apache.hadoop.hbase.util.Pair;
61 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
62
63 import com.google.common.collect.Lists;
64
65 @InterfaceAudience.Private
66 public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService {
67
68 private static final Log LOG = LogFactory.getLog(DefaultVisibilityLabelServiceImpl.class);
69
70
71 private static final int SYSTEM_LABEL_ORDINAL = 1;
72 private static final Tag[] LABELS_TABLE_TAGS = new Tag[1];
73 private static final byte[] DUMMY_VALUE = new byte[0];
74
75 private volatile int ordinalCounter = -1;
76 private Configuration conf;
77 private HRegion labelsRegion;
78 private VisibilityLabelsCache labelsCache;
79 private List<ScanLabelGenerator> scanLabelGenerators;
80
81 static {
82 ByteArrayOutputStream baos = new ByteArrayOutputStream();
83 DataOutputStream dos = new DataOutputStream(baos);
84 try {
85 StreamUtils.writeRawVInt32(dos, SYSTEM_LABEL_ORDINAL);
86 } catch (IOException e) {
87
88 }
89 LABELS_TABLE_TAGS[0] = new Tag(VISIBILITY_TAG_TYPE, baos.toByteArray());
90 }
91
92 public DefaultVisibilityLabelServiceImpl() {
93
94 }
95
96 @Override
97 public void setConf(Configuration conf) {
98 this.conf = conf;
99 }
100
101 @Override
102 public Configuration getConf() {
103 return this.conf;
104 }
105
106 @Override
107 public void init(RegionCoprocessorEnvironment e) throws IOException {
108 ZooKeeperWatcher zk = e.getRegionServerServices().getZooKeeper();
109 try {
110 labelsCache = VisibilityLabelsCache.createAndGet(zk, this.conf);
111 } catch (IOException ioe) {
112 LOG.error("Error creating VisibilityLabelsCache", ioe);
113 throw ioe;
114 }
115 this.scanLabelGenerators = VisibilityUtils.getScanLabelGenerators(this.conf);
116 if (e.getRegion().getRegionInfo().getTable().equals(LABELS_TABLE_NAME)) {
117 this.labelsRegion = e.getRegion();
118 Pair<Map<String, Integer>, Map<String, List<Integer>>> labelsAndUserAuths =
119 extractLabelsAndAuths(getExistingLabelsWithAuths());
120 Map<String, Integer> labels = labelsAndUserAuths.getFirst();
121 Map<String, List<Integer>> userAuths = labelsAndUserAuths.getSecond();
122
123 addSystemLabel(this.labelsRegion, labels, userAuths);
124 int ordinal = SYSTEM_LABEL_ORDINAL;
125 for (Integer i : labels.values()) {
126 if (i > ordinal) {
127 ordinal = i;
128 }
129 }
130 this.ordinalCounter = ordinal + 1;
131 if (labels.size() > 0) {
132
133 byte[] serialized = VisibilityUtils.getDataToWriteToZooKeeper(labels);
134 this.labelsCache.writeToZookeeper(serialized, true);
135 this.labelsCache.refreshLabelsCache(serialized);
136 }
137 if (userAuths.size() > 0) {
138 byte[] serialized = VisibilityUtils.getUserAuthsDataToWriteToZooKeeper(userAuths);
139 this.labelsCache.writeToZookeeper(serialized, false);
140 this.labelsCache.refreshUserAuthsCache(serialized);
141 }
142 }
143 }
144
145 protected List<List<Cell>> getExistingLabelsWithAuths() throws IOException {
146 Scan scan = new Scan();
147 RegionScanner scanner = labelsRegion.getScanner(scan);
148 List<List<Cell>> existingLabels = new ArrayList<List<Cell>>();
149 try {
150 while (true) {
151 List<Cell> cells = new ArrayList<Cell>();
152 scanner.next(cells);
153 if (cells.isEmpty()) {
154 break;
155 }
156 existingLabels.add(cells);
157 }
158 } finally {
159 scanner.close();
160 }
161 return existingLabels;
162 }
163
164 protected Pair<Map<String, Integer>, Map<String, List<Integer>>> extractLabelsAndAuths(
165 List<List<Cell>> labelDetails) {
166 Map<String, Integer> labels = new HashMap<String, Integer>();
167 Map<String, List<Integer>> userAuths = new HashMap<String, List<Integer>>();
168 for (List<Cell> cells : labelDetails) {
169 for (Cell cell : cells) {
170 if (Bytes.equals(cell.getQualifierArray(), cell.getQualifierOffset(),
171 cell.getQualifierLength(), LABEL_QUALIFIER, 0, LABEL_QUALIFIER.length)) {
172 labels.put(
173 Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()),
174 Bytes.toInt(cell.getRowArray(), cell.getRowOffset()));
175 } else {
176
177 String user = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(),
178 cell.getQualifierLength());
179 List<Integer> auths = userAuths.get(user);
180 if (auths == null) {
181 auths = new ArrayList<Integer>();
182 userAuths.put(user, auths);
183 }
184 auths.add(Bytes.toInt(cell.getRowArray(), cell.getRowOffset()));
185 }
186 }
187 }
188 return new Pair<Map<String, Integer>, Map<String, List<Integer>>>(labels, userAuths);
189 }
190
191 protected void addSystemLabel(HRegion region, Map<String, Integer> labels,
192 Map<String, List<Integer>> userAuths) throws IOException {
193 if (!labels.containsKey(SYSTEM_LABEL)) {
194 Put p = new Put(Bytes.toBytes(SYSTEM_LABEL_ORDINAL));
195 p.addImmutable(LABELS_TABLE_FAMILY, LABEL_QUALIFIER, Bytes.toBytes(SYSTEM_LABEL));
196
197 List<String> superUsers = getSystemAndSuperUsers();
198 for (String superUser : superUsers) {
199 p.addImmutable(LABELS_TABLE_FAMILY, Bytes.toBytes(superUser), DUMMY_VALUE,
200 LABELS_TABLE_TAGS);
201 }
202 region.put(p);
203 labels.put(SYSTEM_LABEL, SYSTEM_LABEL_ORDINAL);
204 for (String superUser : superUsers) {
205 List<Integer> auths = userAuths.get(superUser);
206 if (auths == null) {
207 auths = new ArrayList<Integer>(1);
208 userAuths.put(superUser, auths);
209 }
210 auths.add(SYSTEM_LABEL_ORDINAL);
211 }
212 }
213 }
214
215 protected List<String> getSystemAndSuperUsers() throws IOException {
216 User user = User.getCurrent();
217 if (user == null) {
218 throw new IOException("Unable to obtain the current user, "
219 + "authorization checks for internal operations will not work correctly!");
220 }
221 if (LOG.isTraceEnabled()) {
222 LOG.trace("Current user name is " + user.getShortName());
223 }
224 String currentUser = user.getShortName();
225 List<String> superUsers = Lists.asList(currentUser,
226 this.conf.getStrings(AccessControlLists.SUPERUSER_CONF_KEY, new String[0]));
227 return superUsers;
228 }
229
230 @Override
231 public OperationStatus[] addLabels(List<byte[]> labels) throws IOException {
232 assert labelsRegion != null;
233 OperationStatus[] finalOpStatus = new OperationStatus[labels.size()];
234 List<Mutation> puts = new ArrayList<Mutation>(labels.size());
235 int i = 0;
236 for (byte[] label : labels) {
237 String labelStr = Bytes.toString(label);
238 if (this.labelsCache.getLabelOrdinal(labelStr) > 0) {
239 finalOpStatus[i] = new OperationStatus(OperationStatusCode.FAILURE,
240 new LabelAlreadyExistsException("Label '" + labelStr + "' already exists"));
241 } else {
242 Put p = new Put(Bytes.toBytes(ordinalCounter));
243 p.addImmutable(LABELS_TABLE_FAMILY, LABEL_QUALIFIER, label, LABELS_TABLE_TAGS);
244 if (LOG.isDebugEnabled()) {
245 LOG.debug("Adding the label " + labelStr);
246 }
247 puts.add(p);
248 ordinalCounter++;
249 }
250 i++;
251 }
252 if (mutateLabelsRegion(puts, finalOpStatus)) {
253 updateZk(true);
254 }
255 return finalOpStatus;
256 }
257
258 @Override
259 public OperationStatus[] setAuths(byte[] user, List<byte[]> authLabels) throws IOException {
260 assert labelsRegion != null;
261 OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()];
262 List<Mutation> puts = new ArrayList<Mutation>(authLabels.size());
263 int i = 0;
264 for (byte[] auth : authLabels) {
265 String authStr = Bytes.toString(auth);
266 int labelOrdinal = this.labelsCache.getLabelOrdinal(authStr);
267 if (labelOrdinal == 0) {
268
269 finalOpStatus[i] = new OperationStatus(OperationStatusCode.FAILURE,
270 new InvalidLabelException("Label '" + authStr + "' doesn't exists"));
271 } else {
272 Put p = new Put(Bytes.toBytes(labelOrdinal));
273 p.addImmutable(LABELS_TABLE_FAMILY, user, DUMMY_VALUE, LABELS_TABLE_TAGS);
274 puts.add(p);
275 }
276 i++;
277 }
278 if (mutateLabelsRegion(puts, finalOpStatus)) {
279 updateZk(false);
280 }
281 return finalOpStatus;
282 }
283
284 @Override
285 public OperationStatus[] clearAuths(byte[] user, List<byte[]> authLabels) throws IOException {
286 assert labelsRegion != null;
287 OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()];
288 List<String> currentAuths = this.getAuths(user, true);
289 List<Mutation> deletes = new ArrayList<Mutation>(authLabels.size());
290 int i = 0;
291 for (byte[] authLabel : authLabels) {
292 String authLabelStr = Bytes.toString(authLabel);
293 if (currentAuths.contains(authLabelStr)) {
294 int labelOrdinal = this.labelsCache.getLabelOrdinal(authLabelStr);
295 assert labelOrdinal > 0;
296 Delete d = new Delete(Bytes.toBytes(labelOrdinal));
297 d.deleteColumns(LABELS_TABLE_FAMILY, user);
298 deletes.add(d);
299 } else {
300
301 finalOpStatus[i] = new OperationStatus(OperationStatusCode.FAILURE,
302 new InvalidLabelException("Label '" + authLabelStr + "' is not set for the user "
303 + Bytes.toString(user)));
304 }
305 i++;
306 }
307 if (mutateLabelsRegion(deletes, finalOpStatus)) {
308 updateZk(false);
309 }
310 return finalOpStatus;
311 }
312
313
314
315
316
317
318
319
320
321 private boolean mutateLabelsRegion(List<Mutation> mutations, OperationStatus[] finalOpStatus)
322 throws IOException {
323 OperationStatus[] opStatus = this.labelsRegion.batchMutate(mutations
324 .toArray(new Mutation[mutations.size()]));
325 int i = 0;
326 boolean updateZk = false;
327 for (OperationStatus status : opStatus) {
328
329 updateZk = updateZk || (status.getOperationStatusCode() == OperationStatusCode.SUCCESS);
330 for (; i < finalOpStatus.length; i++) {
331 if (finalOpStatus[i] == null) {
332 finalOpStatus[i] = status;
333 break;
334 }
335 }
336 }
337 return updateZk;
338 }
339
340 @Override
341 public List<String> getAuths(byte[] user, boolean systemCall) throws IOException {
342 assert (labelsRegion != null || systemCall);
343 if (systemCall || labelsRegion == null) {
344 return this.labelsCache.getAuths(Bytes.toString(user));
345 }
346 Scan s = new Scan();
347 s.addColumn(LABELS_TABLE_FAMILY, user);
348 Filter filter = VisibilityUtils.createVisibilityLabelFilter(this.labelsRegion,
349 new Authorizations(SYSTEM_LABEL));
350 s.setFilter(filter);
351 List<String> auths = new ArrayList<String>();
352 RegionScanner scanner = this.labelsRegion.getScanner(s);
353 List<Cell> results = new ArrayList<Cell>(1);
354 while (true) {
355 scanner.next(results);
356 if (results.isEmpty()) break;
357 Cell cell = results.get(0);
358 int ordinal = Bytes.toInt(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
359 String label = this.labelsCache.getLabel(ordinal);
360 if (label != null) {
361 auths.add(label);
362 }
363 results.clear();
364 }
365 return auths;
366 }
367
368 @Override
369 public List<Tag> createVisibilityExpTags(String visExpression, boolean withSerializationFormat,
370 boolean checkAuths) throws IOException {
371 Set<Integer> auths = null;
372 if (checkAuths) {
373 auths = this.labelsCache.getAuthsAsOrdinals(VisibilityUtils.getActiveUser().getShortName());
374 }
375 return VisibilityUtils.createVisibilityExpTags(visExpression, withSerializationFormat,
376 checkAuths, auths, labelsCache);
377 }
378
379 protected void updateZk(boolean labelAddition) throws IOException {
380
381
382
383
384 Pair<Map<String, Integer>, Map<String, List<Integer>>> labelsAndUserAuths =
385 extractLabelsAndAuths(getExistingLabelsWithAuths());
386 Map<String, Integer> existingLabels = labelsAndUserAuths.getFirst();
387 Map<String, List<Integer>> userAuths = labelsAndUserAuths.getSecond();
388 if (labelAddition) {
389 byte[] serialized = VisibilityUtils.getDataToWriteToZooKeeper(existingLabels);
390 this.labelsCache.writeToZookeeper(serialized, true);
391 } else {
392 byte[] serialized = VisibilityUtils.getUserAuthsDataToWriteToZooKeeper(userAuths);
393 this.labelsCache.writeToZookeeper(serialized, false);
394 }
395 }
396
397 @Override
398 public VisibilityExpEvaluator getVisibilityExpEvaluator(Authorizations authorizations)
399 throws IOException {
400
401
402 if (isReadFromSuperUser()) {
403 return new VisibilityExpEvaluator() {
404 @Override
405 public boolean evaluate(Cell cell) throws IOException {
406 return true;
407 }
408 };
409 }
410 List<String> authLabels = null;
411 for (ScanLabelGenerator scanLabelGenerator : scanLabelGenerators) {
412 try {
413
414 authLabels = scanLabelGenerator.getLabels(VisibilityUtils.getActiveUser(), authorizations);
415 authLabels = (authLabels == null) ? new ArrayList<String>() : authLabels;
416 authorizations = new Authorizations(authLabels);
417 } catch (Throwable t) {
418 LOG.error(t);
419 throw new IOException(t);
420 }
421 }
422 int labelsCount = this.labelsCache.getLabelsCount();
423 final BitSet bs = new BitSet(labelsCount + 1);
424 if (authLabels != null) {
425 for (String authLabel : authLabels) {
426 int labelOrdinal = this.labelsCache.getLabelOrdinal(authLabel);
427 if (labelOrdinal != 0) {
428 bs.set(labelOrdinal);
429 }
430 }
431 }
432
433 return new VisibilityExpEvaluator() {
434 @Override
435 public boolean evaluate(Cell cell) throws IOException {
436 boolean visibilityTagPresent = false;
437
438 if (cell.getTagsLengthUnsigned() > 0) {
439 Iterator<Tag> tagsItr = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(),
440 cell.getTagsLengthUnsigned());
441 while (tagsItr.hasNext()) {
442 boolean includeKV = true;
443 Tag tag = tagsItr.next();
444 if (tag.getType() == VISIBILITY_TAG_TYPE) {
445 visibilityTagPresent = true;
446 int offset = tag.getTagOffset();
447 int endOffset = offset + tag.getTagLength();
448 while (offset < endOffset) {
449 Pair<Integer, Integer> result = StreamUtils
450 .readRawVarint32(tag.getBuffer(), offset);
451 int currLabelOrdinal = result.getFirst();
452 if (currLabelOrdinal < 0) {
453
454
455 int temp = -currLabelOrdinal;
456 if (bs.get(temp)) {
457 includeKV = false;
458 break;
459 }
460 } else {
461 if (!bs.get(currLabelOrdinal)) {
462 includeKV = false;
463 break;
464 }
465 }
466 offset += result.getSecond();
467 }
468 if (includeKV) {
469
470
471 return true;
472 }
473 }
474 }
475 }
476 return !(visibilityTagPresent);
477 }
478 };
479 }
480
481 protected boolean isReadFromSuperUser() throws IOException {
482 byte[] user = Bytes.toBytes(VisibilityUtils.getActiveUser().getShortName());
483 return havingSystemAuth(user);
484 }
485
486 @Override
487 public boolean havingSystemAuth(byte[] user) throws IOException {
488 List<String> auths = this.getAuths(user, true);
489 if (LOG.isTraceEnabled()) {
490 LOG.trace("The auths for user " + Bytes.toString(user) + " are " + auths);
491 }
492 return auths.contains(SYSTEM_LABEL);
493 }
494
495 @Override
496 public boolean matchVisibility(List<Tag> putVisTags, Byte putTagsFormat, List<Tag> deleteVisTags,
497 Byte deleteTagsFormat) throws IOException {
498 if ((deleteTagsFormat != null && deleteTagsFormat == SORTED_ORDINAL_SERIALIZATION_FORMAT)
499 && (putTagsFormat == null || putTagsFormat == SORTED_ORDINAL_SERIALIZATION_FORMAT)) {
500 if (putVisTags.size() == 0) {
501
502 return false;
503 }
504 if (putTagsFormat == null) {
505 return matchUnSortedVisibilityTags(putVisTags, deleteVisTags);
506 } else {
507 return matchOrdinalSortedVisibilityTags(putVisTags, deleteVisTags);
508 }
509 }
510 throw new IOException("Unexpected tag format passed for comparison, deleteTagsFormat : "
511 + deleteTagsFormat + ", putTagsFormat : " + putTagsFormat);
512 }
513
514
515
516
517
518
519
520 private static boolean matchUnSortedVisibilityTags(List<Tag> putVisTags,
521 List<Tag> deleteVisTags) throws IOException {
522 return compareTagsOrdinals(sortTagsBasedOnOrdinal(putVisTags),
523 sortTagsBasedOnOrdinal(deleteVisTags));
524 }
525
526
527
528
529
530
531
532 private static boolean matchOrdinalSortedVisibilityTags(List<Tag> putVisTags,
533 List<Tag> deleteVisTags) {
534 boolean matchFound = false;
535
536 if ((deleteVisTags.size()) == putVisTags.size()) {
537 for (Tag tag : deleteVisTags) {
538 matchFound = false;
539 for (Tag givenTag : putVisTags) {
540 if (Bytes.equals(tag.getBuffer(), tag.getTagOffset(), tag.getTagLength(),
541 givenTag.getBuffer(), givenTag.getTagOffset(), givenTag.getTagLength())) {
542 matchFound = true;
543 break;
544 }
545 }
546 if (!matchFound) break;
547 }
548 }
549 return matchFound;
550 }
551
552 private static List<List<Integer>> sortTagsBasedOnOrdinal(List<Tag> tags) throws IOException {
553 List<List<Integer>> fullTagsList = new ArrayList<List<Integer>>();
554 for (Tag tag : tags) {
555 if (tag.getType() == VISIBILITY_TAG_TYPE) {
556 getSortedTagOrdinals(fullTagsList, tag);
557 }
558 }
559 return fullTagsList;
560 }
561
562 private static void getSortedTagOrdinals(List<List<Integer>> fullTagsList, Tag tag)
563 throws IOException {
564 List<Integer> tagsOrdinalInSortedOrder = new ArrayList<Integer>();
565 int offset = tag.getTagOffset();
566 int endOffset = offset + tag.getTagLength();
567 while (offset < endOffset) {
568 Pair<Integer, Integer> result = StreamUtils.readRawVarint32(tag.getBuffer(), offset);
569 tagsOrdinalInSortedOrder.add(result.getFirst());
570 offset += result.getSecond();
571 }
572 Collections.sort(tagsOrdinalInSortedOrder);
573 fullTagsList.add(tagsOrdinalInSortedOrder);
574 }
575
576
577
578
579 private static boolean compareTagsOrdinals(List<List<Integer>> putVisTags,
580 List<List<Integer>> deleteVisTags) {
581 boolean matchFound = false;
582 if (deleteVisTags.size() == putVisTags.size()) {
583 for (List<Integer> deleteTagOrdinals : deleteVisTags) {
584 matchFound = false;
585 for (List<Integer> tagOrdinals : putVisTags) {
586 if (deleteTagOrdinals.equals(tagOrdinals)) {
587 matchFound = true;
588 break;
589 }
590 }
591 if (!matchFound) break;
592 }
593 }
594 return matchFound;
595 }
596 }