1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.security.visibility;
20
21 import static org.apache.hadoop.hbase.HConstants.OperationStatusCode.SANITY_CHECK_FAILURE;
22 import static org.apache.hadoop.hbase.HConstants.OperationStatusCode.SUCCESS;
23 import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_FAMILY;
24 import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_NAME;
25
26 import java.io.IOException;
27 import java.net.InetAddress;
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36 import org.apache.hadoop.conf.Configuration;
37 import org.apache.hadoop.hbase.Cell;
38 import org.apache.hadoop.hbase.CellScanner;
39 import org.apache.hadoop.hbase.CellUtil;
40 import org.apache.hadoop.hbase.CoprocessorEnvironment;
41 import org.apache.hadoop.hbase.DoNotRetryIOException;
42 import org.apache.hadoop.hbase.HBaseInterfaceAudience;
43 import org.apache.hadoop.hbase.HColumnDescriptor;
44 import org.apache.hadoop.hbase.HConstants;
45 import org.apache.hadoop.hbase.HTableDescriptor;
46 import org.apache.hadoop.hbase.MetaTableAccessor;
47 import org.apache.hadoop.hbase.TableName;
48 import org.apache.hadoop.hbase.Tag;
49 import org.apache.hadoop.hbase.TagRewriteCell;
50 import org.apache.hadoop.hbase.TagType;
51 import org.apache.hadoop.hbase.classification.InterfaceAudience;
52 import org.apache.hadoop.hbase.client.Append;
53 import org.apache.hadoop.hbase.client.Delete;
54 import org.apache.hadoop.hbase.client.Get;
55 import org.apache.hadoop.hbase.client.Increment;
56 import org.apache.hadoop.hbase.client.Mutation;
57 import org.apache.hadoop.hbase.client.Put;
58 import org.apache.hadoop.hbase.client.Result;
59 import org.apache.hadoop.hbase.client.Scan;
60 import org.apache.hadoop.hbase.constraint.ConstraintException;
61 import org.apache.hadoop.hbase.coprocessor.BaseMasterAndRegionObserver;
62 import org.apache.hadoop.hbase.coprocessor.BaseRegionServerObserver;
63 import org.apache.hadoop.hbase.coprocessor.CoprocessorException;
64 import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
65 import org.apache.hadoop.hbase.coprocessor.CoprocessorService;
66 import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
67 import org.apache.hadoop.hbase.coprocessor.ObserverContext;
68 import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
69 import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment;
70 import org.apache.hadoop.hbase.exceptions.DeserializationException;
71 import org.apache.hadoop.hbase.exceptions.FailedSanityCheckException;
72 import org.apache.hadoop.hbase.filter.Filter;
73 import org.apache.hadoop.hbase.filter.FilterBase;
74 import org.apache.hadoop.hbase.filter.FilterList;
75 import org.apache.hadoop.hbase.io.hfile.HFile;
76 import org.apache.hadoop.hbase.ipc.RequestContext;
77 import org.apache.hadoop.hbase.master.MasterServices;
78 import org.apache.hadoop.hbase.protobuf.ResponseConverter;
79 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.RegionActionResult;
80 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos;
81 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.GetAuthsRequest;
82 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.GetAuthsResponse;
83 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.ListLabelsRequest;
84 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.ListLabelsResponse;
85 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.SetAuthsRequest;
86 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabel;
87 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsRequest;
88 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse;
89 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsService;
90 import org.apache.hadoop.hbase.regionserver.BloomType;
91 import org.apache.hadoop.hbase.regionserver.DeleteTracker;
92 import org.apache.hadoop.hbase.regionserver.DisabledRegionSplitPolicy;
93 import org.apache.hadoop.hbase.regionserver.HRegion;
94 import org.apache.hadoop.hbase.regionserver.InternalScanner;
95 import org.apache.hadoop.hbase.regionserver.MiniBatchOperationInProgress;
96 import org.apache.hadoop.hbase.regionserver.OperationStatus;
97 import org.apache.hadoop.hbase.regionserver.RegionScanner;
98 import org.apache.hadoop.hbase.replication.ReplicationEndpoint;
99 import org.apache.hadoop.hbase.security.AccessDeniedException;
100 import org.apache.hadoop.hbase.security.User;
101 import org.apache.hadoop.hbase.security.access.AccessControlLists;
102 import org.apache.hadoop.hbase.security.access.AccessController;
103 import org.apache.hadoop.hbase.util.ByteStringer;
104 import org.apache.hadoop.hbase.util.Bytes;
105 import org.apache.hadoop.hbase.util.Pair;
106
107 import com.google.common.collect.Lists;
108 import com.google.common.collect.MapMaker;
109 import com.google.protobuf.ByteString;
110 import com.google.protobuf.RpcCallback;
111 import com.google.protobuf.RpcController;
112 import com.google.protobuf.Service;
113
114
115
116
117
118 @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG)
119 public class VisibilityController extends BaseMasterAndRegionObserver implements
120 VisibilityLabelsService.Interface, CoprocessorService {
121
122 private static final Log LOG = LogFactory.getLog(VisibilityController.class);
123 private static final Log AUDITLOG = LogFactory.getLog("SecurityLogger."
124 + VisibilityController.class.getName());
125
126 private boolean labelsRegion = false;
127
128 private boolean acOn = false;
129 private Configuration conf;
130 private volatile boolean initialized = false;
131 private boolean checkAuths = false;
132
133 private Map<InternalScanner,String> scannerOwners =
134 new MapMaker().weakKeys().makeMap();
135
136 private List<String> superUsers;
137 private List<String> superGroups;
138 private VisibilityLabelService visibilityLabelService;
139
140
141 private static ArrayList<Byte> RESERVED_VIS_TAG_TYPES = new ArrayList<Byte>();
142 static {
143 RESERVED_VIS_TAG_TYPES.add(TagType.VISIBILITY_TAG_TYPE);
144 RESERVED_VIS_TAG_TYPES.add(TagType.VISIBILITY_EXP_SERIALIZATION_FORMAT_TAG_TYPE);
145 RESERVED_VIS_TAG_TYPES.add(TagType.STRING_VIS_TAG_TYPE);
146 }
147
148 @Override
149 public void start(CoprocessorEnvironment env) throws IOException {
150 this.conf = env.getConfiguration();
151 if (HFile.getFormatVersion(conf) < HFile.MIN_FORMAT_VERSION_WITH_TAGS) {
152 throw new RuntimeException("A minimum HFile version of " + HFile.MIN_FORMAT_VERSION_WITH_TAGS
153 + " is required to persist visibility labels. Consider setting " + HFile.FORMAT_VERSION_KEY
154 + " accordingly.");
155 }
156
157 if (env instanceof RegionServerCoprocessorEnvironment) {
158 throw new RuntimeException("Visibility controller should not be configured as "
159 + "'hbase.coprocessor.regionserver.classes'.");
160 }
161
162 if (!(env instanceof MasterCoprocessorEnvironment)) {
163 visibilityLabelService = VisibilityLabelServiceManager.getInstance()
164 .getVisibilityLabelService(this.conf);
165 }
166 Pair<List<String>, List<String>> superUsersAndGroups =
167 VisibilityUtils.getSystemAndSuperUsers(this.conf);
168 this.superUsers = superUsersAndGroups.getFirst();
169 this.superGroups = superUsersAndGroups.getSecond();
170 }
171
172 @Override
173 public void stop(CoprocessorEnvironment env) throws IOException {
174
175 }
176
177
178
179 @Override
180 public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
181
182 MasterServices master = ctx.getEnvironment().getMasterServices();
183 if (!MetaTableAccessor.tableExists(master.getConnection(), LABELS_TABLE_NAME)) {
184 HTableDescriptor labelsTable = new HTableDescriptor(LABELS_TABLE_NAME);
185 HColumnDescriptor labelsColumn = new HColumnDescriptor(LABELS_TABLE_FAMILY);
186 labelsColumn.setBloomFilterType(BloomType.NONE);
187 labelsColumn.setBlockCacheEnabled(false);
188
189 labelsTable.addFamily(labelsColumn);
190
191
192 labelsTable.setValue(HTableDescriptor.SPLIT_POLICY,
193 DisabledRegionSplitPolicy.class.getName());
194 labelsTable.setValue(Bytes.toBytes(HConstants.DISALLOW_WRITES_IN_RECOVERING),
195 Bytes.toBytes(true));
196 master.createTable(labelsTable, null);
197 }
198 }
199
200 @Override
201 public void preModifyTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
202 TableName tableName, HTableDescriptor htd) throws IOException {
203 if (LABELS_TABLE_NAME.equals(tableName)) {
204 throw new ConstraintException("Cannot alter " + LABELS_TABLE_NAME);
205 }
206 }
207
208 @Override
209 public void preAddColumn(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName,
210 HColumnDescriptor column) throws IOException {
211 if (LABELS_TABLE_NAME.equals(tableName)) {
212 throw new ConstraintException("Cannot alter " + LABELS_TABLE_NAME);
213 }
214 }
215
216 @Override
217 public void preModifyColumn(ObserverContext<MasterCoprocessorEnvironment> ctx,
218 TableName tableName, HColumnDescriptor descriptor) throws IOException {
219 if (LABELS_TABLE_NAME.equals(tableName)) {
220 throw new ConstraintException("Cannot alter " + LABELS_TABLE_NAME);
221 }
222 }
223
224 @Override
225 public void preDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> ctx,
226 TableName tableName, byte[] c) throws IOException {
227 if (LABELS_TABLE_NAME.equals(tableName)) {
228 throw new ConstraintException("Cannot alter " + LABELS_TABLE_NAME);
229 }
230 }
231
232 @Override
233 public void preDisableTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName)
234 throws IOException {
235 if (LABELS_TABLE_NAME.equals(tableName)) {
236 throw new ConstraintException("Cannot disable " + LABELS_TABLE_NAME);
237 }
238 }
239
240
241
242 @Override
243 public void postOpen(ObserverContext<RegionCoprocessorEnvironment> e) {
244
245 if (e.getEnvironment().getRegion().getRegionInfo().getTable().equals(LABELS_TABLE_NAME)) {
246 this.labelsRegion = true;
247 this.acOn = CoprocessorHost.getLoadedCoprocessors().contains(AccessController.class.getName());
248
249 if (!e.getEnvironment().getRegion().isRecovering()) {
250 initVisibilityLabelService(e.getEnvironment());
251 }
252 } else {
253 checkAuths = e.getEnvironment().getConfiguration()
254 .getBoolean(VisibilityConstants.CHECK_AUTHS_FOR_MUTATION, false);
255 initVisibilityLabelService(e.getEnvironment());
256 }
257 }
258
259 @Override
260 public void postLogReplay(ObserverContext<RegionCoprocessorEnvironment> e) {
261 if (this.labelsRegion) {
262 initVisibilityLabelService(e.getEnvironment());
263 LOG.debug("post labels region log replay");
264 }
265 }
266
267 private void initVisibilityLabelService(RegionCoprocessorEnvironment env) {
268 try {
269 this.visibilityLabelService.init(env);
270 this.initialized = true;
271 } catch (IOException ioe) {
272 LOG.error("Error while initializing VisibilityLabelService..", ioe);
273 throw new RuntimeException(ioe);
274 }
275 }
276
277 @Override
278 public void preBatchMutate(ObserverContext<RegionCoprocessorEnvironment> c,
279 MiniBatchOperationInProgress<Mutation> miniBatchOp) throws IOException {
280 if (c.getEnvironment().getRegion().getRegionInfo().getTable().isSystemTable()) {
281 return;
282 }
283
284 Map<String, List<Tag>> labelCache = new HashMap<String, List<Tag>>();
285 for (int i = 0; i < miniBatchOp.size(); i++) {
286 Mutation m = miniBatchOp.getOperation(i);
287 CellVisibility cellVisibility = null;
288 try {
289 cellVisibility = m.getCellVisibility();
290 } catch (DeserializationException de) {
291 miniBatchOp.setOperationStatus(i,
292 new OperationStatus(SANITY_CHECK_FAILURE, de.getMessage()));
293 continue;
294 }
295 boolean sanityFailure = false;
296 boolean modifiedTagFound = false;
297 Pair<Boolean, Tag> pair = new Pair<Boolean, Tag>(false, null);
298 for (CellScanner cellScanner = m.cellScanner(); cellScanner.advance();) {
299 pair = checkForReservedVisibilityTagPresence(cellScanner.current(), pair);
300 if (!pair.getFirst()) {
301 miniBatchOp.setOperationStatus(i, new OperationStatus(SANITY_CHECK_FAILURE,
302 "Mutation contains cell with reserved type tag"));
303 sanityFailure = true;
304 break;
305 } else {
306
307 Tag tag = pair.getSecond();
308 if (cellVisibility == null && tag != null) {
309
310 cellVisibility = new CellVisibility(Bytes.toString(tag.getBuffer(), tag.getTagOffset(),
311 tag.getTagLength()));
312 modifiedTagFound = true;
313 }
314 }
315 }
316 if (!sanityFailure) {
317 if (cellVisibility != null) {
318 String labelsExp = cellVisibility.getExpression();
319 List<Tag> visibilityTags = labelCache.get(labelsExp);
320 if (visibilityTags == null) {
321
322 boolean authCheck = this.checkAuths && !(isSystemOrSuperUser());
323 try {
324 visibilityTags = this.visibilityLabelService.createVisibilityExpTags(labelsExp, true,
325 authCheck);
326 } catch (InvalidLabelException e) {
327 miniBatchOp.setOperationStatus(i,
328 new OperationStatus(SANITY_CHECK_FAILURE, e.getMessage()));
329 }
330 if (visibilityTags != null) {
331 labelCache.put(labelsExp, visibilityTags);
332 }
333 }
334 if (visibilityTags != null) {
335 List<Cell> updatedCells = new ArrayList<Cell>();
336 for (CellScanner cellScanner = m.cellScanner(); cellScanner.advance();) {
337 Cell cell = cellScanner.current();
338 List<Tag> tags = Tag.asList(cell.getTagsArray(), cell.getTagsOffset(),
339 cell.getTagsLength());
340 if (modifiedTagFound) {
341
342 removeReplicationVisibilityTag(tags);
343 }
344 tags.addAll(visibilityTags);
345 Cell updatedCell = new TagRewriteCell(cell, Tag.fromList(tags));
346 updatedCells.add(updatedCell);
347 }
348 m.getFamilyCellMap().clear();
349
350 for (Cell cell : updatedCells) {
351 if (m instanceof Put) {
352 Put p = (Put) m;
353 p.add(cell);
354 } else if (m instanceof Delete) {
355 Delete d = (Delete) m;
356 d.addDeleteMarker(cell);
357 }
358 }
359 }
360 }
361 }
362 }
363 }
364
365 @Override
366 public void prePrepareTimeStampForDeleteVersion(
367 ObserverContext<RegionCoprocessorEnvironment> ctx, Mutation delete, Cell cell,
368 byte[] byteNow, Get get) throws IOException {
369 CellVisibility cellVisibility = null;
370 try {
371 cellVisibility = delete.getCellVisibility();
372 } catch (DeserializationException de) {
373 throw new IOException("Invalid cell visibility specified " + delete, de);
374 }
375
376
377 List<Tag> visibilityTags = new ArrayList<Tag>();
378 if (cellVisibility != null) {
379 String labelsExp = cellVisibility.getExpression();
380 try {
381 visibilityTags = this.visibilityLabelService.createVisibilityExpTags(labelsExp, false,
382 false);
383 } catch (InvalidLabelException e) {
384 throw new IOException("Invalid cell visibility specified " + labelsExp, e);
385 }
386 }
387 get.setFilter(new DeleteVersionVisibilityExpressionFilter(visibilityTags,
388 VisibilityConstants.SORTED_ORDINAL_SERIALIZATION_FORMAT));
389 List<Cell> result = ctx.getEnvironment().getRegion().get(get, false);
390
391 if (result.size() < get.getMaxVersions()) {
392
393 CellUtil.updateLatestStamp(cell, Long.MIN_VALUE);
394 return;
395 }
396 if (result.size() > get.getMaxVersions()) {
397 throw new RuntimeException("Unexpected size: " + result.size()
398 + ". Results more than the max versions obtained.");
399 }
400 Cell getCell = result.get(get.getMaxVersions() - 1);
401 CellUtil.setTimestamp(cell, getCell.getTimestamp());
402
403
404
405
406
407
408 ctx.bypass();
409 }
410
411
412
413
414
415
416
417
418
419
420
421
422
423 private Pair<Boolean, Tag> checkForReservedVisibilityTagPresence(Cell cell,
424 Pair<Boolean, Tag> pair) throws IOException {
425 if (pair == null) {
426 pair = new Pair<Boolean, Tag>(false, null);
427 } else {
428 pair.setFirst(false);
429 pair.setSecond(null);
430 }
431
432
433
434 if (isSystemOrSuperUser()) {
435
436
437
438 Tag modifiedTag = null;
439 if (cell.getTagsLength() > 0) {
440 Iterator<Tag> tagsIterator = CellUtil.tagsIterator(cell.getTagsArray(),
441 cell.getTagsOffset(), cell.getTagsLength());
442 while (tagsIterator.hasNext()) {
443 Tag tag = tagsIterator.next();
444 if (tag.getType() == TagType.STRING_VIS_TAG_TYPE) {
445 modifiedTag = tag;
446 break;
447 }
448 }
449 }
450 pair.setFirst(true);
451 pair.setSecond(modifiedTag);
452 return pair;
453 }
454 if (cell.getTagsLength() > 0) {
455 Iterator<Tag> tagsItr = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(),
456 cell.getTagsLength());
457 while (tagsItr.hasNext()) {
458 if (RESERVED_VIS_TAG_TYPES.contains(tagsItr.next().getType())) {
459 return pair;
460 }
461 }
462 }
463 pair.setFirst(true);
464 return pair;
465 }
466
467
468
469
470
471
472
473
474
475
476
477
478
479 private boolean checkForReservedVisibilityTagPresence(Cell cell) throws IOException {
480
481
482
483
484
485 if (isSystemOrSuperUser()) {
486 return true;
487 }
488 if (cell.getTagsLength() > 0) {
489 Iterator<Tag> tagsItr = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(),
490 cell.getTagsLength());
491 while (tagsItr.hasNext()) {
492 if (RESERVED_VIS_TAG_TYPES.contains(tagsItr.next().getType())) {
493 return false;
494 }
495 }
496 }
497 return true;
498 }
499
500 private void removeReplicationVisibilityTag(List<Tag> tags) throws IOException {
501 Iterator<Tag> iterator = tags.iterator();
502 while (iterator.hasNext()) {
503 Tag tag = iterator.next();
504 if (tag.getType() == TagType.STRING_VIS_TAG_TYPE) {
505 iterator.remove();
506 break;
507 }
508 }
509 }
510
511 @Override
512 public RegionScanner preScannerOpen(ObserverContext<RegionCoprocessorEnvironment> e, Scan scan,
513 RegionScanner s) throws IOException {
514 if (!initialized) {
515 throw new VisibilityControllerNotReadyException("VisibilityController not yet initialized!");
516 }
517 HRegion region = e.getEnvironment().getRegion();
518 Authorizations authorizations = null;
519 try {
520 authorizations = scan.getAuthorizations();
521 } catch (DeserializationException de) {
522 throw new IOException(de);
523 }
524 if (authorizations == null) {
525
526
527
528 TableName table = region.getRegionInfo().getTable();
529 if (table.isSystemTable() && !table.equals(LABELS_TABLE_NAME)) {
530 return s;
531 }
532 }
533
534 Filter visibilityLabelFilter = VisibilityUtils.createVisibilityLabelFilter(region,
535 authorizations);
536 if (visibilityLabelFilter != null) {
537 Filter filter = scan.getFilter();
538 if (filter != null) {
539 scan.setFilter(new FilterList(filter, visibilityLabelFilter));
540 } else {
541 scan.setFilter(visibilityLabelFilter);
542 }
543 }
544 return s;
545 }
546
547 @Override
548 public DeleteTracker postInstantiateDeleteTracker(
549 ObserverContext<RegionCoprocessorEnvironment> ctx, DeleteTracker delTracker)
550 throws IOException {
551 HRegion region = ctx.getEnvironment().getRegion();
552 TableName table = region.getRegionInfo().getTable();
553 if (table.isSystemTable()) {
554 return delTracker;
555 }
556
557
558
559
560
561 return new VisibilityScanDeleteTracker();
562 }
563
564 @Override
565 public RegionScanner postScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
566 final Scan scan, final RegionScanner s) throws IOException {
567 User user = VisibilityUtils.getActiveUser();
568 if (user != null && user.getShortName() != null) {
569 scannerOwners.put(s, user.getShortName());
570 }
571 return s;
572 }
573
574 @Override
575 public boolean preScannerNext(final ObserverContext<RegionCoprocessorEnvironment> c,
576 final InternalScanner s, final List<Result> result, final int limit, final boolean hasNext)
577 throws IOException {
578 requireScannerOwner(s);
579 return hasNext;
580 }
581
582 @Override
583 public void preScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
584 final InternalScanner s) throws IOException {
585 requireScannerOwner(s);
586 }
587
588 @Override
589 public void postScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
590 final InternalScanner s) throws IOException {
591
592 scannerOwners.remove(s);
593 }
594
595
596
597
598
599 private void requireScannerOwner(InternalScanner s) throws AccessDeniedException {
600 if (RequestContext.isInRequestContext()) {
601 String requestUName = RequestContext.getRequestUserName();
602 String owner = scannerOwners.get(s);
603 if (owner != null && !owner.equals(requestUName)) {
604 throw new AccessDeniedException("User '" + requestUName + "' is not the scanner owner!");
605 }
606 }
607 }
608
609 @Override
610 public void preGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get, List<Cell> results)
611 throws IOException {
612 if (!initialized) {
613 throw new VisibilityControllerNotReadyException("VisibilityController not yet initialized!");
614 }
615 HRegion region = e.getEnvironment().getRegion();
616 Authorizations authorizations = null;
617 try {
618 authorizations = get.getAuthorizations();
619 } catch (DeserializationException de) {
620 throw new IOException(de);
621 }
622 if (authorizations == null) {
623
624
625
626 TableName table = region.getRegionInfo().getTable();
627 if (table.isSystemTable() && !table.equals(LABELS_TABLE_NAME)) {
628 return;
629 }
630 }
631 Filter visibilityLabelFilter = VisibilityUtils.createVisibilityLabelFilter(e.getEnvironment()
632 .getRegion(), authorizations);
633 if (visibilityLabelFilter != null) {
634 Filter filter = get.getFilter();
635 if (filter != null) {
636 get.setFilter(new FilterList(filter, visibilityLabelFilter));
637 } else {
638 get.setFilter(visibilityLabelFilter);
639 }
640 }
641 }
642
643 private boolean isSystemOrSuperUser() throws IOException {
644 User activeUser = VisibilityUtils.getActiveUser();
645 if (this.superUsers.contains(activeUser.getShortName())) {
646 return true;
647 }
648 String[] groups = activeUser.getGroupNames();
649 if (groups != null && groups.length > 0) {
650 for (String group : groups) {
651 if (this.superGroups.contains(group)) {
652 return true;
653 }
654 }
655 }
656 return false;
657 }
658
659 @Override
660 public Result preAppend(ObserverContext<RegionCoprocessorEnvironment> e, Append append)
661 throws IOException {
662 for (CellScanner cellScanner = append.cellScanner(); cellScanner.advance();) {
663 if (!checkForReservedVisibilityTagPresence(cellScanner.current())) {
664 throw new FailedSanityCheckException("Append contains cell with reserved type tag");
665 }
666 }
667 return null;
668 }
669
670 @Override
671 public Result preIncrement(ObserverContext<RegionCoprocessorEnvironment> e, Increment increment)
672 throws IOException {
673 for (CellScanner cellScanner = increment.cellScanner(); cellScanner.advance();) {
674 if (!checkForReservedVisibilityTagPresence(cellScanner.current())) {
675 throw new FailedSanityCheckException("Increment contains cell with reserved type tag");
676 }
677 }
678 return null;
679 }
680
681 @Override
682 public Cell postMutationBeforeWAL(ObserverContext<RegionCoprocessorEnvironment> ctx,
683 MutationType opType, Mutation mutation, Cell oldCell, Cell newCell) throws IOException {
684 List<Tag> tags = Lists.newArrayList();
685 CellVisibility cellVisibility = null;
686 try {
687 cellVisibility = mutation.getCellVisibility();
688 } catch (DeserializationException e) {
689 throw new IOException(e);
690 }
691 if (cellVisibility == null) {
692 return newCell;
693 }
694
695
696 boolean authCheck = this.checkAuths && !(isSystemOrSuperUser());
697 tags.addAll(this.visibilityLabelService.createVisibilityExpTags(cellVisibility.getExpression(),
698 true, authCheck));
699
700 if (newCell.getTagsLength() > 0) {
701
702 Iterator<Tag> tagsItr = CellUtil.tagsIterator(newCell.getTagsArray(),
703 newCell.getTagsOffset(), newCell.getTagsLength());
704 while (tagsItr.hasNext()) {
705 Tag tag = tagsItr.next();
706 if (tag.getType() != TagType.VISIBILITY_TAG_TYPE
707 && tag.getType() != TagType.VISIBILITY_EXP_SERIALIZATION_FORMAT_TAG_TYPE) {
708 tags.add(tag);
709 }
710 }
711 }
712
713 Cell rewriteCell = new TagRewriteCell(newCell, Tag.fromList(tags));
714 return rewriteCell;
715 }
716
717 @Override
718 public Service getService() {
719 return VisibilityLabelsProtos.VisibilityLabelsService.newReflectiveService(this);
720 }
721
722
723 @Override
724 public synchronized void addLabels(RpcController controller, VisibilityLabelsRequest request,
725 RpcCallback<VisibilityLabelsResponse> done) {
726 VisibilityLabelsResponse.Builder response = VisibilityLabelsResponse.newBuilder();
727 List<VisibilityLabel> visLabels = request.getVisLabelList();
728 if (!initialized) {
729 setExceptionResults(visLabels.size(),
730 new VisibilityControllerNotReadyException("VisibilityController not yet initialized!"),
731 response);
732 } else {
733 List<byte[]> labels = new ArrayList<byte[]>(visLabels.size());
734 try {
735 checkCallingUserAuth();
736 RegionActionResult successResult = RegionActionResult.newBuilder().build();
737 for (VisibilityLabel visLabel : visLabels) {
738 byte[] label = visLabel.getLabel().toByteArray();
739 labels.add(label);
740 response.addResult(successResult);
741
742
743 }
744 if (!labels.isEmpty()) {
745 OperationStatus[] opStatus = this.visibilityLabelService.addLabels(labels);
746 logResult(true, "addLabels", "Adding labels allowed", null, labels, null);
747 int i = 0;
748 for (OperationStatus status : opStatus) {
749 while (response.getResult(i) != successResult)
750 i++;
751 if (status.getOperationStatusCode() != SUCCESS) {
752 RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder();
753 failureResultBuilder.setException(ResponseConverter
754 .buildException(new DoNotRetryIOException(status.getExceptionMsg())));
755 response.setResult(i, failureResultBuilder.build());
756 }
757 i++;
758 }
759 }
760 } catch (AccessDeniedException e) {
761 logResult(false, "addLabels", e.getMessage(), null, labels, null);
762 LOG.error("User is not having required permissions to add labels", e);
763 setExceptionResults(visLabels.size(), e, response);
764 } catch (IOException e) {
765 LOG.error(e);
766 setExceptionResults(visLabels.size(), e, response);
767 }
768 }
769 done.run(response.build());
770 }
771
772 private void setExceptionResults(int size, IOException e,
773 VisibilityLabelsResponse.Builder response) {
774 RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder();
775 failureResultBuilder.setException(ResponseConverter.buildException(e));
776 RegionActionResult failureResult = failureResultBuilder.build();
777 for (int i = 0; i < size; i++) {
778 response.addResult(i, failureResult);
779 }
780 }
781
782 @Override
783 public synchronized void setAuths(RpcController controller, SetAuthsRequest request,
784 RpcCallback<VisibilityLabelsResponse> done) {
785 VisibilityLabelsResponse.Builder response = VisibilityLabelsResponse.newBuilder();
786 List<ByteString> auths = request.getAuthList();
787 if (!initialized) {
788 setExceptionResults(auths.size(),
789 new VisibilityControllerNotReadyException("VisibilityController not yet initialized!"),
790 response);
791 } else {
792 byte[] user = request.getUser().toByteArray();
793 List<byte[]> labelAuths = new ArrayList<byte[]>(auths.size());
794 try {
795 checkCallingUserAuth();
796
797 for (ByteString authBS : auths) {
798 labelAuths.add(authBS.toByteArray());
799 }
800 OperationStatus[] opStatus = this.visibilityLabelService.setAuths(user, labelAuths);
801 logResult(true, "setAuths", "Setting authorization for labels allowed", user, labelAuths,
802 null);
803 RegionActionResult successResult = RegionActionResult.newBuilder().build();
804 for (OperationStatus status : opStatus) {
805 if (status.getOperationStatusCode() == SUCCESS) {
806 response.addResult(successResult);
807 } else {
808 RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder();
809 failureResultBuilder.setException(ResponseConverter
810 .buildException(new DoNotRetryIOException(status.getExceptionMsg())));
811 response.addResult(failureResultBuilder.build());
812 }
813 }
814 } catch (AccessDeniedException e) {
815 logResult(false, "setAuths", e.getMessage(), user, labelAuths, null);
816 LOG.error("User is not having required permissions to set authorization", e);
817 setExceptionResults(auths.size(), e, response);
818 } catch (IOException e) {
819 LOG.error(e);
820 setExceptionResults(auths.size(), e, response);
821 }
822 }
823 done.run(response.build());
824 }
825
826 private void logResult(boolean isAllowed, String request, String reason, byte[] user,
827 List<byte[]> labelAuths, String regex) {
828 if (AUDITLOG.isTraceEnabled()) {
829 RequestContext ctx = RequestContext.get();
830 InetAddress remoteAddr = null;
831 if (ctx != null) {
832 remoteAddr = ctx.getRemoteAddress();
833 }
834
835 List<String> labelAuthsStr = new ArrayList<>();
836 if (labelAuths != null) {
837 int labelAuthsSize = labelAuths.size();
838 labelAuthsStr = new ArrayList<>(labelAuthsSize);
839 for (int i = 0; i < labelAuthsSize; i++) {
840 labelAuthsStr.add(Bytes.toString(labelAuths.get(i)));
841 }
842 }
843
844 User requestingUser = null;
845 try {
846 requestingUser = VisibilityUtils.getActiveUser();
847 } catch (IOException e) {
848 LOG.warn("Failed to get active system user.");
849 LOG.debug("Details on failure to get active system user.", e);
850 }
851 AUDITLOG.trace("Access " + (isAllowed ? "allowed" : "denied") + " for user "
852 + (requestingUser != null ? requestingUser.getShortName() : "UNKNOWN") + "; reason: "
853 + reason + "; remote address: " + (remoteAddr != null ? remoteAddr : "") + "; request: "
854 + request + "; user: " + (user != null ? Bytes.toShort(user) : "null") + "; labels: "
855 + labelAuthsStr + "; regex: " + regex);
856 }
857 }
858
859 @Override
860 public synchronized void getAuths(RpcController controller, GetAuthsRequest request,
861 RpcCallback<GetAuthsResponse> done) {
862 GetAuthsResponse.Builder response = GetAuthsResponse.newBuilder();
863 if (!initialized) {
864 controller.setFailed("VisibilityController not yet initialized");
865 } else {
866 byte[] user = request.getUser().toByteArray();
867 List<String> labels = null;
868 try {
869
870
871 if (this.acOn && !isSystemOrSuperUser()) {
872 User requestingUser = VisibilityUtils.getActiveUser();
873 throw new AccessDeniedException("User '"
874 + (requestingUser != null ? requestingUser.getShortName() : "null")
875 + "' is not authorized to perform this action.");
876 }
877 if (AccessControlLists.isGroupPrincipal(Bytes.toString(user))) {
878
879
880 try {
881 this.visibilityLabelService.getClass().getDeclaredMethod("getGroupAuths",
882 new Class[] { String[].class, Boolean.TYPE });
883 } catch (SecurityException e) {
884 throw new AccessDeniedException("Failed to obtain getGroupAuths implementation");
885 } catch (NoSuchMethodException e) {
886 throw new AccessDeniedException(
887 "Get group auth is not supported in this implementation");
888 }
889 String group = AccessControlLists.getGroupName(Bytes.toString(user));
890 labels = this.visibilityLabelService.getGroupAuths(new String[] { group }, false);
891 } else {
892 labels = this.visibilityLabelService.getAuths(user, false);
893 }
894 logResult(true, "getAuths", "Get authorizations for user allowed", user, null, null);
895 } catch (AccessDeniedException e) {
896 logResult(false, "getAuths", e.getMessage(), user, null, null);
897 ResponseConverter.setControllerException(controller, e);
898 } catch (IOException e) {
899 ResponseConverter.setControllerException(controller, e);
900 }
901 response.setUser(request.getUser());
902 if (labels != null) {
903 for (String label : labels) {
904 response.addAuth(ByteStringer.wrap(Bytes.toBytes(label)));
905 }
906 }
907 }
908 done.run(response.build());
909 }
910
911 @Override
912 public synchronized void clearAuths(RpcController controller, SetAuthsRequest request,
913 RpcCallback<VisibilityLabelsResponse> done) {
914 VisibilityLabelsResponse.Builder response = VisibilityLabelsResponse.newBuilder();
915 List<ByteString> auths = request.getAuthList();
916 if (!initialized) {
917 setExceptionResults(auths.size(), new CoprocessorException(
918 "VisibilityController not yet initialized"), response);
919 } else {
920 byte[] requestUser = request.getUser().toByteArray();
921 List<byte[]> labelAuths = new ArrayList<byte[]>(auths.size());
922 try {
923
924 if (this.acOn && !isSystemOrSuperUser()) {
925 User user = VisibilityUtils.getActiveUser();
926 throw new AccessDeniedException("User '" + (user != null ? user.getShortName() : "null")
927 + " is not authorized to perform this action.");
928 }
929 checkCallingUserAuth();
930
931 for (ByteString authBS : auths) {
932 labelAuths.add(authBS.toByteArray());
933 }
934
935 OperationStatus[] opStatus =
936 this.visibilityLabelService.clearAuths(requestUser, labelAuths);
937 logResult(true, "clearAuths", "Removing authorization for labels allowed", requestUser,
938 labelAuths, null);
939 RegionActionResult successResult = RegionActionResult.newBuilder().build();
940 for (OperationStatus status : opStatus) {
941 if (status.getOperationStatusCode() == SUCCESS) {
942 response.addResult(successResult);
943 } else {
944 RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder();
945 failureResultBuilder.setException(ResponseConverter
946 .buildException(new DoNotRetryIOException(status.getExceptionMsg())));
947 response.addResult(failureResultBuilder.build());
948 }
949 }
950 } catch (AccessDeniedException e) {
951 logResult(false, "clearAuths", e.getMessage(), requestUser, labelAuths, null);
952 LOG.error("User is not having required permissions to clear authorization", e);
953 setExceptionResults(auths.size(), e, response);
954 } catch (IOException e) {
955 LOG.error(e);
956 setExceptionResults(auths.size(), e, response);
957 }
958 }
959 done.run(response.build());
960 }
961
962 @Override
963 public synchronized void listLabels(RpcController controller, ListLabelsRequest request,
964 RpcCallback<ListLabelsResponse> done) {
965 ListLabelsResponse.Builder response = ListLabelsResponse.newBuilder();
966 if (!initialized) {
967 controller.setFailed("VisibilityController not yet initialized");
968 } else {
969 List<String> labels = null;
970 String regex = request.hasRegex() ? request.getRegex() : null;
971 try {
972
973
974 if (this.acOn && !isSystemOrSuperUser()) {
975 User requestingUser = VisibilityUtils.getActiveUser();
976 throw new AccessDeniedException("User '"
977 + (requestingUser != null ? requestingUser.getShortName() : "null")
978 + "' is not authorized to perform this action.");
979 }
980 labels = this.visibilityLabelService.listLabels(regex);
981 logResult(false, "listLabels", "Listing labels allowed", null, null, regex);
982 } catch (AccessDeniedException e) {
983 logResult(false, "listLabels", e.getMessage(), null, null, regex);
984 ResponseConverter.setControllerException(controller, e);
985 } catch (IOException e) {
986 ResponseConverter.setControllerException(controller, e);
987 }
988 if (labels != null && !labels.isEmpty()) {
989 for (String label : labels) {
990 response.addLabel(ByteStringer.wrap(Bytes.toBytes(label)));
991 }
992 }
993 }
994 done.run(response.build());
995 }
996
997 private void checkCallingUserAuth() throws IOException {
998 if (!this.acOn) {
999 User user = VisibilityUtils.getActiveUser();
1000 if (user == null) {
1001 throw new IOException("Unable to retrieve calling user");
1002 }
1003 boolean havingSystemAuth = false;
1004 try {
1005 this.visibilityLabelService.getClass().getDeclaredMethod("havingSystemAuth",
1006 new Class[] { User.class });
1007 havingSystemAuth = this.visibilityLabelService.havingSystemAuth(user);
1008 } catch (SecurityException e) {
1009
1010 } catch (NoSuchMethodException e) {
1011
1012
1013 havingSystemAuth = this.visibilityLabelService.havingSystemAuth(Bytes.toBytes(user
1014 .getShortName()));
1015 }
1016 if (!havingSystemAuth) {
1017 throw new AccessDeniedException("User '" + user.getShortName()
1018 + "' is not authorized to perform this action.");
1019 }
1020 }
1021 }
1022
1023 private static class DeleteVersionVisibilityExpressionFilter extends FilterBase {
1024 private List<Tag> deleteCellVisTags;
1025 private Byte deleteCellVisTagsFormat;
1026
1027 public DeleteVersionVisibilityExpressionFilter(List<Tag> deleteCellVisTags,
1028 Byte deleteCellVisTagsFormat) {
1029 this.deleteCellVisTags = deleteCellVisTags;
1030 this.deleteCellVisTagsFormat = deleteCellVisTagsFormat;
1031 }
1032
1033 @Override
1034 public ReturnCode filterKeyValue(Cell cell) throws IOException {
1035 List<Tag> putVisTags = new ArrayList<Tag>();
1036 Byte putCellVisTagsFormat = VisibilityUtils.extractVisibilityTags(cell, putVisTags);
1037 boolean matchFound = VisibilityLabelServiceManager
1038 .getInstance().getVisibilityLabelService()
1039 .matchVisibility(putVisTags, putCellVisTagsFormat, deleteCellVisTags,
1040 deleteCellVisTagsFormat);
1041 return matchFound ? ReturnCode.INCLUDE : ReturnCode.SKIP;
1042 }
1043
1044
1045
1046 @Override
1047 public Cell transformCell(Cell v) {
1048 return v;
1049 }
1050 }
1051
1052
1053
1054
1055
1056
1057
1058
1059 public static class VisibilityReplication extends BaseRegionServerObserver {
1060 private Configuration conf;
1061 private VisibilityLabelService visibilityLabelService;
1062
1063 @Override
1064 public void start(CoprocessorEnvironment env) throws IOException {
1065 this.conf = env.getConfiguration();
1066 visibilityLabelService = VisibilityLabelServiceManager.getInstance()
1067 .getVisibilityLabelService(this.conf);
1068 }
1069
1070 @Override
1071 public void stop(CoprocessorEnvironment env) throws IOException {
1072 }
1073
1074 @Override
1075 public ReplicationEndpoint postCreateReplicationEndPoint(
1076 ObserverContext<RegionServerCoprocessorEnvironment> ctx, ReplicationEndpoint endpoint) {
1077 return new VisibilityReplicationEndpoint(endpoint, visibilityLabelService);
1078 }
1079 }
1080 }