Re: Amendment 1 to Making SRFI-170 less of a monster Lassi Kortela 02 Aug 2019 21:24 UTC
> Oops, I left out of my first insomnia inspired message removing *all* > of 3.6, User and group database access, that was why I later added > back a direct group-info:members procedure. > > Still thinking about this. I continue to think that because stat() > exposes uids and gids, there needs to be a way to convert them to the > strings we all know and love. IMHO: - include a procedure to get user info by UID (getpwuid()) - include a procedure to get group info by GID (getgrgid()) - leave out procedure to get user/group info by name (getpwnam()) Programs should not use Unix usernames for access control. Sysadmins should set permissions using usernames interactively, but under the hood, the UIDs become the ground truth. - leave out the procedure to enumerate all users/groups (getpwent()). These may involve deeply complex NIS (network user database) stuff. Even the getpwent() BSD manual page says: "NOTE that getpwent() may cause a very lengthy search for user account records by opendirectoryd and may result in a large number of user account records being cached by the calling process. Use of this function is not advised." Getting one user/group can also invoke NIS, but doesn't need to walk the entire list, and most requests will probably hit the local cache. The object returned by the getpwuid() wrapper can be an opaque object with the pw_name (username), pw_uid, pw_gid, pw_gecos (full name) and pw_dir (home directory) fields. Plus any optional implementation-defined fields The object returned by the getgrgid() wrapper should have the gr_gid and gr_name fields, plus optional implementation-defined ones. > Except now that you point it out, we should also include a single > direct call that returns the home directory, > > And sets it. Lots of programs are greatly simplified if they can set > the working directory. - The home directory is most reliably found out via getpwuid() as above. - The current directory is getcwd() / chdir(). I support including both of these as straightforward wrappers of those Unix calls. > Unless any of you can think of sufficient 80/20 reasons to browse > other people's accounts, or find out who's in X group, etc.?? > > Not I. Neither do I. > Especially with John's suggestion to keep file-info-regular?, I'm > thinking of doing this in an inverse fashion, only providing > predicates for the file system objects that are relevant to the > putative user of this SRFI, i.e. regular files and directories. > > I think that makes sense: regular-file? and directory?. I'd maybe favor a single file-type procedure that returns a symbol. Ruby has that (ftype, which returns a string). Easy to check in a case statement. It can return 'file or 'directory or an implementation-defined value. (It might be worth it to also standardize 'socket, as well as 'pipe a.k.a. fifo. We could have a 'device value but as both of you pointed out, there's little that can portably be done with devices - except things like reading /dev/null or /dev/random where you don't have to care it's a device.) > It's a *colossal* can of worms, my last Lisp project in anger, a > Clojure website for a non-profit I was volunteering for a few years > ago, had to deal with them, but only the US ones. I haven't even > found a POSIX way to return the current time zone as a string, > although I haven't looked very hard yet. > > Can't be done, though /etc/timezone is an approximation. But come to > think of it, the *user's* notion of the current timezone is and should > be simply the TZ variable. So flush current-timezone. These comments makes me think timezones should have their own SRFI. A delightful array of historical, political, notational, scientific and technical problems rolled into one happy bundle. > Luckily, we can > bootstrap a full implementation of timezones using the binary data files > in /usr/share/zoneinfo. It's not there on Windows, but every copy of > Java, Ruby, Python, etc. plusave individual apps (each their own copy, > of course, it's Windows) that don't want to expose people to the > *severe* limitations of Windows timezones. Or we can just have people > download them from > <https://www.iana.org/time-zones/repository/tzdata-latest.tar.gz>. > (WinZip and 7-Zip can extract tarballs.) We could have a canonical Scheme library to parse the tzdata files and convert to S-expressions which Scheme can easily read. Maybe there's such a library already. > That's a bit messy to do portably as we're doing it now, seems the > strategy is to make them high numbers. A problem this current POSIX > SRFI has is that it has to use the official ones, so errno-error will > return a sane string. > > I think Lassi meant that idea only for Scheme-level exceptions like > "can't open input file" and making it possible to pull the errno out of > that, not to *supply* arbitrary errno values to arbitrary user exceptions. Correct. The ability for users to make new exceptions with arbitrary errno values could come in handy too, didn't think about that. > But all that said, *we* can't prevent someone from setting > their binary set-uid root. Luckily the problems of setuid have been publicized enough by now that most sysadmins worth their salt are going to recoil in horror at the suggestion to install any new setuid binaries on their premises :) > We *could* view giving them setuid > etc. calls in this base SRFI to use after doing high privilege stuff > as encouraging them to go down this dark path. > > Just so. Flush it all. I agree that it's best to leave those calls to some kind of security SRFI (if Scheme should offer them at all). It's not really a dark path - starting as root to do some initialization and then dropping privileges is sound design. (OpenBSD's new pledge() framework is a great case study in gradually relinquishing privileges, and how that solves a plethora of issues in a simple way.) But root privileges should always be dropped very early on in the lifetime of the program, or perhaps leave just a main poll() loop running as root and forking off unprivileged subprocesses as needed. I agree with John that Scheme is not an ideal substrate for this kind of work. Simplicity at the low level is key here. The daemontools / runit uses chain-loaded shell commands to configure and load daemons, which works brilliantly. For example, there's a 'setuidgid' program that drops to the UID and GID you want and then runs the remainder of the command line under those credentials. Similar chain-loading commands for setting resource limits, etc. So you write a simple script using those commands to start the daemon that actually stays alive to do all the complex stuff that daemons do. But by the time that daemon starts, most of the setup has been done by the chain-loading scripts already. It's simple and brilliant. And the chain-loading tools are reusable and so simple that they are easy to audit.