2

I received a bug report that has me scratching my head. In my program, I test the writability of a directory (which is the user's home directory by default) before having readline collect history and write it to a file in that directory at program exit.

Here is the problematic code:

if (access(dirname, W_OK) != 0)
  complain("cannot create %s in %s", filename, dirname, errno);
else {
  use_readline_for_stuff();
  write_history("%s/%s", dirname, filename);
  exit(0);
}

(This is pseudo-C, of course. The real code is here)

The user (let's call her USER) reports getting an error message "cannot create xxx in /home/USER: permission denied", that disappears after manually creating the file with USER@host > touch /home/USER/xxx.

I would expect an unwritable home directory to lead to all kind of problems, but the user can even simply touch a file in that directory.

The program doesn't run suid, USER owns her home directory (and clearly can create files in it). My program is apparently the only program that displays this kind of problem. It has been fairly widely used for years, and this is the first time this bug gets reported

The Linux manpage for access (2) says

if a directory is found to be writable, it probably means that files can be created in the directory

Why probably? Why (and when) not always (apart from a race condition like someone changing permissions just after the access() call, full inode tables or hitting user limits) all of which don't seem to be the problem here - especially since the access() call fails, and touch xxx succeeds.

Hans Lub
  • 5,513
  • 1
  • 23
  • 43
  • An ACL — Access Control List? Even if the user-group-other permissions say "user X as a member of group Y cannot create a file here", and ACL may override that. Another possible problem could be an NFS auto-mounted home directory, though by the time someone's logged in, that usually is no longer a problem. – Jonathan Leffler Oct 10 '18 at 21:38
  • Yes, I was thinking about that also. But can an ACL make it impossible for my program to create a file, while still allowing `touch` to do the same? – Hans Lub Oct 10 '18 at 21:40
  • Given that this is in the home directory of USER, it probably isn't an ACL issue. It is surprising. What diagnostics have you got available? Have you analyzed the permissions on `/`, `/home`, `/home/USER`? Have you double-checked the current permissions on the executable, as opposed to the ones you expect to be on it? If there is a SUID or SGID bit lurking around, `access()` can give the wrong answer. Is there a chance that the primary group of the user is Group1, but the home directory belongs to Group2? It shouldn't be an issue if `USER` owns their home directory, but that's why you check. – Jonathan Leffler Oct 10 '18 at 21:44
  • As you can tell, I have questions but no answer. But I'm not sure that anyone can provide a useful answer with the information available as yet. The issue is going to depend on the platform, the file system(s), the permissions on programs and directories. Is there any chance you're looking in the wrong place? You look in (potentially) different locations if someone uses `~/xxx` vs `~USER/xxx`. (On my work machine, `~jleffler` maps to `/u/jleffler` and `~` maps to `/work4/jleffler` — as required by POSIX when I set `HOME=/work4/jleffler`.) – Jonathan Leffler Oct 10 '18 at 21:46
  • Many of those questions have already been asked in the discussion [here](https://github.com/hanslub42/rlwrap/issues/90). I'm still intrigued by the _probably_ in the manpage text. What is the author hinting at? – Hans Lub Oct 10 '18 at 21:50
  • How are you creating the file? Where is that error message coming from, it does not seem to be standard? `EACCES` can be raised for a number reasons, see `man open`. – cdarke Oct 10 '18 at 21:50
  • I'm not creating any file yet. `access(homedir, W_OK)` returns -1, with `errno == EACCES`, while the user can touch any file in `homedir`. That is what I don't understand. – Hans Lub Oct 10 '18 at 21:52
  • 1
    The *probably* could mean that the create file might still fail for other reasons, for example if the inode table is full. – cdarke Oct 10 '18 at 21:53
  • 1
    Sorry, I misunderstood, I thought the creation was failing and the `access` working. Check there is execute (search) access on all the directories in the path. – cdarke Oct 10 '18 at 21:54
  • 1
    I note that the claimed output line `drwxr-xr-x 1 /home/REDACTED_USER users 3.0K Sep 20 10:41 REDACTED_USER` is a very curious output from `ls`; the file name is in the wrong place, and the user/group are missing. – Jonathan Leffler Oct 10 '18 at 21:56
  • I can't see what's wrong. I wonder if `/home` is a circuitous symlink? But I'm still not sure whether that explains what is reported. I also wonder why you get ENOENT rather than EACCES when you try to `access()` the files `/home/USER/.terminfo` and `/home/USER/.sqlhosts_plus` — I think I'd expect EACCES if the home directory isn't accessible as indicated by accessing that. Very peculiar. – Jonathan Leffler Oct 10 '18 at 22:07
  • You could show us the strace-output for your process, this might shed some light on this (actually, the line with the access-call should suffice) – Ctx Oct 10 '18 at 22:09
  • @Ctx: in the link [**`here`**](https://github.com/hanslub42/rlwrap/issues/90) in the [comment](https://stackoverflow.com/questions/52749093/directory-not-writable-according-to-access-user-still-able-to-create-files-in?noredirect=1#comment92422903_52749093) to me, there is the `strace` output. – Jonathan Leffler Oct 10 '18 at 22:11
  • @Ctx: see the [bug report](https://github.com/hanslub42/rlwrap/issues/90). The `strace` output doesn't give any more information than `access(homedir, W_OK)` failing with `errno == EACCES` – Hans Lub Oct 10 '18 at 22:13
  • @HansLub ah, ok. Btw. the "probably" means, that it is filesystem dependent; on some filesystems, you really can open the directory like a file for writing – Ctx Oct 10 '18 at 22:14
  • Near the top of the `strace` output, there's a successful open and read of `/etc/ld.so.cache`. Is that normal? Could that account for any of the trouble if it contains something abnormal? – Jonathan Leffler Oct 10 '18 at 22:14
  • @JonathanLeffler normal for linux, just speeds up finding the libraries for dynamic linking – Ctx Oct 10 '18 at 22:15
  • @Ctx: Thanks; I wasn't sure — it could have been harmless (like not finding `/etc/ld.so.preload` is harmless, but finding it might not be), or not. It was probably worth the check. – Jonathan Leffler Oct 10 '18 at 22:18
  • I'm completely out of ideas in the absence of the output from `ls -ld / /home /home/USER; ls -la /home/USER`. It couldn't be something weird like there's a named (Unix-domain) socket `.sqlplus_history` or some other eccentric file type? I still don't have any good, plausible ideas; this is clutching at straws, but I suspect you're close to that stage as well. Is there any chance that some weird file system like AFS is in play? – Jonathan Leffler Oct 10 '18 at 22:19
  • @Jonathan: changing the history filename to something else doesn't make a difference (_except_ when specifying a different directory: creating the file in `/tmp` works just fine). I'm also out of ideas, that's why I post here. Thanks a lot for your suggestions anyway!! – Hans Lub Oct 10 '18 at 22:22
  • The original poster at github seems to have "redacted" a lot of things. I suspect, that he introduced a inconsistency there. – Ctx Oct 10 '18 at 22:24
  • I'd suggest asking the bug reporter whether their home directory is FUSE mounted. Of course, they might not know but someone probably does. It could well be a buggy or non-existent access implementation in the FUSEd virtual filesystem. But honestly, fix your code. Don't test before use; it's a bad idiom and it was bound to get you into trouble sooner or later. – rici Oct 11 '18 at 00:12
  • @rci: If I wouldn't test, `readline` would only try to create the history file (and fail) when the user closes the session. She might have appreciated the error message before typing in all those obscure commands and then losing them... – Hans Lub Oct 11 '18 at 06:34
  • 1
    Wild guess: something like AppArmor/SELinux getting in the way? (`touch` being whitelisted, `rlwrap` not?) – Volker Stolz Oct 11 '18 at 07:01

0 Answers0