File/Directory Permissions Trailing + ( Drwxr-Xr-X+ )

file/directory permissions trailing + ( drwxr-xr-x+ )

The trailing + signify that ACL, Access Control List, is set on the directory.

You can use getfacl to get the details

getfacl directory

Following output is from getfacl Codespace which have ACL set by setfacl -m u:umesh:rw Codespace.
Here setfacl is giving rw permission to Codespace directory for user umesh.

# file: Codespace/
# owner: root
# group: root
user::rwx
user:umesh:rw-
group::r-x
mask::rwx
other::r-x

and we can remove the ACL using setfacl, for example, for the above sample

setfacl -x u:umesh Codespace/

More details at man setfacl and man getfacl

What does the dot mean after -rwxr-xr-x in the ls -l output?

If you have a look at info ls What\ information, it tells you

Following the file mode bits is a single character that specifies
whether an alternate access method such as an access control list
applies to the file. When the character following the file mode bits
is a space, there is no alternate access method. When it is a
printing character, then there is such a method.

GNU 'ls' uses a '.' character to indicate a file with an SELinux
security context, but no other alternate access method.

A file with any other combination of alternate access methods is
marked with a '+' character.

What does the dot at the end of the permissions in the output of ls -lah mean?

From info coreutils 'ls invocation' under Linux


GNU `ls' uses a `.' character to indicate a file with an SELinux
security context, but no other alternate access method.

