19034156 PAM coversation function for passwd auth method has an incorrect assumption
18890096 migrate PAM enhancements from SunSSH to 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 ,