#!/bin/blog

July 7, 2017

How expiration dates in the shadow file really work

Filed under: Uncategorized, UNIX & Linux — Tags: , , , , — martin @ 6:24 pm

tl;dr: Accounts expire as soon as UTC reaches the expiration date.

In today‘s installment of my classic shame-inducing series “UNIX basics for UNIX professionals”, I want to talk about account (and password) expiration in /etc/shadow on Linux.

The expiration time is specified as days since january 1st, 1970. In the case of account expiration, the according value can be found in the second to last field in /etc/shadow.

Account expiration can be configured using the option „-E“ to the „chage“ tool. In this case, I want the user „games“, which I‘ll be using for demonstration purposes, to expire on the 31st of december, 2017:

# chage -E 2017-12-31 games

Using the „-l“ option, I can now list the expiration date of the user:

# chage -l games
[…]
Account expires : Dec 31, 2017
[…]

The first thing to be taken away here is that, as I can only use a number of days, I can not let a user expire at any given time of day. In /etc/shadow, I have now:

# getent shadow | awk -F: '/^games:/{print $8}'
17531

This of course can to be converted to a readable date:

# date --date='1970-01-01 00:00:00 UTC 17531 days'
Sun Dec 31 01:00:00 CET 2017

So, will the account still be usable on december 31st? Let‘s change it‘s expiration to today (the 7th of July, 2017) to see what happens:

# date
Fri Jul 7 12:58:32 CEST 2017
# chage -E today games
# chage -l games
[…]
Account expires : Jul 07, 2017
[…]
# su - games
Your account has expired; please contact your system administrator
[…]

I’m now only left with the question whether this expiration day is aligned on UTC or local time.

# getent shadow | awk -F: '/^games:/{print $8}'
17354
# date --date='1970-01-01 00:00:00 UTC 17354 days'
Fri Jul 7 02:00:00 CEST 2017

I‘ll stop my NTP daemon, manually set the date to 00:30 today and see if the games user has already expired:

# date --set 00:30:00
Fri Jul 7 00:30:00 CEST 2017
# su - games
This account is currently not available.

This is the output from /usr/sbin/nologin, meaning that the account is not expired yet, so I know for sure that the expiration date is not according to local time but to UTC.

Let‘s move closer to our expected threshold:

# date --set 01:30:00
Fri Jul 7 01:30:00 CEST 2017
# su - games
This account is currently not available.

Still not expired. And after 02:00:

# date --set 02:30:00
Fri Jul 7 02:30:00 CEST 2017
# su - games
Your account has expired; please contact your system administrator

So, in order to tell from a script whether an account has expired, I simply need to get the number of days since 1970-01-01. If this number is greater or equal to the value in /etc/shadow, the user has expired.

DAYSSINCE=$(( $(date +%s) / 86400 )) # This is days till now as per UTC.
EXPIREDAY=$(getent shadow | awk -F: '/^games:/{print $8}')
if [[ $DAYSSINCE -ge $EXPIREDAY ]] # Greater or equal
then
    EXPIRED=true
fi

One last thought: We’ve looked at a time zone with a small offset from UTC. What about timezones with larger offsets, in the other direction?

  • If we move the timezone to the east, further into the positive from UTC, it will behave the same as here in CEST and the account will expire sometime during the specified day, when UTC hits the same date.
  • If we move the timezone far to the west, like e.g. PST, and an absolute date is given to “chage -E“, the account will probably expire early, the day before scheduled expiration. I was not able to find anything useful on the web and even my oldest UNIX books from the 1990s mention password expiration only casually, without any detail. Active use of password expiration based on /etc/shadow seems to be uncommon. The code that seems to do the checking is here and it does not appear to care about time zones at all.
  • Any comments that clarify the behaviour in negative offsets from UTC will be appreciated.

October 25, 2014

CentOS 7 on MD-RAID 1

Filed under: UNIX & Linux — Tags: , , , — martin @ 2:47 pm

Figuring this out took me quite a bit of time. In the end, I approached the starter of this hilariously useless CentOS mailing list thread, who assured me that indeed he had found a way to configure MD-RAID in the installer, and behold, here’s how to install CentOS 7 with glorious old-school software RAID.

