From e2f64d2b71ba914afe4e3d275c006cd7139b23c8 Mon Sep 17 00:00:00 2001 From: Albert Louis Rossi Date: Mon, 19 Apr 2021 12:50:14 -0500 Subject: [PATCH] dcache-xroot: store most recent login subject in door Motivation: In the xrootd4j library, the authentication handling API entails the return of the subject via an implementation-specific login. The authorization API, however, is based upon the check of permissions on a specific path, and does not provide for side-effects to the subject. This has been complicated, however, by the SciTokens authorization protocol, at least in its dCache manifestation, because the bearer token may actually contain new information about the subject that was not present at login, as well as the specific restrictions now granted overriding the preceding restrictions established at login. The original solution was to stack login information in the door such that a request accesses the most recent restrictions. What was not done, however, was to update in a similar manner the actual login subject. This needs to happen because the xrootd specific session info, which contains the original login Subject, is not updated during authorization. Using the xrootd4j session Subject in the case of GSI, for instance, works fine, but using it with SciTokens will not work unless the ZTN protocol is active at login AND the ZTN token is identical to the one used on the path URL, which very likely may not be the case. One could try to fix this in the xrootd4j library by adjusting the authorization handler API to be able to update the session subject, but since dCache will continue to require the login info stack in the door to get the correct, dCache-specific Restriction object, it makes more sense simply not to use the xrootd4j session Subject at all, and instead store the most recent Subject with the Restriction in the door. Modification: Rename SessionInfo to LoginSessionInfo for clarity, and add the Subject to it. Always access this Subject in the door, and ignore the one contained in the request object's xrootd Session. Result: Correct subject is used to access the path for both GSI and SciToken protocols (and hopefully any future ones). Target: master Request: 7.0 Request: 6.2 Patch: https://rb.dcache.org/r/12988/ Acked-by: Tigran --- .../xrootd/door/XrootdRedirectHandler.java | 143 ++++++++++-------- 1 file changed, 84 insertions(+), 59 deletions(-) diff --git a/modules/dcache-xrootd/src/main/java/org/dcache/xrootd/door/XrootdRedirectHandler.java b/modules/dcache-xrootd/src/main/java/org/dcache/xrootd/door/XrootdRedirectHandler.java index 05cdc335005..9b0210e109c 100644 --- a/modules/dcache-xrootd/src/main/java/org/dcache/xrootd/door/XrootdRedirectHandler.java +++ b/modules/dcache-xrootd/src/main/java/org/dcache/xrootd/door/XrootdRedirectHandler.java @@ -60,7 +60,7 @@ import org.dcache.xrootd.core.XrootdException; import org.dcache.xrootd.core.XrootdSession; import org.dcache.xrootd.protocol.XrootdProtocol; -import org.dcache.xrootd.protocol.XrootdProtocol.FilePerm; +import org.dcache.xrootd.protocol.XrootdProtocol.*; import org.dcache.xrootd.protocol.messages.AwaitAsyncResponse; import org.dcache.xrootd.protocol.messages.CloseRequest; import org.dcache.xrootd.protocol.messages.DirListRequest; @@ -103,23 +103,26 @@ public class XrootdRedirectHandler extends ConcurrentXrootdRequestHandler private static final Logger _log = LoggerFactory.getLogger(XrootdRedirectHandler.class); - private class SessionInfo + private class LoginSessionInfo { + private final Subject subject; private final Restriction restriction; private final OptionalLong maximumUploadSize; private final FsPath userRootPath; private final boolean loggedIn; - SessionInfo(Restriction restriction) + LoginSessionInfo(Restriction restriction) { + subject = new Subject(); this.restriction = restriction; maximumUploadSize = OptionalLong.empty(); userRootPath = null; loggedIn = false; } - SessionInfo(LoginReply reply) + LoginSessionInfo(LoginReply reply) { + subject = reply.getSubject(); restriction = reply.getRestriction(); userRootPath = reply.getLoginAttributes().stream() .filter(RootDirectory.class::isInstance) @@ -137,6 +140,11 @@ public Restriction getRestriction() return restriction; } + public Subject getSubject() + { + return subject; + } + public OptionalLong getMaximumUploadSize() { return maximumUploadSize; @@ -155,8 +163,8 @@ public boolean isLoggedIn() private final XrootdDoor _door; private final Map _appIoQueues; - private final SessionInfo _defaultSessionInfo; - private final Deque _logins; + private final LoginSessionInfo _defaultLoginSessionInfo; + private final Deque _logins; private final FsPath _rootPath; /** @@ -173,7 +181,7 @@ public XrootdRedirectHandler(XrootdDoor door, FsPath rootPath, ExecutorService e _rootPath = rootPath; _queryConfig = queryConfig; _appIoQueues = appIoQueues; - _defaultSessionInfo = new SessionInfo(Restrictions.denyAll()); + _defaultLoginSessionInfo = new LoginSessionInfo(Restrictions.denyAll()); _logins = new ArrayDeque<>(2); } @@ -256,7 +264,7 @@ protected XrootdResponse doOnOpen(ChannelHandlerContext ctx, OpenRe InetSocketAddress localAddress = getDestinationAddress(); InetSocketAddress remoteAddress = getSourceAddress(); - SessionInfo sessionInfo = sessionInfo(); + LoginSessionInfo loginSessionInfo = sessionInfo(); Map opaque; @@ -279,7 +287,7 @@ protected XrootdResponse doOnOpen(ChannelHandlerContext ctx, OpenRe XrootdResponse response = conditionallyHandleThirdPartyRequest(req, - sessionInfo, + loginSessionInfo, opaque, path, remoteAddress.getHostName()); @@ -336,11 +344,13 @@ protected XrootdResponse doOnOpen(ChannelHandlerContext ctx, OpenRe // TODO: replace with req.isPersistOnSuccessfulClose() with the latest xrootd4j transfer = _door.write(remoteAddress, path, triedHosts, ioQueue, uuid, true, overwrite, size, - sessionInfo.getMaximumUploadSize(), - localAddress, req.getSubject(), sessionInfo.getRestriction(), + loginSessionInfo.getMaximumUploadSize(), + localAddress, + loginSessionInfo.getSubject(), + loginSessionInfo.getRestriction(), persistOnSuccessfulClose, - ((sessionInfo.isLoggedIn()) ? - sessionInfo.getUserRootPath() : _rootPath), + ((loginSessionInfo.isLoggedIn()) ? + loginSessionInfo.getUserRootPath() : _rootPath), req.getSession().getDelegatedCredential(), opaque); } else { @@ -357,14 +367,14 @@ protected XrootdResponse doOnOpen(ChannelHandlerContext ctx, OpenRe Subject subject; if (opaque.get("tpc.key") == null) { - subject = req.getSubject(); + subject = loginSessionInfo.getSubject(); } else { subject = Subjects.ROOT; } transfer = _door.read(remoteAddress, path, triedHosts, ioQueue, uuid, localAddress, subject, - sessionInfo.getRestriction(), opaque); + loginSessionInfo.getRestriction(), opaque); /* * Again, if this is a tpc transfer, then dCache is source here. @@ -459,7 +469,7 @@ protected XrootdResponse doOnOpen(ChannelHandlerContext ctx, OpenRe */ private XrootdResponse conditionallyHandleThirdPartyRequest(OpenRequest req, - SessionInfo sessionInfo, + LoginSessionInfo loginSessionInfo, Map opaque, FsPath fsPath, String remoteHost) @@ -470,10 +480,13 @@ protected XrootdResponse doOnOpen(ChannelHandlerContext ctx, OpenRe "Read permission denied"); } + Subject subject = loginSessionInfo.getSubject(); + Restriction restriction = loginSessionInfo.getRestriction(); + if ("placement".equals(opaque.get("tpc.stage"))) { FileStatus status = _door.getFileStatus(fsPath, - req.getSubject(), - sessionInfo.getRestriction(), + subject, + restriction, remoteHost); int fd = _door.nextTpcPlaceholder(); _log.debug("placement response to {} sent to {} with fhandle {}.", @@ -581,8 +594,8 @@ protected XrootdResponse doOnOpen(ChannelHandlerContext ctx, OpenRe _log.debug("Open request {} from client to door as source, " + "info {}: OK.", req, info); FileStatus status = _door.getFileStatus(fsPath, - req.getSubject(), - sessionInfo.getRestriction(), + subject, + restriction, remoteHost); int flags = status.getFlags(); @@ -721,12 +734,14 @@ protected XrootdResponse doOnClose(ChannelHandlerContext ctx, Clos protected XrootdResponse doOnStat(ChannelHandlerContext ctx, StatRequest req) throws XrootdException { - String path = req.getPath(); try { + String path = req.getPath(); + LoginSessionInfo loginSessionInfo = sessionInfo(); InetSocketAddress client = getSourceAddress(); - SessionInfo sessionInfo = sessionInfo(); - return new StatResponse(req, _door.getFileStatus(createFullPath(path), req.getSubject(), - sessionInfo.getRestriction(), + + return new StatResponse(req, _door.getFileStatus(createFullPath(path), + loginSessionInfo.getSubject(), + loginSessionInfo.getRestriction(), client.getAddress().getHostAddress())); } catch (FileNotFoundCacheException e) { throw xrootdException(e.getRc(), "No such file"); @@ -753,11 +768,13 @@ protected XrootdResponse doOnStatx(ChannelHandlerContext ctx, Stat for (int i = 0; i < paths.length; i++) { paths[i] = createFullPath(req.getPaths()[i]); } - SessionInfo sessionInfo = sessionInfo(); + LoginSessionInfo loginSessionInfo = sessionInfo(); + Subject subject = loginSessionInfo.getSubject(); + Restriction restriction = loginSessionInfo.getRestriction(); return new StatxResponse(req, _door.getMultipleFileStatuses(paths, - req.getSubject(), - sessionInfo.getRestriction())); + subject, + restriction)); } catch (TimeoutCacheException e) { throw xrootdException(e.getRc(), "Internal timeout"); } catch (PermissionDeniedCacheException e) { @@ -781,10 +798,10 @@ protected XrootdResponse doOnRm(ChannelHandlerContext ctx, RmRequest _log.info("Trying to delete {}", req.getPath()); try { - SessionInfo sessionInfo = sessionInfo(); + LoginSessionInfo loginSessionInfo = sessionInfo(); _door.deleteFile(createFullPath(req.getPath()), - req.getSubject(), - sessionInfo.getRestriction()); + loginSessionInfo.getSubject(), + loginSessionInfo.getRestriction()); return withOk(req); } catch (TimeoutCacheException e) { throw xrootdException(e.getRc(), "Internal timeout"); @@ -810,10 +827,10 @@ protected XrootdResponse doOnRmDir(ChannelHandlerContext ctx, RmDi _log.info("Trying to delete directory {}", req.getPath()); try { - SessionInfo sessionInfo = sessionInfo(); + LoginSessionInfo loginSessionInfo = sessionInfo(); _door.deleteDirectory(createFullPath(req.getPath()), - req.getSubject(), - sessionInfo.getRestriction()); + loginSessionInfo.getSubject(), + loginSessionInfo.getRestriction()); return withOk(req); } catch (TimeoutCacheException e) { throw xrootdException(e.getRc(), "Internal timeout"); @@ -838,11 +855,11 @@ protected XrootdResponse doOnMkDir(ChannelHandlerContext ctx, MkDi _log.info("Trying to create directory {}", req.getPath()); try { - SessionInfo sessionInfo = sessionInfo(); + LoginSessionInfo loginSessionInfo = sessionInfo(); _door.createDirectory(createFullPath(req.getPath()), req.shouldMkPath(), - req.getSubject(), - sessionInfo.getRestriction()); + loginSessionInfo.getSubject(), + loginSessionInfo.getRestriction()); return withOk(req); } catch (TimeoutCacheException e) { throw xrootdException(e.getRc(), "Internal timeout"); @@ -874,11 +891,11 @@ protected XrootdResponse doOnMv(ChannelHandlerContext ctx, MvRequest _log.info("Trying to rename {} to {}", req.getSourcePath(), req.getTargetPath()); try { - SessionInfo sessionInfo = sessionInfo(); + LoginSessionInfo loginSessionInfo = sessionInfo(); _door.moveFile(createFullPath(req.getSourcePath()), createFullPath(req.getTargetPath()), - req.getSubject(), - sessionInfo.getRestriction()); + loginSessionInfo.getSubject(), + loginSessionInfo.getRestriction()); return withOk(req); } catch (TimeoutCacheException e) { throw xrootdException(e.getRc(), "Internal timeout"); @@ -941,10 +958,10 @@ protected XrootdResponse doOnQuery(ChannelHandlerContext ctx, Quer try { ChecksumInfo checksumInfo = new ChecksumInfo(msg.getPath(), msg.getOpaque()); - SessionInfo sessionInfo = sessionInfo(); + LoginSessionInfo loginSessionInfo = sessionInfo(); Set checksums = _door.getChecksums(createFullPath(msg.getPath()), - msg.getSubject(), - sessionInfo.getRestriction()); + loginSessionInfo.getSubject(), + loginSessionInfo.getRestriction()); return selectChecksum(checksumInfo, checksums, msg); } catch (CacheException e) { throw xrootdException(e); @@ -970,15 +987,15 @@ protected XrootdResponse doOnDirList(ChannelHandlerContext ctx, if (!_door.isReadAllowed(fullListPath)) { throw new PermissionDeniedCacheException("Permission denied."); } - SessionInfo sessionInfo = sessionInfo(); + LoginSessionInfo loginSessionInfo = sessionInfo(); + Subject subject = loginSessionInfo.getSubject(); + Restriction restriction = loginSessionInfo.getRestriction(); if (request.isDirectoryStat()) { - _door.listPath(fullListPath, request.getSubject(), - sessionInfo.getRestriction(), - new StatListCallback(request, fullListPath, ctx), + _door.listPath(fullListPath, subject, restriction, + new StatListCallback(request, subject, restriction, fullListPath, ctx), _door.getRequiredAttributesForFileStatus()); } else { - _door.listPath(fullListPath, request.getSubject(), - sessionInfo.getRestriction(), + _door.listPath(fullListPath, subject, restriction, new ListCallback(request, ctx), EnumSet.noneOf(FileAttribute.class)); } @@ -1200,23 +1217,30 @@ public void timeout(String error) { private class StatListCallback extends ListCallback { - private final String _client; protected final FsPath _dirPath; - - public StatListCallback(DirListRequest request, FsPath dirPath, ChannelHandlerContext context) + private final String _client; + private final Subject _subject; + private final Restriction _restriction; + + public StatListCallback(DirListRequest request, + Subject subject, + Restriction restriction, + FsPath dirPath, + ChannelHandlerContext context) { super(request, context); _client = getSourceAddress().getAddress().getHostAddress(); _dirPath = dirPath; + _subject = subject; + _restriction = restriction; } @Override public void success(PnfsListDirectoryMessage message) { - SessionInfo sessionInfo = sessionInfo(); message.getEntries().stream().forEach( - e -> _response.add(e.getName(), _door.getFileStatus(_request.getSubject(), - sessionInfo.getRestriction(), + e -> _response.add(e.getName(), _door.getFileStatus(_subject, + _restriction, _dirPath.child(e.getName()), _client, e.getFileAttributes()))); if (message.isFinal()) { @@ -1237,9 +1261,9 @@ private void loggedIn(LoginEvent event) { } LoginReply reply = event.getLoginReply(); - SessionInfo info = reply == null - ? new SessionInfo(Restrictions.none()) - : new SessionInfo(reply); + LoginSessionInfo info = reply == null + ? new LoginSessionInfo(Restrictions.none()) + : new LoginSessionInfo(reply); _logins.push(info); } @@ -1264,16 +1288,17 @@ private FsPath createFullPath(String path) * * @return current info. */ - private SessionInfo sessionInfo() + private LoginSessionInfo sessionInfo() { if (_logins.size() > 1) { return _logins.pop(); } if (_logins.isEmpty()) { - return _defaultSessionInfo; + return _defaultLoginSessionInfo; } return _logins.peek(); } + }