diff -ur cvs-1.12.1/config.h.in cvs-1.12.1.new/config.h.in --- cvs-1.12.1/config.h.in 2003-06-02 00:07:49.000000000 +0100 +++ cvs-1.12.1.new/config.h.in 2003-05-25 15:40:03.000000000 +0100 @@ -270,6 +270,9 @@ /* Define to 1 if you have the header file, and it defines `DIR'. */ #undef HAVE_NDIR_H +/* Define if you have the pam_open_session function. */ +#undef HAVE_PAM + /* Define to 1 if the `printf' function supports the %p format for printing pointers. */ #undef HAVE_PRINTF_PTR diff -ur cvs-1.12.1/configure cvs-1.12.1.new/configure --- cvs-1.12.1/configure 2003-01-16 20:16:56.000000000 +0000 @@ -8368,6 +8368,117 @@ fi +echo "$as_me:$LINENO: checking for library containing pam_open_session" >&5 +echo $ECHO_N "checking for library containing pam_open_session... $ECHO_C" >&6 +if test "${ac_cv_search_pam_open_session+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_pam_open_session=no +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char pam_open_session (); +int +main () +{ +pam_open_session (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_pam_open_session="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_pam_open_session" = no; then + for ac_lib in pam; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char pam_open_session (); +int +main () +{ +pam_open_session (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_pam_open_session="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_pam_open_session" >&5 +echo "${ECHO_T}$ac_cv_search_pam_open_session" >&6 +if test "$ac_cv_search_pam_open_session" != no; then + test "$ac_cv_search_pam_open_session" = "none required" || LIBS="$ac_cv_search_pam_open_session $LIBS" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_PAM 1 +_ACEOF + +fi + + echo "$as_me:$LINENO: checking whether utime accepts a null argument" >&5 echo $ECHO_N "checking whether utime accepts a null argument... $ECHO_C" >&6 if test "${ac_cv_func_utime_null+set}" = set; then +++ cvs-1.12.1.new/configure 2003-01-30 01:32:00.000000000 +0000 diff -ur cvs-1.12.1/configure.in cvs-1.12.1.new/configure.in --- cvs-1.12.1/configure.in 2003-01-16 20:16:56.000000000 +0000 +++ cvs-1.12.1.new/configure.in 2003-01-30 01:32:00.000000000 +0000 @@ -258,6 +258,12 @@ AC_SEARCH_LIBS(getspnam, sec gen, AC_DEFINE(HAVE_GETSPNAM, 1, [Define if you have the getspnam function.])) +dnl +dnl Check for pam support. +dnl +AC_SEARCH_LIBS(pam_open_session, pam, AC_DEFINE(HAVE_PAM, 1, +[Define if you have the pam_open_session function.])) + AC_FUNC_UTIME_NULL AC_SYS_LONG_FILE_NAMES diff -ur cvs-1.12.1/src/cvs.h cvs-1.12.1.new/src/cvs.h --- cvs-1.12.1/src/cvs.h 2002-12-28 18:01:30.000000000 +0000 +++ cvs-1.12.1.new/src/cvs.h 2003-01-30 01:32:17.000000000 +0000 @@ -457,6 +457,7 @@ void root_allow_add PROTO ((char *)); void root_allow_free PROTO ((void)); int root_allow_ok PROTO ((char *)); +void set_default_pam_user PROTO ((char *)); char *gca PROTO((const char *rev1, const char *rev2)); extern void check_numeric PROTO ((const char *, int, char **)); diff -ur cvs-1.12.1/src/main.c cvs-1.12.1.new/src/main.c --- cvs-1.12.1/src/main.c 2002-10-24 19:38:37.000000000 +0100 +++ cvs-1.12.1.new/src/main.c 2003-01-30 01:32:56.000000000 +0000 @@ -416,6 +416,9 @@ {"help-synonyms", 0, NULL, 2}, {"help-options", 0, NULL, 4}, {"allow-root", required_argument, NULL, 3}, +#ifdef HAVE_PAM + {"default-pam-user", required_argument, NULL, 5}, +#endif {0, 0, 0, 0} }; /* `getopt_long' stores the option index here, but right now we @@ -520,6 +523,12 @@ /* --allow-root */ root_allow_add (optarg); break; +#ifdef HAVE_PAM + case 5: + /* --default-pam-user */ + set_default_pam_user (optarg); + break; +#endif case 'Q': really_quiet = 1; /* FALL THROUGH */ diff -ur cvs-1.12.1/src/parseinfo.c cvs-1.12.1.new/src/parseinfo.c --- cvs-1.12.1/src/parseinfo.c 2002-12-06 19:09:26.000000000 +0000 +++ cvs-1.12.1.new/src/parseinfo.c 2003-01-30 01:33:22.000000000 +0000 @@ -398,6 +398,12 @@ else if (strcmp (p, "stat") == 0) RereadLogAfterVerify = LOGMSG_REREAD_STAT; } + else if (strcmp (line, "DefaultPamUser") == 0) + { +#ifdef HAVE_PAM + set_default_pam_user(p); +#endif + } /* Don't complain if we don't have PAM here... */ else { /* We may be dealing with a keyword which was added in a diff -ur cvs-1.12.1/src/server.c cvs-1.12.1.new/src/server.c --- cvs-1.12.1/src/server.c 2003-01-16 15:10:55.000000000 +0000 +++ cvs-1.12.1.new/src/server.c 2003-01-30 01:35:23.000000000 +0000 @@ -16,6 +16,12 @@ #include "getline.h" #include "buffer.h" +#ifdef HAVE_PAM +#include +#include +static char *default_pam_username = NULL; +#endif + #if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) # ifdef HAVE_GSSAPI /* This stuff isn't included solely with SERVER_SUPPORT since some of these @@ -5502,6 +5508,143 @@ return retval; } +#ifdef HAVE_PAM +/* The callback function that the pam modules will use to talk to + us. Modelled closely on the misc_conv module of Linux-PAM. This + blatantly subverts one of the principles of PAM - PAM is meant to + handle all the password work. But this does the job and means I can + transition to LDAP right now. SAM 2001/12/23 */ +int cvs_conv(int num_msg, const struct pam_message **msgm, + struct pam_response **response, void *appdata_ptr) +{ + int count=0; + struct pam_response *reply; + + if (num_msg <= 0) + return PAM_CONV_ERR; + + reply = (struct pam_response *) calloc(num_msg, + sizeof(struct pam_response)); + if (reply == NULL) + return PAM_CONV_ERR; + + for (count=0; count < num_msg; ++count) + { + char *string=NULL; + + switch (msgm[count]->msg_style) + { + case PAM_PROMPT_ECHO_OFF: + case PAM_PROMPT_ECHO_ON: + string = (char *)appdata_ptr; + break; + default: + break; + } + + if (string) /* must add to reply array */ + { + /* add string to list of responses */ + reply[count].resp_retcode = 0; + reply[count].resp = string; + string = NULL; + } + } + + *response = reply; + reply = NULL; + + return PAM_SUCCESS; +} + +static struct pam_conv conv = { + cvs_conv, + NULL +}; + +/* Modelled very closely on the example code in "The Linux-PAM + Application Developers' Guide" by Andrew G. Morgan. */ +/* Return a hosting username if password matches, else NULL. */ +static char * +check_pam_password (username, password, repository) + char *username, *password, *repository; +{ + pam_handle_t *pamh = NULL; + struct passwd *pw = NULL; + int retval; + int rc = 0; + char *host_user = NULL; + + conv.appdata_ptr = xstrdup(password); + + retval = pam_start("cvs", username, &conv, &pamh); + if (retval != PAM_SUCCESS) + return NULL; + + if (retval == PAM_SUCCESS) + retval = pam_authenticate(pamh, 0); /* is user really user? */ + + if (retval == PAM_SUCCESS) + retval = pam_acct_mgmt(pamh, 0); /* permitted access? */ + + /* This is where we have been authorized or not. */ + + switch(retval) + { + case PAM_SUCCESS: + host_user = xstrdup(username); + rc = 1; + break; + case PAM_AUTH_ERR: + host_user = NULL; + rc = 2; + break; + default: + host_user = NULL; + rc = 0; + break; + } + + /* now close PAM */ + if ( (rc != 0) && (pam_end(pamh,retval) != PAM_SUCCESS) ) + { + pamh = NULL; + error(0, 0, "pam: failed to release authenticator\n"); + } + + /* An issue with using pam is that the host may well not have a + local user entry to match the authenticated user. Check with + getpwnam; if that fails, then we can optionally fall back to a + specified local username */ + if(1 == rc) + { + pw = getpwnam (host_user); + if (pw == NULL) + { + if(NULL != default_pam_username) + { + free(host_user); + host_user = xstrdup(default_pam_username); + /* And don't check existence again - switch_to_user() + will do it for us later */ + } + } + } + + return host_user; +} + +/* Set the default user to use for a remote pam user for whom + getpwnam() will fail */ +void +set_default_pam_user (username) + char *username; +{ + if( (username != NULL) && (strlen(username) > 0)) + default_pam_username = xstrdup(username); +} + +#endif /* HAVE_PAM */ /* Return a hosting username if password matches, else NULL. */ static char * @@ -5581,7 +5731,7 @@ char *password = NULL; size_t password_allocated = 0; - char *host_user; + char *host_user = NULL; char *descrambled_password; #endif /* AUTH_SERVER_SUPPORT */ int verify_and_exit = 0; @@ -5715,7 +5865,15 @@ /* We need the real cleartext before we hash it. */ descrambled_password = descramble (password); - host_user = check_password (username, descrambled_password, repository); + +#ifdef HAVE_PAM + if (system_auth) + host_user = check_pam_password (username, descrambled_password, repository); +#endif /* HAVE_PAM */ + + if(NULL == host_user) + host_user = check_password (username, descrambled_password, repository); + if (host_user == NULL) { #ifdef HAVE_SYSLOG_H