In the “Installation Destination” screen, select the drives you want to install onto and “I will configure partitioning”. Then click “Done”:
20141025134323In the “Manual Partitioning” screen, let CentOS create the partitions automatically, or create your own partitioning layout. I will let CentOS create them automatically for this test. 20141025134926Apparently due to restrictions in the Installer, /boot is required, but can’t be on a logical volume, so it appears as primary partition /dev/sda1. The root and swap volumes are in a volume group named centos.
The centos volume group will need to be converted to RAID 1 first. Select the root volume and find the “Modify…” button next to the Volume Group selection drop-down. A window will open. In this window, make sure both drives are selected and select “RAID 1 (Redundancy)” from the “RAID Level” drop-down. Repeat this for all volumes in the centos volume group.  If you are using the automatic partition layout, note at this point, how, after this step, the file system sizes have been reduced to half their size.

20141025135637As the final step, select the /boot entry and use the “Device Type” drop-down to convert /boot to a “RAID” partition. A new menu will appear, with “RAID 1 (Redundancy)” pre-selected. The sda1 subscript below the /boot file system will change into the “boot” label once you click anywhere else in the list of file systems.
20141025140445Click “Done”, review the “Summary of Changes”, which should immediately make sense if you have ever configured MD-RAID, and the system will be ready for installation.

October 17, 2014

What does the slash in crontab(5) actually do?

Filed under: UNIX & Linux — Tags: , , , — martin @ 2:16 pm

That’s a bit of a stupid question. Of course you know what the slash in crontab(5) does, everyone knows what it does.

I sure know what it does, because I’ve been a UNIX and Linux guy for almost 20 years.

Unfortunately, I actually didn’t until recently.

The manpage for crontab(5) says the following:

20141017150008

It’s clear to absolutely every reader that */5 * * * * in crontab means, run every 5 minutes. And this is the same for every proper divisor of 60, which there actually are a lot of: 2, 3, 4, 5, 6, 10, 12, 15, 20, 30

However, */13 * * * * does not mean that the job will be run every 13 minutes. It means that within the range *, which implicitly means 0-59, the job will run every 13th minute: 0, 13, 26, 39, 52. Between the :52 and the :00 run will be only 8 minutes.

Up to here, things look like a simple modulo operation: if minute mod interval equals zero, run the job.

Now, let’s look at 9-59/10 * * * *. The range starts at 9, but unfortunately, our naive modulo calculation based on wall clock time fails. Just as described in the manpage, the job will run every 10th minute within the range. For the first time at :09, after which it will run at :19 and subsequently at :29, :39, :49 and :59 and then :09 again.

Let’s look at a job that is supposed to run every second day at 06:00 in the morning: 0 6 */2 * *. The implied range in */2 is 1-31, so the job will run on all odd days, which means that it will run on the 31st, directly followed by the 1st of the following month. The transitions from April, June, September and November to the following months will work as expected, while after all other months (February only in leap years), the run on the last day of the month will be directly followed by one on the next day.

The same applies for scheduled execution on every second weekday at 06:00: 0 6 * * */2. This will lead to execution on Sunday, Tuesday, Thursday, Saturday and then immediately Sunday again.

So, this is what the slash does: It runs the job every n steps within the range, which may be one of the default ranges 0-59, 0-23, 1-31, 1-11 or 0-7, but does not carry the remaining steps of the interval into the next pass of the range. The “every n steps” rule works well with minutes and hours, because they have many divisors, but will not work as expected in most cases that involve day-of-month or day-of-week schedules.

But we all knew this already, didn’t we?

October 21, 2011

Update: Booten vom verschlüsselten USB-Stick

Filed under: Paranoia — Tags: , , — martin @ 9:02 pm

Also, die Sache mit den USB-Sticks von Lok-IT war sicher eine tolle Idee, das Problem ist aber, daß man de-facto nicht von ihnen booten kann funktioniert.

