19034156 PAM coversation function for passwd auth method has an incorrect assumption
authorHuie-Ying Lee <huieying.lee@oracle.com>
Tue, 01 Jul 2014 13:51:11 -0700
changeset 1979 bdbb0de8834e
parent 1978 e9a8064da9fd
child 1980 aa71e25d90c5
19034156 PAM coversation function for passwd auth method has an incorrect assumption 18890096 migrate PAM enhancements from SunSSH to OpenSSH
components/openssh/Makefile
components/openssh/patches/015-pam_conversation_fix.patch
components/openssh/patches/016-pam_enhancement.patch
--- a/components/openssh/Makefile	Fri Jun 27 10:51:53 2014 -0600
+++ b/components/openssh/Makefile	Tue Jul 01 13:51:11 2014 -0700
@@ -47,7 +47,7 @@
 # Enable ASLR for this component
 ASLR_MODE = $(ASLR_ENABLE)
 
-CONFIGURE_OPTIONS += CFLAGS="$(CFLAGS) -DSET_USE_PAM -DDEPRECATE_SUNSSH_OPT -DLASTLOG_FIX -DKRB5_BUILD_FIX -DAUE_openssh=6172 -DDTRACE_SFTP -DDISABLE_BANNER"
+CONFIGURE_OPTIONS += CFLAGS="$(CFLAGS) -DSET_USE_PAM -DDEPRECATE_SUNSSH_OPT -DLASTLOG_FIX -DKRB5_BUILD_FIX -DAUE_openssh=6172 -DDTRACE_SFTP -DDISABLE_BANNER -DPAM_ENHANCEMENT -DPAM_BUGFIX"
 
 # We need to disable lazyloading of dynamic dependent libraries. During the
 # pre-authentication phase, sshd will chroot to /var/empty which doesn't
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openssh/patches/015-pam_conversation_fix.patch	Tue Jul 01 13:51:11 2014 -0700
@@ -0,0 +1,64 @@
+#
+# This patch contains an important bug fix for the PAM password userauth
+# conversation function. This bug fix was contributed back to the upstream in 
+# 2009, but it was not accepted by the upstream.  For more information, see
+# https://bugzilla.mindrot.org/show_bug.cgi?id=1681.
+#
+--- orig/auth-pam.c	Fri Jun 20 14:55:27 2014
++++ new/auth-pam.c	Fri Jun 20 14:54:39 2014
[email protected]@ -1111,11 +1111,13 @@
+ 	free(env);
+ }
+ 
++#ifndef PAM_BUGFIX
+ /*
+  * "Blind" conversation function for password authentication.  Assumes that
+  * echo-off prompts are for the password and stores messages for later
+  * display.
+  */
++#endif
+ static int
+ sshpam_passwd_conv(int n, sshpam_const struct pam_message **msg,
+     struct pam_response **resp, void *data)
[email protected]@ -1137,6 +1139,17 @@
+ 	for (i = 0; i < n; ++i) {
+ 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
+ 		case PAM_PROMPT_ECHO_OFF:
++#ifdef PAM_BUGFIX
++                       /*
++                        * PAM conversation function for the password userauth
++			* method (non-interactive) really cannot do any 
++			* prompting.  We set the PAM_AUTHTOK item in 
++                        * sshpam_auth_passwd()to avoid conversation. If some
++			* modules still try to converse, then the password
++			* userauth will fail.
++			*/
++			goto fail;
++#else
+ 			if (sshpam_password == NULL)
+ 				goto fail;
+ 			if ((reply[i].resp = strdup(sshpam_password)) == NULL)
[email protected]@ -1143,6 +1156,7 @@
+ 				goto fail;
+ 			reply[i].resp_retcode = PAM_SUCCESS;
+ 			break;
++#endif
+ 		case PAM_ERROR_MSG:
+ 		case PAM_TEXT_INFO:
+ 			len = strlen(PAM_MSG_MEMBER(msg, i, msg));
[email protected]@ -1197,6 +1211,15 @@
+ 	    options.permit_root_login != PERMIT_YES))
+ 		sshpam_password = badpw;
+ 
++#ifdef PAM_BUGFIX
++  	sshpam_err = pam_set_item(sshpam_handle, PAM_AUTHTOK, password);
++	if (sshpam_err != PAM_SUCCESS) {
++		debug("PAM: %s: failed to set PAM_AUTHTOK: %s", __func__,
++		    pam_strerror(sshpam_handle, sshpam_err));
++		return 0;
++	}
++#endif
++
+ 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
+ 	    (const void *)&passwd_conv);
+ 	if (sshpam_err != PAM_SUCCESS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openssh/patches/016-pam_enhancement.patch	Tue Jul 01 13:51:11 2014 -0700
@@ -0,0 +1,478 @@
+#
+# This patch contains a couple of PAM enhancements:
+#   1) Each SSHv2 userauth method has its own PAM service name so that PAM can
+#      be used to control what userauth methods are allowed.
+#   2) The PAMServiceName and PAMServicePrefix options.
+# 
+# We have contributed back this feature to the OpenSSH upstream community. 
+# For more information, see https://bugzilla.mindrot.org/show_bug.cgi?id=2246
+# In the future, if these enhancements are accepted by the upsteam in a 
+# later release, we will remove this patch when we upgrade to that release.
+#
+--- orig/auth-pam.c	Fri Jun 20 15:39:05 2014
++++ new/auth-pam.c	Fri Jun 20 16:47:09 2014
[email protected]@ -617,6 +617,72 @@
+ 	sshpam_handle = NULL;
+ }
+ 
++#ifdef PAM_ENHANCEMENT
++char *
++derive_pam_service_name(Authctxt *authctxt)
++{
++	char *svcname = xmalloc(BUFSIZ);
++
++	/*
++	 * If PamServiceName is set we use that for everything, including
++	 * SSHv1
++	 */
++	if (options.pam_service_name != NULL) {
++		(void) strlcpy(svcname, options.pam_service_name, BUFSIZ);
++		return (svcname);
++	}
++
++	if (compat20) {
++		char *method_name = authctxt->authmethod_name;
++
++		if (!method_name)
++			fatal("Userauth method unknown while starting PAM");
++
++		/*
++		 * For SSHv2 we use "sshd-<userauth name>
++		 * The "sshd" prefix can be changed via the PAMServicePrefix
++		 * sshd_config option.
++		 */
++		if (strcmp(method_name, "none") == 0) {
++			snprintf(svcname, BUFSIZ, "%s-none",
++			    options.pam_service_prefix);
++		}
++		if (strcmp(method_name, "password") == 0) {
++			snprintf(svcname, BUFSIZ, "%s-password",
++			    options.pam_service_prefix);
++		}
++		if (strcmp(method_name, "keyboard-interactive") == 0) {
++			/* "keyboard-interactive" is too long, shorten it */
++			snprintf(svcname, BUFSIZ, "%s-kbdint",
++			    options.pam_service_prefix);
++		}
++		if (strcmp(method_name, "publickey") == 0) {
++			/* "publickey" is too long, shorten it */
++			snprintf(svcname, BUFSIZ, "%s-pubkey",
++			    options.pam_service_prefix);
++		}
++		if (strcmp(method_name, "hostbased") == 0) {
++			snprintf(svcname, BUFSIZ, "%s-hostbased",
++			    options.pam_service_prefix);
++		}
++		if (strncmp(method_name, "gssapi-", 7) == 0) {
++		        /*
++			 * Although OpenSSH only supports "gssapi-with-mic"
++			 * for now. We will still map any userauth method
++                         * prefixed with "gssapi-" to the gssapi PAM service.
++			 */ 
++			snprintf(svcname, BUFSIZ, "%s-gssapi",
++			    options.pam_service_prefix);
++		}
++		return svcname;
++	} else {
++		/* SSHv1 doesn't get to be so cool */
++	        snprintf(svcname, BUFSIZ, "sshd-v1");
++	}
++	return svcname;
++}
++#endif /* PAM_ENHANCEMENT */
++
+ static int
+ sshpam_init(Authctxt *authctxt)
+ {
[email protected]@ -624,18 +690,61 @@
+ 	const char *pam_rhost, *pam_user, *user = authctxt->user;
+ 	const char **ptr_pam_user = &pam_user;
+ 
++#ifdef PAM_ENHANCEMENT
++	const char *pam_service;
++        const char **ptr_pam_service = &pam_service;
++	char *svc = NULL;
++
++	svc = derive_pam_service_name(authctxt);
++        debug3("PAM service is %s", svc);
++#endif
++
+ 	if (sshpam_handle != NULL) {
++#ifdef PAM_ENHANCEMENT
++	        /* get the pam service name */
++		sshpam_err = pam_get_item(sshpam_handle,
++		    PAM_SERVICE, (sshpam_const void **)ptr_pam_service);
++                if (sshpam_err != PAM_SUCCESS) 
++		    fatal("Failed to get the PAM service name");
++		debug3("Previous pam_service is %s", pam_service ?
++                    pam_service : "NULL");
++
++		/* get the pam user name */
++		sshpam_err = pam_get_item(sshpam_handle,
++		    PAM_USER, (sshpam_const void **)ptr_pam_user);
++
++		/*
++		 * only need to re-start if either user or service is 
++                 * different.
++                 */
++		if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0
++		    && strncmp(svc, pam_service, strlen(svc)) == 0) {
++		        free(svc);
++			return (0);
++                }
++
++#else /* Original */
+ 		/* We already have a PAM context; check if the user matches */
+ 		sshpam_err = pam_get_item(sshpam_handle,
+ 		    PAM_USER, (sshpam_const void **)ptr_pam_user);
+ 		if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
+ 			return (0);
++#endif /* PAM_ENHANCEMENT */
+ 		pam_end(sshpam_handle, sshpam_err);
+ 		sshpam_handle = NULL;
+ 	}
+ 	debug("PAM: initializing for \"%s\"", user);
++
++#ifdef PAM_ENHANCEMENT
++        debug3("Starting PAM service %s for user %s method %s", svc, user,
++            authctxt->authmethod_name);
+ 	sshpam_err =
++	    pam_start(svc, user, &store_conv, &sshpam_handle);
++	free(svc);
++#else /* Original */
++	sshpam_err =
+ 	    pam_start(SSHD_PAM_SERVICE, user, &store_conv, &sshpam_handle);
++#endif
+ 	sshpam_authctxt = authctxt;
+ 
+ 	if (sshpam_err != PAM_SUCCESS) {
+--- orig/auth.h	Fri Jun 20 15:39:05 2014
++++ new/auth.h	Fri Jun 20 15:39:05 2014
[email protected]@ -76,6 +76,9 @@
+ #endif
+ 	Buffer		*loginmsg;
+ 	void		*methoddata;
++#ifdef PAM_ENHANCEMENT
++        char            *authmethod_name;
++#endif 
+ };
+ /*
+  * Every authentication method has to handle authentication requests for
+--- orig/auth2.c	Fri Jun 20 15:39:05 2014
++++ new/auth2.c	Fri Jun 20 15:39:05 2014
[email protected]@ -249,10 +249,13 @@
+ 			PRIVSEP(audit_event(SSH_INVALID_USER));
+ #endif
+ 		}
++
++#ifndef PAM_ENHANCEMENT
+ #ifdef USE_PAM
+ 		if (options.use_pam)
+ 			PRIVSEP(start_pam(authctxt));
+ #endif
++#endif
+ 		setproctitle("%s%s", authctxt->valid ? user : "unknown",
+ 		    use_privsep ? " [net]" : "");
+ 		authctxt->service = xstrdup(service);
[email protected]@ -286,6 +289,14 @@
+ 	/* try to authenticate user */
+ 	m = authmethod_lookup(authctxt, method);
+ 	if (m != NULL && authctxt->failures < options.max_authtries) {
++
++#ifdef PAM_ENHANCEMENT
++                authctxt->authmethod_name = xstrdup(method);
++                if (use_privsep)
++                       mm_inform_authmethod(method);
++		if (options.use_pam)
++		       PRIVSEP(start_pam(authctxt));
++#endif
+ 		debug2("input_userauth_request: try method %s", method);
+ 		authenticated =	m->userauth(authctxt);
+ 	}
[email protected]@ -303,6 +314,10 @@
+ 	char *methods;
+ 	int partial = 0;
+ 
++#ifdef  PAM_ENHANCEMENT
++        debug3("%s: entering", __func__);
++#endif
++
+ 	if (!authctxt->valid && authenticated)
+ 		fatal("INTERNAL ERROR: authenticated invalid user %s",
+ 		    authctxt->user);
[email protected]@ -623,5 +638,3 @@
+ 		fatal("%s: method not in AuthenticationMethods", __func__);
+ 	return 0;
+ }
+-
+-
+--- orig/monitor_wrap.c	Fri Jun 20 15:39:05 2014
++++ new/monitor_wrap.c	Fri Jun 20 15:39:05 2014
[email protected]@ -338,6 +338,24 @@
+ 	buffer_free(&m);
+ }
+ 
++#ifdef PAM_ENHANCEMENT
++/* Inform the privileged process about the authentication method */
++void
++mm_inform_authmethod(char *authmethod)
++{
++	Buffer m;
++
++	debug3("%s entering", __func__);
++
++	buffer_init(&m);
++	buffer_put_cstring(&m, authmethod);
++
++	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHMETHOD, &m);
++
++	buffer_free(&m);
++}
++#endif
++
+ /* Do the password authentication */
+ int
+ mm_auth_password(Authctxt *authctxt, char *password)
+--- orig/monitor.c	Fri Jun 20 15:39:05 2014
++++ new/monitor.c	Fri Jun 20 15:39:05 2014
[email protected]@ -146,6 +146,9 @@
+ int mm_answer_pwnamallow(int, Buffer *);
+ int mm_answer_auth2_read_banner(int, Buffer *);
+ int mm_answer_authserv(int, Buffer *);
++#ifdef PAM_ENHANCEMENT
++int mm_answer_authmethod(int, Buffer *);
++#endif
+ int mm_answer_authpassword(int, Buffer *);
+ int mm_answer_bsdauthquery(int, Buffer *);
+ int mm_answer_bsdauthrespond(int, Buffer *);
[email protected]@ -225,10 +228,17 @@
+     {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign},
+     {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow},
+     {MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv},
++#ifdef PAM_ENHANCEMENT
++    {MONITOR_REQ_AUTHMETHOD, MON_ISAUTH, mm_answer_authmethod},
++#endif
+     {MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner},
+     {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword},
+ #ifdef USE_PAM
++#ifdef PAM_ENHANCEMENT
++    {MONITOR_REQ_PAM_START, MON_ISAUTH, mm_answer_pam_start},
++#else
+     {MONITOR_REQ_PAM_START, MON_ONCE, mm_answer_pam_start},
++#endif
+     {MONITOR_REQ_PAM_ACCOUNT, 0, mm_answer_pam_account},
+     {MONITOR_REQ_PAM_INIT_CTX, MON_ISAUTH, mm_answer_pam_init_ctx},
+     {MONITOR_REQ_PAM_QUERY, MON_ISAUTH, mm_answer_pam_query},
[email protected]@ -828,6 +838,10 @@
+ 		/* Allow service/style information on the auth context */
+ 		monitor_permit(mon_dispatch, MONITOR_REQ_AUTHSERV, 1);
+ 		monitor_permit(mon_dispatch, MONITOR_REQ_AUTH2_READ_BANNER, 1);
++#ifdef PAM_ENHANCEMENT
++                /* Allow authmethod information on the auth context */
++		monitor_permit(mon_dispatch, MONITOR_REQ_AUTHMETHOD, 1);
++#endif
+ 	}
+ #ifdef USE_PAM
+ 	if (options.use_pam)
[email protected]@ -868,7 +882,25 @@
+ 	return (0);
+ }
+ 
++#ifdef PAM_ENHANCEMENT
+ int
++mm_answer_authmethod(int sock, Buffer *m)
++{
++	monitor_permit_authentications(1);
++
++	authctxt->authmethod_name = buffer_get_string(m, NULL);
++	debug3("%s: authmethod_name=%s", __func__, authctxt->authmethod_name);
++
++	if (strlen(authctxt->authmethod_name) == 0) {
++		free(authctxt->authmethod_name);
++		authctxt->authmethod_name = NULL;
++	}
++
++	return (0);
++}
++#endif
++
++int
+ mm_answer_authpassword(int sock, Buffer *m)
+ {
+ 	static int call_count;
+--- orig/monitor.h	Fri Jun 20 15:39:05 2014
++++ new/monitor.h	Fri Jun 20 15:39:05 2014
[email protected]@ -70,6 +70,9 @@
+ 	MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111,
+ 	MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113,
+ 
++#ifdef PAM_ENHANCEMENT
++        MONITOR_REQ_AUTHMETHOD = 114,
++#endif        
+ };
+ 
+ struct mm_master;
+--- orig/servconf.c	Fri Jun 20 15:39:05 2014
++++ new/servconf.c	Fri Jun 20 15:39:05 2014
[email protected]@ -154,6 +154,10 @@
+ 	options->ip_qos_interactive = -1;
+ 	options->ip_qos_bulk = -1;
+ 	options->version_addendum = NULL;
++#ifdef PAM_ENHANCEMENT
++	options->pam_service_name = NULL;
++	options->pam_service_prefix = NULL;
++#endif
+ }
+ 
+ void
[email protected]@ -303,6 +307,12 @@
+ 		options->ip_qos_bulk = IPTOS_THROUGHPUT;
+ 	if (options->version_addendum == NULL)
+ 		options->version_addendum = xstrdup("");
++
++#ifdef PAM_ENHANCEMENT
++        if (options->pam_service_prefix == NULL)
++                options->pam_service_prefix = _SSH_PAM_SERVICE_PREFIX;
++#endif
++
+ 	/* Turn privilege separation on by default */
+ 	if (use_privsep == -1)
+ 		use_privsep = PRIVSEP_NOSANDBOX;
[email protected]@ -351,6 +361,9 @@
+ 	sKexAlgorithms, sIPQoS, sVersionAddendum,
+ 	sAuthorizedKeysCommand, sAuthorizedKeysCommandUser,
+ 	sAuthenticationMethods, sHostKeyAgent,
++#ifdef PAM_ENHANCEMENT
++	sPAMServicePrefix, sPAMServiceName,
++#endif
+ 	sDeprecated, sUnsupported
+ } ServerOpCodes;
+ 
[email protected]@ -482,6 +495,10 @@
+ 	{ "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL },
+ 	{ "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL },
+ 	{ "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL },
++#ifdef PAM_ENHANCEMENT
++        { "pamserviceprefix", sPAMServicePrefix, SSHCFG_GLOBAL },
++        { "pamservicename", sPAMServiceName, SSHCFG_GLOBAL },
++#endif
+ 	{ NULL, sBadOption, 0 }
+ };
+ 
[email protected]@ -1632,6 +1649,30 @@
+ 		}
+ 		return 0;
+ 
++	case sPAMServicePrefix:
++		arg = strdelim(&cp);
++		if (!arg || *arg == '\0')
++			fatal("%s line %d: Missing argument.",
++			    filename, linenum);
++		if (options->pam_service_name != NULL)
++			fatal("%s line %d: PAMServiceName and PAMServicePrefix"
++			    " are mutually exclusive.", filename, linenum);
++		if (options->pam_service_prefix == NULL)
++			options->pam_service_prefix = xstrdup(arg);
++		break;
++
++	case sPAMServiceName:
++		arg = strdelim(&cp);
++		if (!arg || *arg == '\0')
++			fatal("%s line %d: Missing argument.",
++			    filename, linenum);
++		if (options->pam_service_prefix != NULL)
++			fatal("%s line %d: PAMServiceName and PAMServicePrefix"
++			    " are mutually exclusive.", filename, linenum);
++		if (options->pam_service_name == NULL)
++			options->pam_service_name = xstrdup(arg);
++		break;
++
+ 	case sDeprecated:
+ 		logit("%s line %d: Deprecated option %s",
+ 		    filename, linenum, arg);
+--- orig/servconf.h	Fri Jun 20 15:39:05 2014
++++ new/servconf.h	Fri Jun 20 15:39:05 2014
[email protected]@ -54,6 +54,10 @@
+ /* Magic name for internal sftp-server */
+ #define INTERNAL_SFTP_NAME	"internal-sftp"
+ 
++#ifdef PAM_ENHANCEMENT
++#define _SSH_PAM_SERVICE_PREFIX "sshd"
++#endif
++
+ typedef struct {
+ 	u_int	num_ports;
+ 	u_int	ports_from_cmdline;
[email protected]@ -185,6 +189,12 @@
+ 
+ 	u_int	num_auth_methods;
+ 	char   *auth_methods[MAX_AUTH_METHODS];
++
++#ifdef PAM_ENHANCEMENT
++	char   *pam_service_prefix;
++	char   *pam_service_name;
++#endif
++        
+ }       ServerOptions;
+ 
+ /* Information about the incoming connection as used by Match */
+--- orig/sshd_config.5	Fri Jun 20 15:39:05 2014
++++ new/sshd_config.5	Fri Jun 20 15:39:05 2014
[email protected]@ -868,6 +868,21 @@
+ are refused if the number of unauthenticated connections reaches
+ .Dq full
+ (60).
++.It Cm PAMServiceName
++Specifies the PAM service name for the PAM session. The PAMServiceName and 
++PAMServicePrefix options are mutually exclusive and if both set, sshd does not
++start. If this option is set the service name is the same for all user 
++authentication methods. The option has no default value. See PAMServicePrefix 
++for more information.
++.It Cm PAMServicePrefix
++Specifies the PAM service name prefix for service names used for individual 
++user authentication methods. The default is sshd. The PAMServiceName and 
++PAMServicePrefix options are mutually exclusive and if both set, sshd does not 
++start.
++.Pp
++For example, if this option is set to admincli, the service name for the 
++keyboard-interactive authentication method is admincli-kbdint instead of the 
++default sshd-kbdint.
+ .It Cm PasswordAuthentication
+ Specifies whether password authentication is allowed.
+ The default is
+--- orig/sshd.8	Fri Jun 20 15:39:05 2014
++++ new/sshd.8	Fri Jun 20 15:39:05 2014
[email protected]@ -951,6 +951,33 @@
+ started last).
+ The content of this file is not sensitive; it can be world-readable.
+ .El
++
++.Sh SECURITY
++sshd uses pam(3PAM) for password and keyboard-interactive methods as well as 
++for account management, session management, and the password management for all
++authentication methods.
++.Pp
++Each SSHv2 userauth type has its own PAM service name:
++
++.Bd -literal -offset 3n
++
++-----------------------------------------------
++| SSHv2 Userauth       | PAM Service Name     |
++-----------------------------------------------
++| none                 | sshd-none            |
++-----------------------------------------------
++| password             | sshd-password        |
++-----------------------------------------------
++| keyboard-interactive | sshd-kbdint          |
++-----------------------------------------------
++| pubkey               | sshd-pubkey          |
++-----------------------------------------------
++| hostbased            | sshd-hostbased       |
++-----------------------------------------------
++| gssapi-with-mic      | sshd-gssapi          |
++-----------------------------------------------
++.Ed
++
+ .Sh SEE ALSO
+ .Xr scp 1 ,
+ .Xr sftp 1 ,