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