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