bug-cvs
[Top][All Lists]
Advanced

[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:

reply via email to

[Prev in Thread] Current Thread [Next in Thread]