Grub, Kernel und initrd werden zwar geladen. Leider erfolgt innerhalb der initrd aber scheinbar ein Reset des USB-Systems. Ein normaler USB-Stick holpert da irgendwie drüber (das funktioniert mit /boot auf dem Stick problemlos), aber der Lok-IT sperrt sich sicherheitshalber automatisch.

Das ist schade, aber kein Beinbruch. Das letzte Wort wird da auch hoffentlich noch nicht gesprochen sein.

Es trifft zu, daß der USB-Bus beim Hochfahren zurückgesetzt wird und sich der Stick in diesem Moment aus Selbstschutz sperrt. Das ist aber überhaupt kein Problem. Man muß lediglich in /etc/fstab dafür sorgen, daß das System beim Hochfahren nicht versucht /boot zu mounten oder zu checken. Beide Daumen nach oben für meinen unknackbaren Terrorlaptop! 🙂

Und, nein, es wird kein HOWTO dazu geben. Wer dafür ein Kochrezept braucht, sollte besser die Finger davon lassen.

June 3, 2011

Rebootless kernel updates

Filed under: UNIX & Linux — Tags: , , — martin @ 9:34 pm

It’s been a while since my last post, and this time, for a change, I have decided to produce a screencast. In which I show you what rebootless linux kernel updates with the great service from Ksplice actually look like.

This is on one of two Ubuntu 10.04 LTS system, for which I have licensed the commercial Ksplice service.

P.S.: Sorry for inflicting my foul accent upon you. 😉

January 30, 2011

Make directory immutable on Linux

Filed under: UNIX & Linux — Tags: , , — martin @ 1:26 pm

Most of you know the immutable flag on Linux filesystems. It marks a given file in a special way that not even root can accidentally delete or modify it:

# touch /tmp/foo
# chattr +i /tmp/foo
# rm /tmp/foo
rm: cannot remove `/tmp/foo': Operation not permitted

Unfortunately it is not possible to apply the same to a directory so it can never be deleted, even when it is empty. At least not, if the directory is supposed to be usable for anything, because immutability means that there can be no files written to it:

# mkdir /tmp/foo
# chattr +i /tmp/foo
# touch /tmp/foo/bar
touch: cannot touch `/tmp/foo/bar': Permission denied

My workaround is to create a hidden file in the directory and make it immutable:

