[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
PAM authentication patch - v2
From: |
Brian Murphy |
Subject: |
PAM authentication patch - v2 |
Date: |
Sun, 13 Apr 2003 18:14:35 +0200 |
User-agent: |
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.0.0) Gecko/20020623 Debian/1.0.0-0.woody.1 |
Please find attached a patch to authenticate pserver cvs access via PAM.
I have re-indented to follow the official cvs policy and have made PAM
a configurable option, only enabled if --with-pam is given as an option
to configure.
It applies to the cvs1-11-x-branch which I presume is the experimental
branch. It should also apply to the trunk.
Please comment, and feel free to test also :-).
/Brian
Index: configure.in
===================================================================
RCS file: /cvs/cvs/configure.in,v
retrieving revision 1.1.1.1
retrieving revision 1.4
diff -u -r1.1.1.1 -r1.4
--- configure.in 12 Apr 2003 18:25:21 -0000 1.1.1.1
+++ configure.in 13 Apr 2003 15:57:04 -0000 1.4
@@ -297,6 +297,37 @@
dnl
dnl
+dnl Check if PAM is available
+dnl
+AC_ARG_WITH(
+ [pam],
+ AC_HELP_STRING(
+ [--with-pam],
+ [Use to enable PAM support]))
+
+AC_ARG_WITH(
+ [broken-pam-appdata],
+ AC_HELP_STRING(
+ [--with-broken-pam-appdata],
+ [Use if you have a broken PAM which calls the conversation function
+ with a null application data pointer, Solaris 2.60 is a culprit here]))
+
+if test -n "$with_pam" && test yes = $with_pam; then
+ AC_CHECK_HEADER(security/pam_appl.h,
+ AC_DEFINE(HAVE_PAM, 1, [Defined if you have pam]),
+ AC_MSG_ERROR([Could not find PAM headers])
+ )
+ AC_CHECK_LIB(pam, pam_start, [LIBS="${LIBS} -lpam"],
+ AC_MSG_ERROR([Could not find PAM libraries]))
+
+ if test -n "$with_broken_pam_appdata" && test yes =
$with_broken_pam_appdata; then
+ AC_DEFINE(HAVE_BROKEN_PAM_APPDATA, 1,
+ [Define if the PAM conversation function is called
+ with a null application data pointer])
+ fi
+fi
+
+dnl
dnl set $(KRB4) from --with-krb4=value -- WITH_KRB4
dnl
dnl If you change this, keep in mind that some systems have a bogus
Index: config.h.in
===================================================================
RCS file: /cvs/cvs/config.h.in,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- config.h.in 12 Apr 2003 18:25:21 -0000 1.1.1.1
+++ config.h.in 12 Apr 2003 19:28:23 -0000 1.3
@@ -206,6 +206,12 @@
/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
#undef HAVE_NDIR_H
+/* Defined if you have pam */
+#undef HAVE_PAM
+
+/* Defined if you have pam */
+#undef HAVE_BROKEN_PAM_APPDATA
+
/* Define to 1 if you have the `putenv' function. */
#undef HAVE_PUTENV
Index: src/server.c
===================================================================
RCS file: /cvs/cvs/src/server.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 server.c
--- src/server.c 12 Apr 2003 18:25:22 -0000 1.1.1.1
+++ src/server.c 13 Apr 2003 16:13:05 -0000
@@ -5517,50 +5517,114 @@
return retval;
}
+#ifdef HAVE_PAM
-/* Return a hosting username if password matches, else NULL. */
-static char *
-check_password (username, password, repository)
- char *username, *password, *repository;
-{
- int rc;
- char *host_user = NULL;
- char *found_passwd = NULL;
- struct passwd *pw;
+#include <security/pam_appl.h>
- /* First we see if this user has a password in the CVS-specific
- password file. If so, that's enough to authenticate with. If
- not, we'll check /etc/passwd. */
+#define PAM_SERVICENAME "cvs"
- rc = check_repository_password (username, password, repository,
- &host_user);
+struct cvs_pam_userinfo {
+ char *username;
+ char *password;
+};
- if (rc == 2)
- return NULL;
+#ifdef HAVE_BROKEN_PAM_APPDATA
+struct cvs_pam_userinfo *global_userinfo;
+#endif
- if (rc == 1)
+static int
+cvs_pam_conv(int num_msg,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *appdata_ptr)
+{
+ int i;
+ struct pam_response *response;
+ struct cvs_pam_userinfo *ui = (struct cvs_pam_userinfo *)appdata_ptr;
+
+#ifdef HAVE_BROKEN_PAM_APPDATA
+ if(!ui)
{
- /* host_user already set by reference, so just return. */
- goto handle_return;
+ ui = global_userinfo;
}
+#endif
- assert (rc == 0);
+ if (!ui || !ui->username || !ui->password || !msg || !resp)
+ {
+ return PAM_CONV_ERR;
+ }
- if (!system_auth)
+ response = malloc(num_msg * sizeof(struct pam_response));
+ if (!response)
{
- /* Note that the message _does_ distinguish between the case in
- which we check for a system password and the case in which
- we do not. It is a real pain to track down why it isn't
- letting you in if it won't say why, and I am not convinced
- that the potential information disclosure to an attacker
- outweighs this. */
- printf ("error 0 no such user %s in CVSROOT/passwd\n", username);
+ return PAM_CONV_ERR;
+ }
+ for (i = 0; i < num_msg; i++)
+ {
+ response[i].resp_retcode = 0;
+ response[i].resp = 0;
+ switch(msg[i]->msg_style)
+ {
+ case PAM_PROMPT_ECHO_ON:
+ response[i].resp = strdup(ui->username);
+ break;
+ case PAM_PROMPT_ECHO_OFF:
+ response[i].resp = strdup(ui->password);
+ break;
+ default:
+ if(response)
+ {
+ free(response);
+ }
+ return PAM_CONV_ERR;
+ }
+ }
- error_exit ();
+ *resp = response;
+ return PAM_SUCCESS;
+}
+
+static int
+check_system_password (username, password)
+ char *username, *password;
+{
+ pam_handle_t *pamh = NULL;
+ int retval;
+ struct cvs_pam_userinfo ui = { username, password };
+ struct pam_conv conv = { cvs_pam_conv, (void *)&ui };
+
+#ifdef HAVE_BROKEN_PAM_APPDATA
+ global_userinfo = &ui;
+#endif
+
+ retval = pam_start(PAM_SERVICENAME, username, &conv, &pamh);
+
+ if (retval == PAM_SUCCESS)
+ {
+ retval = pam_authenticate(pamh, 0);
}
- /* No cvs password found, so try /etc/passwd. */
+ if (retval == PAM_SUCCESS)
+ {
+ retval = pam_acct_mgmt(pamh, 0);
+ }
+
+ if (pam_end(pamh,retval) != PAM_SUCCESS)
+ {
+ printf("E Fatal error, aborting.\n
+ pam failed to release authenticator\n");
+ error_exit ();
+ }
+ return (retval == PAM_SUCCESS); /* indicate success */
+}
+#else
+static int
+check_system_password (username, password)
+ char *username, *password;
+{
+ char *found_passwd = NULL;
+ struct passwd *pw;
#ifdef HAVE_GETSPNAM
{
struct spwd *spw;
@@ -5568,7 +5632,7 @@
spw = getspnam (username);
if (spw != NULL)
{
- found_passwd = spw->sp_pwdp;
+ found_passwd = spw->sp_pwdp;
}
}
#endif
@@ -5606,33 +5670,83 @@
/* user exists and has a password */
if (strcmp (found_passwd, crypt (password, found_passwd)) == 0)
{
- host_user = xstrdup (username);
+ return 1;
}
else
{
- host_user = NULL;
#ifdef LOG_AUTHPRIV
- syslog (LOG_AUTHPRIV | LOG_NOTICE,
- "password mismatch for %s: %s vs. %s", username,
- crypt(password, found_passwd), found_passwd);
+ syslog (LOG_AUTHPRIV | LOG_NOTICE,
+ "password mismatch for %s: %s vs. %s", username,
+ crypt(password, found_passwd), found_passwd);
#endif
+ return 0;
}
- goto handle_return;
}
if (password && *password)
{
- /* user exists and has no system password, but we got
- one as parameter */
- host_user = xstrdup (username);
+ return 1;
+ }
+
+ return 0;
+}
+#endif
+
+/* Return a hosting username if password matches, else NULL. */
+static char *
+check_password (username, password, repository)
+ char *username, *password, *repository;
+{
+ int rc;
+ char *host_user = NULL;
+
+ /* First we see if this user has a password in the CVS-specific
+ password file. If so, that's enough to authenticate with. If
+ not, we'll check /etc/passwd. */
+
+ rc = check_repository_password (username, password, repository,
+ &host_user);
+
+ if (rc == 2)
+ return NULL;
+
+ if (rc == 1)
+ {
+ /* host_user already set by reference, so just return. */
goto handle_return;
}
- /* user exists but has no password at all */
+ assert (rc == 0);
+
+ if (!system_auth)
+ {
+ /* Note that the message _does_ distinguish between the case in
+ which we check for a system password and the case in which
+ we do not. It is a real pain to track down why it isn't
+ letting you in if it won't say why, and I am not convinced
+ that the potential information disclosure to an attacker
+ outweighs this. */
+ printf ("error 0 no such user %s in CVSROOT/passwd\n", username);
+
+ error_exit ();
+ }
+
+ /* No cvs password found, so try /etc/passwd. */
+ if ( check_system_password(username, password) )
+ {
+ host_user = xstrdup (username);
+ }
+ else
+ {
host_user = NULL;
+ }
+
#ifdef LOG_AUTHPRIV
+ if (!host_user)
+ {
syslog (LOG_AUTHPRIV | LOG_NOTICE,
"login refused for %s: user has no password", username);
+ }
#endif
handle_return:
- PAM authentication patch - v2,
Brian Murphy <=