[openssh] replace RequiredAuthentications2 with AuthenticationMethods according to upstream the upstream refus
plautrba
plautrba at fedoraproject.org
Mon Dec 3 09:42:00 UTC 2012
commit bffd1c2234eb41a912b2a278531eb470ccc67344
Author: Petr Lautrbach <plautrba at redhat.com>
Date: Fri Nov 30 16:23:29 2012 +0100
replace RequiredAuthentications2 with AuthenticationMethods according to upstream
the upstream refused original patch with RequiredAuthentications2, but they came with their own implementation of required authentications,
see https://bugzilla.mindrot.org/show_bug.cgi?id=983. The new method is more robust and flexible
it will be included in next openssh-6.2 release
openssh-6.1p1-authenticationmethods.patch | 841 +++++++++++++++++++++++++++++
openssh.spec | 5 +-
2 files changed, 843 insertions(+), 3 deletions(-)
---
diff --git a/openssh-6.1p1-authenticationmethods.patch b/openssh-6.1p1-authenticationmethods.patch
new file mode 100644
index 0000000..7b5a06a
--- /dev/null
+++ b/openssh-6.1p1-authenticationmethods.patch
@@ -0,0 +1,841 @@
+diff --git a/auth.c b/auth.c
+index ee0cb05..1b2fc2b 100644
+--- a/auth.c
++++ b/auth.c
+@@ -251,7 +251,8 @@ allowed_user(struct passwd * pw)
+ }
+
+ void
+-auth_log(Authctxt *authctxt, int authenticated, char *method, char *info)
++auth_log(Authctxt *authctxt, int authenticated, int partial,
++ const char *method, const char *submethod, const char *info)
+ {
+ void (*authlog) (const char *fmt,...) = verbose;
+ char *authmsg;
+@@ -268,12 +269,15 @@ auth_log(Authctxt *authctxt, int authenticated, char *method, char *info)
+
+ if (authctxt->postponed)
+ authmsg = "Postponed";
++ else if (partial)
++ authmsg = "Partial";
+ else
+ authmsg = authenticated ? "Accepted" : "Failed";
+
+- authlog("%s %s for %s%.100s from %.200s port %d%s",
++ authlog("%s %s%s%s for %s%.100s from %.200s port %d%s",
+ authmsg,
+ method,
++ submethod != NULL ? "/" : "", submethod == NULL ? "" : submethod,
+ authctxt->valid ? "" : "invalid user ",
+ authctxt->user,
+ get_remote_ipaddr(),
+@@ -303,7 +307,7 @@ auth_log(Authctxt *authctxt, int authenticated, char *method, char *info)
+ * Check whether root logins are disallowed.
+ */
+ int
+-auth_root_allowed(char *method)
++auth_root_allowed(const char *method)
+ {
+ switch (options.permit_root_login) {
+ case PERMIT_YES:
+diff --git a/auth.h b/auth.h
+index 0d786c4..29823bb 100644
+--- a/auth.h
++++ b/auth.h
+@@ -64,6 +64,8 @@ struct Authctxt {
+ #ifdef BSD_AUTH
+ auth_session_t *as;
+ #endif
++ char **auth_methods; /* modified from server config */
++ u_int num_auth_methods;
+ #ifdef KRB5
+ krb5_context krb5_ctx;
+ krb5_ccache krb5_fwd_ccache;
+@@ -142,12 +144,17 @@ void disable_forwarding(void);
+ void do_authentication(Authctxt *);
+ void do_authentication2(Authctxt *);
+
+-void auth_log(Authctxt *, int, char *, char *);
+-void userauth_finish(Authctxt *, int, char *);
++void auth_log(Authctxt *, int, int, const char *, const char *,
++ const char *);
++void userauth_finish(Authctxt *, int, const char *, const char *);
++int auth_root_allowed(const char *);
++
+ void userauth_send_banner(const char *);
+-int auth_root_allowed(char *);
+
+ char *auth2_read_banner(void);
++int auth2_methods_valid(const char *, int);
++int auth2_update_methods_lists(Authctxt *, const char *);
++int auth2_setup_methods_lists(Authctxt *);
+
+ void privsep_challenge_enable(void);
+
+diff --git a/auth1.c b/auth1.c
+index cc85aec..458a110 100644
+--- a/auth1.c
++++ b/auth1.c
+@@ -253,7 +253,8 @@ do_authloop(Authctxt *authctxt)
+ if (options.use_pam && (PRIVSEP(do_pam_account())))
+ #endif
+ {
+- auth_log(authctxt, 1, "without authentication", "");
++ auth_log(authctxt, 1, 0, "without authentication",
++ NULL, "");
+ return;
+ }
+ }
+@@ -352,7 +353,8 @@ do_authloop(Authctxt *authctxt)
+
+ skip:
+ /* Log before sending the reply */
+- auth_log(authctxt, authenticated, get_authname(type), info);
++ auth_log(authctxt, authenticated, 0, get_authname(type),
++ NULL, info);
+
+ if (client_user != NULL) {
+ xfree(client_user);
+@@ -406,6 +408,11 @@ do_authentication(Authctxt *authctxt)
+ authctxt->pw = fakepw();
+ }
+
++ /* Configuration may have changed as a result of Match */
++ if (options.num_auth_methods != 0)
++ fatal("AuthenticationMethods is not supported with SSH "
++ "protocol 1");
++
+ setproctitle("%s%s", authctxt->valid ? user : "unknown",
+ use_privsep ? " [net]" : "");
+
+diff --git a/auth2-chall.c b/auth2-chall.c
+index e6dbffe..5f7ec6d 100644
+--- a/auth2-chall.c
++++ b/auth2-chall.c
+@@ -283,7 +283,7 @@ input_userauth_info_response(int type, u_int32_t seq, void *ctxt)
+ KbdintAuthctxt *kbdintctxt;
+ int authenticated = 0, res;
+ u_int i, nresp;
+- char **response = NULL, *method;
++ char *devicename = NULL, **response = NULL;
+
+ if (authctxt == NULL)
+ fatal("input_userauth_info_response: no authctxt");
+@@ -329,9 +329,7 @@ input_userauth_info_response(int type, u_int32_t seq, void *ctxt)
+ /* Failure! */
+ break;
+ }
+-
+- xasprintf(&method, "keyboard-interactive/%s", kbdintctxt->device->name);
+-
++ devicename = kbdintctxt->device->name;
+ if (!authctxt->postponed) {
+ if (authenticated) {
+ auth2_challenge_stop(authctxt);
+@@ -341,8 +339,8 @@ input_userauth_info_response(int type, u_int32_t seq, void *ctxt)
+ auth2_challenge_start(authctxt);
+ }
+ }
+- userauth_finish(authctxt, authenticated, method);
+- xfree(method);
++ userauth_finish(authctxt, authenticated, "keyboard-interactive",
++ devicename);
+ }
+
+ void
+diff --git a/auth2-gss.c b/auth2-gss.c
+index 0d59b21..338c748 100644
+--- a/auth2-gss.c
++++ b/auth2-gss.c
+@@ -163,7 +163,7 @@ input_gssapi_token(int type, u_int32_t plen, void *ctxt)
+ }
+ authctxt->postponed = 0;
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
+- userauth_finish(authctxt, 0, "gssapi-with-mic");
++ userauth_finish(authctxt, 0, "gssapi-with-mic", NULL);
+ } else {
+ if (send_tok.length != 0) {
+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
+@@ -251,7 +251,7 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt)
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
+- userauth_finish(authctxt, authenticated, "gssapi-with-mic");
++ userauth_finish(authctxt, authenticated, "gssapi-with-mic", NULL);
+ }
+
+ static void
+@@ -291,7 +291,7 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt)
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
+- userauth_finish(authctxt, authenticated, "gssapi-with-mic");
++ userauth_finish(authctxt, authenticated, "gssapi-with-mic", NULL);
+ }
+
+ Authmethod method_gssapi = {
+diff --git a/auth2-jpake.c b/auth2-jpake.c
+index a460e82..e4ba9aa 100644
+--- a/auth2-jpake.c
++++ b/auth2-jpake.c
+@@ -556,7 +556,7 @@ input_userauth_jpake_client_confirm(int type, u_int32_t seq, void *ctxt)
+ authctxt->postponed = 0;
+ jpake_free(authctxt->jpake_ctx);
+ authctxt->jpake_ctx = NULL;
+- userauth_finish(authctxt, authenticated, method_jpake.name);
++ userauth_finish(authctxt, authenticated, method_jpake.name, NULL);
+ }
+
+ #endif /* JPAKE */
+diff --git a/auth2.c b/auth2.c
+index b66bef6..ea0fd92 100644
+--- a/auth2.c
++++ b/auth2.c
+@@ -96,8 +96,10 @@ static void input_service_request(int, u_int32_t, void *);
+ static void input_userauth_request(int, u_int32_t, void *);
+
+ /* helper */
+-static Authmethod *authmethod_lookup(const char *);
+-static char *authmethods_get(void);
++static Authmethod *authmethod_lookup(Authctxt *, const char *);
++static char *authmethods_get(Authctxt *authctxt);
++static int method_allowed(Authctxt *, const char *);
++static int list_starts_with(const char *, const char *);
+
+ char *
+ auth2_read_banner(void)
+@@ -255,6 +257,8 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
+ if (use_privsep)
+ mm_inform_authserv(service, style);
+ userauth_banner();
++ if (auth2_setup_methods_lists(authctxt) != 0)
++ packet_disconnect("no authentication methods enabled");
+ } else if (strcmp(user, authctxt->user) != 0 ||
+ strcmp(service, authctxt->service) != 0) {
+ packet_disconnect("Change of username or service not allowed: "
+@@ -277,12 +281,12 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
+ authctxt->server_caused_failure = 0;
+
+ /* try to authenticate user */
+- m = authmethod_lookup(method);
++ m = authmethod_lookup(authctxt, method);
+ if (m != NULL && authctxt->failures < options.max_authtries) {
+ debug2("input_userauth_request: try method %s", method);
+ authenticated = m->userauth(authctxt);
+ }
+- userauth_finish(authctxt, authenticated, method);
++ userauth_finish(authctxt, authenticated, method, NULL);
+
+ xfree(service);
+ xfree(user);
+@@ -290,13 +294,17 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
+ }
+
+ void
+-userauth_finish(Authctxt *authctxt, int authenticated, char *method)
++userauth_finish(Authctxt *authctxt, int authenticated, const char *method,
++ const char *submethod)
+ {
+ char *methods;
++ int partial = 0;
+
+ if (!authctxt->valid && authenticated)
+ fatal("INTERNAL ERROR: authenticated invalid user %s",
+ authctxt->user);
++ if (authenticated && authctxt->postponed)
++ fatal("INTERNAL ERROR: authenticated and postponed");
+
+ /* Special handling for root */
+ if (authenticated && authctxt->pw->pw_uid == 0 &&
+@@ -307,6 +315,19 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
+ #endif
+ }
+
++ if (authenticated && options.num_auth_methods != 0) {
++ if (!auth2_update_methods_lists(authctxt, method)) {
++ authenticated = 0;
++ partial = 1;
++ }
++ }
++
++ /* Log before sending the reply */
++ auth_log(authctxt, authenticated, partial, method, submethod, " ssh2");
++
++ if (authctxt->postponed)
++ return;
++
+ #ifdef USE_PAM
+ if (options.use_pam && authenticated) {
+ if (!PRIVSEP(do_pam_account())) {
+@@ -325,17 +346,10 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
+ #ifdef _UNICOS
+ if (authenticated && cray_access_denied(authctxt->user)) {
+ authenticated = 0;
+- fatal("Access denied for user %s.",authctxt->user);
++ fatal("Access denied for user %s.", authctxt->user);
+ }
+ #endif /* _UNICOS */
+
+- /* Log before sending the reply */
+- auth_log(authctxt, authenticated, method, " ssh2");
+-
+- if (authctxt->postponed)
+- return;
+-
+- /* XXX todo: check if multiple auth methods are needed */
+ if (authenticated == 1) {
+ /* turn off userauth */
+ dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore);
+@@ -348,7 +362,8 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
+
+ /* Allow initial try of "none" auth without failure penalty */
+ if (!authctxt->server_caused_failure &&
+- (authctxt->attempt > 1 || strcmp(method, "none") != 0))
++ (authctxt->attempt > 1 || strcmp(method, "none") != 0) &&
++ partial == 0)
+ authctxt->failures++;
+ if (authctxt->failures >= options.max_authtries) {
+ #ifdef SSH_AUDIT_EVENTS
+@@ -356,34 +371,61 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
+ #endif
+ packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
+ }
+- methods = authmethods_get();
++ methods = authmethods_get(authctxt);
++ debug3("%s: failure partial=%d next methods=\"%s\"", __func__,
++ partial, methods);
+ packet_start(SSH2_MSG_USERAUTH_FAILURE);
+ packet_put_cstring(methods);
+- packet_put_char(0); /* XXX partial success, unused */
++ packet_put_char(partial);
+ packet_send();
+ packet_write_wait();
+ xfree(methods);
+ }
+ }
+
++/*
++ * Checks whether method is allowed by at least one AuthenticationMethods
++ * methods list. Returns 1 if allowed, or no methods lists configured.
++ * 0 otherwise.
++ */
++static int
++method_allowed(Authctxt *authctxt, const char *method)
++{
++ u_int i;
++
++ /*
++ * NB. authctxt->num_auth_methods might be zero as a result of
++ * auth2_setup_methods_lists(), so check the configuration.
++ */
++ if (options.num_auth_methods == 0)
++ return 1;
++ for (i = 0; i < authctxt->num_auth_methods; i++) {
++ if (list_starts_with(authctxt->auth_methods[i], method))
++ return 1;
++ }
++ return 0;
++}
++
+ static char *
+-authmethods_get(void)
++authmethods_get(Authctxt *authctxt)
+ {
+ Buffer b;
+ char *list;
+- int i;
++ u_int i;
+
+ buffer_init(&b);
+ for (i = 0; authmethods[i] != NULL; i++) {
+ if (strcmp(authmethods[i]->name, "none") == 0)
+ continue;
+- if (authmethods[i]->enabled != NULL &&
+- *(authmethods[i]->enabled) != 0) {
+- if (buffer_len(&b) > 0)
+- buffer_append(&b, ",", 1);
+- buffer_append(&b, authmethods[i]->name,
+- strlen(authmethods[i]->name));
+- }
++ if (authmethods[i]->enabled == NULL ||
++ *(authmethods[i]->enabled) == 0)
++ continue;
++ if (!method_allowed(authctxt, authmethods[i]->name))
++ continue;
++ if (buffer_len(&b) > 0)
++ buffer_append(&b, ",", 1);
++ buffer_append(&b, authmethods[i]->name,
++ strlen(authmethods[i]->name));
+ }
+ buffer_append(&b, "\0", 1);
+ list = xstrdup(buffer_ptr(&b));
+@@ -392,7 +434,7 @@ authmethods_get(void)
+ }
+
+ static Authmethod *
+-authmethod_lookup(const char *name)
++authmethod_lookup(Authctxt *authctxt, const char *name)
+ {
+ int i;
+
+@@ -400,10 +442,152 @@ authmethod_lookup(const char *name)
+ for (i = 0; authmethods[i] != NULL; i++)
+ if (authmethods[i]->enabled != NULL &&
+ *(authmethods[i]->enabled) != 0 &&
+- strcmp(name, authmethods[i]->name) == 0)
++ strcmp(name, authmethods[i]->name) == 0 &&
++ method_allowed(authctxt, authmethods[i]->name))
+ return authmethods[i];
+ debug2("Unrecognized authentication method name: %s",
+ name ? name : "NULL");
+ return NULL;
+ }
+
++/*
++ * Check a comma-separated list of methods for validity. Is need_enable is
++ * non-zero, then also require that the methods are enabled.
++ * Returns 0 on success or -1 if the methods list is invalid.
++ */
++int
++auth2_methods_valid(const char *_methods, int need_enable)
++{
++ char *methods, *omethods, *method;
++ u_int i, found;
++ int ret = -1;
++
++ if (*_methods == '\0') {
++ error("empty authentication method list");
++ return -1;
++ }
++ omethods = methods = xstrdup(_methods);
++ while ((method = strsep(&methods, ",")) != NULL) {
++ for (found = i = 0; !found && authmethods[i] != NULL; i++) {
++ if (strcmp(method, authmethods[i]->name) != 0)
++ continue;
++ if (need_enable) {
++ if (authmethods[i]->enabled == NULL ||
++ *(authmethods[i]->enabled) == 0) {
++ error("Disabled method \"%s\" in "
++ "AuthenticationMethods list \"%s\"",
++ method, _methods);
++ goto out;
++ }
++ }
++ found = 1;
++ break;
++ }
++ if (!found) {
++ error("Unknown authentication method \"%s\" in list",
++ method);
++ goto out;
++ }
++ }
++ ret = 0;
++ out:
++ free(omethods);
++ return ret;
++}
++
++/*
++ * Prune the AuthenticationMethods supplied in the configuration, removing
++ * any methods lists that include disabled methods. Note that this might
++ * leave authctxt->num_auth_methods == 0, even when multiple required auth
++ * has been requested. For this reason, all tests for whether multiple is
++ * enabled should consult options.num_auth_methods directly.
++ */
++int
++auth2_setup_methods_lists(Authctxt *authctxt)
++{
++ u_int i;
++
++ if (options.num_auth_methods == 0)
++ return 0;
++ debug3("%s: checking methods", __func__);
++ authctxt->auth_methods = xcalloc(options.num_auth_methods,
++ sizeof(*authctxt->auth_methods));
++ authctxt->num_auth_methods = 0;
++ for (i = 0; i < options.num_auth_methods; i++) {
++ if (auth2_methods_valid(options.auth_methods[i], 1) != 0) {
++ logit("Authentication methods list \"%s\" contains "
++ "disabled method, skipping",
++ options.auth_methods[i]);
++ continue;
++ }
++ debug("authentication methods list %d: %s",
++ authctxt->num_auth_methods, options.auth_methods[i]);
++ authctxt->auth_methods[authctxt->num_auth_methods++] =
++ xstrdup(options.auth_methods[i]);
++ }
++ if (authctxt->num_auth_methods == 0) {
++ error("No AuthenticationMethods left after eliminating "
++ "disabled methods");
++ return -1;
++ }
++ return 0;
++}
++
++static int
++list_starts_with(const char *methods, const char *method)
++{
++ size_t l = strlen(method);
++
++ if (strncmp(methods, method, l) != 0)
++ return 0;
++ if (methods[l] != ',' && methods[l] != '\0')
++ return 0;
++ return 1;
++}
++
++/*
++ * Remove method from the start of a comma-separated list of methods.
++ * Returns 0 if the list of methods did not start with that method or 1
++ * if it did.
++ */
++static int
++remove_method(char **methods, const char *method)
++{
++ char *omethods = *methods;
++ size_t l = strlen(method);
++
++ if (!list_starts_with(omethods, method))
++ return 0;
++ *methods = xstrdup(omethods + l + (omethods[l] == ',' ? 1 : 0));
++ free(omethods);
++ return 1;
++}
++
++/*
++ * Called after successful authentication. Will remove the successful method
++ * from the start of each list in which it occurs. If it was the last method
++ * in any list, then authentication is deemed successful.
++ * Returns 1 if the method completed any authentication list or 0 otherwise.
++ */
++int
++auth2_update_methods_lists(Authctxt *authctxt, const char *method)
++{
++ u_int i, found = 0;
++
++ debug3("%s: updating methods list after \"%s\"", __func__, method);
++ for (i = 0; i < authctxt->num_auth_methods; i++) {
++ if (!remove_method(&(authctxt->auth_methods[i]), method))
++ continue;
++ found = 1;
++ if (*authctxt->auth_methods[i] == '\0') {
++ debug2("authentication methods list %d complete", i);
++ return 1;
++ }
++ debug3("authentication methods list %d remaining: \"%s\"",
++ i, authctxt->auth_methods[i]);
++ }
++ /* This should not happen, but would be bad if it did */
++ if (!found)
++ fatal("%s: method not in AuthenticationMethods", __func__);
++ return 0;
++}
+diff --git a/monitor.c b/monitor.c
+index 1dc42f5..66f3eea 100644
+--- a/monitor.c
++++ b/monitor.c
+@@ -199,6 +199,7 @@ static int key_blobtype = MM_NOKEY;
+ static char *hostbased_cuser = NULL;
+ static char *hostbased_chost = NULL;
+ static char *auth_method = "unknown";
++static char *auth_submethod = NULL;
+ static u_int session_id2_len = 0;
+ static u_char *session_id2 = NULL;
+ static pid_t monitor_child_pid;
+@@ -352,7 +353,7 @@ void
+ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
+ {
+ struct mon_table *ent;
+- int authenticated = 0;
++ int authenticated = 0, partial = 0;
+
+ debug3("preauth child monitor started");
+
+@@ -379,8 +380,26 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
+
+ /* The first few requests do not require asynchronous access */
+ while (!authenticated) {
++ partial = 0;
+ auth_method = "unknown";
++ auth_submethod = NULL;
+ authenticated = (monitor_read(pmonitor, mon_dispatch, &ent) == 1);
++
++ /* Special handling for multiple required authentications */
++ if (options.num_auth_methods != 0) {
++ if (!compat20)
++ fatal("AuthenticationMethods is not supported"
++ "with SSH protocol 1");
++ if (authenticated &&
++ !auth2_update_methods_lists(authctxt,
++ auth_method)) {
++ debug3("%s: method %s: partial", __func__,
++ auth_method);
++ authenticated = 0;
++ partial = 1;
++ }
++ }
++
+ if (authenticated) {
+ if (!(ent->flags & MON_AUTHDECIDE))
+ fatal("%s: unexpected authentication from %d",
+@@ -403,9 +422,10 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
+ }
+
+ if (ent->flags & (MON_AUTHDECIDE|MON_ALOG)) {
+- auth_log(authctxt, authenticated, auth_method,
++ auth_log(authctxt, authenticated, partial,
++ auth_method, auth_submethod,
+ compat20 ? " ssh2" : "");
+- if (!authenticated)
++ if (!authenticated && !partial)
+ authctxt->failures++;
+ }
+ #ifdef JPAKE
+@@ -781,7 +801,17 @@ mm_answer_pwnamallow(int sock, Buffer *m)
+ COPY_MATCH_STRING_OPTS();
+ #undef M_CP_STROPT
+ #undef M_CP_STRARRAYOPT
+-
++
++ /* Create valid auth method lists */
++ if (compat20 && auth2_setup_methods_lists(authctxt) != 0) {
++ /*
++ * The monitor will continue long enough to let the child
++ * run to it's packet_disconnect(), but it must not allow any
++ * authentication to succeed.
++ */
++ debug("%s: no valid authentication method lists", __func__);
++ }
++
+ debug3("%s: sending MONITOR_ANS_PWNAM: %d", __func__, allowed);
+ mm_request_send(sock, MONITOR_ANS_PWNAM, m);
+
+@@ -918,7 +948,11 @@ mm_answer_bsdauthrespond(int sock, Buffer *m)
+ debug3("%s: sending authenticated: %d", __func__, authok);
+ mm_request_send(sock, MONITOR_ANS_BSDAUTHRESPOND, m);
+
+- auth_method = "bsdauth";
++ if (compat20)
++ auth_method = "keyboard-interactive"; /* XXX auth_submethod */
++ else
++ auth_method = "bsdauth";
++
+
+ return (authok != 0);
+ }
+@@ -1057,7 +1091,9 @@ mm_answer_pam_query(int sock, Buffer *m)
+ xfree(prompts);
+ if (echo_on != NULL)
+ xfree(echo_on);
+- auth_method = "keyboard-interactive/pam";
++ auth_method = "keyboard-interactive";
++ auth_submethod = "pam";
++
+ mm_request_send(sock, MONITOR_ANS_PAM_QUERY, m);
+ return (0);
+ }
+@@ -1086,7 +1122,8 @@ mm_answer_pam_respond(int sock, Buffer *m)
+ buffer_clear(m);
+ buffer_put_int(m, ret);
+ mm_request_send(sock, MONITOR_ANS_PAM_RESPOND, m);
+- auth_method = "keyboard-interactive/pam";
++ auth_method = "keyboard-interactive";
++ auth_submethod= "pam";
+ if (ret == 0)
+ sshpam_authok = sshpam_ctxt;
+ return (0);
+@@ -1100,7 +1137,8 @@ mm_answer_pam_free_ctx(int sock, Buffer *m)
+ (sshpam_device.free_ctx)(sshpam_ctxt);
+ buffer_clear(m);
+ mm_request_send(sock, MONITOR_ANS_PAM_FREE_CTX, m);
+- auth_method = "keyboard-interactive/pam";
++ auth_method = "keyboard-interactive";
++ auth_submethod = "pam";
+ return (sshpam_authok == sshpam_ctxt);
+ }
+ #endif
+@@ -1178,7 +1216,8 @@ mm_answer_keyallowed(int sock, Buffer *m)
+ hostbased_chost = chost;
+ } else {
+ /* Log failed attempt */
+- auth_log(authctxt, 0, auth_method, compat20 ? " ssh2" : "");
++ auth_log(authctxt, 0, 0, auth_method, NULL,
++ compat20 ? " ssh2" : "");
+ xfree(blob);
+ xfree(cuser);
+ xfree(chost);
+diff --git a/servconf.c b/servconf.c
+index 906778f..2c84993 100644
+--- a/servconf.c
++++ b/servconf.c
+@@ -48,6 +48,8 @@
+ #include "groupaccess.h"
+ #include "canohost.h"
+ #include "packet.h"
++#include "hostfile.h"
++#include "auth.h"
+
+ static void add_listen_addr(ServerOptions *, char *, int);
+ static void add_one_listen_addr(ServerOptions *, char *, int);
+@@ -329,6 +331,7 @@ typedef enum {
+ sZeroKnowledgePasswordAuthentication, sHostCertificate,
+ sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
+ sKexAlgorithms, sIPQoS, sVersionAddendum,
++ sAuthenticationMethods,
+ sDeprecated, sUnsupported
+ } ServerOpCodes;
+
+@@ -454,6 +457,7 @@ static struct {
+ { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL },
+ { "ipqos", sIPQoS, SSHCFG_ALL },
+ { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL },
++ { "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL },
+ { NULL, sBadOption, 0 }
+ };
+
+@@ -1498,6 +1502,24 @@ process_server_config_line(ServerOptions *options, char *line,
+ }
+ return 0;
+
++ case sAuthenticationMethods:
++ if (*activep && options->num_auth_methods == 0) {
++ while ((arg = strdelim(&cp)) && *arg != '\0') {
++ if (options->num_auth_methods >=
++ MAX_AUTH_METHODS)
++ fatal("%s line %d: "
++ "too many authentication methods.",
++ filename, linenum);
++ if (auth2_methods_valid(arg, 0) != 0)
++ fatal("%s line %d: invalid "
++ "authentication method list.",
++ filename, linenum);
++ options->auth_methods[
++ options->num_auth_methods++] = xstrdup(arg);
++ }
++ }
++ return 0;
++
+ case sDeprecated:
+ logit("%s line %d: Deprecated option %s",
+ filename, linenum, arg);
+@@ -1925,6 +1947,8 @@ dump_config(ServerOptions *o)
+ dump_cfg_strarray(sAllowGroups, o->num_allow_groups, o->allow_groups);
+ dump_cfg_strarray(sDenyGroups, o->num_deny_groups, o->deny_groups);
+ dump_cfg_strarray(sAcceptEnv, o->num_accept_env, o->accept_env);
++ dump_cfg_strarray_oneline(sAuthenticationMethods,
++ o->num_auth_methods, o->auth_methods);
+
+ /* other arguments */
+ for (i = 0; i < o->num_subsystems; i++)
+diff --git a/servconf.h b/servconf.h
+index 096d596..ef80eef 100644
+--- a/servconf.h
++++ b/servconf.h
+@@ -28,6 +28,7 @@
+ #define MAX_ACCEPT_ENV 256 /* Max # of env vars. */
+ #define MAX_MATCH_GROUPS 256 /* Max # of groups for Match. */
+ #define MAX_AUTHKEYS_FILES 256 /* Max # of authorized_keys files. */
++#define MAX_AUTH_METHODS 256 /* Max # of AuthenticationMethods. */
+
+ /* permit_root_login */
+ #define PERMIT_NOT_SET -1
+@@ -168,6 +169,9 @@ typedef struct {
+ char *authorized_principals_file;
+
+ char *version_addendum; /* Appended to SSH banner */
++
++ u_int num_auth_methods;
++ char *auth_methods[MAX_AUTH_METHODS];
+ } ServerOptions;
+
+ /* Information about the incoming connection as used by Match */
+@@ -197,6 +201,7 @@ struct connection_info {
+ M_CP_STRARRAYOPT(allow_groups, num_allow_groups); \
+ M_CP_STRARRAYOPT(deny_groups, num_deny_groups); \
+ M_CP_STRARRAYOPT(accept_env, num_accept_env); \
++ M_CP_STRARRAYOPT(auth_methods, num_auth_methods); \
+ } while (0)
+
+ struct connection_info *get_connection_info(int, int);
+diff --git a/sshd.c b/sshd.c
+index d5ec4e6..cb4bdd3 100644
+--- a/sshd.c
++++ b/sshd.c
+@@ -1333,6 +1333,7 @@ main(int ac, char **av)
+ int remote_port;
+ char *line;
+ int config_s[2] = { -1 , -1 };
++ u_int n;
+ u_int64_t ibytes, obytes;
+ mode_t new_umask;
+ Key *key;
+@@ -1555,6 +1556,26 @@ main(int ac, char **av)
+ if (options.challenge_response_authentication)
+ options.kbd_interactive_authentication = 1;
+
++ /*
++ * Check whether there is any path through configured auth methods.
++ * Unfortunately it is not possible to verify this generally before
++ * daemonisation in the presence of Match block, but this catches
++ * and warns for trivial misconfigurations that could break login.
++ */
++ if (options.num_auth_methods != 0) {
++ if ((options.protocol & SSH_PROTO_1))
++ fatal("AuthenticationMethods is not supported with "
++ "SSH protocol 1");
++ for (n = 0; n < options.num_auth_methods; n++) {
++ if (auth2_methods_valid(options.auth_methods[n],
++ 1) == 0)
++ break;
++ }
++ if (n >= options.num_auth_methods)
++ fatal("AuthenticationMethods cannot be satisfied by "
++ "enabled authentication methods");
++ }
++
+ /* set default channel AF */
+ channel_set_af(options.address_family);
+
+diff --git a/sshd_config.5 b/sshd_config.5
+index 314ecfb..ed81ac8 100644
+--- a/sshd_config.5
++++ b/sshd_config.5
+@@ -151,6 +151,28 @@ See
+ in
+ .Xr ssh_config 5
+ for more information on patterns.
++.It Cm AuthenticationMethods
++Specifies the authentication methods that must be successfully completed
++for a user to be granted access.
++This option must be followed by one or more comma-separated lists of
++authentication method names.
++Successful authentication requires completion of every method in at least
++one of these lists.
++.Pp
++For example, an argument of
++.Dq publickey,password publickey,keyboard-interactive
++would require the user to complete public key authentication, followed by
++either password or keyboard interactive authentication.
++Only methods that are next in one or more lists are offered at each stage,
++so for this example, it would not be possible to attempt password or
++keyboard-interactive authentication before public key.
++.Pp
++This option is only available for SSH protocol 2 and will yield a fatal
++error if enabled if protocol 1 is also enabled.
++Note that each authentication method listed should also be explicitly enabled
++in the configuration.
++The default is not to require multiple authentication; successful completion
++of a single authentication method is sufficient.
+ .It Cm AuthorizedKeysFile
+ Specifies the file that contains the public keys that can be used
+ for user authentication.
+@@ -711,6 +733,7 @@ Available keywords are
+ .Cm AllowGroups ,
+ .Cm AllowTcpForwarding ,
+ .Cm AllowUsers ,
++.Cm AuthenticationMethods ,
+ .Cm AuthorizedKeysFile ,
+ .Cm AuthorizedPrincipalsFile ,
+ .Cm Banner ,
diff --git a/openssh.spec b/openssh.spec
index be9fa5f..737c89d 100644
--- a/openssh.spec
+++ b/openssh.spec
@@ -109,8 +109,7 @@ Patch102: openssh-5.8p1-getaddrinfo.patch
#https://bugzilla.mindrot.org/show_bug.cgi?id=1889
Patch103: openssh-5.8p1-packet.patch
#https://bugzilla.mindrot.org/show_bug.cgi?id=983
-#Patch104: openssh-5.9p1-2auth.patch
-Patch104: openssh-6.1p1-required-authentications.patch
+Patch104: openssh-6.1p1-authenticationmethods.patch
#https://bugzilla.mindrot.org/show_bug.cgi?id=1402
Patch200: openssh-5.8p1-audit0.patch
@@ -389,7 +388,7 @@ The module is most useful for su and sudo service stacks.
%patch101 -p1 -b .fingerprint
%patch102 -p1 -b .getaddrinfo
%patch103 -p1 -b .packet
-%patch104 -p1 -b .required-authentication
+%patch104 -p1 -b .authenticationmethods
%patch200 -p1 -b .audit0
%patch201 -p1 -b .audit1
More information about the scm-commits
mailing list