INN gegen MySQL Datenbank authentifizieren

Bei einer etwas grösseren Userzahl oder wenn man die Userverwaltung per PHP,
Java oder anderem Frontend, sowohl dem Admin alsauch den Usern (ihre eigenen
Accounts), ermöglichen möchte ist eine Authentifizierung gegen eine MySQL
Datenbank die bessere Methode als eine lokale Passwortdatei.
Und es ist auch gar nicht so schwer wie es hier auf den ersten Blick ausieht.


Ich gehe hier von einer Installation wie ich sie unter „INN 2.3.5“ beschrieben
habe aus. Desweiteren gehe ich davon aus das eine funktionsfähige MySQL Datenbank
sowie die benötigten Perlmodule „DBI“ und DBD::mysql“ installiert sind.

Zu Anfang muss erstmal die MySQL für news eingerichtet sein.
Ich habe mich für folgende Struktur der MySQL entschieden.
Datenbank: „userdb“
Table: „users“
Spalten: „userid“, „user“, „password“, „active“

userid: Eindeutige benutzer ID mit der man ggf. kreuzverweise in andere Datenbanken
machen kann. Das ist einfacher als mit Strings(wie dem Usernamen) zu arbeiten.
user : Username
pass : Passwort
active: Hiermit kann man den Benutzer Sperren ohne ihn komplett löschen zu müssen.
Erlaubte Werte: „1“(aktiv) und „0“(inaktiv).

Wenn man eine andere Struktur möchte oder bereits hat so sind die folgenden Queries
und die im Script weiter unten anzupassen. Wenn man auf die „active“-Funktion
verzichten will so muss man im Script auch noch die Variable „$activecheck“ auf
„0“ setzen.

In der MySQL ist nun also folgendes zu machen:
– Anlegen eines Users für news.
mysql> use mysql;
mysql> grant select on `userdb`.`users` to „news“@“localhost“ identified by „12345678“;
Wobei „localhost“ ggf. durch den host zu ersetzen ist auf dem der newsserver läuft.
Das „12345678“ ist auf jeden Fall durch ein geeignetes Passwort zu ersetzen und danach
auch so in das Script weiter unten in die Variable „$dbpw“ einzutragen.

– Erzeugen der Datenbank und der Table mit userid und user als unique Key.
mysql> create database userdb;
mysql> use userdb;

Nun gibt es zwei Möglichkeiten: MySQL „verwaltet“ die userids selbst
mysql> create table users (`userid` int not null auto_increment, `user` varchar(16) not null, `password` varchar(16) not null, `active` char(1) not null, primary key (userid));
Oder man gibt sie von extern vor:
mysql> create table users (`userid` varchar(16) not null, `user` varchar(16) not null, `password` varchar(16) not null, `active` char(1) not null);

mysql> alter table `users` add unique (`userid`);
mysql> alter table `users` add unique (`user`);

– User einrichten. Die Passwörter aus der /usr/local/news/db/users.db können
1:1 in die MySQL übernommen werden. Beispiel eines Testusers:
mysql> insert into `users` (`userid` , `user` , `password` , `active`) values (‚0000000000000001‘, ‚read‘, ’23OkswX5htm56′, ‚1‘);

– Testen ob man vom Rechner auf dem der INN läuft auch ein MySQL query auf die
Datenbank loslassen kann. Wenn nicht sind entsprechende Maßnahmen zu ergreifen.

Nun können wir uns dem INN selbst widmen.
Das folgende ist alles als User „news“ gemacht.

/usr/local/news/etc $ vi readers.conf

auth all {
auth: „ckpasswd.pl“
}

Der Ersatz für das bisherige „ckpasswd“ sieht folgendermassen aus:
ACHTUNG: mind. $dbhost und $dbpw sind anzupassen!
/usr/local/news/bin/auth/passwd $ vi ckpasswd.pl

#!/usr/bin/env perl
#
# INN ckpasswd replacement
# Authentication against MySQL Database
#
# Written by Sven Weise (sven@futzelnet.de)
# Covered under the same license as INN in general.
#
# Build DB:
# create database userdb;
# create table users (`userid` varchar(16) not null, `user` varchar(16) not null, `password` varchar(16) not null, `active` CHAR(1) not null);
# alter table `users` add unique (`userid`);
# alter table `users` add unique (`user`);
# grant select on `userdb`.`users` to "news"@"localhost" identified by "12345678";
# Example User:
# insert into `users` (`userid` , `user` , `password` , `active`) values ('0000000000000001', 'read', '23OkswX5htm56', '1');
#
# user          = Username
# password = Passwort
# active      = User active/inactive? (for temp. suspension)
##############################################################################################################################################

use strict;     # Or the inquisition will hunt you down
use DBI;

### Read User/Pass
my ($user,$pass);
while ( <STDIN> ) {
s/\n//;
s/\r//;
if ($_ =~ /^ClientAuthname: (.*)/) {$user=$1;}
if ($_ =~ /^ClientPassword: (.*)/) {$pass=$1;}
}
if (!$user || !$pass) {exit(502);}

# Secure input for MySQL-Query
$user =~ s/'//;
$user =~ s/;//;
$user =~ s/--//;
$pass =~ s/'//;
$pass =~ s/;//;
$pass =~ s/--//;
###

### DB Vars - EDIT ME!
my ($dbq,$rc,@row);
my $dbhost="localhost";
my $database="userdb";
my $table="users";
my $driver="mysql";
my $dbuser="news";
my $dbpw="12345678";
my $activecheck=1;
### Normaly ;-) nothing to edit below this point ###

### DB init
my $dbs="DBI:$driver:database=$database;host=$dbhost";
my $dbc=DBI->connect($dbs, $dbuser, $dbpw, { PrintError => 1 });
###

### Query database and disconnect.
$dbq=$dbc->prepare("select `password`, `active` from $table where `user`=?");
$dbq->execute($user);
@row = $dbq->fetchrow_array;
$rc=$dbq->finish;
$dbq=$dbc->disconnect;
###

### check password and respond appropriate
if (@row) {
if ($activecheck == 1) {
if ($row[1] == 0) {
exit(502); # "Authentication error"
}
}
if (crypt($pass, $row[0]) eq $row[0]) {
print "user: $usern"; # return based on what nnrpd want
exit(0);
} else {
exit(502); # "Authentication error"
}
} else {
exit(502); # "Authentication error"
}
### EOF ###


Das ganze ist hier auch nochmal zum download verfügbar.

/usr/local/news/bin/auth/passwd $ chmod +x ckpasswd.pl
# Änderungen wirksam werden lassen:
/usr/local/news/bin/auth/passwd $ ctlinnd reload all rekonfig

Schlussendlich dann noch mit einem echten User testen und Logfiles prüfen.

Die Schnittstelle Frontend->Mysql und das Frontend selber werde ich nicht
behandeln. Das möge jeder selbst, nach eigenem Gusto, bauen.