K
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:
Wie ist das Benutzersystem von Unix / Linux aufgebaut?
Wie werte ich die Informationen aus?
Die Klasse FUnixpasswords
Was noch zu tun ist
Beispiele
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:
BenutzernameUID:GID:Kommentar:Heimatverzeichnis:Programm
Korbinian500Korbinians 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
users100: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);
}