Lokale Benutzerdatenbank unter Unix / Linux



  • Thema: Wie mache ich mir die lokale Benutzerdatenbank unter Unix / Linux zunutze?

    Für ein größeres, systemnahes Projekt unter Linux benötigte ich eine Benutzerdatenbank. Da es aber
    weit aus mehr Administrationsaufwand macht, die eigene Datenbank synchron mit der lokalen Userdatenbank
    unter Unix / Linux zu halten, habe ich mich entschlossen, eine Klasse zu schreiben, die mir einfachen
    und sicheren Zugriff auf das Benutzersystem gibt. Im folgenden setze ich gewisse Übung mit dem Umgang
    mit Unix / Linux voraus.

    In diesem FAQ Beitrag geht es um:

    1. Wie ist das Benutzersystem von Unix / Linux aufgebaut?
    2. Wie werte ich die Informationen aus?
    3. Die Klasse FUnixpasswords
    4. Was noch zu tun ist
    5. Beispiele
    6. Anhang (Vollst. Implementierung der Klasse)

    1) Wie ist das Benutzersystem von Unix / Linux aufgebaut?

    Die Benutzer werden unter Unix / Linux (ich werde im folgenden nur noch von Linux sprechen) mit
    sehr detaillierten Informationen gespeichert. Die wichtigsten Merkmale sind: Name, Passwort,
    UID (Benutzernummer, einmalig auf dem System), GID (Nummer der Gruppe, der er angehört),
    Heimatverzeichnis und Shell (oder ein anderes Programm. Dieses wird aufgerufen, wenn sich der
    Benutzer einloggt). Diese und weitere Informationen werden in 2 Dateien gespeichert, die
    üblicherweise einen festen Platz im System haben: /etc/passwd und /etc/shadow. Des weiteren
    finden wir noch die Datei /etc/group.

    Die /etc/passwd ist aus Zeilen aufgebaut, die vom Gerüst so aussehen:

    Benutzername❌UID:GID:Kommentar:Heimatverzeichnis:Programm
    Korbinian❌500💯Korbinians Account:/home/korbinian:/bin/bash

    An der Stelle, an der x steht, kann auch nichts stehen. Dann hat der Benutzer kein Passwort (selten, da sehr unsicher). Steht ein 'x', so wird das Passwort
    in der /etc/shadow gespeichert.

    Die /etc/shadow besteht aus solchen Zeilen:

    Benutzername:VerschlüsseltesPasswort:LetzteÄnderung:MinGültigkeit:MaxGültigkeit:Vorwarnung:Verfall:UnbenutztesFlag

    Wichtig hierbei ist, dass der Name der selbe ist, wie in der /etc/passwd. Die letzte
    Änderung wird in Tagen seit dem 01.01.1970 (offizieller Entstehungstag von Unix) gezählt.
    Min- und MaxGültigkeit werden in Tagen angegeben. Damit kann man kontrollieren, dass kein
    Account länger besteht als erlaubt (oft wichtig für Netzwerkanwendungen). Vorwarnzeit
    (in Tagen) gibt die Zeitspanne an, in der der Benutzer ermahnt wird, sein Passwort zu ändern. Nach dem
    Ablauf des bei Verfall angegebenen Datums ist der Account verloren, der Sysadmin muss ihn wieder
    herstellen. Die letzte Flag ist noch unbenutzt.

    Die /etc/group enthält eine Zuordnungstabelle der Gruppen.

    Gruppenname:GruppenPasswort:NumerischeID:Liste_der_Benutzer
    users❌100:korbinian, sebastian, barbara

    Das Passwort ist unbenutzt (habe noch kein System gesehen, wo es verwendet wird, drum lass
    ich es außer acht). Wenn sich jetzt ein Benutzer anmeldet (login), dann macht das Programm folgendes:
    Es sucht erst nach einem Eintrag in der /etc/passwd, in dem der eingegebene Name steht, und dann
    nach der entsprechenden Zeile in /etc/shadow. Nun wird das eingegebene Passwort verschlüsselt
    (dazu später mehr) und mit dem aus /etc/shadow verglichen. Stimmen sie überein, wird der Anmeldevorgang
    fortgesetzt, andernfalls der Zugriff verweigert. Anschließend wird das Programm ausgeführt, dass in
    /etc/passwd spezifiziert worden ist (meist /bin/bash).

    2) Wie werte ich die Informationen aus?

    Nachdem wir jetzt einen Überblick über die Funktion der Benutzerverwaltung von Linux haben,
    können wir daran gehen, diese Dateien auszuwerten. Das grobe Prinzip ist einfach: Zeilenweises
    Auslesen der 3 Dateien und sichern der Informationen im Speicher. Dann kann man sich User zeigen
    lassen, bearbeiten und neu erstellen. Anschließend müssen diese 3 Dateien wieder neu geschrieben
    werden, damit die Änderungen wirksam werden.

    VORSICHT: Obwohl die Funktionsweise, die ich hier vorstelle, einwandfrei funktioniert,
    gebe ich keine Garantie dafür ab, dass sie auf anderen Systemen nicht funktioniert. Leichtsinnige
    Änderungen in den Dateien (z.B.: User root löschen) sind dringendst zu vermeiden!

    3) Die Klasse FUnixpasswords

    Diese Klasse bringt nun alles (Auslesen, Speichern, Bearbeite/Lösche/Neuanlegen,
    Zurückschreiben) unter einen Hut. Sie ist getestet und funktioniert. Ich will im Folgenden die
    Schritte erläutern, wie ich zu diesem Ergebnis gekommen bin. Dazu erst mal einige theoretische
    Überlegungen (sollte man übrigens immer erst machen 🙂

    Was muss unsere Klasse können, wenn sie fertig ist?
    - Schneller, sicherer Zugriff auf vorhandene Benutzer
    - Leichtes ändern von Benutzerdaten
    - Debug Möglichkeiten

    Wie realisiert man einen schnellen Zugriff? Genau, man speichert einfach die ganzen Daten im RAM.
    Soweit so gut. Wäre da nicht das Problem: Was ist, wenn jemand parallel zu uns in den Dateien
    arbeitet? Dann machen wir alles kaputt. Ergo: wir bauen eine Kontrollfunktion ein, die
    überprüft, ob die Datei geändert worden ist. Diese wird dann vor jeder Aktion aufgerufen,
    die z.B.: Benutzerinformationen haben will, und lädt, falls erforderlich, die Dateien neu
    in den Speicher.
    Sicherer Zugriff? Ganz einfach: Idiotensicher programmieren. Will heißen dass sämtliche
    Änderungen an Benutzerdatem im Speicher über Set Methoden gemacht werden, das verschafft
    Kontrolle über Usereingaben. Debug Möglichkeiten baut man am besten mit Include Guards (DEBUG) ein.
    Die kann man dann ganz locker dem g++ mit der Option -DDEGBUG übergeben.
    Ein letzter Schritt Theorie noch: Die Benutzerdaten speichert man am besten in einer
    separaten Klasse, welche die einzelnen Felder aus den Dateien (Name, Passwort, etc) speichert.
    Um einen schnellen Zugriff zu bekommen, werden die Instanzen dieser Klassen in einer Map gespeichert,
    die dann über den Namen des Users als Key (wer die map nicht kennt, www.cppreference.com)) schnell den zugehörigen User liefert.

    Jetzt aber mal etwas Code!

    funixpasswords.h:

    /******************************************************************************/
    /* Library: FUnixpasswords                                                    */
    /* -------------------------------------------------------------------------  */
    /* Description: This library gives access to the unix user files to           */
    /* have an easy and secure port for administrating the                        */
    /* system. It is designed for the use with GUIs and programs                  */
    /* the require the authentification system of a local unix                    */
    /* system.                                                                    */
    /* -------------------------------------------------------------------------  */
    /* Files: funixpasswords.h (this one), funixpasswords.cpp                     */
    /* Version: 0.8                                                               */
    /* Author: Korbinian Riedhammer                                               */
    /* email: faulerhund@faulerhund.net                                           */
    /* www: http://www.faulerhund.net                                              */
    /* License: GPL                                                               */
    /******************************************************************************/
    #ifndef __FUNIXPASSWORDS__    
    #define __FUNIXPASSWORDS__
    #include <map>
    #include <vector>
    #include <string>
    #include <fstream>
    #include <sys/types.h>    // Brauchen wir für die Überprüfung
    #include <sys/stat.h>     // des letzten Schreibzugriffs
    #include <sys/time.h>
    #include <time.h>
    #ifdef DEBUG              // Zum Debuggen brauchen wir Ausgabe!
    #include <iostream>
    #endif
    class FUserentry {  // Diese Klasse speichert alle Informationen über einen user
        public:        
            FUserentry(std::string passwdline, std::string shadowline);
            bool validatepasswd(std::string plaintext);
            // Set/Get Methoden
            std::string get_passwdline() const; // Zum Neuschreiben der passwd
            std::string get_shadowline() const; // Zum Neuschreiben der shadow
            std::string get_name() const { return m_name; }
            std::string get_cryptedpasswd() const { return m_crypted_passwd; }
            std::string get_uid() const { return m_uid; }
            std::string get_gid() const { return m_gid; }
            std::string get_comment() const { return m_comment; }
            std::string get_homedir() const { return m_homedir; }
            std::string get_shell() const { return m_shell; }
            void set_passwd(std::string plaintextpasswd);
            void set_uid(std::string uid) { m_uid = uid; }
            void set_gid(std::string gid) { m_gid = gid; }
            void set_comment(std::string comment) { m_comment = comment; }
            void set_homedir(std::string homedir) { m_homedir = homedir; }
            void set_shell(std::string shell) { m_shell = shell; }
            void mark_deleted() { is_deleted = true; }
        private:
            // Userdaten
            FUserentry() {}    // Verhindert ein "normales" erstellen
            std::string m_name; // Benutzername
            std::string m_passwd; // Passwort, unverschlüsselt (meist 'x')
            std::string m_crypted_passwd; // Passwort, verschlüsselt
            std::string m_uid; // User ID, numerischer Wert
            std::string m_gid; // Gruppen ID, numerischer Wert
            std::string m_comment; // Kommentar
            std::string m_homedir; // Heimatverzeichnis
            std::string m_shell; // Shell
            // Weitere Einträge in shadow, jedoch noch nicht bearbeitbar in dieser Klasse
            std::string m_lastchange;
            std::string m_min;
            std::string m_max;
            std::string m_warn;
            std::string m_inactive;
            std::string m_expire;
            std::string m_flag;
            // Weitere Flags/Variablen/Funktionen
            bool is_deleted; // Soll der User gelöscht werden?
            void newencrypt(std::string &plaintext);  // soll ein neues Passwort erstellt werden?
            void oldencrypt(std::string &plaintext);  // soll ein altes überprüft werden?
            int getrnd(int x, int y);                 // erstellt eine Zufallszahl (für Verschlüsseung)
    }; 
    
    typedef map < std::string, FUserentry > UserMap;
    typedef map < std::string, std::string > GroupMap;
    
    class FUnixpasswords { // Kontrollstruktur
        public:
            FUnixpasswords();
            FUnixpasswords(std::string passwd, std::string group, std::string shadow);
            bool validate_user(std::string name, std::string passwd);
            FUserentry &get_user(std::string name, bool &flag);
            int applychanges();
            #ifdef DEBUG
            void displaymap();
            #endif
            std::string gid2group(std::string gid) { return groups[gid]; }
        private:
            std::string m_file_passwd;
            std::string m_file_group;
            std::string m_file_shadow;
            int refresh();
            bool is_refresh_needed();
            void setlastwrite();
            time_t m_lastwrite_passwd;
            time_t m_lastwrite_shadow;
            time_t m_lastwrite_group;
    
            UserMap users; 
            GroupMap groups; // groups["gid"] == "name", z.B.: groups["0"] == "root"
    };
    #endif
    

    Jetzt langsam. Ich geh jetzt nur auf die wichtigsten Funktionen und Abläufe ein. Freaks können
    sich dann unten die ganze Implementation anschauen (ist aber nicht so gut kommentiert 😉 An die
    Kritiker, die mosern, dass Weder CpyCtor noch Zuweisungsop fehlen: Da brauchts keinen speziellen 🙂
    Die Klasse FUserentry ist weitgehend selbsterklärend. Hier werden einfach alle Infos über diesen
    User zusammengetragen und gespeichert (wie bei der Stasi ;).Im Konstruktor wird ordentlich
    geparst (siehe Implementation) und Anschließend gespeichert. Wichtig: Der Defaultkonstruktor wurde
    unterbunden. Nur wer über (gültige!) Zeilen aus /etc/passwd und /etc/shadow verfügt,
    darf auch erstellen. Das aber macht Kontrollstruktur FUnixpasswords. Die Sicherungsfunktionen bei
    FUserentry sind noch nicht eingebaut, da das nicht so wichtig ist, dort zu kontrollieren,
    weil die Informationen eigentlich nur von FUnixpasswords kommen , und damit korrekt sein sollten.

    [cpp]bool validatepasswd(std::string passwd); [/code]

    Diese Funktion übernimmt für uns das überprüfen der Benutzerdaten. Sie nimmt das im Plaintext
    vorliegende Passwort, verschlüsselt es, und überprüft es mit dem Vorhandenen.
    Bei Übereinstimmung true, sonst false.

    [cpp]void mark_deleted();[/code]

    Einmal aufgerufen, setzt sie das Lösch-Flag auf true, und der User wird nicht wieder in die
    Datenbank zurückgeschrieben. Damit wird er gelöscht.
    [cpp]
    void newencrypt(std::string &plaintext);
    void oldencrypt(std::string &plaintext);[/code]
    Diese Funktionen übernehmen die Verschlüsselung von Plaintext. Sie benutzen den Unix Befehl
    char *crypt(const char*, char *); siehe [man]crypt(3)[/man]

    Die Klasse FUnixpasswords ist die Kontrollstruktur. Sie erhält über den Konstruktor die
    Positionen der Dateien passwd, shadow und group (Default: /etc/passwd, /etc/shadow, /etc/group).
    Sie liest dann die Dateien aus, ordnet nach Namen zu, und füllt damit die Map auf.
    Wichtig: solange man nur Lesezugriff macht (also nicht applychanges aufruft)
    passiert diesen Dateien nichts.
    Die Funktion validate_user übernimmt das schnelle Überprüfen von Benutzerdaten. Über get_user
    bekommt man eine Referenz auf den angegebenen User. Falls dieses fehlschlägt (der Benutzer
    also nicht vorhanden ist) ist die übergebene Flag false (SEHR wichtig, wg Speicherfehlern...).
    Wie das Funktioniert, sieht man am besten im Beispiel unten.
    Die Funktion applychanges
    speichert die aktuellen Daten in die Dateien zurück.
    displaymap ist für Debug Zwecke, und zeigt den kompletten Inhalt der Map, also
    damit alle user auf dem System an.

    4) Was noch zu tun ist

    Es fehlt noch ne ganze Menge in der Klasse:
    - Auflistungssystem aller User (über const_iteratoren)
    - Gruppenadminsystem
    - Neuerstellen der User, Defaultparameter (sind nämlich ganz schön viele 😉
    - Sicherungsfunktionen, die vor Speicherlecks und Fehlinputs schützen
    - z.B. Implementation von [man]getpwnam(3)[/man]

    5) Beispiele
    Ein Login Klon:
    [cpp]
    #include <iostream>
    #include <string>
    #include "funixpasswords.h"
    using namespace std;

    int main(int argc, char **argv)
    {
    FUnixpasswords pwds("/etc/passwd.old", "/etc/group.old", "/etc/shadow.old");
    // Zur Sicherheit nur die Backupdateien!
    string name, pwd;
    cout << "Geben Sie ihren Loginnamen an: ";
    cin >> name;
    cout << "Geben Sie ihr Passwort ein: ";
    cin >> pwd;
    if ( !pwds.validate_user(name, pwd) )
    {
    cout << "Falsche Daten." << endl;
    return 0;
    }
    cout << "OK" << endl;
    // Hier müsste man noch die Shell ausführen, das spar ich mir 🙂
    return 1;
    }[/cpp]
    Ein passwd Klon:

    #include <iostream>
    #include <string>
    #include "funixpasswords.h"
    using namespace std;
    
    int main(int argc, char **argv)
    {
        if (argc != 2)
        {
            cout << "usage: " << argv[0] << " username" << endl;
            return 0;
        }
        FUnixpasswords pwds("/etc/passwd.old", "/etc/group.old", "/etc/shadow.old");
        // Zur Sicherheit nur die Backupdateien!
        string pwd_old, pwd_new1, pwd_new2;
        cout << "Altes Passwort eingeben: ";
        cin >> pwd_old;
        cout << "Neues Passwort eingeben: ";
        cin >> pwd_new1;
        cout << "Passwort eingeben (nochmal): ";
        cin >> pwd_new2;
        if (pwd_new1 != pwd_new2)
        {
            cout << "Passwörter stimmen nicht überein" << endl;
            return 0;
        }
        if ( !pwds.validate_user(argv[1], pwd_old) )
        {
            cout << "Falsche Daten." << endl;
            return 0;
        }
        bool flag;
        FUserentry &user = pwds.get_user(argv[1], flag);
        if (!flag)
        {
            cout << "Unbekannter Benutzer" << endl;
            return 0;
        }
        user.setpwd(pwd_new1);
        pwds.applychanges();
        cout << "Passwort geändert" << endl;
        return 1;
    }
    

    Welche Benutzer sind auf dem System? (kompilieren mit -DDEBUG, spuckt ausserdem noch die Arbeitsgänge beim Parsen aus)

    #include <iostream>
    #include <string>
    #include "funixpasswords.h"
    using namespace std;
    
    int main(int argc, char **argv)
    {
        FUnixpasswords pwds; // Hier kann man bedenkenlos die echten Dateien nehmen, es wird nicht geschrieben.
        pwds.displaymap();
        return 1;
    }
    

    6) Anhang (Vollst. Implementierung der Klasse)

    /******************************************************************************/
    /*  Library:        FUnixpasswords                                            */
    /*  ------------------------------------------------------------------------- */
    /*  Description:    This library gives access to the unix user files to       */
    /*                  have an easy and secure port for administrating the       */
    /*                  system. It is designed for the use with GUIs and programs */
    /*                  the require the authentification system of a local unix   */
    /*                  system.                                                   */
    /*  ------------------------------------------------------------------------- */
    /*  Files:          funixpasswords.h, funixpasswords.cpp (this one)           */
    /*  Version:        0.8                                                       */
    /*  Author:         Korbinian Riedhammer                                      */
    /*                  email: faulerhund@faulerhund.net                          */
    /*                  www:   http://www.faulerhund.net                           */
    /*  License:        GPL                                                       */
    /******************************************************************************/
    #include "funixpasswords.h"
    #include <map>
    #include <vector>
    #include <string>
    #include <fstream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/time.h>
    #include <unistd.h>
    #include <crypt.h>
    #ifdef DEBUG
    #include <iostream>
    #endif
    using namespace std;
    FUnixpasswords::FUnixpasswords() 
     : m_file_passwd("/etc/passwd"), m_file_group("/etc/group"), m_file_shadow("/etc/shadow")
    {
        setlastwrite();
        refresh();
        srand((unsigned)time(NULL));
    }
    FUnixpasswords::FUnixpasswords(string passwd, string group, string shadow) 
     : m_file_passwd(passwd), m_file_group(group), m_file_shadow(shadow)
    {
        setlastwrite();
        refresh();
    }
    bool FUnixpasswords::is_refresh_needed()
    {
        struct stat stat_passwd, stat_shadow, stat_group;
        stat(m_file_passwd.c_str(), &stat_passwd);
        stat(m_file_shadow.c_str(), &stat_shadow);
        stat(m_file_group.c_str(), &stat_group);
        if (m_lastwrite_passwd != stat_passwd.st_mtime)   return true;
        if (m_lastwrite_shadow != stat_shadow.st_mtime)   return true;
        if (m_lastwrite_group != stat_group.st_mtime)     return true;
        return false;
    }
    void FUnixpasswords::setlastwrite()
    {
        struct stat stat_passwd, stat_shadow, stat_group;
        stat(m_file_passwd.c_str(), &stat_passwd);
        stat(m_file_shadow.c_str(), &stat_shadow);
        stat(m_file_group.c_str(), &stat_group);
        m_lastwrite_passwd = stat_passwd.st_mtime;
        m_lastwrite_shadow = stat_shadow.st_mtime;
        m_lastwrite_group = stat_group.st_mtime;
    }
    bool FUnixpasswords::validate_user(string name, string passwd)
    {
        if (is_refresh_needed())    refresh();
        if (users.find(name) == users.end())    return false;
        return users[name].validatepasswd(passwd);
    }
    FUserentry &FUnixpasswords::get_user(string name, bool &flag)
    {
        if (is_refresh_needed())
        {
            #ifdef DEBUG
            cout << "Refresh erforderlich" << endl;
            #endif
            refresh();
        }
        flag = (users.find(name) != users.end());
        return users[name];
    }
    bool FUnixpasswords::updateuser(std::string name, FUserentry &user)
    {
        if (name != user.get_name())    return false;
        if (users.find(name) == users.end())    return false;
        users[name] = user;
        return true;
    }
    int FUnixpasswords::applychanges()
    {
        ofstream fpasswd(m_file_passwd.c_str()), fshadow(m_file_shadow.c_str());
        if (!fpasswd.is_open() ||!fshadow.is_open())    return -1;
        for (UserMap::const_iterator iter = users.begin(); iter != users.end(); iter++)
        {
            fpasswd << iter->second.get_passwdline() << endl;
            fshadow << iter->second.get_shadowline() << endl;
        }
        fpasswd.close();
        fshadow.close();
    }
    int FUnixpasswords::refresh()
    {
        users.clear();
        groups.clear();
        map < string , string > passwdlines, shadowlines;
        vector < string > names;
        ifstream fshadow(m_file_shadow.c_str()), fpasswd(m_file_passwd.c_str()), fgroup(m_file_group.c_str());
        if (!fshadow.is_open() ||!fpasswd.is_open() || !fgroup.is_open())   return 0;
        #ifdef DEBUG
        int zeilencounter = 0;
        cout << "DEBUG: Lese " << m_file_passwd << " aus..." << endl;
        #endif
        while (!fpasswd.eof())
        {
            #ifdef DEBUG
            zeilencounter++;
            #endif
            string zeile, name;
            getline(fpasswd, zeile);
            if (zeile.length() < 1) continue;
            for (int i = 0; i < zeile.length(); i++)
            {
                if (zeile[i] == ':')    break;
                name += zeile[i];
            }
            names.push_back(name);
            passwdlines[name] = zeile;
            #ifdef DEBUG
            cout << "DEBUG: " << m_file_passwd << ", Zeile " << zeilencounter << ": Name: " << name 
            << " Zeile: " << zeile << endl;
            cout << "DEBUG: passwdlines[" << name << "] == " << passwdlines[name] << endl;
            #endif
        }
        #ifdef DEBUG
        cout << "DEBUT: ...fertig" << endl;
        zeilencounter = 0;
        cout << "DEBUG: Lese " << m_file_shadow << " aus..." << endl;
        #endif
        while (!fshadow.eof())
        {
            #ifdef DEBUG
            zeilencounter++;
            #endif
            string zeile, name;
            getline(fshadow, zeile);
            if (zeile.length() < 1) continue;
            for (int i = 0; i < zeile.length(); i++)
            {
                if (zeile[i] == ':')    break;
                name += zeile[i];
            }
            shadowlines[name] = zeile;
            #ifdef DEBUG
            cout << "DEBUG: " << m_file_shadow << ", Zeile " << zeilencounter << ": Name: " << name 
            << " Zeile: " << zeile << endl;
            cout << "DEBUG: shadowlines[" << name << "] == " << shadowlines[name] << endl;
            #endif
        }
        #ifdef DEBUG
        cout << "DEBUG: ...fertig" << endl;
        cout << "DEBUG: Lese " << m_file_group << " aus..." << endl;
        #endif
        while (!fgroup.eof())
        {
            string zeile, name, gid;
            getline(fgroup, zeile);
            if (zeile.length() < 1) continue;
            int i;
            for (i = 0; i < zeile.length(); i++)
            {
                if (zeile[i] == ':')    break;
                name += zeile[i];
            }
            ++i;
            for (; i < zeile.length(); i++) if (zeile[i] == ':')    break;
            ++i;
            for (; i < zeile.length(); i++)
            {
                if (zeile[i] == ':')    break;
                gid += zeile[i];
            }
            groups[gid] = name;
            #ifdef DEBUG
            cout << "DEBUG: name: " << name << " gid: " << gid << endl;
            #endif
        }
        #ifdef DEBUG
        cout << "DEBUG: ...fertig" << endl;
        cout << "DEBUG: Namensliste start" << endl;
        for (int i = 0; i < names.size(); i++)
            cout << "DEBUG: *** " << names[i] << endl;
        cout << "DEBUG: Namensliste ende" << endl;
        cout << "DEBUG: Speichere in Klassenweiter Map..." << endl;
        #endif
        for (int i = 0; i < names.size(); i++)
        {
            FUserentry tmp(passwdlines[names[i]], shadowlines[names[i]]);
            users[names[i]] = tmp;
        }
        #ifdef DEBUG
        cout << "DEBUG: ...fertig" << endl;
        cout << "DEBUG: Ausgabe der eingelesenen User..." << endl;
        displaymap();
        cout << "DEBUG: ...fertig" << endl;
        #endif
        return 1;
    }
    #ifdef DEBUG
    void FUnixpasswords::displaymap()
    {
        cout << "DEBUG: Beginn Inhalt der Map users" << endl;
        for (UserMap::const_iterator iter = users.begin(); iter != users.end(); iter++)
        {
             string name = iter->first;
             FUserentry tmp = iter->second;
             cout << "DEBUG: user " << name << endl;
             cout << "DEBUG: ** name: " << tmp.get_name() << endl;
             cout << "DEBUG: ** crypted pwd: " 
                  << tmp.get_cryptedpasswd() << endl;
             cout << "DEBUG: ** uid: " << tmp.get_uid() << endl;
             cout << "DEBUG: ** gid: " << tmp.get_gid() << endl;
             cout << "DEBUG: ** comment: " << tmp.get_comment() << endl;
             cout << "DEBUG: ** homedir: " << tmp.get_homedir() << endl;
             cout << "DEBUG: ** shell: " << tmp.get_shell() << endl;
        }
        cout << "DEBUG: Ende Inhalt der Map users" << endl;
    }
    #endif
    FUserentry::FUserentry(string passwdline, string shadowline)
    {
        // Zeile aus passwd parsen -> name, uid, gid, comment, homedir, shell
        int i = 0;
        for (;; i++)
        {
            if (passwdline[i] == ':')   { ++i; break; }
            m_name += passwdline[i];
        }
        for (;; i++)
        {
            if (passwdline[i] == ':')   { ++i; break; }
            m_passwd += passwdline[i];
        }
        for (;; i++)
        {
            if (passwdline[i] == ':')   { ++i; break; }
            m_uid += passwdline[i];
        }
        for (;; i++)
        {
            if (passwdline[i] == ':')   { ++i; break; }
            m_gid += passwdline[i];
        }
        for (;; i++)
        {
            if (passwdline[i] == ':')   { ++i; break; }
            m_comment += passwdline[i];
        }
        for (;; i++)
        {
            if (passwdline[i] == ':')   { ++i; break; }
            m_homedir += passwdline[i];
        }
        for (;i < passwdline.length(); i++)
        {
            m_shell += passwdline[i];
        }
        // Zeile aus shadow parsen
        i = 0;
        for (;; i++)
        {
            if (shadowline[i] == ':')   { ++i; break; } // Benutzer interessiert nicht
        }
        for (;; i++)
        {
            if (shadowline[i] == ':')   { ++i; break; }
            m_crypted_passwd += shadowline[i];
        }
        for (;; i++)
        {
            if (shadowline[i] == ':')   { ++i; break; }
            m_lastchange += shadowline[i];
        }
        for (;; i++)
        {
            if (shadowline[i] == ':')   { ++i; break; }
            m_min += shadowline[i];
        }
        for (;; i++)
        {
            if (shadowline[i] == ':')   { ++i; break; }
            m_max += shadowline[i];
        }
        for (;; i++)
        {
            if (shadowline[i] == ':')   { ++i; break; }
            m_warn += shadowline[i];
        }
        for (;; i++)
        {
            if (shadowline[i] == ':')   { ++i; break; }
            m_inactive += shadowline[i];
        }
        for (;; i++)
        {
            if (shadowline[i] == ':')   { ++i; break; }
            m_expire += shadowline[i];
        }
        for (;i < shadowline.length(); i++)
        {
            m_flag += shadowline[i];
        }
    }
    string FUserentry::get_passwdline() const
    {
        string zeile = m_name;
        zeile += ':';
        zeile += m_passwd;
        zeile += ':';
        zeile += m_uid;
        zeile += ':';
        zeile += m_gid;
        zeile += ':';
        zeile += m_comment;
        zeile += ':';
        zeile += m_homedir;
        zeile += ':';
        zeile += m_shell;
        return zeile;
    }
    string FUserentry::get_shadowline() const
    {
        string zeile = m_name;
        zeile += ':';
        zeile += m_crypted_passwd;
        zeile += ':';
        zeile += m_lastchange;
        zeile += ':';
        zeile += m_min;
        zeile += ':';
        zeile += m_max;
        zeile += ':';
        zeile += m_warn;
        zeile += ':';
        zeile += m_inactive;
        zeile += ':';
        zeile += m_expire;
        zeile += ':';
        zeile += m_flag;
        return zeile;
    }
    bool FUserentry::validatepasswd(string plaintext)
    {
        oldencrypt(plaintext);
        return (plaintext == m_crypted_passwd);
    }
    void FUserentry::set_passwd(string plaintext)
    {
        newencrypt(plaintext);
        m_crypted_passwd = plaintext;
    }
    void FUserentry::newencrypt(string &plaintext)
    {
        string set = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
        string plain = plaintext;
        char seed[] = {set[getrnd(0, (set.length()-1))], set[getrnd(0, (set.length()-1))] };
        char *pwd = crypt(plaintext.c_str(), seed);
        plaintext = pwd;
    }
    void FUserentry::oldencrypt(string &plaintext)
    {
        string plain = plaintext;
        char seed[] = {m_crypted_passwd[0], m_crypted_passwd[1]};
        char *pwd = crypt(plaintext.c_str(), seed);
        plaintext = pwd;
    }
    int FUserentry::getrnd(int x, int y)
    {
        if (x > y)
        {
            int tmp = y;
            y = x;
            x = tmp;
        }
        y += 1;
        return ((rand() % (y - x)) + x);
    }
    

Anmelden zum Antworten