Imitar el comando adduser de Linux en C

La programación por diversión también puede manifestarse en los trabajos de la práctica de codificación pausada. Independientemente de ser académicamente inadecuado, la tarea ciertamente contribuye a que el programador comprenda el ingenio del lenguaje. Aquí veamos una actividad de programación distinta que también se traduce en un ejercicio de construcción de conocimiento y fluidez de bajo nivel.

Declaración del problema: imite el comando adduser en Linux para crear un usuario ordinario o del sistema usando el programa C como lenguaje de programación. Esto es lo que la pregunta exige implícitamente como solución para que el programador se adhiera a la ejecución formal del comando:

  • Se debe proporcionar una replicación precisa de la CLI en un terminal Linux durante la ejecución.
  • Uno debe manejar un archivo passwd .
  • También se debe manejar un archivo shadow .
  • La ejecución de ambos archivos debe regirse por la especificación de la variable de entorno PFILE .

Requisitos previos: echemos un vistazo rápido a los requisitos previos antes de comenzar a trabajar en el código. Primero, comience por captar la idea detrás del archivo passwd . Además, no dude en consultar las páginas de manual de Linux.

1. El archivo passwd: Técnicamente representado como el archivo /etc/passwd, el propósito principal de este archivo es monitorear o almacenar un registro de usuarios registrados a quienes se les ha otorgado acceso al sistema. Cuenta con un registro de 7 campos importantes.
 

passwd file

UID es esencialmente la ID de usuario y GID es la ID de grupo. Por su parte, GECOS es el campo que se utiliza para almacenar el nombre real y el número de teléfono del usuario. El Shell de inicio de sesión es un campo responsable de leer archivos y configurar las variables de entorno .

Cada uno de estos campos está separado por dos puntos. A los efectos de este programa, los detalles se almacenarán hasta el campo GECOS. No obstante, se alienta a los lectores a explorar más a fondo para incorporar también otros campos.

2. El archivo shadow: Formalmente definido como el archivo /etc/shadow, este archivo juega un papel similar al archivo passwd. Sin embargo, se centra en todo lo relacionado con la contraseña cifrada en el archivo passwd. A diferencia del archivo passwd, solo el usuario raíz puede leerlo. Por lo general, presenta un registro de 9 campos.

shadow file

La contraseña debe permanecer en su formato cifrado; aquí hay una parte interesante en la que desarrollaremos nuestro propio algoritmo de cifrado (uno básico). Los campos 3 y 4 se refieren a la brecha mínima y máxima en días entre cambios de contraseña (0 por defecto). Los campos 5 y 6 definen cuánto tiempo antes de que el usuario caduque la contraseña y deshabilite las notificaciones de la cuenta. Todos estos campos son completamente personalizables. Recuerde cifrar todos los datos que ingresan a este archivo.

3. El PFILE ENV: El propósito de las variables de entorno es afectar la forma en que un programa o software se comportaría de otro modo. Por ejemplo, la variable $LANG le comunicaría al software que el usuario entiende un idioma en particular y, por lo tanto, debe proporcionar el mismo idioma en el nivel de la interfaz. Aquí usaremos una variable PATH para imitar la ubicación en la que el sistema busca los archivos passwd y shadow. Le asignaremos el nombre PFILE.

Opciones de comando: al escribir useradd en la terminal, se acostumbra especificar otro valor conocido como ‘opción’. Las opciones pueden ser varias letras. Sin embargo, para limitar este programa, utilice únicamente las opciones  -r y -m .

  • La opción -r crea un usuario del sistema.
  • La opción -m asocia un directorio de inicio con la cuenta del usuario.

Sintaxis:

-m command

Nota:

  • useradd tiene 7 caracteres.
  • useradd va seguido de un espacio y luego el nombre de usuario que se extiende hasta que se alcanza el siguiente carácter de espacio.
  • Después del segundo espacio, el carácter es un guión y el carácter de opción.
  • Una vez que se ingresa el comando, se establece la contraseña.

A continuación se muestran los pasos para implementarlo usando el programa C:

1. El main.c: aquí, especifique todos los archivos de encabezado y las variables y realice todas las llamadas a funciones. Servirá como modelo para el compilador.

