components/openssh/patches/016-pam_enhancement.patch
author Jan Parcel <jan.parcel@oracle.com>
Mon, 01 Feb 2016 06:53:50 -0800
changeset 5376 4615bc2f4a50
parent 5036 06e4fcc325a1
child 7649 69d7508f0d66
permissions -rw-r--r--
22631538 Patch comment incorrect/outdated for patch 039-sshd_config_5_defaults.patch

#
# 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.
#
diff -pur old/auth-pam.c new/auth-pam.c
--- old/auth-pam.c	2015-04-28 06:15:57.335765454 -0700
+++ new/auth-pam.c	2015-04-28 06:15:57.417753483 -0700
@@ -617,6 +617,72 @@ sshpam_cleanup(void)
 	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,71 @@ sshpam_init(Authctxt *authctxt)
 	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);
+                }
+
+		/*
+		 * Clean up previous PAM state.  No need to clean up session 
+		 * and creds.
+		 */
+                sshpam_authenticated = 0;
+                sshpam_account_status = -1;
+
+		sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, NULL);
+         	if (sshpam_err != PAM_SUCCESS)
+		        debug3("Cannot remove PAM conv"); /* a warning only */
+#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) {
diff -pur old/auth.h new/auth.h
--- old/auth.h	2015-03-16 22:49:20.000000000 -0700
+++ new/auth.h	2015-04-28 06:18:25.719914272 -0700
@@ -81,6 +81,9 @@ struct Authctxt {
 
 	struct sshkey	**prev_userkeys;
 	u_int		 nprev_userkeys;
+#ifdef PAM_ENHANCEMENT
+        char            *authmethod_name;
+#endif 
 };
 /*
  * Every authentication method has to handle authentication requests for
diff -pur old/auth2.c new/auth2.c
--- old/auth2.c	2015-03-16 22:49:20.000000000 -0700
+++ new/auth2.c	2015-04-28 06:15:57.419262466 -0700
@@ -243,10 +243,21 @@ input_userauth_request(int type, u_int32
 			PRIVSEP(audit_event(SSH_INVALID_USER));
 #endif
 		}
+
+
 #ifdef USE_PAM
+#ifdef PAM_ENHANCEMENT
+		/*
+		 * Start PAM here and once only, if each userauth does not
+		 * has its own PAM service.
+		 */
+	        if (options.use_pam && !options.pam_service_per_authmethod)
+			PRIVSEP(start_pam(authctxt));
+#else
 		if (options.use_pam)
 			PRIVSEP(start_pam(authctxt));
 #endif
+#endif
 		setproctitle("%s%s", authctxt->valid ? user : "unknown",
 		    use_privsep ? " [net]" : "");
 		authctxt->service = xstrdup(service);
