components/openssh/patches/016-pam_enhancement.patch
author Jan Parcel <jan.parcel@oracle.com>
Mon, 25 Jan 2016 10:57:40 -0800
branchs11u3-sru
changeset 5324 5683175b6e99
parent 4098 19376bf84775
child 7946 165bf092aa9c
permissions -rw-r--r--
PSARC/2015/395 OpenSSH 7.1p1 PSARC 2014/390 OpenSSH GSSKEY 21696247 upgrade OpenSSH to 7.1p1 22031540 problem in UTILITY/OPENSSH 22022180 problem in UTILITY/OPENSSH 22048638 problem in UTILITY/OPENSSH 19775805 OpenSSH contains a redundant call to do_pam_setcred() 21379157 OpenSSH shouldn't call setproject(3PROJECT) when configured to use PAM 20919294 upgrade OpenSSH to 6.8p1 19130869 migrate the Xforwarding bug fix (15350344) from SunSSH to OpenSSH 21861322 OpenSSH client hangs on broken pipe 22018764 remove cast128-cbc from OpenSSH 21919790 add GSSKeyEx as an alias to GSSAPIKeyExchange in OpenSSH 19941148 GSS-API Key Exchange for OpenSSH 21643415 OpenSSH should use AI_ADDRCONFIG per bug 19827438 20370803 OpenSSH patch number collision 20711463 OpenSSH wants to be able to login to a role too 22389801 OpenSSH: remove cast from ssh(1), sshd(8), ssh_config(5) and sshd_config(5) 22582153 openssh system/linker should be added to core REQ

#
# 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