patch for pam authentication in cvs

Brian Murphy <[email protected]>
Newsgroups gmane.comp.version-control.cvs.bugs
Message-ID <[email protected]>
Here is a patch to allow cvs to authenticate via pam.
You need a configuration file like this (for linux)
in /etc/pam.d/cvs:

auth            sufficient        pam_ldap.so
account         sufficient        pam_ldap.so

auth            required        pam_unix.so
account         required        pam_unix.so

to authenticate via ldap first and then to try via
the standard unix shadow/passwd method.

I have not tested this but it should also work on
any system supporting PAM (sun, hp, freebsd).

I hope this comes into the standard distribution. If not
I know there are others with the same needs as me who
can use this.

If there are any problems just contact me and I'll be
glad to fix them.

/Brian

_______________________________________________
Bug-cvs mailing list
[email protected]
http://mail.gnu.org/mailman/listinfo/bug-cvs
cvs-pam.patch.current (text/plain, 7.1 KB)
Index: config.h.in
===================================================================
RCS file: /cvs/ccvs/config.h.in,v
retrieving revision 1.74
diff -u -r1.74 config.h.in
--- config.h.in	19 Mar 2003 21:13:29 -0000	1.74
+++ config.h.in	11 Apr 2003 13:47:00 -0000
@@ -205,6 +205,9 @@
 
 /* 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
 
 /* Define to 1 if you have the `putenv' function. */
 #undef HAVE_PUTENV
Index: configure.in
===================================================================
RCS file: /cvs/ccvs/configure.in,v
retrieving revision 1.187
diff -u -r1.187 configure.in
--- configure.in	1 Apr 2003 22:49:26 -0000	1.187
+++ configure.in	11 Apr 2003 13:47:07 -0000
@@ -315,6 +315,11 @@
 dnl begin --with-*
 dnl
 
+AC_CHECK_HEADER(security/pam_appl.h, 
+  AC_DEFINE(HAVE_PAM, 1, [Defined if you have pam])
+)
+AC_CHECK_LIB(pam, pam_start, [LIBS="${LIBS} -lpam"])
+
 dnl
 dnl set $(KRB4) from --with-krb4=value -- WITH_KRB4
 dnl
Index: src/server.c
===================================================================
RCS file: /cvs/ccvs/src/server.c,v
retrieving revision 1.290
diff -u -r1.290 server.c
--- src/server.c	17 Mar 2003 06:32:11 -0000	1.290
+++ src/server.c	11 Apr 2003 13:47:12 -0000
@@ -5548,50 +5548,99 @@
     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;
+#if SOLARIS2 == 260
+struct cvs_pam_userinfo *global_userinfo;
+#endif
 
-    if (rc == 1)
-    {
-	/* host_user already set by reference, so just return. */
-	goto handle_return;
-    }
+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;
+
+#if SOLARIS2 == 260
+    if(!ui)
+        ui = global_userinfo;
+#endif
 
-    assert (rc == 0);
+    if (!ui || !ui->username || !ui->password || !msg || !resp)
+        return PAM_CONV_ERR;
 
-    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);
+    response = malloc(num_msg * sizeof(struct pam_response));
+    if (!response)
+        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;
+        }
+    }
+
+    *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 };
 
-	error_exit ();
-    }
+#if SOLARIS2 == 260
+      global_userinfo = &ui;
+#endif
 
-    /* No cvs password found, so try /etc/passwd. */
+    retval = pam_start(PAM_SERVICENAME, username, &conv, &pamh);
+
+    if (retval == PAM_SUCCESS)
+        retval = pam_authenticate(pamh, 0);
+
+    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;
@@ -5637,32 +5686,74 @@
 	/* 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);
 #endif
+            return 0;
 	}
-	goto handle_return;
     }
 
     if (password && *password)
+        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)
     {
-	/* user exists and has no system password, but we got
-	   one as parameter */
-	host_user = xstrdup (username);
+	/* host_user already set by reference, so just return. */
 	goto handle_return;
     }
 
-    /* user exists but has no password at all */
-    host_user = NULL;
+    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
-    syslog (LOG_AUTHPRIV | LOG_NOTICE,
+    if (!host_user)
+        syslog (LOG_AUTHPRIV | LOG_NOTICE,
 	    "login refused for %s: user has no password", username);
 #endif
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.