components/openssh/patches/016-pam_enhancement.patch
author Huie-Ying Lee <huieying.lee@oracle.com>
Fri, 13 Mar 2015 17:05:08 -0700
branchs11-update
changeset 3946 b1e0e68de63b
child 4071 4b68c2b0134b
permissions -rw-r--r--
PSARC 2012/335 OpenSSH migration PSARC 2013/115 Shared configuration for SunSSH & OpenSSH PSARC 2014/078 OpenSSH 6.5 PSARC 2014/342 pam_unix_session lastlog support 15769261 SUNBT7135649 Deliver OpenSSH 6.0P1 in the userland gate 18205826 upgrade OpenSSH to 6.5p1 19579776 OpenSSH doesn't need to reference lastlog anymore now that PAM session mgmt does 18267729 Delegating credentials in OpenSSH 18828925 migrate the disablebanner feature from SunSSH to OpenSSH 18890096 migrate PAM enhancements from SunSSH to OpenSSH 19629847 OpenSSH does not support Solaris Audit for login/logout. 17997193 misc. problems in Makefile and openssh.p5m 18268681 openssh has non-existent /usr/local/lib in its runpath 18528305 /var/empty should be delivered readonly 19034156 PAM coversation function for passwd auth method has an incorrect assumption 19906401 should set AUTHTOK to NULL after pam_authenticate in sshpam_auth_passwd() 19517432 OpenSSH does not update utmpx on login 19570656 GSSAPIAuthentication option should default to yes 19591379 X11Forwarding and ForwardX11Trusted should default to yes 19465507 Deprecate SunSSH-only server options (e.g. iMaxAuthTriesLog) in OpenSSH 18898794 ssh connections fail with openssh, same config works with sunssh 20549448 OpenSSH X86 server core dump at audit_event 20656125 OpenSSH ed25519 algorithm signature verification failure 18435439 problem in UTILITY/OPENSSH 18491957 problem in UTILITY/OPENSSH

#
# 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
@@ -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)
 {
@@ -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
@@ -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
@@ -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);
@@ -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);
 	}
@@ -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);
@@ -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
@@ -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
@@ -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 *);
@@ -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},
@@ -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)
@@ -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
@@ -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
@@ -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
@@ -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;
@@ -351,6 +361,9 @@
 	sKexAlgorithms, sIPQoS, sVersionAddendum,
 	sAuthorizedKeysCommand, sAuthorizedKeysCommandUser,
 	sAuthenticationMethods, sHostKeyAgent,
+#ifdef PAM_ENHANCEMENT
+	sPAMServicePrefix, sPAMServiceName,
+#endif
 	sDeprecated, sUnsupported
 } ServerOpCodes;
 
@@ -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 }
 };
 
@@ -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
@@ -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;
@@ -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
@@ -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
@@ -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 ,