View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.server.handler;
20  
21  import java.nio.file.Files;
22  import java.nio.file.Path;
23  
24  import org.eclipse.jetty.server.handler.ContextHandler.AliasCheck;
25  import org.eclipse.jetty.util.log.Log;
26  import org.eclipse.jetty.util.log.Logger;
27  import org.eclipse.jetty.util.resource.PathResource;
28  import org.eclipse.jetty.util.resource.Resource;
29  
30  
31  /* ------------------------------------------------------------ */
32  /** Symbolic Link AliasChecker.
33   * <p>An instance of this class can be registered with {@link ContextHandler#addAliasCheck(AliasCheck)}
34   * to check resources that are aliased to other locations.   The checker uses the 
35   * Java {@link Files#readSymbolicLink(Path)} and {@link Path#toRealPath(java.nio.file.LinkOption...)}
36   * APIs to check if a file is aliased with symbolic links.</p>
37   */
38  public class AllowSymLinkAliasChecker implements AliasCheck
39  {
40      private static final Logger LOG = Log.getLogger(AllowSymLinkAliasChecker.class);
41      
42      @Override
43      public boolean check(String uri, Resource resource)
44      {
45          // Only support PathResource alias checking
46          if (!(resource instanceof PathResource))
47              return false;
48          
49          PathResource pathResource = (PathResource)resource;
50  
51          try
52          {
53              Path path = pathResource.getPath();
54              Path alias = pathResource.getAliasPath();
55              
56              // is the file itself a symlink?
57              if (Files.isSymbolicLink(path))
58              {        
59                  alias = path.getParent().resolve(alias);
60                  if (LOG.isDebugEnabled())
61                  {
62                      LOG.debug("path ={}",path);
63                      LOG.debug("alias={}",alias);
64                  }
65                  if (Files.isSameFile(path,alias))
66                  {
67                      if (LOG.isDebugEnabled())
68                          LOG.debug("Allow symlink {} --> {}",resource,pathResource.getAliasPath());
69                      return true;
70                  }
71              }
72  
73              // No, so let's check each element ourselves
74              boolean linked=true;
75              Path target=path;
76              int loops=0;
77              while (linked)
78              {
79                  if (++loops>100)
80                  {
81                      if (LOG.isDebugEnabled())
82                          LOG.debug("Too many symlinks {} --> {}",resource,target);
83                      return false;
84                  }
85                  linked=false;
86                  Path d = target.getRoot();
87                  for (Path e:target)
88                  {
89                      Path r=d.resolve(e);
90                      d=r;
91  
92                      while (Files.exists(d) && Files.isSymbolicLink(d))
93                      {
94                          Path link=Files.readSymbolicLink(d);    
95                          if (!link.isAbsolute())
96                              link=d.getParent().resolve(link);
97                          d=link;
98                          linked=true;
99                      }
100                 }
101                 target=d;
102             }
103             
104             if (pathResource.getAliasPath().equals(target))
105             {
106                 if (LOG.isDebugEnabled())
107                     LOG.debug("Allow path symlink {} --> {}",resource,target);
108                 return true;
109             }
110         }
111         catch(Exception e)
112         {
113             LOG.ignore(e);
114         }
115         
116         return false;
117     }
118 
119 }