A file with any other combination of alternate access methods is
marked with a `+' character.

Change permissions of directory

Let's analyze the desired result:

drwxr-xr-x

d just means this is a directory, we can ignore it:

rwxr-xr-x

translate it to binary code:

11110101 (0 is equivalent to -).

Translate each 3 digits to a decimal number:

755: 111 → 7, 101 → 5

Eventually just type this:

chmod 755 <dirname>

File permission set to 755 but still no web access

drwxr-xr-x.

The trailing dot means the directory has an SElinux ACL, which is likely the cause of your problem. -- the ACL denies access.

How do I 'cd' to a symbolically linked directory that says Permission denied?

It could be the permissions anywhere up the path to either source directory or symlink, including also any other symlinks which are encountered in the paths to these things.

Here is a script that will show all those permissions for you. It will show the permissions on everything from the root directory down to the path in question, and if it encounters the symlink then it will start again based on the link target, except that it won't repeat the output for any directory that it has already shown you (so for example the permissions on / are only ever shown once).

#!/usr/bin/perl
#
use strict;

my $usage=<<EOF;
Usage: $0 file [file ...]

Shows permissions on every path component from root directory to the named
file(s), including paths traversed while following symlinks.

Also shows permissions and contents of any .ftpaccess or .htaccess files
encountered.

If multiple files are specified, gives a listing for them all, but for later
files does not show again the permissions on a path component already shown
for earlier files.

EOF
#----------------------------------------------------------------------

use Cwd;
my $cwd = cwd;
chomp $cwd;

if ($#ARGV==-1) {
print $usage;
exit 1;
};

my @history=();
my @accessfiles=();

# loop over files on command line.
FILE: foreach my $fullpath (@ARGV) {

# prepend cwd if relative path
$fullpath="$cwd/$fullpath" unless $fullpath =~ /^\//;

# fix pathname..
$fullpath=stripslashes($fullpath);

print "\n------ $fullpath ------\n\n";

my $len=length $fullpath;
my $pos=-1;
my @targets=($fullpath);

ELEMENT: while ($pos<$len) {

# get full pathname of next element (etc)

my $stem=($pos>=0) ? substr($fullpath,0,$pos) : ''; # parent

$pos=index($fullpath,'/',$pos+1);
$pos=$len if $pos==-1;
my $path=($pos>0) ? substr($fullpath,0,$pos) : '/';

my $remainder=substr($fullpath,$pos);

#
# stat the path element.
#
my @stat=lstat($path);
my $okay=($#stat != -1);

# tidy up as follows:
# if trailing "/foo/.." or "/." strip it and move to next element.
#
# We can do this because at this stage "foo" isn't a link,
# or we would already have followed it, but only do it if stat
# succeeded as parent might not be a directory (in that case, the
# error trap later will be executed).
#
my ($newpath, $subst);
if ($okay) {
$subst=1;
if ( $path =~ /\.\.$/ ) {
if ($stem eq '') {
$newpath='/';
$fullpath=$remainder;
} else {
($newpath = $stem) =~ s,/([^/]*?)$,, ;
$fullpath=$newpath.$remainder;
if ($newpath eq '') {$newpath='/';}
}
}
elsif ( $path =~ /\.$/ ) {
if ($stem eq '') {
$newpath='/';
$fullpath=$remainder;
} else {
$newpath = $stem;
$fullpath=$newpath.$remainder;
}
}
else {
$subst=0;
}
if ($subst) {
$path=$newpath;
$pos=length $path;
$len=length $fullpath;
next ELEMENT;
}
}

# have we been here before?
my $beenthere=grep {$_ eq $path} @history;
push @history,$path;

# is it a symlink?
if (-l _) {

# yes. see where it points
my $pointsto=readlink $path;
my $newfile;
if ($pointsto =~ /^\//) {
# link is absolute pathname
$newfile=$pointsto.substr($fullpath,$pos);
$pos=-1;
} else {
# link is relative pathname
$newfile=$stem.'/'.$pointsto.substr($fullpath,$pos);
$pos=length $stem;
}
if (!$beenthere) {
show_stat(\@stat,$path,$pointsto);
}
# strip extra "/"
$newfile=stripslashes($newfile);

# check for recursive links
my $beenthere2=grep {$_ eq $newfile} @targets;
push @targets,$newfile;
if ($beenthere2) {
print " === SYMLINK LOOP DETECTED ===\n";
next FILE;
}
# follow the link (stripping extra "/"s)
$fullpath=$newfile;
$len=length $fullpath;
} else {
# no, not a symlink
# print info, with usernames/groupnames where possible
# unless we've been here before...
if (!$beenthere) {
if (! $okay) {
print "\n*** ERROR : $path : $! ***\n";
next FILE;
}
show_stat(\@stat,$path);

#
# check for any {.htaccess,.ftpaccess} files
#
for my $name (qw(.htaccess .ftpaccess)) {
my $file = "$path/$name";
if (-e $file) {
push @accessfiles,$file;
}
}
}
}
} # end ELEMENT loop
} # end FILE loop
continue {
print "\n";
}

if ($#accessfiles > -1) {
print "Access control files of possible relevance:\n\n";
for my $file (@accessfiles) {
my @stat = stat($file);
show_stat(\@stat,$file);
if (open my $access,$file) {
print "\n ---- Contents of $file ----\n";
while (defined (my $line=<$access>)){
chomp $line;
print " $line\n";
}
close $access;
print " ------- End of $file -------\n";
} else {
print " Could not open $file: $!\n";
}
}
print "\n";
}

exit 0;

#-------------------------------------------------------------
sub stripslashes
{
my $line = shift;

# strip trailing or repeated '/'s
$line =~ s,/*$,,;
$line =~ s,//+,/,g;
$line =~ s,^$,/,;

return $line;
}

sub show_stat
{
use Fcntl ':mode';

my ($stat,$path,$linktarget) = @_;

my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = @$stat;

my @pwent=getpwuid($uid);
my $username=($#pwent == -1) ? "($uid)" : $pwent[0];
my @grent=getgrgid($gid);
my $grpname=($#grent == -1) ? "($gid)" : $grent[0];
printf ("%s %4o %-8s %-8s %s\n",
symbolic_mode($mode),
$mode&07777,$username,$grpname,$path);

if (S_ISLNK($mode) and defined($linktarget)) {
print " -> $linktarget\n";
}

}

sub symbolic_mode
{
# takes a numerical mode returned by stat,
# returns a symbolic string as per "ls -l"
# e.g. for 0640 returns "-rw-r-----"

my $mode = shift;

use Fcntl ':mode';

my $typechar =
S_ISREG($mode) ? '-' :
S_ISDIR($mode) ? 'd' :
S_ISLNK($mode) ? 'l' :
S_ISBLK($mode) ? 'b' :
S_ISCHR($mode) ? 'c' :
S_ISFIFO($mode) ? 'p' :
S_ISSOCK($mode) ? 's' :
'?';

my $modestr = $typechar . 'rwxrwxrwx';
for (my $bit=0; $bit <= 8 ; $bit++) {
if (($mode & (1<<$bit)) == 0) {
substchar(\$modestr,9-$bit,"-");
}
}
if ($mode & 01000) {
substchar(\$modestr,9,
($mode & 0001) ? "t" : "T");
}
if ($mode & 02000) {
substchar(\$modestr,6,
($mode & 0010) ? "s" : "S");
}
if ($mode & 04000) {
substchar(\$modestr,3,
($mode & 0100) ? "s" : "S");
}
return $modestr;
}

sub substchar
{
# substitutes a character in a string
my ($sref, $pos, $newchar) = @_;
my $string = $$sref;
my $newstring = substr($string,0,$pos) . $newchar . substr($string,$pos+1);
$$sref=$newstring;
}

example output:

$ mkdir -p /tmp/mydir/stuff
$ ln -s stuff /tmp/mydir/stuff2
$ cal > /tmp/mydir/stuff2/myfile
$ mkdir /tmp/mydir2
$ ls /tmp/mydir2/stuff2/myfile
ls: cannot access '/tmp/mydir2/stuff2/myfile': No such file or directory
$ ls /tmp/mydir/stuff2/
myfile
$ ln -s /tmp/mydir/stuff2/myfile /tmp/mydir/stuff2/thingy
$ chmod 700 /tmp/mydir
$ permsto /tmp/mydir/stuff2/thingy

------ /tmp/mydir/stuff2/thingy ------

drwxr-xr-x 755 root root /
drwxrwxrwt 1777 root root /tmp
drwx------ 700 myuser mygroup /tmp/mydir
lrwxrwxrwx 777 myuser mygroup /tmp/mydir/stuff2
-> stuff
drwxr-xr-x 755 myuser mygroup /tmp/mydir/stuff
lrwxrwxrwx 777 myuser mygroup /tmp/mydir/stuff/thingy
-> /tmp/mydir/stuff2/myfile
-rw-r--r-- 644 myuser mygroup /tmp/mydir/stuff/myfile

So you can look at the output and see at a glance where any permissions problem is lurking.


Now that the question has been updated to include the output of the script on the directory in question, it is clear that the issue is with the permissions on the following directories:

    drwxr--r--  744 lucifer  lucifer  /home/lucifer
drwxr--r-- 744 segeeslice segeeslice /home/segeeslice

Where a process has read but not execute access to a directory, it will be able to list the directory but not otherwise access any paths contained within it. For a directory with octal mode 744, this will be the case for any processes not running as the owner (or root), which will be reliant on the "group" or "other" permissions. If it is changed to octal mode 755 (i.e. add execute permission for "group" and "other"), then these will have the necessary access.

Same permissions, different rights?

The Apache server is installed within a Linux distribution with SELinux enabled. SELinux by default denies Apache to read files in /home folders. By using ls -Z one can check SELinux labels of files. In my case, while usual (DAC) permissions were the same and there were no ACLs set, SELinux context was different:

Next one is readable from Apache daemon and scripts:

unconfined_u:object_r:httpd_sys_content_t:

Next one is the file that SELinux denies access to Apache:

unconfined_u:object_r:user_home_t


Related Topics



Leave a reply



Submit