GNOME Bugzilla – Bug 347871
gdm doesn't write login records
Last modified: 2007-11-27 21:55:22 UTC
currently gdm defers wtmp login session accounting to the sessreg program which is executed by the PreSession and PostSession scripts. The doesn't handle btmp logging (for failed login attempts). There is a "perhaps" item in the TODO file to implement this type of record keeping directly. I've commited a first cut at this in rawhide, and so I've filed this bug to keep track of the patch.
Created attachment 69086 [details] [review] initial wtmp/btmp support
Thanks, Ray. I asked Gary Winiger, our security expert here at Sun to review your patch. I'd like to hear if he has any comments before I commit the patch.
Note this patch is actually causing some problems with gnome-volume-manager: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=201440 g-v-m was recently changed to read utmp entries to determine whether the user is a console user (which is a bit dubious, given there is no gaurantee that a system even has /var/run/utmp). My patch only writes wtmp entries and not utmp entries (where as sessreg before wrote both). My code only writes wtmp entries because of a line in the linux utmp man page: xdm(8) should not create a utmp record, because there is no assigned terminal. Letting it create one will result in errors, such as ’fin- ger: cannot stat /dev/machine.dom’. It should create wtmp entries, though, just like ftpd(8) does. Maybe gdm should write utmp entries for *local* logins though (and use the tty X is running on as the assigned terminal). hard to say.
Any updates on this? I notice your patch didn't remove the sessreg call from PostSession, is this a bug? I do think it would probably be better to manage utmp/wtmp in the c-code rather than depending on sessreg. Perhaps looking at the Xorg sessreg code would give a better clue about how to manage utmp? I don't really know about the xdm manpage. Probably should get an opinion from a utmp/wtmp expert to help us decide how to manage this. At the very least, we should probably continue using sessreg until we figure out how not to break gnome-volume-manager.
It is a bug that it didn't get removed from PostSession. No updates yet. I think the right way to go is to write utmp entries for local logins and not for remote logins (like I mentioned as a possiblity in comment 3). I'll try to update the patch soonish (and some of the other pending gdm patches), but I'm a bit busy with RHEL-5 beta blockers at the moment.
Ray, I think that note about xdm doesn't apply to gdm here because the utmp entry is added by the slave process which a) has a controlling terminal b) has a PID that execv's gnome-session c) has a $DISPLAY. So in the end we have a utmp entry that makes sense for our desktop session. I think we want a utmp entry for each remote (XDMCP) gdm login too for the same reasons. Slightly off topic, we currently have gnome-terminal writing utmp entries which I think is a little bogus. I think moving utmp logging from relying on sessreg to the slave C code is a good idea. As far as reliability goes, I don't think we have any better options at this point. If the system has <utmp.h> and the _PATH_UTMP symbol then I think it is as good as we can get without designing an entirely new login accounting system. I wonder if OS security contexts can help enforce what processes can write to utmp? For utmp consumers, perhaps they can sanity check the utmp entries by ensuring the user/pid own the tty etc?
Created attachment 88620 [details] [review] patch updated to 2.19.1
Ray/Matthias: I think you will be happy to hear that I got this patch working well enough with utmp and integrated the code into SVN head. I did make some changes to the patch provided, which may require some additional work on your end to support Linux perfectly. Can you test and perhaps provide me with a patch that fixes any build issues you find. Changes I made include: + On Solaris we use utmpx instead of utmp. If your platform doesn't support utmpx, then we probably need to add some checking to configure to detect which one is being used (see gnome-terminal module configure since it does this already). The code differences between the two interfaces are pretty trivial (the structure name and the function name ends in x when using utmpx and also the utmpx structure has a few additional entries which are ifdef'ed out if using utmp. I suspect this might be the only thing you might need to fix. + I moved the utmp/wtmp processing to slave.c instead of verify-pam.c. Remember verify-pam.c only gets compiled when PAM is configured. Otherwise verify-shadow.c or verify-crypt.c will be used. We still want to do utmp and wtmp processing even if PAM isn't being used, don't we? + I modified the code a bit so that on Sun, the ut_line is set to the device rather than DISPLAY since this is how things need to work on Solaris. + Cleaned up the code quite a bit, added some better error handling and better debug messages. + I fixed the PreSession and PostSession script to remove all sessreg cruft properly (as discussed so far in this bug report). + The utmp code works great on Solaris, but you should verify it works well on Linux also, I'd think. I'll keep this bug open until I hear word that things work on Linux, so please attach a patch if its necessary to further tweak the code to get things working on Linux again. For reference. Along with this change, I also made some other changes to the SVN head including: + Did some further cleanup in daemon/getvt.c and wrote some access functions so that you can easily get the XFree86_VT atom value. + Modified gdm_verify_user and gdm_verify_setup_user functions so we no longer pass in display and local arguments. Since these values are in the GdmDisplay structure directly, it makes the code much cleaner to just access them from one place. + Fixed QUERY_VT so that if gdm_get_current_vt returns -1 it returns an error message rather than OK. + Add some code so that Solaris PAM_TTY, Solaris logindevperm processing, and Solaris audit all work with VT which is under development here at Sun. Cleaned up the Solaris di_devperm logic and now fixed GDM so it accesses the sound card by setting ACL on the sound device rather than calling di_devperm functions on the gdm user, which is much cleaner. This is all Solaris specific (#ifdef __sun) code, so probably not that interesting to other distros.
It seems this does break things on Linux. Refer to bug #450453. Anybody on Linux able to help fix this?
i'll look at it, thanks for picking up the work on this one Brian.
Created attachment 90634 [details] [review] updated to work with linux So getting it to compile was pretty easy. Linux does support the utmpx interfaces if you compile with _GNU_SOURCE. It doesn't support the ut_syslen field, however. The above patch addresses that by defining HAVE_UT_SYSLEN if the field is present, and only initializing it in that case. Note, I changed the actual value of ut_syslen to be the length of the host string, not the size. I think the latter was an off-by-one error, but it's a solaris specific fix, so I'd appreciate if you could verify. Another change I made was changing this piece of code: #ifdef __sun device_name = gdm_get_current_vt_device (d); if (device_name == NULL) { device_name = g_strdup ("/dev/console"); } #else /* Linux seems to want to use display as the device name */ device_name = g_strdup (d->name); #endif I'm not sure either side of the #if/#else block is really right. gdm_get_current_vt_device () is going to return the device on the remote system if this is an xdmcp setup. You probably don't want the /dev/tty7 or whatever from the remote system to get logged to your wtmp file. I think maybe the right approach is to use the vt device if it's a local connection and leave ut_line empty if it's a remote connection. The other thing I did in the patch was rename GdmVerifyRecord to GdmSessionRecord since it's not longer in verify-pam.h
Ray. I committed your patch. Thanks for helping with this. I didn't expect it would be too difficult to get the #ifdef's working for Linux, but since I don't have a Linux box handy I really appreciate your help with this. I think your recommended changes are good regarding setting the device to the current vt device only for attached displays, and fixing the ut_syslen. I have been working with the Sun Ray team and they have some specific requirements for how utmp processing needs to work. On Solaris, for auditing to work properly for Sun Ray and for remote machines the utmpx, PAM_TTY and Sun audit code needs to all work in a special way. (Note PAM_TTY and Sun audit code are "#ifdef __sun" so this doesn't affect Linux). I've been reviewing the CDE login (dtlogin) code and it has special logic to handle this. It works like this: If the display is really the first login, then this is special and gets the "/dev/console" string. (Actually this can be configured in the /usr/dt/config/Xsessions script but by default it just starts one display and calls it console). For all other attached displays that do not have a VT number, it uses "/dev/dtlocal". For all remote displays it uses "/dev/dtremote". /dev/dtlocal and /dev/dtremote are referred to as pseudo-devices. If creating a record with /dev/dtremote or /dev/dtlocal, CDE login will check if the file exists. If not, a symlink is created pointing to /dev/null. If the file does already exist, the symlink is touched. So, to make GDM mimic this sort of behavior I could make GDM work like this: - Perhaps add a new configuration option so you can specify which display is the "console" session (or just assume it to be ":0"), and if this is an attached display also (as it should be), then set it to "/dev/console". - Otherwise, if it is attached there is a VT# associated with the display, set it to the vt device. - Otherwise, if there is no VT associated with the display and it is attached, set the string to /dev/dtlocal. Create or touch the symlink - Otherwise, if a remote display, set it to /dev/dtremote. Create or touch the symlink. Since "dt" is a CDE login specific prefix, the alternative strings "xdmlocal" and "xdmremote" have been proposed as more generic strings to use for GDM. I'm not sure if this makes sense for Linux, but if this seems reasonable I could make the code work like this for all platforms. Otherwise, I'll probably have to #ifdef the code so it works something like this just for Solaris. Even if you think this is a Solaris specific thing, I'd appreciate your comments about whether you think this makes sense, or if there is a better way to get the same sort of effect. I believe this would make utmp work a bit better since commands like "who" would return more sensible output. The ut_line argument is really only used for searching the database and for output in programs like who. A string like "xdmlocal" is just as meaningful, I'd think, as "/dev/console". Either should work just fine. What are your thoughts about this?
Hi Brian, So on linux, /dev/console is an interface to the "system console". If you write messages there then they'll go to whatever tty (or pseudoterminal!) that the kernel is currently configured to send them to. This is controlled by an ioctl (TIOCCONS), but defaults to /dev/tty1 (which is vt1). gdm by default starts the :0 display on /dev/tty7 (which is vt7), so there isn't really much relation between /dev/console and gdm. Is the idea of marking the first session the "console session" because it gets special privileges (device ownership permissions, etc)? What do you mean by "if there is no VT associated with the display and it is attached" ? Are you talking about Xnest flexiserver logins ? To be honest, I'm not a big fan of the fake pseudo-devices. You've already pointed out that ut_line is really just presentational. We even strip the /dev prefix when generating the line, so why do we need to have /dev/null aliases set up and in place? I would be more of fan of either leaving it blank or giving more descriptive information. Maybe things like, :0 on tty7 :20 over xdmcp Not sure. I think that having gdm creating device nodes in /dev might cause SELinux issues. And since they don't do anything, is there any advantage to creating them?
The reason for marking a particular session (probably the first session) special is because it should get special privileges. This is probably a Sun specific issue since on Sun we use logindevperm logic (note di_devperm_login/logout code in verify-pam.c) to control which display gets device permissions. There are a few situations where there could be no VT associated with an attached display (aside from Xnest): - If you are using an Xserver that doesn't support VT, such as the Xsun Xserver. This is probably more a Sun problem, though GDM should work reasonably for non-Xorg Xservers in general. - If you are using a multi-display environment where you launch many terminals via gdmdynamic, or if you have multiple displays in your [servers] section of the GDM configuration. For example, if you have a kiosk environment with 100 displays attached, then it probably isn't correct to call them all "/dev/console". - The combination of the above is a bit more complicated. In this case you probably want the actual console (likely display :0) to be treated a bit special. - My understanding is that on Solaris, the audit code, PAM_TTY setting, logindevperm, and utmpx logic must use device names. This is because other programs such as who/last/finger depend on the pseudo-device in order to work and correctly report when the session started. I've raised the concerns about pseudo-devices you raise with the Sun Ray team and also with the Sun security team to find out whether this is the right approach. Even though CDE login currently works this way, perhaps we should solve this problem another way with GDM? I'll find out. Perhaps the existing hacks in Solaris who/last/finger should be reworked to avoid needing ut_line to be set a certain way? I'm not sure exactly what will come out of the discussions we're having internally here at Sun. I'm hoping it won't be necessary to #ifdef special logic for Solaris, so I'm hoping to avoid that. One idea might be to add some configuration options so that people can specify what strings to use for different types of displays. Now the code hardcodes to "/dev/console" when the display is attached and there is no vt. Perhaps this string could be in the configuration file, along with a string for remote displays. This might be an improvement. So you could define it like this: UtmpLocal=%D on %d (which becomes :0 on tty7) -or- UtmpLocal=/dev/console UtmpRemote=%D over xdmcp (which becomes :20 over xdmcp) This might help to avoid hacking the code with #ifdefs. Thoughts? I know this is all probably icky Solaris specific stuff, but I definately appreciate your feedback on how things should work sensibly.
Okay, so I just had a peek in the finger program we ship and it has this: bcopy(ut->ut_line, w->tty, UT_LINESIZE); ... struct stat sb; /* No device for X console. Utmp entry by XDM login (":0"). */ if (w->tty[0] == ':') { w->idletime = 0; /* would be nice to have it emit ??? */ w->writable = 0; return; } snprintf(tbuf, TBUFLEN, "%s/%s", _PATH_DEV, w->tty); if (stat(tbuf, &sb) < 0) { w->idletime = 0; /* No tty no write, no idle data */ w->writable = 0; switch (errno) { case ENOENT: break; default: eprintf("finger: %s: %s\n", tbuf, strerror(errno)); return; } } w->idletime = now < sb.st_atime ? 0 : now - sb.st_atime; So it tries to figure out idle time based on ut_line, unless we start ut_line with : as an escape hatch. Note also, there's a bug in finger here, because it's using sb.st_atime after the stat call fails before the device doesn't exist. So I guess some programs do depend on it having a certain format... Note in this case, unless you updated the atime of your psuedo-devices every time the user did activity, then the idle time reported by finger would be wrong.
Ray, again thanks for helping with the research, etc. I have been talking with the Sun Ray team, and Mike Oliver (Mike.Oliver@sun.com) was able to provide the following insights to how things work with CDE and why. ---- [Regarding psuedo-devices], yes, they are the wrong answer insofar as they mislead programs like 'finger' which believe that they can get meaningful information by doing a stat() of the /dev node whose name is in the ut_line field. On the other hand they're the right answer in that they provide the basis for allowing a program (like 'last') to understand that this utmpx entry refers to a local or remote X login and therefore the utmpx entry should be interpreted differently from a traditional entry. Fixing 'finger' so that it recognises that it can't use stat() to get a meaningful result for utmpx entries tagged as 'dtlocal' or 'dtremote' would be good. Even better would be to invent a mechanism that 'finger' and similar programs can use to get meaningful information for such entries. [Setting the pseudo-device to /dev/null] is a hack, pure and simple, to forestall naive historical utmpx readers from blowing up when they stat() the ut_line entry. 'finger' is one such program; even if we fix all of the ones in Solaris I doubt that it's possible to know how many such programs are out there, and nobody wants to be on the hook when some furious customer phones up wanting to know why some old app has suddenly started dying because of a failed stat() call. (The result of today's successful stat() is of course meaningless, but that doesn't bother people as much as an exploding program does.) Unless you have a better solution GDM should try to make things no worse than they already are. So yes, at a minimum GDM should do what dtlogin does. If GDM is going to do something different then that's swell but it needs to be something that does not undermine naive utmpx readers or cause less-naive programs like 'last' to start producing bogus results. g-v-m has no business trying to sniff "/dev/console" out of utmpx. It's somebody else's job (logindevperm, pam_devices, ...) to assign devices to users as they log in. [ut_line is not just presentational. Please do not set it to strings like ":0 on tty7" or ":20 over xdmcp"]. This is not a free-form text field. Putting junk like this into it isn't going to help naive programs, it's going to confuse any scripts that consume output from (say) 'last' and it's not even going to help humans (consider I18N/L10N). It's not a large field either, it's already not big enough to hold an X display name even without extra trailing descriptive cruft. Using lines like this probably will not cause 'last' to blow up but its output will change (for the worse) and scripts that consume its output will probably blow up. 'finger' and other naive utmpx readers will either blow up or start generating error messages. Who knows what things-that-read-utmpx-but-shouldn't will do. The problem is that utmpx doesn't have a well-defined way of representing X logins. Solaris (actually CDE in general I think) has invented a halfway-decent convention that some programs ('last') understand but it hasn't solved the problem for things like 'finger' that have baked-in expectations based on the historical tty (later pty) device model. The dtlocal/dtremote thing goes far enough to fake out 'finger' so that it doesn't blow up, but that's as far as it goes. Whatever creates the utmpx records would need to play a part in any scheme that tries to fill in the missing pieces of the story. Filling in the missing pieces by fabricating a bunch of nodes in /dev that can be stat()'ed by 'finger' to get the idle time for an X desktop would be pretty disgusting, I hope we can do better than that. Doing something better will require 'finger' surgery. ----- Based on this feedback, I think it is probably necessary for me to enhance GDM so that users can specify a local/remote psuedo-device name in the GDM configuration. If these are defined, then it will use them. If not, it will leave ut_name blank. The default value for these configuration will be blank, and on Solaris we can turn on this feature by setting the configuration to dtlocal and dtremote for CDE compatibility. Does this seem reasonable? Since Solaris doesn't need to use strings like "%D over %d" I don't think I plan to implement code to parse and replace the string to things like ":20 on tty7" as you suggest, Ray. However, someone in the future could obviously add such code if someone thinks this is useful. This will meet Sun's needs for Sun Ray support, and also allow people who want to run Sun Ray on Linux to configure GDM and make things work like they do on Solaris, which is probably desirable. And it will avoid making other distros use pseudo-devices unless they want to. ----- I'll still need to think about how to manage logindevperm processing when the Xserver doesn't support VT. Since we only want the actual console to call devperm calls in this case, there might be a need to be able to specify which display gets device permissions. I'd prefer to not add another configuration option for this since I suspect this is really Solaris specific (logindevperm logic is only in #ifdef __sun blocks), so I want to think about this more.
ConsoleKit was specifically designed to solve these problems. IMO, utmp(x) should just fade away. Someone should probably add an audit trail to CK (even if only as a log). Finger can even be patched to talk to ConsoleKit if ck-list-sessions isn't sufficient. g-v-m could also listen to CK. All of this futzing with devices is really gross.
So, let me summarize the problem we're having and brain dump a little... utmp entries are designed around the idea of the user having a controlling terminal (either a tty or pty) for their session. The X session doesn't have a controlling terminal, instead we hookup console output to a pipe that feeds ~/.xsession-errors and console input to /dev/null. The ut_line field, is assumed by various programs to be a file in /dev. Some programs (like the finger program we ship on Linux) also somewhat cope with ut_line being a display number (:0 or whatever). Some programs on Solaris die completely if the ut_line isn't a valid file in /dev. There is the additional problem that Solaris wants to mark one of the sessions a "console" session which means that session gets access to local devices. Note that only giving the first user logged in device permissions will make flexiserver login's a lot less useful. ConsoleKit is probably a better way to handle device permissions in the long run. ConsoleKit also exposes much of the same information that is available in the utmp file, but it's gotten from a different way. Jon proposes that we could add logging to ConsoleKit akin to the wtmp file. One option is to dive into the various unix utilities we ship and get them all using the ConsoleKit interfaces. Even if we did that though, we probably need to have some sort of backward compatibility for apps that don't get ported. We don't want regressions. On some level it might make sense for ConsoleKit to be writing the wtmp and utmp entries. On the other hand, it has no way of knowing when to write btmp entries, and programs like /bin/login already handle the record keeping themselves, so I think keeping wtmp/btmp/utmp logging in GDM is the right way to go for now. Brian, did Solaris use sessreg previously with GDM? If so, I think we should just follow the same format sessreg follows.
Okay, I have an idea... We would name ut_line just like sessreg does, based on the display. So display :3 would have ut_line set to :3. Optionally, if a compatibility option was turned on, then pseudo-devices would be created named /dev/:1, /dev/:2, /dev/:3 . In Fedora/RHEL we would probably default to the compatibility mode being turned off, since it has SELinux implications and our tools already have the ut_line[0] == ':' escape hatch. That only leaves the question of when :0 (or rather the first display that isn't configured to use a vt?) should have ut_line be "console" instead of ":0". Not sure if we'd want to make this a config option or #ifdef __sun. Maybe we could just reuse the same compatibility option that adds the pseudo-devices. The above doesn't solve the /usr/bin/finger misstates idle time on solaris problem, but I think fixing that should really be a finger fix, so it's a bit of an orthogonal issue.
Okay, I think I have updated GDM so that it works in a way that should meet everybody's needs. Since it seems that different distros want to take different approaches to how this is set, it is good to make it configurable. Now it works like this: 1) If an attached display and there is a VT device that is associated with the display, this is always used. In other words, when we know the actual device we should use it. 2) If an attached display and there is no associated VT device, then the configuration can specify the device associated with a display in the [servers] section like follows: :0 device=/dev/foo The string can contain "%d" which is translated to the DISPLAY value and "%h" which is translated to the hostname. This allows people to configure the device for a given display if desired. For most distros this is probably not useful. This would only be useful in situations where VT is not being used. 3) If the above 2 steps still haven't found a device for the display, then GDM will use the values in the two new configuration options: UtmpLineAttached for attached displays UtmpLineRemote for remote displays Again, %d" is translated to the DISPLAY value and "%h" is translated to the hostname. 4) GDM also has a new configuration option UtmpPseudoDevice. If true, then GDM checks if the device from the above steps exists. If not, it creates a symlink to /dev/null. If the symlink to /dev/null already exists, then it touches it. If UtmpPseudoDevice is false, then GDM doesn't care if the device exists or not and will just set ut_line to whatever value is specified. I'd appreciate a review of the code, mostly in slave.c (gdm_slave_write_utmp_wtmp_record, gdm_slave_get_display_device, and gdm_slave_update_pseudo_device). I've tested the way this works, and it works as described based on my testing. Now the Sun specific logindevperm code only sets device permissions if the device is a vt device or /dev/console, which is the desired behavior for Sun I think. For Solaris, I am setting these as follows for CDE compatibility: UtmpLineAttached=/dev/dtlocal UtmpLineRemote=/dev/dtremote UtmpPsuedoDevice=true For Linux, I am setting these as follows UtmpLineAttached=/dev/console UtmpLineRemote= UtmpPsuedoDevice=false If you think other defaults make more sense for Linux, let me know. It's easy to update how they are set in configure.ac. Perhaps, over time more configuration will be desirable, which isn't a problem. Perhaps more things like "%d" and "%h" would be desirable, for example. I'm happy to make this more configurable or accept patches to make this more configurable if there is value in this. ---- Also, while working on this I noticed that the GDM documentation suggests that you can specify things in the [servers] section like this: 0=xserver command -args 0=Standard -extra X args However, looking at the code, and looking at older versions of GDM (going back to 2.16), this doesn't seem to actually work. Since nobody has complained that this is broken, I have removed the information from the docs suggesting that this works. If someone thinks that this is a feature that should be added back, then we can open a new bug. I just mention it so people can think about this.
So, I think this bug is fixed. I'll leave it open for a few days to see if there are any comments or changes based on review. If not, I'll go ahead and close it.
I'm closing this bug. I think that now utmp/wtmp processing is reasonable in both 2.20 and SVN head.