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: