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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy