domingo, 7 de diciembre de 2008

Cómo crear usuarios en C / Linux

En ocasiones necesitamos crear usuarios desde un programa en C y aunque podría bastar haciendo una llamada a 'useradd' siempre es más elegante usar las llamadas al sistema. En este artículo voy a explicar cómo crear un usuario con su correspondiente registro en /etc/passwd y en /etc/shadow.

Lo primero que necesitamos es una estructura donde guardar los datos que almacenaremos en /etc/passwd, para usar esta estructura 'struct passwd' necesitaremos la cabecera 'pwd.h'.

struct passwd p;

p.pw_name = "myusername";
p.pw_passwd = "x";
p.pw_uid = 1000;
p.pw_gid = 1000;
p.pw_gecos = "Test User";
p.pw_dir = "/home/myusername";
p.pw_shell = "/bin/sh";


A destacar la 'x' en pw_passwd, pues recordemos que estamos usando shadow passwords y por lo tanto la password se guardará en /etc/shadow. Respecto a los demas campos, a elegir.

Aun así, es interesante ver como podemos escoger un id para un usuario para no repetirlo.

struct passwd *pw = NULL;
int min = 1000;
int max = 65000;

f = fopen("/etc/passwd", "r");

/* check user and get valid id */
while ((pw = fgetpwent(f)))
{
if (strcmp(pw->pw_name, p.pw_name) == 0)
{
perror("user_add(): user exists");
exit(0);
}

if ((pw->pw_uid >= p.pw_uid) && (pw->pw_uid <>pw_uid >= min))
{
p.pw_uid = pw->pw_uid + 1;
}
}

fclose(f);



Ya tenemos la entrada de /etc/passwd preparada, así que la añadiremos al fichero de la siguiente manera:


f = fopen("/etc/passwd", "a+");
if(!f)
{
perror("user_add(): cannot open /etc/passwd");
exit(0);
}

if (putpwent(&p, f) == -1)
{
perror("user_add(): putpwent() error");
exit(0);
}

fclose(f);




Vayamos ahora a por las shadow. El proceso es similar al anterior, solo que ahora usaremos una estructura 'struct spwd sp;' para la cual necesitamos la cabecera 'shadow.h'.
Además ha llegado el momento de crear la contraseña, para lo que usaremos la función 'crypt()'. Esta a su vez, necesita una salt, que la crearemos tal y como vemos a continuación.



struct timeval tv;
static char salt[40];

salt[0] = '\0';

gettimeofday (&tv, (struct timezone *) 0);
strcat(salt, l64a (tv.tv_usec));
strcat(salt, l64a (tv.tv_sec + getpid () + clock ()));

if (strlen (salt) > 3 + 8)
salt[11] = '\0';



Esta forma de crear la salt es bastante común, aunque hay otros enfoques válidos. Al final, se trata de genererar diferentes salt cada vez que se crea un usuario.

Vamos ahora a rellenar la estructura shadow:


#define DAY (24L*3600L)
#define WEEK (7*DAY)
#define SCALE DAY
...
struct spwd sp;
sp.sp_namp = p.pw_name;
sp.sp_pwdp = (char*)crypt(passwd, salt);
sp.sp_min = 0;
sp.sp_max = (10000L * DAY) / SCALE;
sp.sp_lstchg = time((time_t *) 0) / SCALE;
sp.sp_warn = -1;
sp.sp_expire = -1;
sp.sp_inact = -1;
sp.sp_flag = -1;



El segundo campo corresponde a la contraseña cifrada en DES, de la forma tradicional y poco segura. Este parámetro se puede mejorar usando otras funciones de cifrado.
Los parámetros siguientes indican los dias de cambios de password, expiración de la cuenta, etc. Ver detalles mediante 'man shadow'.

Finalmente, solo nos queda añadir el registro en /etc/shadow:

f = fopen("/etc/shadow", "a+");
if(!f)
{
perror("user_add(): cannot open /etc/shadow");
exit(0);
}

if (putspent(&sp, f) == -1)
{
perror("user_add(): putspent() error");
exit(0);
}

fclose(f);




Algunos cambios interesantes son usar una función de cifrado más segura, bloquear los archivos /etc/passwd y /etc/shadow antes de abrirlos, y verificar en /etc/shadow que no existe ningun registro que coincida con el que vamos a añadir, tal y como hemos hecho con /etc/passwd. Tambien quedaría realizar una gestión del grupo, como por ejemplo, crearlo si es necesario. En cualquier caso quedan cubiertos los objetivos de este artículo, ver como se hece en leguaje C para crear un usuario de Linux.

A propósito, borrar un usuario consisitiría, simplemente, en eliminar la linea correspondiente de /etc/passwd y /etc/shadow. Así pues, que nadie me pida que explique cómo hacer un programa en c para borrar usuarios ;)

No hay comentarios: