This patch adds a new option "--with-pam" to the configure script. Built
this way, ngIRCd will authenticate all connecting users using PAM.

Alexander Barton, <alex@barton.de>

 INSTALL                                         |    6 
 configure.in                                    |   33 +++++
 contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj |    8 +
 doc/PAM.txt                                     |   40 ++++++
 src/ngircd/Makefile.am                          |    6 
 src/ngircd/client.c                             |   44 +++++++
 src/ngircd/client.h                             |    6 
 src/ngircd/conf.c                               |    8 +
 src/ngircd/irc-login.c                          |   15 +-
 src/ngircd/ngircd.c                             |    5 
 src/ngircd/pam.c                                |  145 ++++++++++++++++++++++++
 src/ngircd/pam.h                                |   25 ++++
 12 files changed, 335 insertions(+), 6 deletions(-)

Index: INSTALL
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/INSTALL,v
retrieving revision 1.25
diff -u -p -r1.25 INSTALL
--- INSTALL	28 Dec 2006 13:53:19 -0000	1.25
+++ INSTALL	28 Dec 2006 23:07:26 -0000
@@ -184,6 +184,12 @@ standard locations.
   to the daemon, for example by using "/etc/hosts.{allow|deny}".
   The "libwrap" is required for this option.
 
+* PAM:
+  --with-pam[=<path>]
+
+  Enable support for PAM, the Pluggable Authentication Modules library.
+  See doc/PAM.txt for details.
+
 
 IV. Useful make-targets
 ~~~~~~~~~~~~~~~~~~~~~~~
Index: configure.in
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/configure.in,v
retrieving revision 1.125
diff -u -p -r1.125 configure.in
--- configure.in	26 Dec 2006 16:00:45 -0000	1.125
+++ configure.in	28 Dec 2006 23:07:26 -0000
@@ -32,6 +32,7 @@ AH_TEMPLATE([TCPWRAP], [Define if TCP wr
 AH_TEMPLATE([IRCPLUS], [Define if IRC+ protocol should be used])
 AH_TEMPLATE([ZEROCONF], [Define if support for Zeroconf should be included])
 AH_TEMPLATE([IDENTAUTH], [Define if the server should do IDENT requests])
+AH_TEMPLATE([PAM], [Define if PAM should be used])
 
 AH_TEMPLATE([TARGET_OS], [Target operating system name])
 AH_TEMPLATE([TARGET_VENDOR], [Target system vendor])
@@ -415,6 +416,33 @@ if test "$x_identauth_on" = "yes"; then
 	AC_CHECK_HEADERS(ident.h,,AC_MSG_ERROR([required C header missing!]))
 fi
 
+# compile in PAM support?
+
+x_pam_on=no
+AC_ARG_WITH(pam,
+	[  --with-pam              enable user authentication using PAM],
+	[	if test "$withval" != "no"; then
+			if test "$withval" != "yes"; then
+				CFLAGS="-I$withval/include $CFLAGS"
+				CPPFLAGS="-I$withval/include $CPPFLAGS"
+				LDFLAGS="-L$withval/lib $LDFLAGS"
+			fi
+			AC_CHECK_LIB(pam, pam_authenticate)
+			AC_CHECK_FUNCS(pam_authenticate, x_pam_on=yes,
+				AC_MSG_ERROR([Can't enable PAM support!])
+			)
+		fi
+	]
+)
+if test "$x_pam_on" = "yes"; then
+	AC_DEFINE(PAM, 1)
+	AC_CHECK_HEADERS(security/pam_appl.h,pam_ok=yes)
+	if test "$pam_ok" != "yes"; then
+		AC_CHECK_HEADERS(pam/pam_appl.h,pam_ok=yes,
+			AC_MSG_ERROR([required C header missing!]))
+	fi
+fi
+
 # compile in IRC+ protocol support?
 
 x_ircplus_on=yes
@@ -572,6 +600,11 @@ test "$x_identauth_on" = "yes" \
 echo $ECHO_N "        I/O backend: $ECHO_C"
 	echo "\"$x_io_backend\""
 
+echo $ECHO_N "        PAM support: $ECHO_C"
+test "$x_pam_on" = "yes" \
+	&& echo "yes" \
+	|| echo "no"
+
 echo
 
 # -eof-
Index: contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj,v
retrieving revision 1.2
diff -u -p -r1.2 project.pbxproj
--- contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj	3 Sep 2005 15:13:14 -0000	1.2
+++ contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj	28 Dec 2006 23:07:26 -0000
@@ -15,6 +15,8 @@
 		FA2329F708C9E81A0093207B /* portab.h in Headers */ = {isa = PBXBuildFile; fileRef = FADE2B5705F14AB40081A16E /* portab.h */; };
 		FA232A7D08C9F0480093207B /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = FADE2B8F05F14C6C0081A16E /* config.h */; };
 		FA83332B0627391D00A909F0 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FA83332A0627391D00A909F0 /* libz.dylib */; };
+		FA9178AE0916965B0071C678 /* pam.c in Sources */ = {isa = PBXBuildFile; fileRef = FA9178AC0916965A0071C678 /* pam.c */; };
+		FA9178AF0916965B0071C678 /* pam.h in Headers */ = {isa = PBXBuildFile; fileRef = FA9178AD0916965A0071C678 /* pam.h */; };
 		FADE2B2305F14A960081A16E /* irc-server.h in Headers */ = {isa = PBXBuildFile; fileRef = FADE2AF205F14A960081A16E /* irc-server.h */; };
 		FADE2B2405F14A960081A16E /* irc-op.c in Sources */ = {isa = PBXBuildFile; fileRef = FADE2AF305F14A960081A16E /* irc-op.c */; };
 		FADE2B2505F14A960081A16E /* irc-login.c in Sources */ = {isa = PBXBuildFile; fileRef = FADE2AF405F14A960081A16E /* irc-login.c */; };
@@ -140,6 +142,8 @@
 		FA83332A0627391D00A909F0 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = /usr/lib/libz.dylib; sourceTree = "<absolute>"; };
 		FA8B5EFD08C9E4C900B781D4 /* SSL.txt */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; name = SSL.txt; path = ../../doc/SSL.txt; sourceTree = SOURCE_ROOT; };
 		FA8B5EFE08C9E4C900B781D4 /* Zeroconf.txt */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; name = Zeroconf.txt; path = ../../doc/Zeroconf.txt; sourceTree = SOURCE_ROOT; };
+		FA9178AC0916965A0071C678 /* pam.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; name = pam.c; path = ../../src/ngircd/pam.c; sourceTree = SOURCE_ROOT; };
+		FA9178AD0916965A0071C678 /* pam.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; name = pam.h; path = ../../src/ngircd/pam.h; sourceTree = SOURCE_ROOT; };
 		FAC0DF4A05F25A150024C8CE /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = Makefile.am; sourceTree = SOURCE_ROOT; };
 		FADE2AA705F1499A0081A16E /* autogen.sh */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text.script.sh; name = autogen.sh; path = ../../autogen.sh; sourceTree = SOURCE_ROOT; };
 		FADE2AA805F1499A0081A16E /* COPYING */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; name = COPYING; path = ../../COPYING; sourceTree = SOURCE_ROOT; };
@@ -446,6 +450,8 @@
 				FADE2AFA05F14A960081A16E /* messages.h */,
 				FADE2B1705F14A960081A16E /* ngircd.c */,
 				FADE2B0605F14A960081A16E /* ngircd.h */,
+				FA9178AC0916965A0071C678 /* pam.c */,
+				FA9178AD0916965A0071C678 /* pam.h */,
 				FADE2AFF05F14A960081A16E /* parse.c */,
 				FADE2B1905F14A960081A16E /* parse.h */,
 				FADE2B0405F14A960081A16E /* rendezvous.c */,
@@ -546,6 +552,7 @@
 				FADE2CC205F152780081A16E /* tool.h in Headers */,
 				FA2329EF08C9E7A30093207B /* array.h in Headers */,
 				FA2329F308C9E7C40093207B /* io.h in Headers */,
+				FA9178AF0916965B0071C678 /* pam.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -697,6 +704,7 @@
 				FA2329EE08C9E7A30093207B /* array.c in Sources */,
 				FA2329F208C9E7C40093207B /* io.c in Sources */,
 				FA2329F508C9E8070093207B /* strdup.c in Sources */,
+				FA9178AE0916965B0071C678 /* pam.c in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
Index: doc/PAM.txt
===================================================================
RCS file: doc/PAM.txt
diff -N doc/PAM.txt
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ doc/PAM.txt	28 Dec 2006 23:07:26 -0000
@@ -0,0 +1,40 @@
+
+                     ngIRCd - Next Generation IRC Server
+
+                        (c)2001-2006 Alexander Barton,
+                    alex@barton.de, http://www.barton.de/
+
+               ngIRCd is free software and published under the
+                   terms of the GNU General Public License.
+
+                                 -- PAM.txt --
+
+
+ngIRCd can optionally be compiled to use PAM, the Pluggable Authentication
+Modules library passing the command line parameter "--with-pam" to the
+"configure" script. When compiled with PAM support, the ngIRCd will
+authenticate all connecting users using the configured PAM modules.
+
+Please see the PAM documentaton ("man 7 pam"!?) for details and information
+about configuring PAM and its individual modules.
+
+A very simple -- and quite useless ;-) -- example would be:
+
+	/etc/pam.d/ngircd:
+	  auth  required  pam_debug.so
+
+Here the "pam_debug" module will be called each time a client connects to
+the ngIRCd and has sent its PASS, NICK, and USER commands.
+
+
+Please note ONE VERY IMPORTANT THING:
+
+All the PAM modules are executed with the privileges of the user ngIRCd is
+running as. Therefore a lot of PAM modules wouldn't work as expected, because
+they would need root privileges ("pam_unix", for example)!
+Only PAM modules not(!) requiring root privileges (such as "pam_pgsql",
+"pam_mysql", ...) can be used in conjuction with ngIRCd.
+
+
+-- 
+$Id$
Index: src/ngircd/Makefile.am
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/src/ngircd/Makefile.am,v
retrieving revision 1.49
diff -u -p -r1.49 Makefile.am
--- src/ngircd/Makefile.am	11 Mar 2006 01:48:50 -0000	1.49
+++ src/ngircd/Makefile.am	28 Dec 2006 23:07:26 -0000
@@ -1,6 +1,6 @@
 #
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2003 by Alexander Barton (alex@barton.de)
+# Copyright (c)2001-2006 Alexander Barton (alex@barton.de)
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -23,7 +23,7 @@ sbin_PROGRAMS = ngircd
 ngircd_SOURCES = ngircd.c array.c channel.c client.c conf.c conn.c conn-func.c \
 	conn-zip.c hash.c io.c irc.c irc-channel.c irc-info.c irc-login.c \
 	irc-mode.c irc-op.c irc-oper.c irc-server.c irc-write.c lists.c log.c \
-	match.c parse.c rendezvous.c resolve.c
+	match.c pam.c parse.c rendezvous.c resolve.c
 
 ngircd_LDFLAGS = -L../portab -L../tool
 
@@ -32,7 +32,7 @@ ngircd_LDADD = -lngportab -lngtool
 noinst_HEADERS = ngircd.h array.h channel.h client.h conf.h conn.h conn-func.h \
 	conn-zip.h hash.h io.h irc.h irc-channel.h irc-info.h irc-login.h \
 	irc-mode.h irc-op.h irc-oper.h irc-server.h irc-write.h lists.h log.h \
-	match.h parse.h rendezvous.h resolve.h \
+	match.h pam.h parse.h rendezvous.h resolve.h \
 	defines.h messages.h
 
 clean-local:
Index: src/ngircd/client.c
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/src/ngircd/client.c,v
retrieving revision 1.94
diff -u -p -r1.94 client.c
--- src/ngircd/client.c	7 Dec 2006 22:24:14 -0000	1.94
+++ src/ngircd/client.c	28 Dec 2006 23:07:26 -0000
@@ -409,6 +409,25 @@ Client_SetUser( CLIENT *Client, char *Us
 } /* Client_SetUser */
 
 
+/**
+ * Set "original" user name of a client.
+ * This function saves the "original" user name, the user name specified by
+ * the peer using the USER command, into the CLIENT structure. This user
+ * name may be used for authentication, for example.
+ * @param Client The client.
+ * @param User User name to set.
+ */
+GLOBAL void
+Client_SetOrigUser(CLIENT *Client, char *User) {
+	assert(Client != NULL);
+	assert(User != NULL);
+
+#ifdef PAM & IDENTAUTH
+	strlcpy(Client->orig_user, User, sizeof(Client->orig_user));
+#endif
+} /* Client_SetOrigUser */
+
+
 GLOBAL void
 Client_SetInfo( CLIENT *Client, char *Info )
 {
@@ -665,6 +684,31 @@ Client_User( CLIENT *Client )
 } /* Client_User */
 
 
+#ifdef PAM
+
+/**
+ * Get the "original" user name as supplied by the USER command.
+ * The user name as given by the client is used for authentication instead
+ * of the one detected using IDENT requests.
+ * @param Client The client.
+ * @return Original user name.
+ */
+GLOBAL char *
+Client_OrigUser(CLIENT *Client) {
+#ifndef IDENTAUTH
+	char *user = Client->user;
+	
+	if (user[0] == '~')
+		user++;
+	return user;
+#else
+	return Client->orig_user;
+#endif
+} /* Client_OrigUser */
+
+#endif
+
+
 GLOBAL char *
 Client_Hostname( CLIENT *Client )
 {
Index: src/ngircd/client.h
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/src/ngircd/client.h,v
retrieving revision 1.45
diff -u -p -r1.45 client.h
--- src/ngircd/client.h	7 Oct 2006 10:40:52 -0000	1.45
+++ src/ngircd/client.h	28 Dec 2006 23:07:26 -0000
@@ -46,6 +46,9 @@ typedef struct _CLIENT
 	char pwd[CLIENT_PASS_LEN];	/* password received of the client */
 	char host[CLIENT_HOST_LEN];	/* hostname of the client */
 	char user[CLIENT_USER_LEN];	/* user name ("login") */
+#ifdef PAM & IDENTAUTH
+	char orig_user[CLIENT_USER_LEN];/* user name supplied to USER command */
+#endif
 	char info[CLIENT_INFO_LEN];	/* long user name (user) / info text (server) */
 	char modes[CLIENT_MODE_LEN];	/* client modes */
 	int hops, token, mytoken;	/* "hops" and "Token" (see SERVER command) */
@@ -98,6 +101,9 @@ GLOBAL char *Client_ID PARAMS(( CLIENT *
 GLOBAL char *Client_Mask PARAMS(( CLIENT *Client ));
 GLOBAL char *Client_Info PARAMS(( CLIENT *Client ));
 GLOBAL char *Client_User PARAMS(( CLIENT *Client ));
+#ifdef PAM
+GLOBAL char *Client_OrigUser PARAMS(( CLIENT *Client ));
+#endif
 GLOBAL char *Client_Hostname PARAMS(( CLIENT *Client ));
 GLOBAL char *Client_Password PARAMS(( CLIENT *Client ));
 GLOBAL char *Client_Modes PARAMS(( CLIENT *Client ));
Index: src/ngircd/conf.c
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/src/ngircd/conf.c,v
retrieving revision 1.96
diff -u -p -r1.96 conf.c
--- src/ngircd/conf.c	20 Nov 2006 19:32:07 -0000	1.96
+++ src/ngircd/conf.c	28 Dec 2006 23:07:26 -0000
@@ -180,7 +180,9 @@ Conf_Test( void )
 	puts( "[GLOBAL]" );
 	printf( "  Name = %s\n", Conf_ServerName );
 	printf( "  Info = %s\n", Conf_ServerInfo );
+#ifndef PAM
 	printf( "  Password = %s\n", Conf_ServerPwd );
+#endif
 	printf( "  AdminInfo1 = %s\n", Conf_ServerAdmin1 );
 	printf( "  AdminInfo2 = %s\n", Conf_ServerAdmin2 );
 	printf( "  AdminEMail = %s\n", Conf_ServerAdminMail );
@@ -1045,6 +1047,12 @@ Validate_Config(bool Configtest, bool Re
 			     "No administrative information configured but required by RFC!");
 	}
 
+#ifdef PAM
+	if (Conf_ServerPwd[0])
+		Config_Error(LOG_ERR,
+		         "This server uses PAM, \"Password\" will be ignored!");
+#endif
+
 #ifdef DEBUG
 	servers = servers_once = 0;
 	for (i = 0; i < MAX_SERVERS; i++) {
Index: src/ngircd/irc-login.c
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/src/ngircd/irc-login.c,v
retrieving revision 1.53
diff -u -p -r1.53 irc-login.c
--- src/ngircd/irc-login.c	3 Oct 2006 10:28:38 -0000	1.53
+++ src/ngircd/irc-login.c	28 Dec 2006 23:07:26 -0000
@@ -31,6 +31,7 @@ static char UNUSED id[] = "$Id: irc-logi
 #include "channel.h"
 #include "log.h"
 #include "messages.h"
+#include "pam.h"
 #include "parse.h"
 #include "irc.h"
 #include "irc-info.h"
@@ -377,6 +378,7 @@ IRC_USER( CLIENT *Client, REQUEST *Req )
 #else
 		Client_SetUser( Client, Req->argv[0], false );
 #endif
+		Client_SetOrigUser(Client, Req->argv[0]);
 
 		/* "Real name" or user info text: Don't set it to the empty string, the original ircd
 		 * can't deal with such "real names" (e. g. "USER user * * :") ... */
@@ -582,11 +584,18 @@ Hello_User( CLIENT *Client )
 	assert( Client != NULL );
 
 	/* Check password ... */
-	if( strcmp( Client_Password( Client ), Conf_ServerPwd ) != 0 )
+#ifdef PAM
+	if (! PAM_Authenticate(Client))
+#else
+	if (strcmp(Client_Password(Client), Conf_ServerPwd) != 0)
+#endif
 	{
 		/* Bad password! */
-		Log( LOG_ERR, "User \"%s\" rejected (connection %d): Bad password!", Client_Mask( Client ), Client_Conn( Client ));
-		Conn_Close( Client_Conn( Client ), NULL, "Bad password", true);
+		Log(LOG_ERR,
+		    "User \"%s\" rejected (connection %d): Access denied!",
+		    Client_Mask(Client), Client_Conn(Client));
+		Conn_Close(Client_Conn(Client), NULL,
+		    "Access denied! Bad password?", true);
 		return DISCONNECTED;
 	}
 
Index: src/ngircd/ngircd.c
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/src/ngircd/ngircd.c,v
retrieving revision 1.114
diff -u -p -r1.114 ngircd.c
--- src/ngircd/ngircd.c	7 Dec 2006 17:57:20 -0000	1.114
+++ src/ngircd/ngircd.c	28 Dec 2006 23:07:26 -0000
@@ -373,6 +373,11 @@ Fill_Version( void )
 
 	strlcat( NGIRCd_VersionAddition, "IDENT", sizeof NGIRCd_VersionAddition );
 #endif
+#ifdef PAM
+	if (NGIRCd_VersionAddition[0])
+		strlcat(NGIRCd_VersionAddition, "+", sizeof NGIRCd_VersionAddition);
+	strlcat(NGIRCd_VersionAddition, "PAM", sizeof NGIRCd_VersionAddition);
+#endif
 #ifdef DEBUG
 	if( NGIRCd_VersionAddition[0] )
 		strlcat( NGIRCd_VersionAddition, "+", sizeof NGIRCd_VersionAddition );
Index: src/ngircd/pam.c
===================================================================
RCS file: src/ngircd/pam.c
diff -N src/ngircd/pam.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/ngircd/pam.c	28 Dec 2006 23:07:26 -0000
@@ -0,0 +1,145 @@
+/*
+ * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2006 Alexander Barton (alex@barton.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * Please read the file COPYING, README and AUTHORS for more information.
+ *
+ * PAM User Authentification
+ */
+
+#include "portab.h"
+
+#ifdef PAM
+
+static char UNUSED id[] = "$Id$";
+
+#include "imp.h"
+#include <assert.h>
+
+#include "defines.h"
+#include "log.h"
+#include "conn.h"
+#include "client.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_SECURITY_PAM_APPL_H
+#include <security/pam_appl.h>
+#endif
+
+#ifdef HAVE_PAM_PAM_APPL_H
+#include <pam/pam_appl.h>
+#endif
+
+#include "exp.h"
+#include "pam.h"
+
+
+static char *password;
+
+
+/**
+ * PAM "conversation function".
+ * This is a callback function used by the PAM library to get the password.
+ * Please see the PAM documentation for details :-)
+ */
+static int
+password_conversation(int num_msg, const struct pam_message **msg,
+ struct pam_response **resp, void *appdata_ptr) {
+	LogDebug("PAM: conv(%d, %d, '%s', '%s')",
+	    num_msg, msg[0]->msg_style, msg[0]->msg, appdata_ptr);
+
+	/* Can we deal with this request? */
+	if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF) {
+		Log(LOG_ERR, "PAM: Unexpected PAM conversation '%d:%s'!",
+		    msg[0]->msg_style, msg[0]->msg);
+		return PAM_CONV_ERR;
+	}
+
+	if (! appdata_ptr) {
+		/* Sometimes appdata_ptr gets lost!? */
+		appdata_ptr = password;
+	}
+
+	/* Duplicate password ("application data") for the PAM library */
+	*resp = calloc(num_msg, sizeof(struct pam_response));
+	if (! *resp) {
+		Log(LOG_ERR, "PAM: Out of memory!");
+		return PAM_CONV_ERR;
+	}
+
+	(*resp)[0].resp = strdup((char *)appdata_ptr);
+	(*resp)[0].resp_retcode = 0;
+
+	return ((*resp)[0].resp ? PAM_SUCCESS : PAM_CONV_ERR);
+}
+
+
+/**
+ * PAM "conversation" structure.
+ */
+static struct pam_conv conv = {
+	&password_conversation,
+	NULL
+};
+
+
+/**
+ * Authenticate a connectiong client using PAM.
+ * @param Client The client to authenticate.
+ * @return true when authentication succeeded, false otherwise.
+ */
+GLOBAL bool
+PAM_Authenticate(CLIENT *Client) {
+	pam_handle_t *pam;
+	int retval = PAM_SUCCESS;
+
+	LogDebug("PAM: Authenticate \"%s\" (%s) ...",
+	    Client_OrigUser(Client), Client_Mask(Client));
+
+	/* Set supplied client password */
+	if (password)
+		free(password);
+	password = strdup(Client_Password(Client));
+	conv.appdata_ptr = password;
+
+	/* Initialize PAM */
+	retval = pam_start("ngircd", Client_OrigUser(Client), &conv, &pam);
+	if (retval != PAM_SUCCESS) {
+		Log(LOG_ERR, "PAM: Failed to create authenticator!");
+		return false;
+	}
+
+	/* Do not delay failed login attempts, this would block the daemon */
+	pam_fail_delay(pam, 0);
+	pam_set_item(pam, PAM_RUSER, Client_User(Client));
+	pam_set_item(pam, PAM_RHOST, Client_Hostname(Client));
+
+	/* PAM authentication ... */
+	retval = pam_authenticate(pam, 0);
+
+	/* Success? */
+	if (retval == PAM_SUCCESS)
+		Log(LOG_INFO, "PAM: Authenticated \"%s\" (%s).",
+		    Client_OrigUser(Client), Client_Mask(Client));
+	else	
+		Log(LOG_ERR, "PAM: Error on \"%s\" (%s): %s",
+		    Client_OrigUser(Client), Client_Mask(Client),
+		    pam_strerror(pam, retval));
+
+	/* Free PAM structures */
+	if (pam_end(pam, retval) != PAM_SUCCESS)
+		Log(LOG_ERR, "PAM: Failed to release authenticator!");
+
+	return (retval == PAM_SUCCESS);
+} /* PAM_Authenticate */
+
+
+#endif /* PAM */
+
+/* -eof- */
Index: src/ngircd/pam.h
===================================================================
RCS file: src/ngircd/pam.h
diff -N src/ngircd/pam.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/ngircd/pam.h	28 Dec 2006 23:07:26 -0000
@@ -0,0 +1,25 @@
+/*
+ * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2006 Alexander Barton (alex@barton.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * Please read the file COPYING, README and AUTHORS for more information.
+ *
+ * PAM User Authentification
+ */
+
+#ifdef PAM
+
+#ifndef __pam_h__
+#define __pam_h__
+
+GLOBAL bool PAM_Authenticate PARAMS((CLIENT *Client));
+
+#endif	/* __pam_h__ */
+
+#endif	/* PAM */
+
+/* -eof- */
