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