Thoughts about PAM

May 25th, 2011

In the OpenPAM source code (used by NetBSD, FreeBSD and OSX since Snow Leopard), one can spot in lib/openpam_ttyconv.c:

sigemptyset(&sigset);
sigaddset(&sigset, SIGINT);
sigaddset(&sigset, SIGTSTP);
sigprocmask(SIG_SETMASK, &sigset, &saved_sigset);

that it misses a third signal that can be triggered interactively, SIGQUIT (C-\ in most terminals). If a user starts an SUID program which doesn’t handle this signal separately, this allows the user to crash the program and avoid the usual timeout on failed attempts. A local brute force attack on passwords could be possible.

All the consumers of this conversation callback, pam_ttyconv (aka misc_conv), the only usable one provided in OpenPAM, could cause this threat.

Under OSX, login is resilient to this signal, and so is passwd as it introduces a delay before closing. Sadly su doesn’t, at least as far as my perception goes (yes, that’s a poor evaluation when considering brute force). One could argue that su ensures that it runs interactively in a tty to prevent scripting, but tools like expect show that ptys are a valid countermeasure.

I understand, or at least hope, that the OpenDirectory stack, involved through pam_opendirectory.so, does prevent such brute force attempts itself. I’d like to find the time and motivation to look at the other BSD-derived operating systems. At least I’d get to look at the source code…

I had a quick look at the situation in GNU coreutils, which provides su under Linux; funnily enough, it turns out that it is not pam-aware, and that Linux vendors maintain patches on top! A bit surprising in 2011… But as far as Linux is concerned, avoiding brute force is simple: use pam_tally.so.

Whilst I still have to look further into it, if you write an application that uses pam to interactively check a user’s password and is therefore SUID (/etc/shadow and all), I would recommend to:

  • Make sure the password verification process cannot be spawned too quickly or in parallel within the process, or that a success cannot be identified quickly
  • If you delay further attempts in case of failure but allow the user to identify a success quickly, try to ensure that you are running interactively (in a tty, if possible a trusted one), and block terminal-related signals (SIGINT, SIGQUIT, SIGTSTP just in case, and give a thought to SIGHUP to fight against pty-based attacks)
  • Most importantly, distribute (or recommend to distributors) an appropriate pam configuration for your application: if pam_tally (or equiv.), enforcing of /etc/securetty, etc. should be involved, state so!

If you administer systems allowing shell access, I would suggest to:

  • Review your whole pam stack: the configuration for all services, the extra settings for the backends involved.
  • Limit the number of processes and file descriptors for your users!
  • If possible, limit the availability of pty automation tools (expect for example, but unfortunately also Python) and involve noexec.
  • Log failed attempts, monitor the logs. With a good password, a brute-force attack involving the creation of one process per attempt will take long (do your own math, that’s years with 100k attempts per second with the entropy of my shell passwords).

But hey, I am not a security expert. And of course, just have very secure passwords :)


Fedora 15 installer: hard to find :)

May 25th, 2011

On fedoraproject.org, the new prominent installation media with Fedora 15 is the live desktop, eg. Fedora-15-x86_64-Live-Desktop.iso.

Download it, boot it in VirtualBox. Gnome 3 will use the fallback mode. How do you start the installer? Well, turns out it is normally made prominent by a Gnome Shell extension (~/.local/share/gnome-shell/extensions/Installer@shell-extensions.fedoraproject.org/).

Did I mention that in fallback mode, which you can enjoy with most virtualization technologies, Gnome Shell is not running? Instead the installer ends up in the menu. Somehow it took me over 15 minutes to find it. Most people would be ashamed, I blog about it.

In case you wonder how to start the Fedora 15 installer in your virtual machine (SSO)… You can find it in Applications → System Tools → Install to Hard Drive. Alternatively, Alt+F2, liveinst. My bad for not partaking in the testing effort…

It makes me feel old and nerdy, but I love console-based installation processes.


Things I wrote, episode 4: 7clock, a console 7-segment clock

May 23rd, 2011

I wanted a minimal, fullscreen clock I could run without X. In the longer term, I am thinking about a locking console “screensaver” (more about that later). I ended up writing a small library (240 lines and counting) to render a 7-segment display in monospaced text at any “resolution”.

The end result adapts to the console size, instantaneously if resized. It lacks any options, if I’m bored again I might come back to it. It should be quite efficient, though more trivial optimizations are possible.

Older screenshots from various prototypes (the test program is available alongside the clock; as usual, I recommend sticking to the instructions in INSTALL at the root of the repo):


Things I wrote, episode 3: errnos, identify what this random error number is

May 21st, 2011

