]> git.ipfire.org Git - people/ms/ipfire-3.x.git/blame - openssh/patches/openssh-6.1p1-authenticationmethods.patch
Merge remote-tracking branch 'stevee/ppp-update'
[people/ms/ipfire-3.x.git] / openssh / patches / openssh-6.1p1-authenticationmethods.patch
CommitLineData
43c69e28
SS
1diff --git a/auth.c b/auth.c
2index ee0cb05..1b2fc2b 100644
3--- a/auth.c
4+++ b/auth.c
5@@ -251,7 +251,8 @@ allowed_user(struct passwd * pw)
6 }
7
8 void
9-auth_log(Authctxt *authctxt, int authenticated, char *method, char *info)
10+auth_log(Authctxt *authctxt, int authenticated, int partial,
11+ const char *method, const char *submethod, const char *info)
12 {
13 void (*authlog) (const char *fmt,...) = verbose;
14 char *authmsg;
15@@ -268,12 +269,15 @@ auth_log(Authctxt *authctxt, int authenticated, char *method, char *info)
16
17 if (authctxt->postponed)
18 authmsg = "Postponed";
19+ else if (partial)
20+ authmsg = "Partial";
21 else
22 authmsg = authenticated ? "Accepted" : "Failed";
23
24- authlog("%s %s for %s%.100s from %.200s port %d%s",
25+ authlog("%s %s%s%s for %s%.100s from %.200s port %d%s",
26 authmsg,
27 method,
28+ submethod != NULL ? "/" : "", submethod == NULL ? "" : submethod,
29 authctxt->valid ? "" : "invalid user ",
30 authctxt->user,
31 get_remote_ipaddr(),
32@@ -303,7 +307,7 @@ auth_log(Authctxt *authctxt, int authenticated, char *method, char *info)
33 * Check whether root logins are disallowed.
34 */
35 int
36-auth_root_allowed(char *method)
37+auth_root_allowed(const char *method)
38 {
39 switch (options.permit_root_login) {
40 case PERMIT_YES:
41diff --git a/auth.h b/auth.h
42index 0d786c4..29823bb 100644
43--- a/auth.h
44+++ b/auth.h
45@@ -64,6 +64,8 @@ struct Authctxt {
46 #ifdef BSD_AUTH
47 auth_session_t *as;
48 #endif
49+ char **auth_methods; /* modified from server config */
50+ u_int num_auth_methods;
51 #ifdef KRB5
52 krb5_context krb5_ctx;
53 krb5_ccache krb5_fwd_ccache;
54@@ -142,12 +144,17 @@ void disable_forwarding(void);
55 void do_authentication(Authctxt *);
56 void do_authentication2(Authctxt *);
57
58-void auth_log(Authctxt *, int, char *, char *);
59-void userauth_finish(Authctxt *, int, char *);
60+void auth_log(Authctxt *, int, int, const char *, const char *,
61+ const char *);
62+void userauth_finish(Authctxt *, int, const char *, const char *);
63+int auth_root_allowed(const char *);
64+
65 void userauth_send_banner(const char *);
66-int auth_root_allowed(char *);
67
68 char *auth2_read_banner(void);
69+int auth2_methods_valid(const char *, int);
70+int auth2_update_methods_lists(Authctxt *, const char *);
71+int auth2_setup_methods_lists(Authctxt *);
72
73 void privsep_challenge_enable(void);
74
75diff --git a/auth1.c b/auth1.c
76index cc85aec..458a110 100644
77--- a/auth1.c
78+++ b/auth1.c
79@@ -253,7 +253,8 @@ do_authloop(Authctxt *authctxt)
80 if (options.use_pam && (PRIVSEP(do_pam_account())))
81 #endif
82 {
83- auth_log(authctxt, 1, "without authentication", "");
84+ auth_log(authctxt, 1, 0, "without authentication",
85+ NULL, "");
86 return;
87 }
88 }
89@@ -352,7 +353,8 @@ do_authloop(Authctxt *authctxt)
90
91 skip:
92 /* Log before sending the reply */
93- auth_log(authctxt, authenticated, get_authname(type), info);
94+ auth_log(authctxt, authenticated, 0, get_authname(type),
95+ NULL, info);
96
97 if (client_user != NULL) {
98 xfree(client_user);
99@@ -406,6 +408,11 @@ do_authentication(Authctxt *authctxt)
100 authctxt->pw = fakepw();
101 }
102
103+ /* Configuration may have changed as a result of Match */
104+ if (options.num_auth_methods != 0)
105+ fatal("AuthenticationMethods is not supported with SSH "
106+ "protocol 1");
107+
108 setproctitle("%s%s", authctxt->valid ? user : "unknown",
109 use_privsep ? " [net]" : "");
110
111diff --git a/auth2-chall.c b/auth2-chall.c
112index e6dbffe..5f7ec6d 100644
113--- a/auth2-chall.c
114+++ b/auth2-chall.c
115@@ -283,7 +283,7 @@ input_userauth_info_response(int type, u_int32_t seq, void *ctxt)
116 KbdintAuthctxt *kbdintctxt;
117 int authenticated = 0, res;
118 u_int i, nresp;
119- char **response = NULL, *method;
120+ char *devicename = NULL, **response = NULL;
121
122 if (authctxt == NULL)
123 fatal("input_userauth_info_response: no authctxt");
124@@ -329,9 +329,7 @@ input_userauth_info_response(int type, u_int32_t seq, void *ctxt)
125 /* Failure! */
126 break;
127 }
128-
129- xasprintf(&method, "keyboard-interactive/%s", kbdintctxt->device->name);
130-
131+ devicename = kbdintctxt->device->name;
132 if (!authctxt->postponed) {
133 if (authenticated) {
134 auth2_challenge_stop(authctxt);
135@@ -341,8 +339,8 @@ input_userauth_info_response(int type, u_int32_t seq, void *ctxt)
136 auth2_challenge_start(authctxt);
137 }
138 }
139- userauth_finish(authctxt, authenticated, method);
140- xfree(method);
141+ userauth_finish(authctxt, authenticated, "keyboard-interactive",
142+ devicename);
143 }
144
145 void
146diff --git a/auth2-gss.c b/auth2-gss.c
147index 0d59b21..338c748 100644
148--- a/auth2-gss.c
149+++ b/auth2-gss.c
150@@ -163,7 +163,7 @@ input_gssapi_token(int type, u_int32_t plen, void *ctxt)
151 }
152 authctxt->postponed = 0;
153 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
154- userauth_finish(authctxt, 0, "gssapi-with-mic");
155+ userauth_finish(authctxt, 0, "gssapi-with-mic", NULL);
156 } else {
157 if (send_tok.length != 0) {
158 packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
159@@ -251,7 +251,7 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt)
160 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
161 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
162 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
163- userauth_finish(authctxt, authenticated, "gssapi-with-mic");
164+ userauth_finish(authctxt, authenticated, "gssapi-with-mic", NULL);
165 }
166
167 static void
168@@ -291,7 +291,7 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt)
169 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
170 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
171 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
172- userauth_finish(authctxt, authenticated, "gssapi-with-mic");
173+ userauth_finish(authctxt, authenticated, "gssapi-with-mic", NULL);
174 }
175
176 Authmethod method_gssapi = {
177diff --git a/auth2-jpake.c b/auth2-jpake.c
178index a460e82..e4ba9aa 100644
179--- a/auth2-jpake.c
180+++ b/auth2-jpake.c
181@@ -556,7 +556,7 @@ input_userauth_jpake_client_confirm(int type, u_int32_t seq, void *ctxt)
182 authctxt->postponed = 0;
183 jpake_free(authctxt->jpake_ctx);
184 authctxt->jpake_ctx = NULL;
185- userauth_finish(authctxt, authenticated, method_jpake.name);
186+ userauth_finish(authctxt, authenticated, method_jpake.name, NULL);
187 }
188
189 #endif /* JPAKE */
190diff --git a/auth2.c b/auth2.c
191index b66bef6..ea0fd92 100644
192--- a/auth2.c
193+++ b/auth2.c
194@@ -96,8 +96,10 @@ static void input_service_request(int, u_int32_t, void *);
195 static void input_userauth_request(int, u_int32_t, void *);
196
197 /* helper */
198-static Authmethod *authmethod_lookup(const char *);
199-static char *authmethods_get(void);
200+static Authmethod *authmethod_lookup(Authctxt *, const char *);
201+static char *authmethods_get(Authctxt *authctxt);
202+static int method_allowed(Authctxt *, const char *);
203+static int list_starts_with(const char *, const char *);
204
205 char *
206 auth2_read_banner(void)
207@@ -255,6 +257,8 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
208 if (use_privsep)
209 mm_inform_authserv(service, style);
210 userauth_banner();
211+ if (auth2_setup_methods_lists(authctxt) != 0)
212+ packet_disconnect("no authentication methods enabled");
213 } else if (strcmp(user, authctxt->user) != 0 ||
214 strcmp(service, authctxt->service) != 0) {
215 packet_disconnect("Change of username or service not allowed: "
216@@ -277,12 +281,12 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
217 authctxt->server_caused_failure = 0;
218
219 /* try to authenticate user */
220- m = authmethod_lookup(method);
221+ m = authmethod_lookup(authctxt, method);
222 if (m != NULL && authctxt->failures < options.max_authtries) {
223 debug2("input_userauth_request: try method %s", method);
224 authenticated = m->userauth(authctxt);
225 }
226- userauth_finish(authctxt, authenticated, method);
227+ userauth_finish(authctxt, authenticated, method, NULL);
228
229 xfree(service);
230 xfree(user);
231@@ -290,13 +294,17 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
232 }
233
234 void
235-userauth_finish(Authctxt *authctxt, int authenticated, char *method)
236+userauth_finish(Authctxt *authctxt, int authenticated, const char *method,
237+ const char *submethod)
238 {
239 char *methods;
240+ int partial = 0;
241
242 if (!authctxt->valid && authenticated)
243 fatal("INTERNAL ERROR: authenticated invalid user %s",
244 authctxt->user);
245+ if (authenticated && authctxt->postponed)
246+ fatal("INTERNAL ERROR: authenticated and postponed");
247
248 /* Special handling for root */
249 if (authenticated && authctxt->pw->pw_uid == 0 &&
250@@ -307,6 +315,19 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
251 #endif
252 }
253
254+ if (authenticated && options.num_auth_methods != 0) {
255+ if (!auth2_update_methods_lists(authctxt, method)) {
256+ authenticated = 0;
257+ partial = 1;
258+ }
259+ }
260+
261+ /* Log before sending the reply */
262+ auth_log(authctxt, authenticated, partial, method, submethod, " ssh2");
263+
264+ if (authctxt->postponed)
265+ return;
266+
267 #ifdef USE_PAM
268 if (options.use_pam && authenticated) {
269 if (!PRIVSEP(do_pam_account())) {
270@@ -325,17 +346,10 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
271 #ifdef _UNICOS
272 if (authenticated && cray_access_denied(authctxt->user)) {
273 authenticated = 0;
274- fatal("Access denied for user %s.",authctxt->user);
275+ fatal("Access denied for user %s.", authctxt->user);
276 }
277 #endif /* _UNICOS */
278
279- /* Log before sending the reply */
280- auth_log(authctxt, authenticated, method, " ssh2");
281-
282- if (authctxt->postponed)
283- return;
284-
285- /* XXX todo: check if multiple auth methods are needed */
286 if (authenticated == 1) {
287 /* turn off userauth */
288 dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore);
289@@ -348,7 +362,8 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
290
291 /* Allow initial try of "none" auth without failure penalty */
292 if (!authctxt->server_caused_failure &&
293- (authctxt->attempt > 1 || strcmp(method, "none") != 0))
294+ (authctxt->attempt > 1 || strcmp(method, "none") != 0) &&
295+ partial == 0)
296 authctxt->failures++;
297 if (authctxt->failures >= options.max_authtries) {
298 #ifdef SSH_AUDIT_EVENTS
299@@ -356,34 +371,61 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
300 #endif
301 packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
302 }
303- methods = authmethods_get();
304+ methods = authmethods_get(authctxt);
305+ debug3("%s: failure partial=%d next methods=\"%s\"", __func__,
306+ partial, methods);
307 packet_start(SSH2_MSG_USERAUTH_FAILURE);
308 packet_put_cstring(methods);
309- packet_put_char(0); /* XXX partial success, unused */
310+ packet_put_char(partial);
311 packet_send();
312 packet_write_wait();
313 xfree(methods);
314 }
315 }
316
317+/*
318+ * Checks whether method is allowed by at least one AuthenticationMethods
319+ * methods list. Returns 1 if allowed, or no methods lists configured.
320+ * 0 otherwise.
321+ */
322+static int
323+method_allowed(Authctxt *authctxt, const char *method)
324+{
325+ u_int i;
326+
327+ /*
328+ * NB. authctxt->num_auth_methods might be zero as a result of
329+ * auth2_setup_methods_lists(), so check the configuration.
330+ */
331+ if (options.num_auth_methods == 0)
332+ return 1;
333+ for (i = 0; i < authctxt->num_auth_methods; i++) {
334+ if (list_starts_with(authctxt->auth_methods[i], method))
335+ return 1;
336+ }
337+ return 0;
338+}
339+
340 static char *
341-authmethods_get(void)
342+authmethods_get(Authctxt *authctxt)
343 {
344 Buffer b;
345 char *list;
346- int i;
347+ u_int i;
348
349 buffer_init(&b);
350 for (i = 0; authmethods[i] != NULL; i++) {
351 if (strcmp(authmethods[i]->name, "none") == 0)
352 continue;
353- if (authmethods[i]->enabled != NULL &&
354- *(authmethods[i]->enabled) != 0) {
355- if (buffer_len(&b) > 0)
356- buffer_append(&b, ",", 1);
357- buffer_append(&b, authmethods[i]->name,
358- strlen(authmethods[i]->name));
359- }
360+ if (authmethods[i]->enabled == NULL ||
361+ *(authmethods[i]->enabled) == 0)
362+ continue;
363+ if (!method_allowed(authctxt, authmethods[i]->name))
364+ continue;
365+ if (buffer_len(&b) > 0)
366+ buffer_append(&b, ",", 1);
367+ buffer_append(&b, authmethods[i]->name,
368+ strlen(authmethods[i]->name));
369 }
370 buffer_append(&b, "\0", 1);
371 list = xstrdup(buffer_ptr(&b));
372@@ -392,7 +434,7 @@ authmethods_get(void)
373 }
374
375 static Authmethod *
376-authmethod_lookup(const char *name)
377+authmethod_lookup(Authctxt *authctxt, const char *name)
378 {
379 int i;
380
381@@ -400,10 +442,152 @@ authmethod_lookup(const char *name)
382 for (i = 0; authmethods[i] != NULL; i++)
383 if (authmethods[i]->enabled != NULL &&
384 *(authmethods[i]->enabled) != 0 &&
385- strcmp(name, authmethods[i]->name) == 0)
386+ strcmp(name, authmethods[i]->name) == 0 &&
387+ method_allowed(authctxt, authmethods[i]->name))
388 return authmethods[i];
389 debug2("Unrecognized authentication method name: %s",
390 name ? name : "NULL");
391 return NULL;
392 }
393
394+/*
395+ * Check a comma-separated list of methods for validity. Is need_enable is
396+ * non-zero, then also require that the methods are enabled.
397+ * Returns 0 on success or -1 if the methods list is invalid.
398+ */
399+int
400+auth2_methods_valid(const char *_methods, int need_enable)
401+{
402+ char *methods, *omethods, *method;
403+ u_int i, found;
404+ int ret = -1;
405+
406+ if (*_methods == '\0') {
407+ error("empty authentication method list");
408+ return -1;
409+ }
410+ omethods = methods = xstrdup(_methods);
411+ while ((method = strsep(&methods, ",")) != NULL) {
412+ for (found = i = 0; !found && authmethods[i] != NULL; i++) {
413+ if (strcmp(method, authmethods[i]->name) != 0)
414+ continue;
415+ if (need_enable) {
416+ if (authmethods[i]->enabled == NULL ||
417+ *(authmethods[i]->enabled) == 0) {
418+ error("Disabled method \"%s\" in "
419+ "AuthenticationMethods list \"%s\"",
420+ method, _methods);
421+ goto out;
422+ }
423+ }
424+ found = 1;
425+ break;
426+ }
427+ if (!found) {
428+ error("Unknown authentication method \"%s\" in list",
429+ method);
430+ goto out;
431+ }
432+ }
433+ ret = 0;
434+ out:
435+ free(omethods);
436+ return ret;
437+}
438+
439+/*
440+ * Prune the AuthenticationMethods supplied in the configuration, removing
441+ * any methods lists that include disabled methods. Note that this might
442+ * leave authctxt->num_auth_methods == 0, even when multiple required auth
443+ * has been requested. For this reason, all tests for whether multiple is
444+ * enabled should consult options.num_auth_methods directly.
445+ */
446+int
447+auth2_setup_methods_lists(Authctxt *authctxt)
448+{
449+ u_int i;
450+
451+ if (options.num_auth_methods == 0)
452+ return 0;
453+ debug3("%s: checking methods", __func__);
454+ authctxt->auth_methods = xcalloc(options.num_auth_methods,
455+ sizeof(*authctxt->auth_methods));
456+ authctxt->num_auth_methods = 0;
457+ for (i = 0; i < options.num_auth_methods; i++) {
458+ if (auth2_methods_valid(options.auth_methods[i], 1) != 0) {
459+ logit("Authentication methods list \"%s\" contains "
460+ "disabled method, skipping",
461+ options.auth_methods[i]);
462+ continue;
463+ }
464+ debug("authentication methods list %d: %s",
465+ authctxt->num_auth_methods, options.auth_methods[i]);
466+ authctxt->auth_methods[authctxt->num_auth_methods++] =
467+ xstrdup(options.auth_methods[i]);
468+ }
469+ if (authctxt->num_auth_methods == 0) {
470+ error("No AuthenticationMethods left after eliminating "
471+ "disabled methods");
472+ return -1;
473+ }
474+ return 0;
475+}
476+
477+static int
478+list_starts_with(const char *methods, const char *method)
479+{
480+ size_t l = strlen(method);
481+
482+ if (strncmp(methods, method, l) != 0)
483+ return 0;
484+ if (methods[l] != ',' && methods[l] != '\0')
485+ return 0;
486+ return 1;
487+}
488+
489+/*
490+ * Remove method from the start of a comma-separated list of methods.
491+ * Returns 0 if the list of methods did not start with that method or 1
492+ * if it did.
493+ */
494+static int
495+remove_method(char **methods, const char *method)
496+{
497+ char *omethods = *methods;
498+ size_t l = strlen(method);
499+
500+ if (!list_starts_with(omethods, method))
501+ return 0;
502+ *methods = xstrdup(omethods + l + (omethods[l] == ',' ? 1 : 0));
503+ free(omethods);
504+ return 1;
505+}
506+
507+/*
508+ * Called after successful authentication. Will remove the successful method
509+ * from the start of each list in which it occurs. If it was the last method
510+ * in any list, then authentication is deemed successful.
511+ * Returns 1 if the method completed any authentication list or 0 otherwise.
512+ */
513+int
514+auth2_update_methods_lists(Authctxt *authctxt, const char *method)
515+{
516+ u_int i, found = 0;
517+
518+ debug3("%s: updating methods list after \"%s\"", __func__, method);
519+ for (i = 0; i < authctxt->num_auth_methods; i++) {
520+ if (!remove_method(&(authctxt->auth_methods[i]), method))
521+ continue;
522+ found = 1;
523+ if (*authctxt->auth_methods[i] == '\0') {
524+ debug2("authentication methods list %d complete", i);
525+ return 1;
526+ }
527+ debug3("authentication methods list %d remaining: \"%s\"",
528+ i, authctxt->auth_methods[i]);
529+ }
530+ /* This should not happen, but would be bad if it did */
531+ if (!found)
532+ fatal("%s: method not in AuthenticationMethods", __func__);
533+ return 0;
534+}
535diff --git a/monitor.c b/monitor.c
536index 1dc42f5..66f3eea 100644
537--- a/monitor.c
538+++ b/monitor.c
539@@ -199,6 +199,7 @@ static int key_blobtype = MM_NOKEY;
540 static char *hostbased_cuser = NULL;
541 static char *hostbased_chost = NULL;
542 static char *auth_method = "unknown";
543+static char *auth_submethod = NULL;
544 static u_int session_id2_len = 0;
545 static u_char *session_id2 = NULL;
546 static pid_t monitor_child_pid;
547@@ -352,7 +353,7 @@ void
548 monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
549 {
550 struct mon_table *ent;
551- int authenticated = 0;
552+ int authenticated = 0, partial = 0;
553
554 debug3("preauth child monitor started");
555
556@@ -379,8 +380,26 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
557
558 /* The first few requests do not require asynchronous access */
559 while (!authenticated) {
560+ partial = 0;
561 auth_method = "unknown";
562+ auth_submethod = NULL;
563 authenticated = (monitor_read(pmonitor, mon_dispatch, &ent) == 1);
564+
565+ /* Special handling for multiple required authentications */
566+ if (options.num_auth_methods != 0) {
567+ if (!compat20)
568+ fatal("AuthenticationMethods is not supported"
569+ "with SSH protocol 1");
570+ if (authenticated &&
571+ !auth2_update_methods_lists(authctxt,
572+ auth_method)) {
573+ debug3("%s: method %s: partial", __func__,
574+ auth_method);
575+ authenticated = 0;
576+ partial = 1;
577+ }
578+ }
579+
580 if (authenticated) {
581 if (!(ent->flags & MON_AUTHDECIDE))
582 fatal("%s: unexpected authentication from %d",
583@@ -403,9 +422,10 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
584 }
585
586 if (ent->flags & (MON_AUTHDECIDE|MON_ALOG)) {
587- auth_log(authctxt, authenticated, auth_method,
588+ auth_log(authctxt, authenticated, partial,
589+ auth_method, auth_submethod,
590 compat20 ? " ssh2" : "");
591- if (!authenticated)
592+ if (!authenticated && !partial)
593 authctxt->failures++;
594 }
595 #ifdef JPAKE
596@@ -781,7 +801,17 @@ mm_answer_pwnamallow(int sock, Buffer *m)
597 COPY_MATCH_STRING_OPTS();
598 #undef M_CP_STROPT
599 #undef M_CP_STRARRAYOPT
600-
601+
602+ /* Create valid auth method lists */
603+ if (compat20 && auth2_setup_methods_lists(authctxt) != 0) {
604+ /*
605+ * The monitor will continue long enough to let the child
606+ * run to it's packet_disconnect(), but it must not allow any
607+ * authentication to succeed.
608+ */
609+ debug("%s: no valid authentication method lists", __func__);
610+ }
611+
612 debug3("%s: sending MONITOR_ANS_PWNAM: %d", __func__, allowed);
613 mm_request_send(sock, MONITOR_ANS_PWNAM, m);
614
615@@ -918,7 +948,11 @@ mm_answer_bsdauthrespond(int sock, Buffer *m)
616 debug3("%s: sending authenticated: %d", __func__, authok);
617 mm_request_send(sock, MONITOR_ANS_BSDAUTHRESPOND, m);
618
619- auth_method = "bsdauth";
620+ if (compat20)
621+ auth_method = "keyboard-interactive"; /* XXX auth_submethod */
622+ else
623+ auth_method = "bsdauth";
624+
625
626 return (authok != 0);
627 }
628@@ -1057,7 +1091,9 @@ mm_answer_pam_query(int sock, Buffer *m)
629 xfree(prompts);
630 if (echo_on != NULL)
631 xfree(echo_on);
632- auth_method = "keyboard-interactive/pam";
633+ auth_method = "keyboard-interactive";
634+ auth_submethod = "pam";
635+
636 mm_request_send(sock, MONITOR_ANS_PAM_QUERY, m);
637 return (0);
638 }
639@@ -1086,7 +1122,8 @@ mm_answer_pam_respond(int sock, Buffer *m)
640 buffer_clear(m);
641 buffer_put_int(m, ret);
642 mm_request_send(sock, MONITOR_ANS_PAM_RESPOND, m);
643- auth_method = "keyboard-interactive/pam";
644+ auth_method = "keyboard-interactive";
645+ auth_submethod= "pam";
646 if (ret == 0)
647 sshpam_authok = sshpam_ctxt;
648 return (0);
649@@ -1100,7 +1137,8 @@ mm_answer_pam_free_ctx(int sock, Buffer *m)
650 (sshpam_device.free_ctx)(sshpam_ctxt);
651 buffer_clear(m);
652 mm_request_send(sock, MONITOR_ANS_PAM_FREE_CTX, m);
653- auth_method = "keyboard-interactive/pam";
654+ auth_method = "keyboard-interactive";
655+ auth_submethod = "pam";
656 return (sshpam_authok == sshpam_ctxt);
657 }
658 #endif
659@@ -1178,7 +1216,8 @@ mm_answer_keyallowed(int sock, Buffer *m)
660 hostbased_chost = chost;
661 } else {
662 /* Log failed attempt */
663- auth_log(authctxt, 0, auth_method, compat20 ? " ssh2" : "");
664+ auth_log(authctxt, 0, 0, auth_method, NULL,
665+ compat20 ? " ssh2" : "");
666 xfree(blob);
667 xfree(cuser);
668 xfree(chost);
669diff --git a/servconf.c b/servconf.c
670index 906778f..2c84993 100644
671--- a/servconf.c
672+++ b/servconf.c
673@@ -48,6 +48,8 @@
674 #include "groupaccess.h"
675 #include "canohost.h"
676 #include "packet.h"
677+#include "hostfile.h"
678+#include "auth.h"
679
680 static void add_listen_addr(ServerOptions *, char *, int);
681 static void add_one_listen_addr(ServerOptions *, char *, int);
682@@ -329,6 +331,7 @@ typedef enum {
683 sZeroKnowledgePasswordAuthentication, sHostCertificate,
684 sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
685 sKexAlgorithms, sIPQoS, sVersionAddendum,
686+ sAuthenticationMethods,
687 sDeprecated, sUnsupported
688 } ServerOpCodes;
689
690@@ -454,6 +457,7 @@ static struct {
691 { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL },
692 { "ipqos", sIPQoS, SSHCFG_ALL },
693 { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL },
694+ { "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL },
695 { NULL, sBadOption, 0 }
696 };
697
698@@ -1498,6 +1502,24 @@ process_server_config_line(ServerOptions *options, char *line,
699 }
700 return 0;
701
702+ case sAuthenticationMethods:
703+ if (*activep && options->num_auth_methods == 0) {
704+ while ((arg = strdelim(&cp)) && *arg != '\0') {
705+ if (options->num_auth_methods >=
706+ MAX_AUTH_METHODS)
707+ fatal("%s line %d: "
708+ "too many authentication methods.",
709+ filename, linenum);
710+ if (auth2_methods_valid(arg, 0) != 0)
711+ fatal("%s line %d: invalid "
712+ "authentication method list.",
713+ filename, linenum);
714+ options->auth_methods[
715+ options->num_auth_methods++] = xstrdup(arg);
716+ }
717+ }
718+ return 0;
719+
720 case sDeprecated:
721 logit("%s line %d: Deprecated option %s",
722 filename, linenum, arg);
723@@ -1925,6 +1947,8 @@ dump_config(ServerOptions *o)
724 dump_cfg_strarray(sAllowGroups, o->num_allow_groups, o->allow_groups);
725 dump_cfg_strarray(sDenyGroups, o->num_deny_groups, o->deny_groups);
726 dump_cfg_strarray(sAcceptEnv, o->num_accept_env, o->accept_env);
727+ dump_cfg_strarray_oneline(sAuthenticationMethods,
728+ o->num_auth_methods, o->auth_methods);
729
730 /* other arguments */
731 for (i = 0; i < o->num_subsystems; i++)
732diff --git a/servconf.h b/servconf.h
733index 096d596..ef80eef 100644
734--- a/servconf.h
735+++ b/servconf.h
736@@ -28,6 +28,7 @@
737 #define MAX_ACCEPT_ENV 256 /* Max # of env vars. */
738 #define MAX_MATCH_GROUPS 256 /* Max # of groups for Match. */
739 #define MAX_AUTHKEYS_FILES 256 /* Max # of authorized_keys files. */
740+#define MAX_AUTH_METHODS 256 /* Max # of AuthenticationMethods. */
741
742 /* permit_root_login */
743 #define PERMIT_NOT_SET -1
744@@ -168,6 +169,9 @@ typedef struct {
745 char *authorized_principals_file;
746
747 char *version_addendum; /* Appended to SSH banner */
748+
749+ u_int num_auth_methods;
750+ char *auth_methods[MAX_AUTH_METHODS];
751 } ServerOptions;
752
753 /* Information about the incoming connection as used by Match */
754@@ -197,6 +201,7 @@ struct connection_info {
755 M_CP_STRARRAYOPT(allow_groups, num_allow_groups); \
756 M_CP_STRARRAYOPT(deny_groups, num_deny_groups); \
757 M_CP_STRARRAYOPT(accept_env, num_accept_env); \
758+ M_CP_STRARRAYOPT(auth_methods, num_auth_methods); \
759 } while (0)
760
761 struct connection_info *get_connection_info(int, int);
762diff --git a/sshd.c b/sshd.c
763index d5ec4e6..cb4bdd3 100644
764--- a/sshd.c
765+++ b/sshd.c
766@@ -1333,6 +1333,7 @@ main(int ac, char **av)
767 int remote_port;
768 char *line;
769 int config_s[2] = { -1 , -1 };
770+ u_int n;
771 u_int64_t ibytes, obytes;
772 mode_t new_umask;
773 Key *key;
774@@ -1555,6 +1556,26 @@ main(int ac, char **av)
775 if (options.challenge_response_authentication)
776 options.kbd_interactive_authentication = 1;
777
778+ /*
779+ * Check whether there is any path through configured auth methods.
780+ * Unfortunately it is not possible to verify this generally before
781+ * daemonisation in the presence of Match block, but this catches
782+ * and warns for trivial misconfigurations that could break login.
783+ */
784+ if (options.num_auth_methods != 0) {
785+ if ((options.protocol & SSH_PROTO_1))
786+ fatal("AuthenticationMethods is not supported with "
787+ "SSH protocol 1");
788+ for (n = 0; n < options.num_auth_methods; n++) {
789+ if (auth2_methods_valid(options.auth_methods[n],
790+ 1) == 0)
791+ break;
792+ }
793+ if (n >= options.num_auth_methods)
794+ fatal("AuthenticationMethods cannot be satisfied by "
795+ "enabled authentication methods");
796+ }
797+
798 /* set default channel AF */
799 channel_set_af(options.address_family);
800
801diff --git a/sshd_config.5 b/sshd_config.5
802index 314ecfb..ed81ac8 100644
803--- a/sshd_config.5
804+++ b/sshd_config.5
805@@ -151,6 +151,28 @@ See
806 in
807 .Xr ssh_config 5
808 for more information on patterns.
809+.It Cm AuthenticationMethods
810+Specifies the authentication methods that must be successfully completed
811+for a user to be granted access.
812+This option must be followed by one or more comma-separated lists of
813+authentication method names.
814+Successful authentication requires completion of every method in at least
815+one of these lists.
816+.Pp
817+For example, an argument of
818+.Dq publickey,password publickey,keyboard-interactive
819+would require the user to complete public key authentication, followed by
820+either password or keyboard interactive authentication.
821+Only methods that are next in one or more lists are offered at each stage,
822+so for this example, it would not be possible to attempt password or
823+keyboard-interactive authentication before public key.
824+.Pp
825+This option is only available for SSH protocol 2 and will yield a fatal
826+error if enabled if protocol 1 is also enabled.
827+Note that each authentication method listed should also be explicitly enabled
828+in the configuration.
829+The default is not to require multiple authentication; successful completion
830+of a single authentication method is sufficient.
831 .It Cm AuthorizedKeysFile
832 Specifies the file that contains the public keys that can be used
833 for user authentication.
834@@ -711,6 +733,7 @@ Available keywords are
835 .Cm AllowGroups ,
836 .Cm AllowTcpForwarding ,
837 .Cm AllowUsers ,
838+.Cm AuthenticationMethods ,
839 .Cm AuthorizedKeysFile ,
840 .Cm AuthorizedPrincipalsFile ,
841 .Cm Banner ,