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