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