main.c

Un vistazo rápido al diseño del código a seguir.

Aquí, defina una estructura que contendrá todos los datos que deben actualizarse. Cualquier otro método introduciría líneas de código adicionales y dificultaría el rastreo del código. Además, tómalo como un gran consejo para cambiar siempre el nombre de la estructura que creas. Cuando se usa struct como parámetros y tipos de devolución, no cambiar el nombre causará cantidades sustanciales de errores de referencia e identificadores durante la compilación.

int main() es la base donde se llaman todas las funciones y se carga el procedimiento. void files_and_env() es donde la variable PATH se asigna a PFILE e inicializa los archivos. Recuerde insertar una ubicación o ruta válida de los archivos creados para f1 y f2. Continuando, Struct chk_getU_op() es una función que verifica el comando de entrada en la terminal, obtiene el nombre de usuario del comando y la opción. Devuelve un objeto de tipo struct

Nota: Use una S mayúscula en Struct (al declarar el tipo de devolución) ya que typedef se usa para cambiar el nombre de la estructura en la línea 12. Finalmente, void setpass() solicita al usuario que asocie una contraseña con el nuevo usuario que se ha creado. Esta función está parametrizada, es decir, toma el objeto que se devolvió anteriormente desde chk_getU_op() .

A continuación se muestra el programa C para implementar el enfoque anterior:

C

// C program to implement
// the above approach
#include "server.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct name_opt {
    char username[32];
    char opt;
    char password[100];
};
  
typedef struct name_opt Struct;
  
// Explicit declaration
void files_and_env(void);
void setpass(Struct);
Struct chk_getU_op(void);
  
// Driver code
int main()
{
    Struct fn;
  
    // Additional character denoting
    // the option
    char opt;
  
    // Function to declare files and
    // set environment variables
    files_and_env();
    fn = chk_getU_op();
    setpass(fn);
    return (0);
}
  
void files_and_env()
{
    FILE *f1, *f2;
  
    // Assigning environment variable
    // PFILE for protecting and
    // restricting access
    putenv(
        "PFILE = -/Environment/rms");
  
    // Displays the path of the
    // environment variable
    printf("PFILE: %s\n",
           getenv("PFILE"));
    f1 = fopen("Location of file\\passwd.txt", "r");
    f2 = fopen("Location of file\\shadow.txt", "r");
}
  
Struct chk_getU_op()
{
    int i = 8;
    int ctr = 0;
    int x, l = 0;
    char command[] = { 'u', 's', 'e', 'r',
                       'a', 'd', 'd' };
  
    // Linux accepts only 42 char
    // usernames
    char FunctionCall[42];
    Struct send;
    gets(FunctionCall);
  
    for (x = 0; x < 7; x++) {
        if (FunctionCall[x] == command[x]) {
            ctr += 1;
        }
    }
    if (ctr != 7) {
        printf("Invalid command.\n");
        exit(0);
    }
    else {
        // Ends when space char is reached
        while ((int)FunctionCall[i] != 32) {
            send.username[i - 8] = FunctionCall[i];
            i++;
        }
        l = strlen(send.username);
        send.opt = FunctionCall[10 + l];
    }
    return send;
}
  
void setpass(Struct F)
{
    char pass[100];
    char UID[4];
    printf("Changing password for user.....\n");
    printf("New UNIX password: ");
    scanf("%s\n", *(F.password));
  
    if (F.opt == 'r') {
        printf("UID: ");
        scanf("%s", UID);
        if (strlen(UID) == 2) {
            // Calling password function
            passwd(F.username, F.password,
                   UID);
  
            // Calling shadow function
            shadow(F.username, F.password,
                   UID);
        }
  
        // When the UID is not 2 characters long
        else {
            while (strlen(UID) != 2) {
                printf("Invalid UID. Retype UID: ");
                scanf("%s", UID);
            }
  
            // Calling password function
            passwd(F.username, F.password,
                   UID);
  
            // Calling shadow function
            shadow(F.username, F.password,
                   UID);
        }
    }
  
    // If the option is 'm'
    else {
        printf("UID: ");
        scanf("%s", UID);
        if (strlen(UID) <= 2) {
            printf("Normal Users not allowed this UID!\n");
            printf("Retype UID: ");
            scanf("%s", UID);
            passwd(F.username, F.password, UID);
            shadow(F.username, F.password, UID);
        }
        else {
            passwd(F.username, F.password, UID);
            shadow(F.username, F.password, UID);
        }
  
        // end of setpass
    }
  
    // end of main.c
}

