Date: Sun, 03 Jun 2012 20:42:04 -0500 From: Bryan Drewery <bryan@shatow.net> To: freebsd-hackers@freebsd.org Subject: [RFC] last(1) with security.bsd.see_other_uids support Message-ID: <4FCC126C.1020600@shatow.net>
next in thread | raw e-mail | index | archive | help
This is an OpenPGP/MIME signed message (RFC 2440 and 3156) --------------enig5FAF18043A57B2C0A76F8C09 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Hi, I've written up a patch to add some privacy to last(1) while still giving non-privileged users access to their own login history. This is still a work in progress. I am reaching out to make sure my approach is proper and to get some input on code sharing. My goal is to add this support to w(1) and who(1) as well. FWIW I have been running a similar patch on my own shared-hosting systems (pre-utmpx) for a few year= s. Changes: * Added utmp group * All utmpx files are 660 root:utmp * last(1) runs setgid(utmp) and drops this as soon as the utmpx files are opened. * Users in the wheel or utmp group can see all entries * IFF security.bsd.see_other_uids=3D0: users only see their own entries, as well as shutdown/boot/init times. * If security.bsd.see_other_uids=3D1, all entries are always shown, as it= does now. Justifications: Why the changes? This makes sense for shared hosting environments where jails are not practical. A user should be able to see their own login history, to see if someone has been accessing their account, but not to see the IPs of other users. The intention is *not* to disallow them to see that other users of the system. Obviously they can just cat /etc/passwd. This is just about IP privacy. Why the setgid? Allow reading the entries, but disallow directly opening the utx files. I've seen some shared hosts incorrectly chmod 0 /usr/bin/last, but still leave the utmp files wide open for reading! Why the utmp group? It's consistent with other systems (OpenBSD, Linux), and allows giving a user access to see all entries, without granting them wheel or allowing a non-privileged user to run as setgid(wheel). It also helps mitigates security concerns by using a specific group only having extra privilege to utmpx files. I originally had not planned for security.bsd.see_other_uids, but considering POLA and consistency, it makes sense. Questions: To add this support to w(1) and who(1), I want to share the is_user_restricted() function among all 3 binaries. I don't think this really belongs in libc/libutil, but maybe it does. I could just add a shared file into usr.bin/last/ and link it in with all 3, but I don't really like this approach as then usr.bin/{w,who} would depend on usr.bin/last. The libc fchown() changes to pututxline.c may not be needed. I am open to removing this. On the other hand, perhaps most of my changes belong in utx functions in libc. Thus all utx readers would benefit and solve my sharing problem. The readers/clients would still need to be setgid(utmp) though to open the utx files. I still need to update the relevant man pages for these changes as well. Regards, Bryan Drewery Patch available at: http://www.shatow.net/freebsd/last-privacy.txt diff --git a/etc/group b/etc/group index 54d5c59..211ff32 100644 --- a/etc/group +++ b/etc/group @@ -17,6 +17,7 @@ sshd:*:22: smmsp:*:25: mailnull:*:26: guest:*:31: +utmp:*:45: bind:*:53: proxy:*:62: authpf:*:63: diff --git a/etc/newsyslog.conf b/etc/newsyslog.conf index 44aff12..1466939 100644 --- a/etc/newsyslog.conf +++ b/etc/newsyslog.conf @@ -34,6 +34,6 @@ /var/log/ppp.log root:network 640 3 100 * JC /var/log/security 600 10 100 * JC /var/log/sendmail.st 640 10 * 168 B -/var/log/utx.log 644 3 * @01T05 B +/var/log/utx.log root:utmp 660 3 * @01T05 B /var/log/weekly.log 640 5 1 $W6D0 JN /var/log/xferlog 600 7 100 * JC diff --git a/etc/rc.d/var b/etc/rc.d/var index 2b41219..7aa9063 100755 --- a/etc/rc.d/var +++ b/etc/rc.d/var @@ -101,9 +101,11 @@ esac # Make sure we have /var/log/utx.lastlogin and /var/log/utx.log files if [ ! -f /var/log/utx.lastlogin ]; then cp /dev/null /var/log/utx.lastlogin - chmod 644 /var/log/utx.lastlogin + chmod 660 /var/log/utx.lastlogin + chown root:utmp /var/log/utx.lastlogin fi if [ ! -f /var/log/utx.log ]; then cp /dev/null /var/log/utx.log - chmod 644 /var/log/utx.log + chmod 660 /var/log/utx.log + chown root:utmp /var/log/utx.log fi diff --git a/lib/libc/gen/pututxline.c b/lib/libc/gen/pututxline.c index 98addee..c1f7199 100644 --- a/lib/libc/gen/pututxline.c +++ b/lib/libc/gen/pututxline.c @@ -179,10 +179,13 @@ int fd; /* Initialize utx.active with a single BOOT_TIME record. */ - fd =3D _open(_PATH_UTX_ACTIVE, O_CREAT|O_RDWR|O_TRUNC, 0644); + fd =3D _open(_PATH_UTX_ACTIVE, O_CREAT|O_RDWR|O_TRUNC, 0660); if (fd < 0) return; - _write(fd, fu, sizeof(*fu)); + if (fchown(fd, 0, _UTMP_GID) < 0) + warnx("Unable to set root:utmp on " _PATH_UTX_ACTIVE); + else + _write(fd, fu, sizeof(*fu)); _close(fd); } @@ -269,13 +272,18 @@ vec[1].iov_len =3D l; l =3D htobe16(l); - fd =3D _open(_PATH_UTX_LOG, O_CREAT|O_WRONLY|O_APPEND, 0644); + fd =3D _open(_PATH_UTX_LOG, O_CREAT|O_WRONLY|O_APPEND, 0660); if (fd < 0) return (-1); - if (_writev(fd, vec, 2) =3D=3D -1) + if (fchown(fd, 0, _UTMP_GID) < 0) { + warnx("Unable to set root:utmp on " _PATH_UTX_LOG); error =3D errno; - else - error =3D 0; + } else { + if (_writev(fd, vec, 2) =3D=3D -1) + error =3D errno; + else + error =3D 0; + } _close(fd); errno =3D error; return (error =3D=3D 0 ? 0 : 1); diff --git a/lib/libc/gen/utxdb.h b/lib/libc/gen/utxdb.h index d9ebc93..25a5164 100644 --- a/lib/libc/gen/utxdb.h +++ b/lib/libc/gen/utxdb.h @@ -34,6 +34,7 @@ #define _PATH_UTX_ACTIVE "/var/run/utx.active" #define _PATH_UTX_LASTLOGIN "/var/log/utx.lastlogin" #define _PATH_UTX_LOG "/var/log/utx.log" +#define _UTMP_GID 45 /* * Entries in struct futx are ordered by how often they are used. In diff --git a/usr.bin/last/Makefile b/usr.bin/last/Makefile index fd63cd9..c18adbd 100644 --- a/usr.bin/last/Makefile +++ b/usr.bin/last/Makefile @@ -2,6 +2,8 @@ # $FreeBSD$ PROG=3D last +BINGRP=3D utmp +BINMODE=3D2555 NO_WFORMAT=3D diff --git a/usr.bin/last/last.c b/usr.bin/last/last.c index e587def..025f948 100644 --- a/usr.bin/last/last.c +++ b/usr.bin/last/last.c @@ -57,6 +57,10 @@ #include <unistd.h> #include <utmpx.h> #include <sys/queue.h> +#include <sys/types.h> +#include <pwd.h> +#include <grp.h> +#include <sys/sysctl.h> #define NO 0 /* false/no */ #define YES 1 /* true/yes */ @@ -193,6 +197,42 @@ struct idtab { } /* + * Return whether or not the given user can see all entries or not + */ +static int +is_user_restricted(struct passwd *pw, int see_other_uids) +{ + int restricted =3D 1; /* Default to restricted access */ + gid_t *groups; + int ngroups, gid, cnt; + long ngroups_max; + struct group *group; + + if (geteuid() =3D=3D 0 || see_other_uids) + restricted =3D 0; + else { + /* Check if the user is in a privileged group */ + ngroups_max =3D sysconf(_SC_NGROUPS_MAX) + 1; + if ((groups =3D malloc(sizeof(gid_t) * (ngroups_max))) =3D=3D NULL) + err(1, "malloc"); + ngroups =3D ngroups_max; + (void) getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups); + for (cnt =3D 0; cnt < ngroups; ++cnt) { + gid =3D groups[cnt]; + group =3D getgrgid(gid); + /* User is in utmp or wheel group, they can see all */ + if (strncmp("utmp", group->gr_name, 4) =3D=3D 0 || strncmp("wheel", group->gr_name, 5) =3D=3D 0) { + restricted =3D 0; + break; + } + } + free(groups); + } + + return (restricted); +} + +/* * wtmp -- * read through the utx.log file */ @@ -205,6 +245,9 @@ struct idtab { time_t t; char ct[80]; struct tm *tm; + struct passwd *pw =3D NULL; + int restricted, see_other_uids; + size_t len; LIST_INIT(&idlist); (void)time(&t); @@ -212,7 +255,30 @@ struct idtab { /* Load the last entries from the file. */ if (setutxdb(UTXDB_LOG, file) !=3D 0) err(1, "%s", file); + + /* drop setgid now that the db is open */ + setgid(getgid()); + + /* Lookup current user information */ + pw =3D getpwuid(getuid()); + + len =3D sizeof(see_other_uids); + if (sysctlbyname("security.bsd.see_other_uids", &see_other_uids, &len, NULL, 0)) + see_other_uids =3D 0; + restricted =3D is_user_restricted(pw, see_other_uids); + while ((ut =3D getutxent()) !=3D NULL) { + /* Skip this entry if the invoking user is not permitted + * to see it */ + if (restricted && + !(ut->ut_type =3D=3D BOOT_TIME || + ut->ut_type =3D=3D SHUTDOWN_TIME || + ut->ut_type =3D=3D OLD_TIME || + ut->ut_type =3D=3D NEW_TIME || + ut->ut_type =3D=3D INIT_PROCESS) && + strncmp(ut->ut_user, pw->pw_name, sizeof(ut->ut_user))) + continue; + if (amount % 128 =3D=3D 0) { buf =3D realloc(buf, (amount + 128) * sizeof *ut); if (buf =3D=3D NULL) --------------enig5FAF18043A57B2C0A76F8C09 Content-Type: application/pgp-signature; name="signature.asc" Content-Description: OpenPGP digital signature Content-Disposition: attachment; filename="signature.asc" -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.10 (MingW32) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ iQIcBAEBAgAGBQJPzBJxAAoJEG54KsA8mwz5CbYP/iU9/srJSr0OHGhNaqfKJtqx SHB3KPVbchR/pwg5YgRYYIvVd1lPy2nd8KZHZOXqbK4H56B8QnMHyNMlk5RCZN/4 HC5AJGQ197m2ynSAwNOqE28qzhaxLWlz9JdRuZePYJuoaykYEPtMgXg2enNAuW0L ZOs0RSeNgMcuRv6TH1GxyXOmfZLMG9YumJHlvR6nGF60vkajwvRYsvRXsvbOmhYl EpVXSpvHKdhzdkwj7bJa4/Oj2zX0NqXO13vMMynGrdvfpwJa35m98B87Qo5iOABX XbrsmUnAptr0tic/3r7z1ipBnmPIbZbKrVG5AaiRKcnTmusoJ/ANFSZRORNN4Gwe dpwRCsm95cbbr4m+kq/x4xeIRXpbp/jOaWq17/76YQRof4o0MbAcK/Vz2UZNFTTW /NsWX7tEszwSRvtNt0DDdMtqRWpoi2sUXTFuynrm5Y/NB7Uy5YKfomjB6uIcrRak a7iHybd10evC+tnuLZ0HI/MF5yWn+779wpR1OR7Drx7tGUle25GD7Cz622GKgiNy kIk1LRlZdCxWOBWn0uox9XE78xjar3odfy062IkZkxMJ3MTWqjdDuv851LoMZMy9 PPk85dWYz7Wf7SO4X9gFNpChBAndkPx4X3sEuEBURHZD+wLALXVhK/dtszJ55ERu 0tICfWmWj+/H92RzTnnd =o2eb -----END PGP SIGNATURE----- --------------enig5FAF18043A57B2C0A76F8C09--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?4FCC126C.1020600>