@@ -277,6 +288,18 @@ input_userauth_request(int type, u_int32
 	/* try to authenticate user */
 	m = authmethod_lookup(authctxt, method);
 	if (m != NULL && authctxt->failures < options.max_authtries) {
+
+#if defined(USE_PAM) && defined(PAM_ENHANCEMENT)
+		/* start PAM service for each userauth */
+                if (options.use_pam && options.pam_service_per_authmethod) {
+       		        if (authctxt->authmethod_name != NULL)
+		                free(authctxt->authmethod_name);
+                        authctxt->authmethod_name = xstrdup(method);
+                        if (use_privsep)
+                                mm_inform_authmethod(method);
+		        PRIVSEP(start_pam(authctxt));
+		}
+#endif
 		debug2("input_userauth_request: try method %s", method);
 		authenticated =	m->userauth(authctxt);
 	}
@@ -295,6 +318,10 @@ userauth_finish(Authctxt *authctxt, int
 	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);
@@ -311,6 +338,25 @@ userauth_finish(Authctxt *authctxt, int
 	}
 
 	if (authenticated && options.num_auth_methods != 0) {
+
+#if defined(USE_PAM) && defined(PAM_ENHANCEMENT)
+                /*
+                 * If each userauth has its own PAM service, then PAM need to 
+                 * perform account check for this service.
+                 */
+                if (options.use_pam && options.pam_service_per_authmethod &&
+                    !PRIVSEP(do_pam_account())) {
+                        /* if PAM returned a message, send it to the user */
+                        if (buffer_len(&loginmsg) > 0) {
+                                buffer_append(&loginmsg, "\0", 1);
+                                userauth_send_banner(buffer_ptr(&loginmsg));
+                                packet_write_wait();
+                        }
+
+                        fatal("Access denied for user %s by PAM account "
+                            "configuration", authctxt->user);
+                }
+#endif
 		if (!auth2_update_methods_lists(authctxt, method, submethod)) {
 			authenticated = 0;
 			partial = 1;
@@ -324,7 +370,20 @@ userauth_finish(Authctxt *authctxt, int
 		return;
 
 #ifdef USE_PAM
+
+#ifdef PAM_ENHANCEMENT
+        /*
+         * PAM needs to perform account checks after auth. However, if each
+         * userauth has its own PAM service and options.num_auth_methods != 0,
+         * then no need to perform account checking, because it was done 
+         * already.
+         */
+        if (options.use_pam && authenticated && 
+            !(options.num_auth_methods != 0 &&
+            options.pam_service_per_authmethod)){
+#else
 	if (options.use_pam && authenticated) {
+#endif
 		if (!PRIVSEP(do_pam_account())) {
 			/* if PAM returned a message, send it to the user */
 			if (buffer_len(&loginmsg) > 0) {
@@ -615,5 +674,3 @@ auth2_update_methods_lists(Authctxt *aut
 		fatal("%s: method not in AuthenticationMethods", __func__);
 	return 0;
 }
-
-
diff -pur old/monitor.c new/monitor.c
--- old/monitor.c	2015-03-16 22:49:20.000000000 -0700
+++ new/monitor.c	2015-04-28 06:15:57.421294814 -0700
@@ -127,6 +127,9 @@ int mm_answer_sign(int, Buffer *);
 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 *);
@@ -206,10 +209,17 @@ struct mon_table mon_dispatch_proto20[]
     {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},
@@ -371,6 +381,24 @@ monitor_child_preauth(Authctxt *_authctx
 			if (!compat20)
 				fatal("AuthenticationMethods is not supported"
 				    "with SSH protocol 1");
+
+#if defined(USE_PAM) && defined(PAM_ENHANCEMENT)
+                        /* 
+                         * If each userauth has its own PAM service, then PAM
+                         * need to perform account check for this service.
+                         */
+                        if (options.use_pam && authenticated &&
+                            options.pam_service_per_authmethod) {
+                                Buffer m;
+
+                                buffer_init(&m);
+                                mm_request_receive_expect(pmonitor->m_sendfd,
+                                    MONITOR_REQ_PAM_ACCOUNT, &m);
+                                authenticated = 
+                                    mm_answer_pam_account(pmonitor->m_sendfd, &m);
+                                buffer_free(&m);
+                         }
+#endif
 			if (authenticated &&
 			    !auth2_update_methods_lists(authctxt,
 			    auth_method, auth_submethod)) {
@@ -389,8 +417,21 @@ monitor_child_preauth(Authctxt *_authctx
 			    !auth_root_allowed(auth_method))
 				authenticated = 0;
 #ifdef USE_PAM
+#ifdef PAM_ENHANCEMENT
+                        /*
+                         * PAM needs to perform account checks after auth.
+                         * However, if each userauth has its own PAM service
+                         * and options.num_auth_methods != 0, then no need to
+                         * perform account checking, because it was done 
+                         * already.
+                         */
+                        if (options.use_pam && authenticated &&
+                            !(options.num_auth_methods != 0 &&
+                            options.pam_service_per_authmethod)) {
+#else
 			/* PAM needs to perform account checks after auth */
 			if (options.use_pam && authenticated) {
+#endif
 				Buffer m;
 
 				buffer_init(&m);
@@ -863,6 +904,10 @@ mm_answer_pwnamallow(int sock, Buffer *m
 		/* 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)
@@ -903,6 +948,24 @@ mm_answer_authserv(int sock, Buffer *m)
 	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)
 {
diff -pur old/monitor.h new/monitor.h
--- old/monitor.h	2015-03-16 22:49:20.000000000 -0700
+++ new/monitor.h	2015-04-28 06:15:57.421684373 -0700
@@ -65,6 +65,9 @@ enum monitor_reqtype {
 	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;
diff -pur old/monitor_wrap.c new/monitor_wrap.c
--- old/monitor_wrap.c	2015-03-16 22:49:20.000000000 -0700
+++ new/monitor_wrap.c	2015-04-28 06:15:57.419906674 -0700
@@ -347,6 +347,24 @@ mm_inform_authserv(char *service, char *
 	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)
diff -pur old/servconf.c new/servconf.c
--- old/servconf.c	2015-04-28 06:15:57.300968063 -0700
+++ new/servconf.c	2015-04-28 06:27:06.330272555 -0700
@@ -163,6 +163,18 @@ initialize_server_options(ServerOptions
 	options->ip_qos_bulk = -1;
 	options->version_addendum = NULL;
 	options->fingerprint_hash = -1;
+#ifdef PAM_ENHANCEMENT
+	options->pam_service_name = NULL;
+	options->pam_service_prefix = NULL;
+
+	/* 
+	 * Each user method will have its own PAM service by default.
+	 * However, if PAMServiceName is specified or the protocal version
+	 * is not compat20, then there will be only one PAM service for the
+	 * entire user authentication.
+	 */
+	options->pam_service_per_authmethod = 1;
+#endif
 }
 
 /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
@@ -332,6 +344,12 @@ fill_default_server_options(ServerOption
 		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
+
 	if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1)
 		options->fwd_opts.streamlocal_bind_mask = 0177;
 	if (options->fwd_opts.streamlocal_bind_unlink == -1)
@@ -400,6 +418,9 @@ typedef enum {
 	sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
 	sUsePrivilegeSeparation, sAllowAgentForwarding,
 	sHostCertificate,
+#ifdef PAM_ENHANCEMENT
+	sPAMServicePrefix, sPAMServiceName,
+#endif
 	sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
 	sKexAlgorithms, sIPQoS, sVersionAddendum,
 	sAuthorizedKeysCommand, sAuthorizedKeysCommandUser,
@@ -534,6 +555,10 @@ static struct {
 	{ "forcecommand", sForceCommand, SSHCFG_ALL },
 	{ "chrootdirectory", sChrootDirectory, SSHCFG_ALL },
 	{ "hostcertificate", sHostCertificate, SSHCFG_GLOBAL },
+#ifdef PAM_ENHANCEMENT
+	{ "pamserviceprefix", sPAMServicePrefix, SSHCFG_GLOBAL },
+	{ "pamservicename", sPAMServiceName, SSHCFG_GLOBAL },
+#endif
 	{ "revokedkeys", sRevokedKeys, SSHCFG_ALL },
 	{ "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL },
 	{ "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL },
@@ -1765,6 +1790,37 @@ process_server_config_line(ServerOptions
 			options->fingerprint_hash = value;
 		break;
 
+	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);
+
+			/*
+			 * When this option is specified, we will not have
+			 * PAM service for each auth method.
+                         */
+			options->pam_service_per_authmethod = 0;
+		}
+		break;
+
 	case sDeprecated:
 		logit("%s line %d: Deprecated option %s",
 		    filename, linenum, arg);
diff -pur old/servconf.h new/servconf.h
--- old/servconf.h	2015-03-16 22:49:20.000000000 -0700
+++ new/servconf.h	2015-04-28 06:28:25.181429777 -0700
@@ -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;
@@ -188,6 +192,12 @@ typedef struct {
 	u_int	num_auth_methods;
 	char   *auth_methods[MAX_AUTH_METHODS];
 
+#ifdef PAM_ENHANCEMENT
+	char   *pam_service_prefix;
+	char   *pam_service_name;
+	int	pam_service_per_authmethod;
+#endif
+        
 	int	fingerprint_hash;
 }       ServerOptions;
 
diff -pur old/sshd.8 new/sshd.8
--- old/sshd.8	2015-04-28 06:15:57.254681499 -0700
+++ new/sshd.8	2015-04-28 06:15:57.426325504 -0700
@@ -945,6 +945,33 @@ concurrently for different ports, this c
 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 ,
diff -pur old/sshd.c new/sshd.c
--- old/sshd.c	2015-04-28 06:15:57.302106750 -0700
+++ new/sshd.c	2015-04-28 06:15:57.427449259 -0700
@@ -2146,6 +2146,11 @@ main(int ac, char **av)
 
 	sshd_exchange_identification(sock_in, sock_out);
 
+#ifdef PAM_ENHANCEMENT
+	if (!compat20)
+	        options.pam_service_per_authmethod = 0;
+#endif
+
 	/* In inetd mode, generate ephemeral key only for proto 1 connections */
 	if (!compat20 && inetd_flag && sensitive_data.server_key == NULL)
 		generate_ephemeral_server_key();
diff -pur old/sshd_config.5 new/sshd_config.5
--- old/sshd_config.5	2015-04-28 06:15:57.256560985 -0700
+++ new/sshd_config.5	2015-04-28 06:15:57.425661853 -0700
@@ -1044,6 +1044,21 @@ The probability increases linearly and a
 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
@@ -1427,8 +1442,7 @@ If
 is enabled, you will not be able to run
 .Xr sshd 8
 as a non-root user.
-The default is
-.Dq no .
+On Solaris, the option is always enabled.
 .It Cm UsePrivilegeSeparation
 Specifies whether
 .Xr sshd 8