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