[Munich-pm] Frage zu HTTP, NTLM, LDAP

Rouchal, Marek marek.rouchal at intel.com
Fr Okt 5 02:47:17 PDT 2012


Hallo Harald,

vielen Dank fuer Deine Kommentare - spaetestens jetzt ist mir klar, dass ich diese Idee
(NTLM) mal lieber stecken lasse... habe HTTP::Daemon::SSL zum Laufen bekommen,
und da kann ich (einigermassen) guten Gewissens auch Basic auth machen - und das 
gegen LDAP checken. Funktioniert prima - anbei der Code, habe nur die Firmen-Spezifka
geloescht. Falls noch jemandem eine gute Idee dazu einfaellt - nur her damit :-)

Gruss,

Marek

#!/usr/bin/perl -w
use strict;
use warnings;
use HTTP::Daemon::SSL;
use HTTP::Status;
use MIME::Base64 qw(decode_base64);
use Net::Domain qw(hostfqdn);
use Log::Log4perl qw(:easy);
Log::Log4perl->easy_init($DEBUG); #  Authen::Simple::LDAP uses this
use Authen::Simple::LDAP;

my $server = HTTP::Daemon::SSL->new(
  LocalAddr => hostfqdn(),
  LocalPort => 8080,
) or die;
print "Contact URL: ", $server->url, "\n";

while (my $connection = $server->accept) {
  while (my $request = $connection->get_request) {
    print $request->as_string;
    my $auth = $request->header( 'Authorization' );
    if($auth && $auth =~ /Basic\s+(\S+)/) {
      if(authenticate($1)) {
        $connection->send_response( make_response() );
        print "SENT response\n\n";
        next;
      }
    }
    $connection->send_response( make_challenge() );
    print "SENT challenge\n\n";
  }
  $connection->close;
}

my %credentials; # cache for credentials
sub authenticate
{
  my $cred = shift;
  if(defined $credentials{$cred}) {
    return $credentials{$cred};
  }
  my $str = decode_base64($cred);
  if($str && $str =~ /^(\w+):(\S+)$/) {
    if(authen_ldap($1,$2)) {
      $credentials{$cred} = 1;
      # TODO check group membership here
      return 1;
    } else {
      $credentials{$cred} = 0;
      return 0;
    }
  } else {
    # cannot parse
    $credentials{$cred} = '';
    return;
  }
}

sub authen_ldap
{
  my ($user,$pw) = @_;
  my $ldap = Authen::Simple::LDAP->new( 
  # from /etc/ldap.conf
    host  => 'ldap.company.com',
    basedn  => 'ou=...,dc=...',
    'log' => Log::Log4perl->get_logger('Authen::Simple::LDAP'),
    #binddn => 
    #bindpw =>
   );
  if ( $ldap->authenticate( $user, $pw ) ) {
    print "authen_ldap: $user OK\n";
    return 1;
  }
  print "authen_ldap: $user NOT ok\n";
  return 0;
}

sub make_challenge {
  print "make_challenge\n";
  my $response = HTTP::Response->new(
    401 => 'Unauthorized',
    [ 'WWW-Authenticate' => 'Basic realm=Dom' ],
  );
}

sub make_response {
  print "make_response\n";
  my $response = HTTP::Response->new(
    200 => 'Ok',
    [ 'Content-Type' => 'text/plain' ],
  );
  $response->content( "Hallo!\n" );
  return $response;
}

-----Original Message-----
From: Harald Jörg [mailto:Harald.Joerg at arcor.de] 
Sent: Thursday, October 04, 2012 8:05 PM
To: Rouchal, Marek
Cc: Perl Mongers Munich
Subject: Re: [Munich-pm] Frage zu HTTP, NTLM, LDAP

Hallo Marek,

Du schreibst:

> ich habe heute mal eine Frage zum Thema Authenti(fi|)zierung?
> folgendes Szenario: Ich moechte einen einfachen Perl-basierten 
> HTTP-Server bereitstellen (HTTP::Deamon?  oder gibt?s was besseres?),

Für "einfach" gibt's HTTP::Server::Simple.  Dem muss man threading/forking beibringen, aber das ist bei HTTP::Daemon genauso.

> welcher allerdings eine halbwegs sichere Zugriffsbeschraenkung haben 
> sollte. Da wir hier alle Win-Kisten haben, und den InternetExplorer 
> benutzen ?duerfen?, habe ich an NTLM gedacht? etwa so:
>
>   * der HTTP-Server besteht auf NTLM (erste Response:
>   * der Client antwortet mit z.B. ?Authorization: NTLM
>     TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAGAbEdAAAADw==?
>     -- soweit bin ich schon gekommen, nun aber fehlt mir foldendes --
>   * der Server interpretiert diese Response, und checkt die
>     Credentials gegen LDAP(?), Samba, ?  wie auch immer, jedenfalls
>     gegen die zentrale User-DB

Tja, wie dieses Protokoll in der reinen Windows-Welt funktioniert, weiß ich nicht.  Ich würde aber eher gegen LDAP oder Samba wetten.

Ich habe mal ein bisschen in Apache2::AuthenNTLM (sponsored by Siemens!) geblättert, das ist ein Perl-Modul, der sich in einen Apache HTTPD einklinken läßt, um NTLM-Authentisierung zu machen.  Der ist sieben Jahre alt, sieht recht gruselig aus und "bedient" sich tatsächlich einer Samba-Authentisierung beim Domain Controller (via Authen::Smb).  Ob und wie man den sinnvoll in einen eigenen HTTP-Server einhängen kann, traue ich mich nicht zu bewerten.

>   * der Server schickt im OK-Fall den angeforderten Inhalt, ansonsten eine
>     hoefliche Ablehnung ;-)
>  
> Hat jemand von Euch einen Hinweis, wo ich mich weiter durchkaempfen kann?
> Das Prinzip ist hier einigermassen erklaert: 
> http://search.cpan.org/~bobtfish/ 
> Authen-NTLM-HTTP-0.33/lib/Authen/NTLM/HTTP.pm
> ? aber so ganz blicke ich noch nicht durch?

Dein Link beschreibt die Sache ja auch nur zwischen dem Client und dem Webserver und nicht den Teil, der zwischen Webserver und DC abläuft.
Das Protokoll ist ja inwischen veröffentlicht (http://msdn.microsoft.com/en-us/library/cc236621), aber auch da habe ich nur den Teil zwischen dem Client und dem Server gefunden :(

Ich gebe ja zu, dass ich NTLM nicht mag (da bin ich mir übrigens auch mit Microsoft einig).  Wenn Du also lieber einen ganz anderen, aber auch nicht ganz einfachen Kampf führen willst, könntest Du die Negotiate-Authentisierung mit Kerberos verwenden.  Das klappt auch in einer reinen Windows-Welt, und da kenne ich mich technisch einigermaßen aus.  Ich habe sogar mal den Server-Teil, den Du brauchst, programmiert... allerdings in C, und für BS2000 mit ISO-TSAP (RFC 1006).

Mit Kerberos geht das im Prinzip so: Der HTTP-Server verlangt "Negotiate" (RFC 4559), der Client (IE, Firefox, libwww-perl) kann das und fordert beim Domain Controller ein Ticket an.  Das schickt er dem Server, und der kann es ohne eigene Kommunikation mit dem DC auswerten.
Wenn Du Interesse hast, schicke ich längliche Details :)
--
Cheers,
haj


Mehr Informationen über die Mailingliste Munich-pm