As a Technical Support Engineer at Red Hat, I got to read a lot of logs, error messages and code. One of my pain points was errnos.

They rely on magic numbers, which is perfectly understandable given their use cases (eg return values for system calls). But obviously, we’re not going to be using magic numbers everywhere, hence they are defined as constants for the C preprocessor.

Numbers are not very user-friendly, so why not display them in a human-readable format when a human might want to read it? strerror comes to the rescue! Unfortunately, not every piece of code presenting errors to the user or administrator uses it. There are various reasons for that:

  • It is a libc function, and the Linux kernel didn’t implement something similar (or most of us aren’t aware of its existence);
  • The length of the (hexa)decimal representation of an integer is bound, whereas the strings it returns are not, which can make memory allocation for log messages trickier.

strerror strings are very readable; for example, it will turn ENOMEM into Cannot allocate memory under OSX. I will not lose too much of your time on the annoyances caused by weird usages of errnos, though I encountered quite a few already: they usually do not delay troubleshooting too much if you’re not too trusting. For example ENOTDIR, represented by glibc’s strerror as Not a directory, indicates when returned by keyctl_search(3) that one of the keyrings is a valid key that isn’t a keyring.

The tricky part now? From a number or an strerror description in logs or an output, there is no trivial way to establish which constant to look for. What’s more, C preprocessor constants are not available at runtime, you’d need the headers at hand, cpp, and your own list to go through as there isn’t a standard one! For example, if you see 44, 0x2c or Channel number out of range, what should you git grep for in the affected software, libc and/or kernel? ECHRNG, of course!

I’d love to give you a simple table listing each errno constant, its representation in decimal and hexadecimal and its strerror description, but there are a few reasons why I can’t:

  • Probably mostly for historical reasons, the association between constants and integer values is not common between operating systems; worse, they can also change between hardware architectures.
  • The strerror representation varies between systems, in particular depending on the libc being used.
  • Some operating systems use the same value for multiple constants. This is not a bug per se as long as no standard function can return those different constants for different reasons. On my systems, those are EDEADLK and EDEADLOCK under Linux, EAGAIN and EWOULDBLOCK under OSX.

However, this led to the creation of a simple command-line utility, errnos. Build and run it on the system you investigate, or a similar one (same operating system, same libc, same CPU architecture), and you will get something you can store and grep at will. It could also make an ironic wallpaper for your child’s room, but don’t blame me if they have nightmares of production systems going down.

For once, I used glib as I needed a hashtable of lists and couldn’t be bothered implementing those for the millionth time in history (comp. sci. students who have to do so tonight, I share your frustration). There’s a limited amount of magic involved in the build process, so please just clone the repository and stick to the build instructions unless you have time to lose.

The first column gives the number in its decimal representation, the second in hexadecimal. The third is either its strerror representation between double quotes or a preprocessor constant.

To close this article, here is the end of its output on my Mac:

$ errnos | tail
Stopped looking at 1128
99	0x63	"Not a STREAM"
99	0x63	ENOSTR
100	0x64	"Protocol error"
100	0x64	EPROTO
101	0x65	"STREAM ioctl timeout"
101	0x65	ETIME
102	0x66	"Operation not supported on socket"
102	0x66	EOPNOTSUPP
103	0x67	"Policy not found"
103	0x67	ENOPOLICY

Things I wrote, episode 2: nato, superglob

May 12th, 2011

When you end up having to dictate Unix commands over the phone, you quickly learn from your colleagues that optimism doesn't compensate your French accent. Turns out cancelling your lovefilm subscription triggers a similar problem.

The most common answer is to involve the NATO phonetic alphabet. Looking up a table pinned to your desk is tedious, getting fluent takes time. That's where nato kicks in.

Not much to say, really:

$ nato 'lspci|grep VGA'
lima, sierra, papa, charlie, india, pipe, golf, romeo, echo, papa, space, uppercase victor, uppercase golf, uppercase alpha,
$

After this very useful piece of software (at least to some), let's have a look at something stupid and useless (I won't even bother trying to explain the silly reason I needed it). If you ever have a good, real-life reason to run superglob, please E-mail me right away, I'll owe you the drink of your choice. The idea is to build a glob matching all the arguments provided, but as little more as possible (and we're far from being clever here).

$ superglob Hello World
[HW][eo][lr]l[do]
$ superglob Foo 'Bar Mitzváh'
[BF][ao][or]*
$ superglob /lib64/libpamc.so.0 \
  /lib64/libpamc.so.0.81.0 /lib64/libpam_misc.so.0 \
  /lib64/libpam_misc.so.0.81.2 /lib64/libpam.so.0 \
  /lib64/libpam.so.0.81.5
