TL;DR: no modificar el archivo /etc/login.conf
Auditoría de seguridad
A partir de un blog que leí hace unos cuantos días me enteré de la existencia del paquete lynis disponible para openbsd.
De la descripción del paquete:
Security auditing tool for Linux, Mac and Unix based systems. Scan your systems in a matter of minutes and know what can be improved.
Así que pensé ¿porqué no probarlo?
Luego de instalarlo (doas pkg_add lynis
) y realizar un escaneo, uno de los
detalles que obtengo es:
2023-04-01 01:41:58 Result: file /etc/login.conf exists
2023-04-01 01:41:58 Hardening: assigned partial number of hardening points (0 of 2). Currently having 3 points (out of 8)
2023-04-01 01:41:58 Result: found umask value 022, which can be more strict
2023-04-01 01:41:58 Suggestion: Umask in /etc/login.conf could be more strict like 027 [test:AUTH-9328] [details:-] [solution:-]
Por supuesto, cualquier resultado obtenido por una herramienta de auditoría debe evaluarse y no aceptarse ciegamente, pero en este caso parece razonable la sugerencia.
Revisando el archivo /etc/login.conf
para ver el valor de umask tenemos que
solo se define en un lugar, la clase default
:
$ grep -C3 umask /etc/login.conf
#
default:\
:path=/usr/bin /bin /usr/sbin /sbin /usr/X11R6/bin /usr/local/bin /usr/local/sbin:\
:umask=022:\
:datasize-max=1024M:\
:datasize-cur=1024M:\
:maxproc-max=256:\
Para modificarla nada más sencillo que hacer:
$ doas sed -i.orig 's/umask=022/umask=027/' /etc/login.conf
Nota: siempre que se modifique un archivo de configuración tener abierta una
terminal con el usuario root
y no cerrarla hasta corroborar que el cambio haya
sido satisfactorio.
Y con ello ya aumentamos la seguridad del sistema.
Problemas de permisos en /etc/resolv.conf
A los días y sin darme cuenta que el fué debido al aumento del hardening del sistema, empiezo a tener problemas para conectarme a internet del tipo:
$ ping www.openbsd.org
ping: no address associated with name
Luego de buscar un poco en internet, encuentro que el problema se debe a los
permisos del archivo /etc/resolv.conf
:
$ ls -lh /etc/resolv.conf
-rw-r----- 1 root wheel 98 Apr 9 21:35 /etc/resolv.conf
Lo cual se corrige fácilmente utilizando:
$ doas chmod 644 /etc/resolv.conf
Lo malo es que periódicamente se resetean los permisos del archivo a 0640
.
¿Quien modifica los permisos de /etc/resolv.conf?
Lo cierto es que luego de cansarme de estar utilizando chmod
y viendo que no
es algo puntual, me embarco en buscar quien está modificando los permisos de
/etc/resolv.conf
.
Luego de un tiempo buscando en internet, me pongo a hacer grep en el código de
openbsd, con lo cual encuentro a quien creo el culpable:
./sbin/resolvd/resolvd.c
. Inspeccionando el código en el archivo encuentro
que resolvd
crea un nuevo archivo cada vez que me conecto/desconecto a la
wifi con permiso 0640
. Esto es razonable, ya que el hardening configurado
previamente indica que el umask
debe ser 027
en lugar de 022
.
¿Mi primer idea de solución?
diff --git sbin/resolvd/resolvd.c sbin/resolvd/resolvd.c
index 2ffdfc6ddb4..133559819f6 100644
--- sbin/resolvd/resolvd.c
+++ sbin/resolvd/resolvd.c
@@ -192,6 +192,8 @@ main(int argc, char *argv[])
if (geteuid())
errx(1, "need root privileges");
+ umask(0022);
+
lockfd = open(_PATH_LOCKFILE, O_CREAT|O_RDWR|O_EXLOCK|O_NONBLOCK, 0600);
if (lockfd == -1) {
if (errno == EAGAIN)
Esto basado en la modificación de usr.sbin/pkg_add/OpenBSD/AddDelete.pm
:
sub do_the_main_work
{
my ($self, $state) = @_;
if ($state->{bad}) {
return;
}
umask 0022;
Y ya que estamos, ¿porqué no compartir el cambio?
Haciendo corto el intercambio de emails, el parche no tiene sentido
conceptualmente. En lugar de modificar el código de unos 20 demonios tendría
más sentido resetear el valor de umask en la clase daemon
:
--- /etc/login.conf.orig
+++ /etc/login.conf
@@ -58,6 +58,7 @@
# Be sure to reset these values to system defaults in the default class!
#
daemon:\
+ :umask=022:\
:ignorenologin:\
:datasize=4096M:\
:maxproc=infinity:\
pero ya que al parecer fuí el único (digamos mejor de los pocos) que rompió su
sistema por modificar el archivo /etc/login.conf
no tiene sentido sellar la
clase daemon
de cambios en la clase default
.
Forma correcta de asignar umask
Ya que aprendí (ver más adelante) que mejor no estar tocando el archivo
/etc/login.conf
, una buena solución (¿la correcta?) es agregar el archivo
/etc/login.conf.d/staff
con el contenido:
#
# Staff have fewer restrictions and can login even when nologins are set.
#
staff:\
:umask=027:\
:datasize-cur=1536M:\
:datasize-max=infinity:\
:maxproc-max=512:\
:maxproc-cur=256:\
:ignorenologin:\
:requirehome@:\
:tc=default:
el cual es una copia de la definición que se encuentra en /etc/login.conf
pero
agregando el valor umask=027
.
¿Porqué staff
? Porque cuando se crea un usuario sería la clase que habría que
agregar por defecto. El login class
del usuario que es creado por el
instalador también se encuentra en dicha clase:
$ doas grep ":$(id -u):" /etc/master.passwd
jmpc:XXX:1000:1000:staff:0:0:JMPC:/home/jmpc:/usr/local/bin/bash
(el formato del archivo se describe en man 5 passwd).
Problemas con comentarios y continuación de linea en getent
Durante la realización de pruebas modificando el archivo /etc/login.conf
para
corroborar que usando umask=027
en la clase default
y umask=022
en la
clase daemon
resetea el cambio de umask en la clase default y resolvd
funciona de la forma esperada, escribí:
--- /etc/login.conf.orig
+++ /etc/login.conf
@@ -57,6 +57,7 @@
# This must be set properly for daemons started as root by inetd as well.
# Be sure to reset these values to system defaults in the default class!
#
+#:umask=022:\
daemon:\
:ignorenologin:\
:datasize=4096M:\
Esto es, comenté la linea y la moví arriba de la definición de la clase
daemon
.
A partir de esto al querer editar el archivo como root utilizando doas
obtuve:
$ doas vim /etc/login.conf
doas: failed to set user context for target
Tampoco me fué posible utilizar su -l
.
¿Que pasó?
Bueno, la primera hipótesis (que resultó acertada) es que en el archivo
/etc/login.conf
la continuación de linea (el \
al final) se interpreta en
las lineas de comentarios.
Una de las ventajas de openbsd es que el kernel y el código del sistema base se
encuentran en el mismo repositorio, por lo cual es bastante sencillo seguir el
código que se está ejecutando. También en los archivos .c
los nombres de las
funciones comienzan en el primer carácter.
Partiendo del mensaje dado por doas
:
-
El mensaje anterior se encuentra en la función
main
, en el archivo usr.bin/doas/doas.c Este ocurre en caso de un error ensetusercontext
.Aquí ya se puede utilizar el manual para obtener información sobre la función y confirmar que ese es el camino correcto. De man setusercontext:
login_getclass, login_getstyle, login_getcapbool, login_getcapnum, login_getcapsize, login_getcapstr, login_getcaptime, login_close, setclasscontext, setusercontext — query login.conf database about a user class
-
La definición de
setusercontext
se encuentra en lib/libc/gen/login_cap.c Allí vemos que se invoca alogin_getclass
que se encuentra en el mismo archivo. -
A su vez,
login_getclass
invoca acgetent
.Buscando en el manual por
cgetent
se tiene (man cgetent):cgetent, cgetset, cgetmatch, cgetcap, cgetnum, cgetstr, cgetustr, cgetfirst, cgetnext, cgetclose, cgetusedb — capability database access routines
Y lo más importante:
Capability database syntax
Capability databases are normally ASCII and may be edited with standard text editors. Blank lines and lines beginning with a ‘#’ are comments and are ignored. Lines ending with a ‘\’ indicate that the next line is a continuation of the current line; the ‘\’ and following newline are ignored. Long lines are usually continued onto several physical lines by ending each line except the last with a ‘\’.
Sin duda mi interpretación de lo anterior es que primero se ignoran los comentarios y luego se interpretan las lineas que terminan con
\
indicando que la siguiente linea es la continuación de la siguiente, lo que no está ocurriendo. -
El código de
cgetent
se encuentra en lib/libc/gen/getcap.c donde llama agetent
. -
La función
getent
se encuentra en el mismo archivo quecgetent
y allí es donde finalmente se realiza el análisis del archivo. A primera vista se ve que primero se lee una linea completa (interpretando primero el\
al final de la linea) y recién después si la linea es vacía o comienza con#
se ignora.
La definición de getent
explica el error que obtuve dejando por error el
carácter \
encima de la definición de daemon
.
También del código entiendo que no se ignora un comentario si el carácter #
no
es el primero de la linea. Esto no lo tenía tan presente, ya que otros
programas permiten blancos antes del comentario en su archivo de configuración.
¿Conclusiones?
- Evitar modificar el archivo
/etc/login.conf
. - Siempre que se edite un archivo de configuración del sistema tener abierta una
terminal en la cual se esté logueado como
root
para poder revertir el cambio. - En los archivos de configuración, tener cuidado con las continuaciones de
linea y los comentarios:
- no usar continuación de linea en comentarios o en caso de utilizarlas asegurarse que la siguiente linea también sea un comentario.
- siempre comenzar los comentarios en la primer columna (sin espacios al principio).
- Es sencillo navegar por el código de openbsd.