sábado, 9 de junio de 2007

Cómo hacer login en UNIX con C

A veces es necesario escribir un programa en C que lea de /etc/passwd y/o /etc/shadow para hacer login. Para este propósito, UNIX ofrece las funciones getpwnam() y getspnam() que permiten acceder a /etc/passwd y /etc/shadow respectivamente. Cada una de estas funciones retorna una estructura con los datos del usuario a partir de su nombre. Las estructuras son las siguientes:


/* Para /etc/passwd usando getpwnam() */

struct passwd
{
char   *pw_name;       /* user name */
char   *pw_passwd;     /* user password */
uid_t   pw_uid;        /* user ID */
gid_t   pw_gid;        /* group ID */
char   *pw_gecos;      /* real name */
char   *pw_dir;        /* home directory */
char   *pw_shell;      /* shell program */
};



/* Para /etc/shadow usando getspnam() */

struct spwd
{
char *sp_namp;         /* Login name */
char *sp_pwdp;         /* Encrypted password */
long  sp_lstchg;       /* Date of last change */
long  sp_min;          /* Min #days between changes */
long  sp_max;          /* Max #days between changes */
long  sp_warn;         /* #days before pwd expires
to warn user to change it */
long  sp_inact;        /* #days after pwd expires
until account is disabled */
long  sp_expire;       /* #days since 1970-01-01
until account is disabled */
unsigned long sp_flag; /* Reserved */
};



A continuación pego unas funciones de ejemplo que permiten hacer login. Con ellas se puede verificar un par user/pass de forma muy simple.

...
if(auth_is_valid_user(user, pass))
login_ok = 1;
...


Para las siguientes funciones es necesario incluir las cabeceras pwd.h, shadow.h y crypt.h. Para compilar, es necesaria la librería crypt (ej: gcc test.c -lcrypt).


int auth_check_pass(const char *clear_pass, const char *encrypted_pass)
{
char *encrypted = crypt(clear_pass, encrypted_pass);

if (strcmp(encrypted, encrypted_pass) == 0)
return 1;
else
return 0;
}

int auth_is_valid_user(const char *user, const char* pass)
{
struct passwd *pw = getpwnam(user);
if(pw==NULL)
return 0;

// password in /etc/shadow
if(strcmp(pw->pw_passwd, "x")==0)
{
/* Solo root puede leer /etc/shadow por lo que esta parte no
funcionara a menos que la ejecute root o el archivo este
setuidado */

struct spwd *sp = getspnam(user);
if(sp==NULL)
return 0;

if(auth_check_pass(pass, sp->sp_pwdp))
return 1;
}

// password in /etc/passwd
else
{
if(auth_check_pass(pass, pw->pw_passwd))
return 1;
}

return 0;
}


No hay comentarios: