Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 04c1f72

Browse files
committed
PAM authentication:
> pam_strerror() should be used a few more times, rather than just saying > "Error!". Also, the configure.in snippet seems wrong. You add > -I$pam_prefix/include/security to $INCLUDES and then you #include > <security/pam_appl.h>. This whole thing is probably unnecessary, since > PAM is a system library on the systems where it exists, so the headers > and libraries are found automatically, unlike OpenSSL and > Kerberos. See attached revised patch. (I'm sure the configure.in stuff can be done right/better, I'm just not enough of a autoconf guru to know what to change it to.) Dominic J. Eidson
1 parent 2a34134 commit 04c1f72

File tree

7 files changed

+290
-17
lines changed

7 files changed

+290
-17
lines changed

configure.in

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,6 @@ PGAC_ARG_BOOL(with, perl, no, [ --with-perl build Perl interface an
432432
AC_MSG_RESULT([$with_perl])
433433
AC_SUBST(with_perl)
434434

435-
436435
#
437436
# Optionally build Python interface module
438437
#
@@ -529,6 +528,23 @@ AC_DEFINE_UNQUOTED([PG_KRB_SRVNAM], ["$with_krb_srvnam"],
529528
[The name of the PostgreSQL service principal in Kerberos])
530529

531530

531+
#
532+
# PAM
533+
#
534+
AC_MSG_CHECKING([whether to build with PAM support])
535+
PGAC_ARG_OPTARG(with, pam,
536+
[ --with-pam[=DIR] build with PAM support [/usr]],
537+
[pam_prefix=/usr],
538+
[pam_prefix=$withval],
539+
[
540+
AC_MSG_RESULT([yes])
541+
AC_DEFINE([USE_PAM], 1, [Define to build with PAM support])
542+
543+
],
544+
[AC_MSG_RESULT(no)])
545+
546+
AC_SUBST(with_pam)
547+
532548

533549
#
534550
# OpenSSL
@@ -752,11 +768,14 @@ if test "$with_openssl" = yes ; then
752768
AC_CHECK_LIB(ssl, [SSL_library_init], [], [AC_MSG_ERROR([library 'ssl' is required for OpenSSL])])
753769
fi
754770

771+
if test "$with_pam" = yes ; then
772+
AC_CHECK_LIB(pam, [pam_start], [], [AC_MSG_ERROR([library 'pam' is required for PAM])])
773+
fi
774+
755775
if test "$enable_nls" = yes ; then
756776
PGAC_CHECK_GETTEXT
757777
fi
758778

759-
760779
##
761780
## Header files
762781
##
@@ -794,6 +813,10 @@ if test "$with_openssl" = yes ; then
794813
AC_CHECK_HEADER([openssl/err.h], [], [AC_MSG_ERROR([header file <openssl/err.h> is required for OpenSSL])])
795814
fi
796815

816+
if test "$with_pam" = yes ; then
817+
AC_CHECK_HEADER([security/pam_appl.h], [], [AC_MSG_ERROR([header file <security/pam_appl.h> is required for PAM])])
818+
fi
819+
797820

798821
##
799822
## Types, structures, compiler characteristics

doc/src/sgml/client-auth.sgml

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.17 2001/08/16 16:24:15 momjian Exp $ -->
1+
<!-- $Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.18 2001/09/06 03:23:38 momjian Exp $ -->
22

33
<chapter id="client-authentication">
44
<title>Client Authentication</title>
@@ -278,6 +278,27 @@ hostssl <replaceable>database</replaceable> <replaceable>IP-address</replaceable
278278
</para>
279279
</listitem>
280280
</varlistentry>
281+
282+
<varlistentry>
283+
<term>pam</term>
284+
<listitem>
285+
<para>
286+
This authentication type operates similar to
287+
<firstterm>password</firstterm>, with the main difference that
288+
it will use PAM (Pluggable Authentication Modules) as the
289+
authentication mechanism. The <replaceable>authentication
290+
option</replaceable> following the <literal>pam</> keyword
291+
specifies the service name that will be passed to PAM. The
292+
default service name is <firstterm>postgresql</firstterm>.
293+
For more information about PAM, please read <ulink
294+
url="http://www.kernel.org/pub/linux/libs/pam/">Linux-PAM
295+
Page</ulink> and <ulink
296+
url="http://www.sun.com/software/solaris/pam/">Solaris-PAM
297+
Page</ulink>.
298+
</para>
299+
</listitem>
300+
</varlistentry>
301+
281302
</variablelist>
282303

283304
</para>

src/backend/libpq/auth.c

Lines changed: 224 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.64 2001/08/21 15:21:25 momjian Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.65 2001/09/06 03:23:38 momjian Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -43,6 +43,24 @@ static int recv_and_check_passwordv0(Port *port);
4343

4444
char *pg_krb_server_keyfile;
4545

46+
#ifdef USE_PAM
47+
#include <security/pam_appl.h>
48+
49+
#define PGSQL_PAM_SERVICE "postgresql" /* Service name passed to PAM */
50+
51+
static int CheckPAMAuth(Port *port, char *user, char *password);
52+
static int pam_passwd_conv_proc(int num_msg, const struct pam_message **msg,
53+
struct pam_response **resp, void *appdata_ptr);
54+
55+
static struct pam_conv pam_passw_conv = {
56+
&pam_passwd_conv_proc,
57+
NULL
58+
};
59+
60+
static char * pam_passwd = NULL; /* Workaround for Solaris 2.6 brokenness */
61+
static Port * pam_port_cludge; /* Workaround for passing "Port
62+
* *port" into pam_passwd_conv_proc */
63+
#endif /* USE_PAM */
4664

4765
#ifdef KRB4
4866
/*----------------------------------------------------------------
@@ -428,6 +446,11 @@ auth_failed(Port *port)
428446
case uaPassword:
429447
authmethod = "Password";
430448
break;
449+
#ifdef USE_PAM
450+
case uaPAM:
451+
authmethod = "PAM";
452+
break;
453+
#endif /* USE_PAM */
431454
}
432455

433456
elog(FATAL, "%s authentication failed for user \"%s\"",
@@ -525,15 +548,21 @@ ClientAuthentication(Port *port)
525548
status = recv_and_check_password_packet(port);
526549
break;
527550

528-
case uaCrypt:
529-
sendAuthRequest(port, AUTH_REQ_CRYPT);
530-
status = recv_and_check_password_packet(port);
531-
break;
532-
533-
case uaPassword:
534-
sendAuthRequest(port, AUTH_REQ_PASSWORD);
535-
status = recv_and_check_password_packet(port);
551+
case uaCrypt:
552+
sendAuthRequest(port, AUTH_REQ_CRYPT);
553+
status = recv_and_check_password_packet(port);
554+
break;
555+
556+
case uaPassword:
557+
sendAuthRequest(port, AUTH_REQ_PASSWORD);
558+
status = recv_and_check_password_packet(port);
559+
break;
560+
#ifdef USE_PAM
561+
case uaPAM:
562+
pam_port_cludge = port;
563+
status = CheckPAMAuth(port, port->user, "");
536564
break;
565+
#endif /* USE_PAM */
537566

538567
case uaTrust:
539568
status = STATUS_OK;
@@ -577,7 +606,190 @@ sendAuthRequest(Port *port, AuthRequest areq)
577606
pq_flush();
578607
}
579608

609+
#ifdef USE_PAM
610+
611+
/*
612+
* PAM conversation function
613+
*/
614+
615+
static int
616+
pam_passwd_conv_proc (int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
617+
{
618+
StringInfoData buf;
619+
int32 len;
620+
621+
if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF) {
622+
switch(msg[0]->msg_style) {
623+
case PAM_ERROR_MSG:
624+
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
625+
"pam_passwd_conv_proc: Error from underlying PAM layer: '%s'\n", msg[0]->msg);
626+
fputs(PQerrormsg, stderr);
627+
pqdebug("%s", PQerrormsg);
628+
return PAM_CONV_ERR;
629+
default:
630+
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
631+
"pam_passwd_conv_proc: Unexpected PAM conversation %d/'%s'\n",
632+
msg[0]->msg_style, msg[0]->msg);
633+
fputs(PQerrormsg, stderr);
634+
pqdebug("%s", PQerrormsg);
635+
return PAM_CONV_ERR;
636+
}
637+
}
638+
639+
if (!appdata_ptr) {
640+
/* Workaround for Solaris 2.6 where the PAM library is broken
641+
* and does not pass appdata_ptr to the conversation routine
642+
*/
643+
appdata_ptr = pam_passwd;
644+
}
645+
646+
/* Password wasn't passed to PAM the first time around - let's go
647+
* ask the client to send a password, which we then stuff into
648+
* PAM.
649+
*/
650+
if(strlen(appdata_ptr) == 0) {
651+
sendAuthRequest(pam_port_cludge, AUTH_REQ_PASSWORD);
652+
if (pq_eof() == EOF || pq_getint(&len, 4) == EOF) {
653+
return PAM_CONV_ERR; /* client didn't want to send password */
654+
}
655+
656+
initStringInfo(&buf);
657+
pq_getstr(&buf);
658+
if (DebugLvl)
659+
fprintf(stderr, "received PAM packet with len=%d, pw=%s\n",
660+
len, buf.data);
661+
662+
if(strlen(buf.data) == 0) {
663+
snprintf(PQerrormsg, PQERRORMSG_LENGTH, "pam_passwd_conv_proc: no password\n");
664+
fputs(PQerrormsg, stderr);
665+
return PAM_CONV_ERR;
666+
}
667+
appdata_ptr = buf.data;
668+
}
669+
670+
/* Explicitly not using palloc here - PAM will free this memory in
671+
* pam_end()
672+
*/
673+
*resp = calloc(num_msg, sizeof(struct pam_response));
674+
if (!*resp) {
675+
snprintf(PQerrormsg, PQERRORMSG_LENGTH, "pam_passwd_conv_proc: Out of memory!\n");
676+
fputs(PQerrormsg, stderr);
677+
pqdebug("%s", PQerrormsg);
678+
if(buf.data)
679+
pfree(buf.data);
680+
return PAM_CONV_ERR;
681+
}
682+
683+
(*resp)[0].resp = strdup((char *) appdata_ptr);
684+
(*resp)[0].resp_retcode = 0;
685+
686+
return ((*resp)[0].resp ? PAM_SUCCESS : PAM_CONV_ERR);
687+
}
688+
689+
690+
/*
691+
* Check authentication against PAM.
692+
*/
693+
static int
694+
CheckPAMAuth(Port *port, char *user, char *password)
695+
{
696+
int retval;
697+
pam_handle_t *pamh = NULL;
698+
699+
/*
700+
* Apparently, Solaris 2.6 is broken, and needs ugly static
701+
* variable workaround
702+
*/
703+
pam_passwd = password;
704+
705+
/* Set the application data portion of the conversation struct
706+
* This is later used inside the PAM conversation to pass the
707+
* password to the authentication module.
708+
*/
709+
pam_passw_conv.appdata_ptr = (char*) password; /* from password above, not allocated */
710+
711+
/* Optionally, one can set the service name in pg_hba.conf */
712+
if(port->auth_arg[0] == '\0') {
713+
retval = pam_start(PGSQL_PAM_SERVICE, "pgsql@", &pam_passw_conv, &pamh);
714+
} else {
715+
retval = pam_start(port->auth_arg, "pgsql@", &pam_passw_conv, &pamh);
716+
}
717+
718+
if (retval != PAM_SUCCESS) {
719+
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
720+
"CheckPAMAuth: Failed to create PAM authenticator: '%s'\n",
721+
pam_strerror(pamh, retval));
722+
fputs(PQerrormsg, stderr);
723+
pqdebug("%s", PQerrormsg);
724+
pam_passwd = NULL; /* Unset pam_passwd */
725+
return STATUS_ERROR;
726+
}
727+
728+
if (retval == PAM_SUCCESS) {
729+
retval = pam_set_item(pamh, PAM_USER, user);
730+
} else {
731+
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
732+
"CheckPAMAuth: pam_set_item(PAM_USER) failed: '%s'\n",
733+
pam_strerror(pamh, retval));
734+
fputs(PQerrormsg, stderr);
735+
pqdebug("%s", PQerrormsg);
736+
pam_passwd = NULL; /* Unset pam_passwd */
737+
return STATUS_ERROR;
738+
}
739+
if (retval == PAM_SUCCESS) {
740+
retval = pam_set_item(pamh, PAM_CONV, &pam_passw_conv);
741+
} else {
742+
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
743+
"CheckPAMAuth: pam_set_item(PAM_CONV) failed: '%s'\n",
744+
pam_strerror(pamh, retval));
745+
fputs(PQerrormsg, stderr);
746+
pqdebug("%s", PQerrormsg);
747+
pam_passwd = NULL; /* Unset pam_passwd */
748+
return STATUS_ERROR;
749+
}
750+
if (retval == PAM_SUCCESS) {
751+
retval = pam_authenticate(pamh, 0);
752+
} else {
753+
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
754+
"CheckPAMAuth: pam_authenticate failed: '%s'\n",
755+
pam_strerror(pamh, retval));
756+
fputs(PQerrormsg, stderr);
757+
pqdebug("%s", PQerrormsg);
758+
pam_passwd = NULL; /* Unset pam_passwd */
759+
return STATUS_ERROR;
760+
}
761+
if (retval == PAM_SUCCESS) {
762+
retval = pam_acct_mgmt(pamh, 0);
763+
} else {
764+
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
765+
"CheckPAMAuth: pam_acct_mgmt failed: '%s'\n",
766+
pam_strerror(pamh, retval));
767+
fputs(PQerrormsg, stderr);
768+
pqdebug("%s", PQerrormsg);
769+
pam_passwd = NULL; /* Unset pam_passwd */
770+
return STATUS_ERROR;
771+
}
772+
if (retval == PAM_SUCCESS) {
773+
retval = pam_end(pamh, retval);
774+
if(retval != PAM_SUCCESS) {
775+
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
776+
"CheckPAMAuth: Failed to release PAM authenticator: '%s'\n",
777+
pam_strerror(pamh, retval));
778+
fputs(PQerrormsg, stderr);
779+
pqdebug("%s", PQerrormsg);
780+
}
781+
782+
pam_passwd = NULL; /* Unset pam_passwd */
783+
784+
return (retval == PAM_SUCCESS ? STATUS_OK : STATUS_ERROR);
785+
} else {
786+
return STATUS_ERROR;
787+
}
788+
}
789+
790+
580791

792+
#endif /* USE_PAM */
581793

582794
/*
583795
* Called when we have received the password packet.
@@ -670,6 +882,9 @@ map_old_to_new(Port *port, UserAuth old, int status)
670882
case uaMD5:
671883
case uaCrypt:
672884
case uaReject:
885+
#ifdef USE_PAM
886+
case uaPAM:
887+
#endif /* USE_PAM */
673888
status = STATUS_ERROR;
674889
break;
675890

src/backend/libpq/hba.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
*
1111
*
1212
* IDENTIFICATION
13-
* $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.68 2001/08/21 15:49:17 momjian Exp $
13+
* $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.69 2001/09/06 03:23:38 momjian Exp $
1414
*
1515
*-------------------------------------------------------------------------
1616
*/
@@ -235,6 +235,10 @@ parse_hba_auth(List *line, ProtocolVersion proto, UserAuth *userauth_p,
235235
*userauth_p = uaMD5;
236236
else if (strcmp(token, "crypt") == 0)
237237
*userauth_p = uaCrypt;
238+
#ifdef USE_PAM
239+
else if (strcmp(token, "pam") == 0)
240+
*userauth_p = uaPAM;
241+
#endif
238242
else
239243
*error_p = true;
240244
line = lnext(line);
@@ -277,7 +281,6 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
277281
line_number = lfirsti(line);
278282
line = lnext(line);
279283
Assert(line != NIL);
280-
281284
/* Check the record type. */
282285
token = lfirst(line);
283286
if (strcmp(token, "local") == 0)

0 commit comments

Comments
 (0)