Y2038, glibc and /var/log/lastlog on 64bit architectures

3 minutes
March 20, 2023 - Last modified: March 21, 2023

On January, 19th 2038 at 03:14:07 UTC the 32bit time_t counter will overflow. For more information about this I suggest to start with the wikipedia Year 2038 problem article. That problem is long known and several groups are working on a solution for 32bit systems, but many people don’t know that pure 64bit systems could be affected, too.

The general statement so far has always been that on 64bit systems with a 64bit time_t you are safe with respect to the Y2038 problem. But glibc uses for compatibility with 32bit userland applications 32bit time_t in some places even on 64bit systems. More information can be found in my previous blog Y2038, glibc and utmp/utmpx on 64bit architectures.

In this article I will present a generic solution for lastlog.

What is lastlog actually good for? From the manual page:

lastlog formats and prints the contents of the last login
log /var/log/lastlog file. The login-name, port, and last
login time will be printed.

What are the problems?

There are two main problems with lastlog with glibc on e.g. x86-64:

  1. A 32bit time_t field is used for the time, which will overflow in 2038
  2. There is no API to access the lastlog file, every application has to implement their own support

As result especially of 2): lastlog can only show the informations if the service to login supports this. If you have a Desktop system and you use gdm, sddm or xdm, lastlog will most likely claim that you did never login on this machine, as this tools don’t support lastlog. So the result is not trustworthy.

Another problem is how the data is stored: the ‘size’ of the lastlog file depends on how big the largest UID is. Which is most of the time no problem, as this is a sparse file, but if your filesystem, backup or other tools don’t support sparse files, this can become very quick a real big problem.

Who is really supporting lastlog today?

In the first place, there is of course the shadow suite, which provides the lastlog binary and where all tools support it. Additional, there are:

The pam_lastlog.so module could enable lastlog support for every application, but is not configured accordingly everywhere.

Design

The last login information will be stored in a sqlite3 database. There is a library liblastlog2.so which provides a Y2038 secure API on all architectures (32bit and 64bit). This also allows to change the database to e.g. another format without the need to touch any application using it itself. Additional, there is a PAM module pam_lastlog2.so and a binary lastlog2.

The module allows to use this functionality in any PAM aware application. Today every application used for login and authentication uses PAM, so this should be no problem. The lastlog2 binary will replace lastlog and allows to import that old /var/log/lastlog file, to maintain the data and of course to show the last login data.

Were other alternatives evaluated?

Yes, but in the end, sqlite was the most widespread, robust, well maintained database library available today. And hiding everything in a library is the only future proof solution, to be flexible in the future and to maintain the code only in one place.

lastlog2 implementation

Source code

The source code for the lastlog2 library, PAM module and application can be found in the lastlog2 repository on github.

Example code

Updating a lastlog entry is as simple as calling

...
#include <lastlog2.h>
...
if (ll2_write_entry (_PATH_LASTLOG2, user, ll_time, tty, rhost, &error) != 0)
    {
      if (error)
        {
          fprintf (stderr, "%s\n", error);
          free (error);
        }
      else
        fprintf (stderr, "Unknown error writing to database %s\n", _PATH_LASTLOG2);
      exit (EXIT_FAILURE)
    }
...

Further documentation