/lib64/libpam[._c][.ms][ios][.os][.0c]*

A good example of a really silly behaviour (here, *ahello would likely be the best choice):

$ ~/usr/bin/superglob ahello bahello cbahello
[abc][abh][aeh][ehl][el][lo]*

Things I wrote, episode 1: mkpasswd

April 18th, 2011

As my blog appears dead and nobody knows about the weird stuff I commit in my stuff repo (probably a good thing, now that I think about it), here comes a series of posts about little tools.

For this first episode, please welcome mkpasswd!

You want passwords that are identical with AZERTY, QWERTY and QWERTZ keyboards? And easier to type on a mobile phone (no uppercase)? Alternatively, with the characters you list and just those?

You don’t want to choose a number of characters, how many digits and special characters should appear (or any nonsensical policy when you know what you are doing), but rather would rely solely on the number of bits of entropy you’ll get?

Generated from /dev/random, as it’s a good source of entropy (under a recent Linux kernel at least)?

Check out mkpasswd.


mplayer in brew (OSX): get OSD menus and subtitles

January 5th, 2011

As mplayer is fairly unusable (and a lot less interesting), this deserves to be googlable…

Did I mention that I love brew?

diff --git a/Library/Formula/mplayer.rb b/Library/Formula/mplayer.rb
index 1526974..0774dd0 100644
--- a/Library/Formula/mplayer.rb
+++ b/Library/Formula/mplayer.rb
@@ -7,6 +7,7 @@ class Mplayer <Formula

   depends_on 'pkg-config' => :build
   depends_on 'yasm' => :optional
+  depends_on 'libiconv'

   def install
     # Do not use pipes, per bug report
@@ -19,8 +20,9 @@ class Mplayer <Formula
     ENV['LD'] = ''
     ENV['CFLAGS'] = ''
     ENV['CXXFLAGS'] = ''
+    ENV.append 'LDFLAGS', '-liconv'

-    args = ["--prefix=#{prefix}", "--enable-largefiles", "--enable-apple-remote"]
+    args = ["--prefix=#{prefix}", "--enable-largefiles", "--enable-apple-remote", "--enable-iconv"]
     args << "--target=x86_64-Darwin" if snow_leopard_64?

     system './configure', *args

DAAP repeater

December 31st, 2010

I have a DAAP server running on a machine I access through a VPN. I wanted my colleagues to be able to enjoy the music from their workstations.

Without further ado, here is the process I followed under RHEL6, with the EPEL repo; you should only need to adapt the DAAP_HOST value:

# The IP address or hostname of the DAAP server
DAAP_HOST=192.168.42.1

# Install a few requirements
yum install -y nginx avahi

# Allow automatic network detection
cat > /etc/avahi/services/daapd.service << EOF
<?xml version="1.0" standalone='no'?><!--*-nxml-*-->
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
<name replace-wildcards="yes">%h</name>
<service>
<type>_daap._tcp</type>
<port>3689</port>
</service>
<service>
<type>_rsp._tcp</type>
<port>3689</port>
</service>
</service-group>
EOF

# Define my proxy
cat > /etc/nginx/conf.d/daap-proxy.conf << EOF
server {
 listen 3689;
 error_log /var/log/daap-error.log;
 access_log /var/log/daap-access.log;
 location / {
  proxy_pass http://${DAAP_HOST}:3689/;
  proxy_redirect off;
  proxy_buffering off;
 }
}
EOF

At that point, I entirely removed the existing server {} entry entirely from /etc/nginx/nginx.conf as I don't want an HTTP server.

# Start services now and during boot
for service in avahi-daemon nginx; do
  chkconfig $service on
  service $service restart
done

Défi geek

April 9th, 2010

Ce défi est une expérience, il n'y aucun prix pour le moment. Si la sauce prend, il est envisagé d'organiser des concours dans un cadre plus intéressant.

Ce problème a été soulevé par une grosse entreprise. La solution est déjà connue, je ne le pose ici que pour le jeu. Bien entendu le problème a été ramené à un cas d'école pour faciliter la compréhension et la réflexion.

Il est inutile de tester votre solution et vous pouvez en proposer plusieurs via les commentaires. Ces derniers sont modérés pour garder le secret quelques jours. Bien sûr cela ne vous empêche pas de commenter que je suis une personne excécrable ou que le multicast marche mieux sous NetBSD. Quiconque laisse un commentaire sera averti par E-mail de la mise en ligne de la solution.

Une version de ce problème correspondant à l'environnement réel de l'entreprise arrivera peu après la résolution de ce premier problème. La complexité et la difficulté seront supérieures.

Objectif :

Les clients envoient des requêtes en broadcast, un service déjà configuré sur le serveur doit y répondre.

Contexte :

  • Serveur Linux avec une carte Ethernet, configuré comme suit :
    • Distribution moderne générique (Debian, Ubuntu, Fedora, etc.) dont noyau Linux 2.6 ;
    • La carte Ethernet est configurée avec ifconfig eth0 10.0.0.1 netmask 255.255.255.0, addresse de broadcast par défaut soit 10.0.0.255 ;
    • L'interface de loopback (lo) est disponible ;
    • Chaînes netfilter vides, politique par défaut ACCEPT, le reste de la configuration est celle de l'installation par défaut de la distribution ;
  • Les clients sont sur le même segment Ethernet (i.e. aucun routeur), sur 10.0.0.0/16, addresse broadcast par défaut soit 10.0.255.255 ;
  • Les ports utilisés ne sont pas connus (par exemple rpc.bootparamd).

Contraintes :

  • Le serveur ne peut écouter que sur 10.0.0.0/24 et 10.0.255.255, ne peut émettre que sur 10.0.0.0/24 (les raisons pour ce type de contraintes seront plus claires dans la version complexe) ;
  • La configuration réseau du serveur est librement modifiable tant que la règle précédente est respectée et assurée dans l'espace noyau ;
  • La configuration réseau des clients ne peut pas être changée ;
  • Pas de patchs ou de paquets ésotériques ;
  • La solution tient sur 3 commandes exécutées en root. Elles ne peuvent reposer que sur des outils disponibles sur toute distribution moderne générique. Inutile de faire en sorte que les changements persistent après un redémarrage, de toute façon un serveur ne crashe pas et n'a pas besoin de redémarrer.

Si vous autorisez le serveur à émettre sur 10.0.255.255 et acceptez d'êtres crades (mais compatibles avec au moins Solaris et Linux), une commande suffit.

Edit du 17 avril 2010, aka « La soluce »

Xav l'emporte avec sauf erreur de ma part 2 fautes d'étourderie sur iptables (il faudrait utiliser -A au lieu de -l et –to-destination au lieu de -to) !

Je préfère ne pas créer de VLAN et reposer sur du SNAT pour que les logiciels « voient » l'adresse de destination des requêtes, c'est plus transparent et ça pourrait jouer un rôle avec une implémentation de bootparamd par exemple (puisque le client demande entre autres quel routeur utiliser).

On peut attacher des IP individuelles à une interface existante sans créer d'interface virtuelle (eg eth0:1) et avec un masque implicite de /32 avec ip addr add.

Ma solution favorite est donc (avec :0-1023 pour désactiver le mécanisme de changement de ports) :

# ip addr add 10.0.255.255 dev eth0
# iptables -t nat -A POSTROUTING -s 10.0.255.255 -j SNAT –-to-source 10.0.0.1:0-1023


Synchronize desktop launchers’ position in RHEL5

March 27th, 2010

This is a simple snippet written for a customer.

#!/usr/bin/env ruby

=begin
=======================================
Synchronize desktop launchers' position
    Ruby prototype for Gnome, RHEL5
******* Not endorsed by Red Hat *******
=======================================

WARNING
-------

This script will NOT work with a recent nautilus.
It should work nautilus 2.16.
Icons positions are stored in gconf in last releases.

LICENSE
-------

Copyright (c) 2010, Pierre Carrier <pcarrier@redhat.com>

Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

EXAMPLE
-------

To synchronize from $SRCUSER to $DSTUSER, use:

script.rb /home/${SRCUSER}/.nautilus/metafiles/file:%2F%2F%2Fhome%2F${SRCUSER}%2FDesktop.xml \
          /home/${DSTUSER}/.nautilus/metafiles/file:%2F%2F%2Fhome%2F${DSTUSER}%2FDesktop.xml

=end

require "rexml/document"
include REXML

srcfile = File.new(ARGV[0])
srcdoc = REXML::Document.new srcfile
srcfile.close

dstfile = File.new(ARGV[1])
dstdoc = REXML::Document.new dstfile
dstfile.close

XPath.each srcdoc, "//directory/file"  do |s|
  d = XPath.first dstdoc, "//directory/file[@name='#{s.attributes['name']}']"
  if d != nil then
    unless d.attributes['icon_position'] == s.attributes['icon_position'] then
      d.attributes['icon_position'] = s.attributes['icon_position']
      print "updated position for #{s.attributes['name']}\n"
    end
  else
    dstdoc.root.add_element 'file', s.attributes
    print "added info for #{s.attributes['name']}\n"
  end
end

dstdoc.write File.new(ARGV[1],'w')