# HG changeset patch # User Huie-Ying Lee # Date 1404247871 25200 # Node ID bdbb0de8834ecfec7092c99ab478df74dc2fcb30 # Parent e9a8064da9fdee44a1e10454fa0d9fb53244cc92 19034156 PAM coversation function for passwd auth method has an incorrect assumption 18890096 migrate PAM enhancements from SunSSH to OpenSSH diff -r e9a8064da9fd -r bdbb0de8834e components/openssh/Makefile --- 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 diff -r e9a8064da9fd -r bdbb0de8834e components/openssh/patches/015-pam_conversation_fix.patch --- /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 +@@ -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) +@@ -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) +@@ -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)); +@@ -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) diff -r e9a8064da9fd -r bdbb0de8834e components/openssh/patches/016-pam_enhancement.patch --- /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 +@@ -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- ++ * 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 ,