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