# mkdir /tmp/foo
# touch /tmp/foo/.immutable
# chattr +i /tmp/foo/.immutable
# rm -rf /tmp/foo
rm: cannot remove `/tmp/foo/.immutable': Operation not permitted

December 4, 2008

Using a USB key for the LUKS passphrase

Filed under: Paranoia, UNIX & Linux — Tags: , , — martin @ 10:43 pm

When I had installed my notebook with Ubuntu 8.04 “Horny Hard-on”, I had opted to put the /home filesystem onto an encrypted partition on /dev/sda4. However, after a few months, entering the passphrase after turning on the computer doesn’t seem to be that attractive anymore. I have therefore decided to try to store the passphrase on a spare USB key.

This is how I migrated my LUKS container to a passphrase stored on USB media.

First, I filled the USB key with random data:
# dd if=/dev/urandom of=/dev/sdc

Then, I siphoned off 256 bytes from the USB key, to be used as the passphrase:
# dd if=/dev/sdc of=/home/martin/foo.key bs=1 count=256

foo.key is required temporarily. You may keep a copy of it stored in a safe place, or you may leave the interactive password in place as a fall-back measure. Which is what I’m doing.

The new passphrase can be added to the LUKS container like this:
# cryptsetup luksAddKey /dev/sda4 /home/martin/foo.key

Cryptsetup asks for “any passphrase”. That is one of the numerous possible passphrases that may be assigned to a LUKS device at once. Such as the interactive passphrase that is already in place.

When the new passphrase has been added, foo.key can be deleted.

Next, I determined the USB id of my USB key:
# ls -l /dev/disk/by-id/ | grep sdc
lrwxrwxrwx 1 root root 9 2008-12-04 21:31 usb-LG_XTICK_AAAAAAAAAAAAAAAAA-0:0 -> ../../sdc

I found that I needed a little helper script that extracts 256 bytes from the USB key and pipes them to stdout:

#!/bin/bash
# Script: /usr/local/sbin/dd-luks-key.sh
if [ -e $1 ]
then
dd if=$1 bs=1 count=256
fi

And now the change to /etc/crypttab:

# Old entry; ask for password:
#sda4_crypt /dev/disk/by-uuid/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee none luks
# New entry; execute the keyscript with the USB id as the argument:
sda4_crypt /dev/disk/by-uuid/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee /dev/disk/by-id/usb-LG_XTICK_AAAAAAAAAAAAAAAAA-0\:0 luks,keyscript=/usr/local/sbin/dd-luks-key.sh

That’s it. I can now reboot with the USB key plugged in and observe how the system automatically mounts the LUKS container. The USB key is not partitioned, so Gnome will not automatically mount it. It can just be pulled anytime after bootup.

If I had chosen to delete the interactive passphrase, which is stored in key slot 0:
# cryptsetup luksDelKey /dev/sda4 0

Be advised that this is no real-deal tough-minded security, but something that will protect the machine only against the type of attackers (e.g. thieves) who are out for your hardware but not for your data. Don’t leave the USB key close to the laptop. Use this responsibly. Thanks!

I’m not conviced that I will stick with this, as it’s far below my usual standard of paranoia. Nevertheless, I have gained a few nice insights into the LUKS system.

October 26, 2008

Old habits die hard

Filed under: Insanity Online — Tags: , — martin @ 7:27 pm

October 10, 2008

Untote Exploits

Filed under: Security, UNIX & Linux — Tags: , , , — martin @ 5:55 am

Jahrelang habe ich auf dem K. herumgehackt, weil “sein” IPS immer Verbindungen unterbrochen hat, nachdem es Bytefolgen auf der Leitung gesehen hatte, mit denen man vor etlichen Jahren mal irgendwelche archaischen Exploits (konkret erlebtes Beispiel: Sendmail decode vulnerability) triggern konnte. Denn mal ehrlich: Wie obskur kann’s noch werden?

Heute bin ich in gewisser Weise einen Schritt weiter, denn bei einem Kunden wurde ein SLES9 aus dem Internet gecrackt, weil der Angreifer sich über einen PHP-Exploit die /etc/passwd herunterladen konnte und darin Passwort-Hashes vorgefunden hat, die ein Administrator beim Anlegen von Usern per Copy&Paste dort eingebaut hat. Die hat er dann auf dem üblichen Weg mit etwas Geduld per Brute-Force geknackt. Ein Szenario aus den 1980ern. Ekelhaft.

October 6, 2008

New ALIX 2d3

Filed under: Hardware, UNIX & Linux — Tags: , , , — martin @ 6:44 pm

I received my first new ALIX of the type 2d3 today. Apparently, this is the successor to the 2c3 and brings no major changes but just minor modifications. According to PC Engines:

• Increase USB current limit.
• USB headers as build option.
• USB ports 3 and 4 on header (not tested).
• Change optional serial header J12 to COM2.
• Add LED and switch pins to I2C header.
• Populate buzzer driver circuit, add pins for use as GPIO.
• Add option for power in header J18.
• Some enhancements to reduce EMI.
• Add second POSCAP to ruggedize 3.3V rail for high power radio cards.

I have highlighted the most apparent changes in the photograph (click to enlarge).

Migration of the pre-installed disk from my development ALIX 2c3 went fine, although I had to resolve a problem with some nasty mis-feature where Debian tries to keep persistent ethernet device names by hard-coding the MAC addresses into some obscure udev configuration file. The system complained about the following network issue, although eth0, eth1 and eth2 showed up properly in the output of dmesg:

Configuring network interfaces…SIOCSIFADDR: No such device
eth0: ERROR while getting interface flags: No such device

Deleting the /etc/udev/rules.d/z25_persistent-net.rules file and rebooting resolved the problem immediately.

I never could quite get the hang of devfs or udev anyway. Here’s yet another reason to hate them. 😀

Older Posts »

Create a free website or blog at WordPress.com.