2. El archivo passwd.c: aquí se almacenan los detalles del nombre de usuario que se acaba de crear. Los datos se escriben en el archivo passwd.txt. Use otra array declarada en la función para enmascarar el contenido de la contraseña que se ingresó anteriormente en la función setpass().

A continuación se muestra el programa C para implementar el enfoque anterior:

C

// C program to implement
// the above approach
#include "server.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
  
void passwd(char* username,
            char* password,
            char* UID)
{
    int len, k = 0;
  
    // Pointing to the password file
    // that will now be appended
    FILE* fs;
    fs = fopen("Location of the file\\passwd.txt",
               "+a");
    len = strlen(password);
    char temp_passwrd[len];
  
    for (k = 0; k < len; k++) {
        // Since the passwd may not
        // reveal user-sensitive info
        temp_passwrd[k] = '$';
    }
  
    fprintf(fs, "%s:%c:%s:%s\n", username,
            'x', temp_passwrd, UID);
    fclose(fs);
  
    // end of passwd.c
}

El archivo passwd.txt debe verse así después de la ejecución:

passwd.txt

3. El archivo shadow.c: como se mencionó anteriormente, la función principal de este archivo es cifrar la contraseña tal como se recibe como entrada desde el terminal. Los lectores deben experimentar con diferentes técnicas de cifrado en lugar de simples esquemas alfanuméricos. Sin embargo, desarrollar una técnica muy básica como la siguiente es un buen comienzo.

The encrypted character is the next character with its case reversed.

A continuación se muestra el programa C para implementar el enfoque anterior:

C

// C program to implement
// the above approach
#include "server.h"
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
  
void shadow(char* username,
            char* password,
            char* UID)
{
    char epass;
  
    // File pointer
    FILE* fp1;
  
    fp1 = fopen("Location of file\\shadow.txt",
                "+a");
  
    // Call the encryption function
    Encryption(epass, fp1, username, UID);
}
  
void Encryption(char* epass, FILE* fp1,
                char* username, char* UID)
{
    // Integer that will help in indexing
    int x = 0;
    char* e = epass;
    char encrypt[100];
  
    // While is preferred over for loops here
    while (*e) {
        if (islower(*e) == true) {
            if (*e == 'a') {
                // a becomes !
                encrypt[x] = '!';
            }
            else if (*e == 'z') {
                // z becomes %
                encrypt[x] = '%';
            }
            else {
                // Make capital
                encrypt[x] = toupper((char)((int)*e) + 1);
            }
        }
  
        // If we have upper case letters
        else {
            // Increment and make lower
            encrypt[x] = tolower((char)(((int)*e) + 1));
        }
        e++;
        x++;
  
        // end of while
    }
  
    // end of shadow.c
}

El cambio en el archivo shadow.txt será evidente como se muestra:

shadow file

Observe el encabezado server.h en los tres archivos de código fuente. Este es un archivo que hace referencia a todas las funciones en el archivo passwd.c y shadow.c. La razón detrás de incluir un archivo de este tipo es que el archivo main.c permanece informado de dónde se encuentran los archivos passwd, shadow y Encryption. Esto es lo que contiene el archivo server.h:

server.h

Una buena práctica para garantizar la ejecución secuencial y la compilación adecuada de varios archivos de código fuente es utilizar archivos MAKE . Makefiles ayuda a mantener todos los archivos de código fuente compilados en el punto de ejecución para que, al editar cualquier otro código fuente, otros archivos permanezcan listos para la ejecución. Se anima a los lectores a desarrollar este hábito.

Makefiles

Publicación traducida automáticamente

Artículo escrito por siddhant_baroth y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *