WordPress <= 2.0.2 security vulnerability
May 27th, 2006
A new security vulnerability has been disclosed for WordPress < = 2.0.2.
The only solution at the moment seems to restrict web access to the wp-content/cache/userlogins/ and wp-content/cache/users/ directories (e.g. with a .htaccess file). Thus, I’ve done so while the WordPress staff confirms and fixes this problem.
Enterprise mail routing with Postfix and LDAP
May 24th, 2006
A LDAP directory offers a single, logically centralized, hierarchical store to keep data, like information about users, groups, mailboxes, services, etc. There are a number of applications out there than can leverage this LDAP store. For example, PAM can authenticate by checking against an LDAP directory, Cyrus-IMAP can check whether a certain user has a IMAP/POP mailbox available, and Postfix can contact an LDAP directory in order to route an incoming mail to its corresponding mail host.
LDAP is flexible enough to describe abstract concepts, like persons, by using object classes. Each object class is built upon attributes. An attribute is the minimal unit of information and tells about a property, such as the name of a person, its postal address or e-mail address. The minimal storage unit is an LDAP entry, however, which is an instance of one ore several object classes. The collection of attributes and object classes is called schema, while the collection of LDAP entries is called Directory Information Tree (DIT).
There are several RFC documents out there describing most of the standard LDAP object classes for the LDAPv3 protocol. There are other object classes which are either propietary or non-standard. For example, Fedora Directory Server defines an object class named mailRecipient. This legacy object class was used by the Netscape Messaging Server 4 to define a mailbox and is defined as:
objectclasses: ( 2.16.840.1.113730.3.2.3 NAME 'mailRecipient' DESC '' SUP top AUXILIARY MUST ( objectClass ) MAY ( cn $ mail $ mailAlternateAddress $ mailHost $ mailRoutingAddress $ mailAccessDomain $ mailAutoReplyMode $ mailAutoReplyText $ mailDeliveryOption $ mailForwardingAddress $ mailMessageStore $ mailProgramDeliveryInfo $ mailQuota $ multiLineDescription $ uid $ userPassword ) X-ORIGIN 'Netscape Messaging Server 4.x' )
This object class is particularly simple and interesting since it defines a few concepts we can use when routing mail, like the idea of multiple mail aliases for a single mailbox.
Describing our purpouses
Take this scenario: we have a real user, John Smith which owns a mailbox which is stored at host mail1.internal. The mailbox for John Smith is john.smith@mail1.internal, since it’s stored in host mail1.internal. Let’s say this real user has the following e-mail addresses assigned to him (assigned to its mailbox):
- sample.user@example.com
- sample_user@example.com
- sampleuser@example.com
- sample@example.com
- john.smith@example.com
These are the e-mail addresses. The last e-mail address is, in fact, the real e-mail address that anyone could expect John Smith to have. The other e-mail addresses are in fact aliases.
Whenever a Postfix MTA in the enterprise receives a mail for any of these e-mail addresses, we want it to be delivered to a mailbox named john.smith in host mail1.internal, that is, delivered to john.smith@mail1.internal, and so, john.smith@mail1.internal is the real address where messages sent to John Smith, using any of the previous e-mail addresses, must be routed to. Thus, john.smith@mail1.internal is what we will call the mail routing address.
We will configure Postfix to use an LDAP-based virtual alias map. Each time a mail is sent to Postfix, it will perform an LDAP query for that e-mail address and will try to guess its mail routing address. If one is found, Postfix will deliver that mail to it:
- If the domain part (the address at the right of the
@sign) of the mail routing address equals the FQDN of the Postfix host:Postfix will try to deliver the message locally, usually using an MDA, like procmail or cyrus-imapd/deliver (Cyrus-IMAP local delivery agent used to deliver messages to a IMAP mailbox).
For example, let be john.smith@mail1.internal the mail routing address and mail1.internal Postfix’s hostname FQDN. In this case, Postfix will deliver the message locally using a MDA.
- If the domain part (the address at the right of the
@sign) of the mail routing address does not equal the FQDN of the Postfix host:Postfix will trigger the message routing process again in order to deliver it to its final destination, checking querying the LDAP directory again if necessary.
To store mail routing addresses and it’s corresponding mail aliases, we will use the mailRecipient object class. The multi-valued mail attribute will hold all mail aliases and mailRoutingAddress will the real, final destination for any of them.
A sample entry, exemplifying the John Smith user we described before, in LDIF syntax is:
dn: uid=john.smith,ou=People, dc=example, dc=com givenName: John sn: Smith mail: sample.user@example.com mail: sample_user@example.com mail: sampleuser@example.com mail: sample@example.com mail: john.smith@example.com objectClass: top objectClass: mailRecipient uid: john.smith mailRoutingAddress: john.smith@mail1.internal
Note the absence of the posixAccount object class. This means John Smith is not a regular UNIX user and thus, this mailbox cannot be used to log in through PAM, for example. Note the absence of the inetOrgPerson object class too, which could mean this mailbox is not assigned to a real user.
Configuring Postfix
The first thing that is required is creating a configuration file with details about how the LDAP directory server should be queried and contacted. The name of this file is not relevant, but I decided to name it /etc/postfix/ldap-aliases.cf:
# cat /etc/postfix/ldap-aliases.cf bind = no version = 3 timeout = 20 ## set the size_limit to 1 since we only ## want to find one email address match size_limit = 1 expansion_limit = 0 start_tls = no #tls_require_cert = no server_host = ldap://ldap1.internal/ ldap://ldap2.internal/ search_base = dc=example,dc=com scope = sub query_filter = (&(objectclass=mailRecipient)(mail=%s)) result_attribute = mailRoutingAddress special_result_filter = %s@%d
The configuration options are described in detail in the Postfix ldap_table(5) manual page. A brief description lies hereafter:
bind = notells Postfix to perform an unauthenticated (anonymous) BIND against the LDAP directory.size_limit = 1tells Postfix to request one, and only one, LDAP entry matching thequery_filter, starting the search operation at the LDAP entry whose DN is specified bysearch_base, and using ascopesearch scope.The search scope is one of: sub, one, base.
server_hostdefines one or several LDAP hosts, trying them in order should the first one fail.query_filterdefines the LDAP search that Postfix will use in order to retrieve the mail routing address given a mail address.In the previous configuration file, the LDAP search filter will look for entries belonging to the mailRecipient object class whose mail attribute matches the recipient mail address of the incoming message.
If one is found (at most only one entry will be retrieved since
size_limit = 1), the mailRoutingAddress attribute, defined to byresult_attribute, points to the final destination for the message, that is the mail routing address.
Finally, we will add the virtual_alias_maps directive to Postfix’s /etc/postfix/main.cf configuration file:
# tail -1 /etc/postfix/main.cf virtual_alias_maps = ldap:/etc/postfix/ldap-aliases.cf
For example, when receiving a message for sample.user@example.com, Postfix will first look to see if this mail address is an alias or a real address. The LDAP query can be tested by running:
$ ldapsearch -x -b"dc=example,dc=coml" \ "(&(objectclass=mailRecipient)(mail=sample.user@example.com))" \ mailRoutingAddress # extended LDIF # # LDAPv3 # basewith scope sub # filter: (&(objectclass=mailRecipient)(mail=sample.user@example.com)) # requesting: mailRoutingAddress # dn: uid=john.smith,ou=People, dc=example, dc=com mailRoutingAddress: john.smith@mail1.internal # search result search: 2 result: 0 Success # numResponses: 2 # numEntries: 1
Thus, Postfix will route any message to sample.user@example.com to a mailbox named john.smith at mailhost mail1.internal.
Postfix and Cyrus-IMAP interactions on x86_64
May 22nd, 2006
The file /etc/postfix/master.cf of postfix-2.1.5-4.2.RHEL4.x86_64 contains a hard-coded path to Cyrus-IMAP LMTP deliver agent, /usr/lib/cyrus-imapd/deliver. However, this causes problems on x86_64 systems since Cyrus-IMAP LMTP deliver agent path is /usr/lib64/cyrus-imapd/deliver.
Fixing this problem is easy: just edit /etc/postfix/master.cf and replace /usr/lib/cyrus-imapd/deliver with /usr/lib64/cyrus-imapd/deliver.
I have filled in a bug report against Red Hat’s Enterprise Linux 4:
Cyrus-IMAP and PAM interactions on x86_64
May 20th, 2006
In Red Hat Enterprise Linux ES 4.1 Update 3, both /etc/pam.d/imap and /etc/pam.d/pop contain hard-coded paths pointing to pam_stack.so, actually /lib/security/pam_stack.so. This causes problems on x86_64 since pam_stack.so is located under /lib64/security/pam_stack.so.
A workaround is to modify /etc/pam.d/imap and /etc/pam.d/pop to look like this:
#%PAM-1.0 auth required pam_stack.so service=system-auth account required pam_stack.so service=system-auth
I filled in a bug report against Red Hat Enterprise Linux 4:
saslauthd and testsaslauthd
May 20th, 2006
Care must be exercised when using testsaslauthd to check whether SASL authentication is working and saslauthd is configured to use PAM:
# cat /etc/sysconfig/saslauthd SOCKETDIR=/var/run/saslauthd MECH=pam FLAGS=
By default, testsaslauthd test whether the authentication process works by invoking authentication onto the imap service, unless the -s service is passed along in the command-line. PAM configuration for the imap service is stored in /etc/pam.d/imap. However, if cyrus-imapd is not installed, the file /etc/pam.d/imap won’t exist and thus any try to use testsaslauthd against the default imap service will fail:
# ls -l /etc/pam.d/imap ls: /etc/pam.d/imap: No such file or directory # testsaslauthd -u felipe.alfaro -p password 0: NO "authentication failed" # tail /var/log/messages May 20 16:36:47 mail2 saslauthd[2626]: do_auth : auth failure: [user=felipe.alfaro] [service=imap] [realm=] [mech=pam] [reason=PAM auth error # testsaslauthd -s login -u felipe.alfaro -p password 0: OK "Success.
User management using LDAP and libuser
May 18th, 2006
libuser is a collection of libraries and tools to manage users and groups under Linux or UNIX-like systems. libuser has several modules allowing to manage users and groups through different backends, like local (password and shadow) and LDAP (the LDAP backend has been somewhat unusable up to version 0.52).
The LDAP module, however, requires the user to supply a BIND_PW (password) for the BIND_DN (user) in order to authenticate against the LDAP directory server an perform the modifications. In automated deployment scenarios, it’s essential to be able to perform operations on to the LDAP directory server without asking the user for the password. Instead, the password used to authenticate against the LDAP directory server could be well stored in libuser’s configuration file, /etc/libuser.conf.
I’ve made a patch against libuser that implements a new configuration parameter for the LDAP module. This new parameter is named password, and specifies the password for the user binddn user.
Here is a sample of libuser’s configuration file, /etc/libuser.conf:
[defaults] # The default (/usr/lib*/libuser) is usually correct # moduledir = /your/custom/directory skeleton = /etc/skel mailspooldir = /var/mail modules = ldap create_modules = ldap crypt_style = md5 [userdefaults] LU_USERNAME = %n LU_UIDNUMBER = 10000 LU_GIDNUMBER = %u LU_HOMEDIRECTORY = /home/%n LU_LOGINSHELL = /bin/bash [groupdefaults] LU_GROUPNAME = %n LU_GIDNUMBER = 10000 [ldap] # Setting these is always necessary. server = ldap://directory.server.fqdn basedn = dc=example,dc=com # Setting these is rarely necessary, since it's usually correct. userBranch = ou=People groupBranch = ou=Groups # Set only if your administrative user uses simple bind operations to # connect to the server. binddn = cn=Directory Manager password = secret
The patched SRPM and diff patch can be obtained from here: libuser-0.52.5-1.el4.1
Integrated DHCP and DNS services using OpenWRT
May 15th, 2006
dnsmasq offers a lightweight, functional and integrated DHCP and DNS service. Using it on OpenWRT brings up and embedded, flexible DNS service, with a very small footprint, for small or home offices.
dnsmasq acts as a caching DNS server and DHCP server. It reserves a DNS domain, called the local DNS domain and usually being .lan, for local name resolution. When queried for an A RR inside the local DNS domain, dnsmasq looks at file /etc/hosts for a match. If one is found, its corresponding IP is returned as the query result. When queried for a PTR RR, it looks into file /etc/hosts for a match by IP and, if one is found, its correspoding hostname, qualified with the local DNS domain, is returned. Thus, /etc/hosts behaves much like a DNS master zone file.
Also, if the DHCP server funcionality of dnsmasq is enabled, when a query under the local DNS domain fails (no record is found in /etc/hosts), it will try to resolve the query from the DHCP lease database.
The DHCP lease database is usually stored at /tmp/dhcp.leases. Its format is pretty simple: it’s a text file, where each line represents an active DHCP lease. Each line is made up of five fields:
- Time of lease expiration
In epoch time (seconds since 1970). States when the lease will expire. Most DHCP clients will try to renew the lease when it reaches 80% of its valid lifetime.
- Client MAC address
The MAC address corresponding to the client to which the lease belongs.
- Leased IP address
A valid IP address, taken from the DHCP pool, which is actually and currently leased to the client whose MAC address is listed in the previous field.
- Client hostname
If known, holds the unqualified host name of the client machine. Else, an asterisk is stored here.
- Client ID
Simon Kelley defines it as:
The client-ID is used as the computer’s unique-ID in preference to the MAC address, if it’s available. Some DHCP clients provide it, and some don’t. The ones that do normally derive it from the MAC address unless explicity configured, but it could be something like a serial number, which would protect a computer from losing its identify if the network interface were replaced.
If not know, an asterisk is stored here.
A sample DHCP database lease:
# cat /tmp/dhcp.leases 1147729862 00:16:3e:3b:56:f1 192.168.0.11 rhel * 1147725355 00:0c:29:09:3d:58 192.168.0.10 rhel-devel *
In this case, there are two active DHCP leases, one for client rhel, another one for rhel-devel.
OpenWRT uses a rc.d script stored at /etc/init.d/S50dnsmasq which, for a squashfs firmware is a symbolic link to /rom/etc/init.d/S50dnsmasq. This rc.d script tries to configure the dnsmasq daemon using NVRAM variables, which helps a lot when reflashing. However, I have found more convenient to the use the traditional /etc/dnsmasq.conf file instead.
Replacing the OpenWRT rc.d script with a custom one, in order to leverage dnsmasq.conf, is as simple as removing /etc/init.d/S50dnsmasq and invoking the dnsmasq daemon directly:
rm -f /etc/init.d/S50dnsmasq cat > /etc/init.d/S50dnsmasq < < EOF #/bin/sh /usr/sbin/dnsmasq EOF
Here is a sample of a /etc/dnsmasq.conf file I use on my Linksys WRT54G router running OpenWRT White Russian RC5:
# filter what we send upstream domain-needed bogus-priv filterwin2k localise-queries # allow /etc/hosts and dhcp lookups via *.lan local=/lan/ domain=lan expand-hosts # enable dhcp (start,end,netmask,leasetime) dhcp-authoritative dhcp-range=10.0.0.10,10.0.0.100,255.255.255.128,12h dhcp-leasefile=/tmp/dhcp.leases # use /etc/ethers for static hosts; same format as --dhcp-host # [hwaddr] [ipaddr] read-ethers # other useful options: # default route(s): dhcp-option=3,192.168.1.1,192.168.1.2 # dns server(s): dhcp-option=6,192.168.1.1,192.168.1.2 dhcp-option=3,10.0.0.126 dhcp-option=6,10.0.0.122
¿Firma digital=Firma manuscrita?
May 7th, 2006
Leo un breve artículo en Kriptópolis que pone en duda la ecuación Firma digital=Firma manuscrita:
La Ley dice que sí, pero algunos nunca hemos creído en esa ecuación. En nuestra opinión, lo más que puede acreditar la firma digital es su relación con determinada clave, pero la voluntariedad e intencionalidad de su uso siempre podrá ser puesta en entredicho.
Llevo mucho tiempo pensando que, efectivamente, deberíamos plantearnos como verdadera la inecuación Firma digital != Firma manuscrita, y la razón es bastante sencilla: la firma digital elimina de la ecuación al usuario.
La clave de firma se almacena, normalmente, en un sistema informático, que es el encargado de realizar la firma. Así pues, ¿qué papel desempeña el usuario en todo el sistema? Teóricamente, el usuario autoriza el proceso de firma. Sin embargo, nada impide al sistema informático firmar digitalmente un documento sin autorización del usuario: el sistema informático, en muchos casos, genera la clave privada de firma, y la almacena en algún lugar del disco duro. En otro casos, el usuario protege la clave privada mediante una contraseña que, igualmente, suministra al sistema informático que, por consiguiente, también conoce.
Sólo veo una forma de proteger relativamente el proceso de firma, y es llevar dicho proceso, y ejecutarlo completamente, a un dispositvo empotrado o embebido, propiedad plena del usuario, de confianza para éste, y mantener al sistema informático alejado de la clave de firma y la contraseña o PIN que la protege. Así mismo, para evitar firmas fraudulentas por parte del sistema informático, el usuario debe autorizar, físicamente sobre el propio dispositivo empotrado, cada una de las operaciones de firma.
Mientras el sistema informático disponga de todos los elementos para realizar y llevar a cabo una firma electrónica, mientras el usuario desempeñe el papel de mero observador y no tenga pleno control del proceso, los sistemas de firma electrónica no serán lo suficientemente seguros como para ser implantados a gran escala.