diff -up openssh-5.9p1/auth1.c.required-authentication openssh-5.9p1/auth1.c --- openssh-5.9p1/auth1.c.required-authentication 2010-08-31 14:36:39.000000000 +0200 +++ openssh-5.9p1/auth1.c 2012-11-26 15:36:02.138986418 +0100 @@ -98,6 +98,55 @@ static const struct AuthMethod1 return (NULL); } +static const struct AuthMethod1 * +lookup_authmethod1_by_name(const char *name) +{ + int i; + + for (i = 0; auth1_methods[i].name != NULL; i++) + if (strcmp(auth1_methods[i].name, name) == 0) + return (&(auth1_methods[i])); + + return NULL; +} + +#define DELIM "," +int +auth1_check_required(const char *list) +{ + char *orig_methods, *methods, *cp; + static const struct AuthMethod1 *m; + int ret = 0; + + orig_methods = methods = xstrdup(list); + for(;;) { /* XXX maybe: while ((cp = ...) != NULL) ? */ + if ((cp = strsep(&methods, DELIM)) == NULL) + break; + debug2("auth1_check_required: method \"%s\"", cp); + if (*cp == '\0') { + debug("auth1_check_required: empty method"); + ret = -1; + } + if ((m = lookup_authmethod1_by_name(cp)) == NULL) { + debug("auth1_check_required: unknown method " + "\"%s\"", cp); + ret = -1; + break; + } + if (*(m->enabled) == 0) { + debug("auth1_check_required: method %s explicitly " + "disabled", cp); + ret = -1; + } + /* Activate method if it isn't already */ + if (*(m->enabled) == -1) + *(m->enabled) = 1; + } + xfree(orig_methods); + return (ret); +} + + static char * get_authname(int type) { @@ -237,6 +286,7 @@ do_authloop(Authctxt *authctxt) { int authenticated = 0; char info[1024]; + const char *meth_name; int prev = 0, type = 0; const struct AuthMethod1 *meth; @@ -244,7 +294,7 @@ do_authloop(Authctxt *authctxt) authctxt->valid ? "" : "invalid user ", authctxt->user); /* If the user has no password, accept authentication immediately. */ - if (options.permit_empty_passwd && options.password_authentication && + if (options.permit_empty_passwd && options.password_authentication && options.password_authentication && #ifdef KRB5 (!options.kerberos_authentication || options.kerberos_or_local_passwd) && #endif @@ -253,7 +303,7 @@ do_authloop(Authctxt *authctxt) if (options.use_pam && (PRIVSEP(do_pam_account()))) #endif { - auth_log(authctxt, 1, "without authentication", ""); + auth_log(authctxt, 1, "without authentication", NULL, ""); return; } } @@ -272,6 +322,7 @@ do_authloop(Authctxt *authctxt) /* Get a packet from the client. */ prev = type; type = packet_read(); + meth_name = get_authname(type); /* * If we started challenge-response authentication but the @@ -287,8 +338,8 @@ do_authloop(Authctxt *authctxt) if (authctxt->failures >= options.max_authtries) goto skip; if ((meth = lookup_authmethod1(type)) == NULL) { - logit("Unknown message during authentication: " - "type %d", type); + logit("Unknown message during authentication: type %d", + type); goto skip; } @@ -297,6 +348,17 @@ do_authloop(Authctxt *authctxt) goto skip; } + /* + * Skip methods not in required list, until all the required + * ones are done + */ + if (options.required_auth1 != NULL && + !auth_method_in_list(options.required_auth1, meth_name)) { + debug("Skipping method \"%s\" until required " + "authentication completed", meth_name); + goto skip; + } + authenticated = meth->method(authctxt, info, sizeof(info)); if (authenticated == -1) continue; /* "postponed" */ @@ -352,7 +414,29 @@ do_authloop(Authctxt *authctxt) skip: /* Log before sending the reply */ - auth_log(authctxt, authenticated, get_authname(type), info); + auth_log(authctxt, authenticated, meth_name, NULL, info); + + /* Loop until the required authmethods are done */ + if (authenticated && options.required_auth1 != NULL) { + if (auth_remove_from_list(&options.required_auth1, + meth_name) == 0) + fatal("INTERNAL ERROR: authenticated method " + "\"%s\" not in required list \"%s\"", + meth_name, options.required_auth1); + debug2("do_authloop: required list now: %s", + options.required_auth1 == NULL ? + "DONE" : options.required_auth1); + if (options.required_auth1 == NULL) + return; + authenticated = 0; + /* + * Disable method so client can't authenticate with it + * after the required authentications are complete. + */ + *(meth->enabled) = 0; + packet_send_debug("Further authentication required"); + goto send_fail; + } if (client_user != NULL) { xfree(client_user); @@ -368,6 +452,7 @@ do_authloop(Authctxt *authctxt) #endif packet_disconnect(AUTH_FAIL_MSG, authctxt->user); } + send_fail: packet_start(SSH_SMSG_FAILURE); packet_send(); diff -up openssh-5.9p1/auth2-chall.c.required-authentication openssh-5.9p1/auth2-chall.c --- openssh-5.9p1/auth2-chall.c.required-authentication 2009-01-28 06:13:39.000000000 +0100 +++ openssh-5.9p1/auth2-chall.c 2012-11-26 15:36:02.138986418 +0100 @@ -341,7 +341,8 @@ input_userauth_info_response(int type, u auth2_challenge_start(authctxt); } } - userauth_finish(authctxt, authenticated, method); + userauth_finish(authctxt, authenticated, "keyboard-interactive", + authctxt->kbdintctxt?kbdintctxt->device->name:NULL); xfree(method); } diff -up openssh-5.9p1/auth2.c.required-authentication openssh-5.9p1/auth2.c --- openssh-5.9p1/auth2.c.required-authentication 2011-05-05 06:04:11.000000000 +0200 +++ openssh-5.9p1/auth2.c 2012-11-26 15:36:02.138986418 +0100 @@ -215,7 +215,7 @@ input_userauth_request(int type, u_int32 { Authctxt *authctxt = ctxt; Authmethod *m = NULL; - char *user, *service, *method, *style = NULL; + char *user, *service, *method, *active_methods, *style = NULL; int authenticated = 0; if (authctxt == NULL) @@ -277,22 +277,31 @@ input_userauth_request(int type, u_int32 authctxt->server_caused_failure = 0; /* try to authenticate user */ - m = authmethod_lookup(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); + active_methods = authmethods_get(); + if (strcmp(method, "none") == 0 || + auth_method_in_list(active_methods, method)) { + m = authmethod_lookup(method); + if (m != NULL) { + debug2("input_userauth_request: try method %s", method); + authenticated = m->userauth(authctxt); + } + } + xfree(active_methods); + userauth_finish(authctxt, authenticated, method, NULL); + xfree(service); xfree(user); xfree(method); } void -userauth_finish(Authctxt *authctxt, int authenticated, char *method) +userauth_finish(Authctxt *authctxt, int authenticated, const char *method, + const char *submethod) { char *methods; + Authmethod *m = NULL; + u_int partial = 0; if (!authctxt->valid && authenticated) fatal("INTERNAL ERROR: authenticated invalid user %s", @@ -330,12 +339,42 @@ userauth_finish(Authctxt *authctxt, int #endif /* _UNICOS */ /* Log before sending the reply */ - auth_log(authctxt, authenticated, method, " ssh2"); + auth_log(authctxt, authenticated, method, submethod, " ssh2"); if (authctxt->postponed) return; - /* XXX todo: check if multiple auth methods are needed */ + /* Handle RequiredAuthentications2: loop until required methods done */ + if (authenticated && options.required_auth2 != NULL) { + if ((m = authmethod_lookup(method)) == NULL) + fatal("INTERNAL ERROR: authenticated method " + "\"%s\" unknown", method); + if (auth_remove_from_list(&options.required_auth2, method) == 0) + fatal("INTERNAL ERROR: authenticated method " + "\"%s\" not in required list \"%s\"", + method, options.required_auth2); + debug2("userauth_finish: required list now: %s", + options.required_auth2 == NULL ? + "DONE" : options.required_auth2); + /* + * if authenticated and no more required methods + * then declare success + */ + if ( authenticated && options.required_auth2 == NULL ) { + debug2("userauth_finish: authenticated and no more required methods"); + } else { + /* + * Disable method so client can't authenticate with it after + * the required authentications are complete. + */ + if (m->enabled != NULL) + *(m->enabled) = 0; + authenticated = 0; + partial = 1; + goto send_fail; + } + } + if (authenticated == 1) { /* turn off userauth */ dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore); @@ -345,7 +384,6 @@ userauth_finish(Authctxt *authctxt, int /* now we can break out */ authctxt->success = 1; } else { - /* Allow initial try of "none" auth without failure penalty */ if (!authctxt->server_caused_failure && (authctxt->attempt > 1 || strcmp(method, "none") != 0)) @@ -356,10 +394,11 @@ userauth_finish(Authctxt *authctxt, int #endif packet_disconnect(AUTH_FAIL_MSG, authctxt->user); } + send_fail: methods = authmethods_get(); 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); @@ -373,6 +412,9 @@ authmethods_get(void) char *list; int i; + if (options.required_auth2 != NULL) + return xstrdup(options.required_auth2); + buffer_init(&b); for (i = 0; authmethods[i] != NULL; i++) { if (strcmp(authmethods[i]->name, "none") == 0) @@ -407,3 +449,43 @@ authmethod_lookup(const char *name) return NULL; } +#define DELIM "," + +int +auth2_check_required(const char *list) +{ + char *orig_methods, *methods, *cp; + struct Authmethod *m; + int i, ret = 0; + + orig_methods = methods = xstrdup(list); + for(;;) { + if ((cp = strsep(&methods, DELIM)) == NULL) + break; + debug2("auth2_check_required: method \"%s\"", cp); + if (*cp == '\0') { + debug("auth2_check_required: empty method"); + ret = -1; + } + for (i = 0; authmethods[i] != NULL; i++) + if (strcmp(cp, authmethods[i]->name) == 0) + break; + if ((m = authmethods[i]) == NULL) { + debug("auth2_check_required: unknown method " + "\"%s\"", cp); + ret = -1; + break; + } + if (m->enabled == NULL || *(m->enabled) == 0) { + debug("auth2_check_required: method %s explicitly " + "disabled", cp); + ret = -1; + } + /* Activate method if it isn't already */ + if (m->enabled != NULL && *(m->enabled) == -1) + *(m->enabled) = 1; + } + xfree(orig_methods); + return (ret); +} + diff -up openssh-5.9p1/auth2-gss.c.required-authentication openssh-5.9p1/auth2-gss.c --- openssh-5.9p1/auth2-gss.c.required-authentication 2011-05-05 06:04:11.000000000 +0200 +++ openssh-5.9p1/auth2-gss.c 2012-11-26 15:36:02.138986418 +0100 @@ -163,7 +163,7 @@ input_gssapi_token(int type, u_int32_t p } 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, 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 ple 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 -up openssh-5.9p1/auth2-none.c.required-authentication openssh-5.9p1/auth2-none.c --- openssh-5.9p1/auth2-none.c.required-authentication 2010-06-26 02:01:33.000000000 +0200 +++ openssh-5.9p1/auth2-none.c 2012-11-26 15:36:02.139986402 +0100 @@ -61,7 +61,7 @@ userauth_none(Authctxt *authctxt) { none_enabled = 0; packet_check_eom(); - if (options.permit_empty_passwd && options.password_authentication) + if (options.permit_empty_passwd && options.password_authentication && options.required_auth2 == NULL) return (PRIVSEP(auth_password(authctxt, ""))); return (0); } diff -up openssh-5.9p1/auth.c.required-authentication openssh-5.9p1/auth.c --- openssh-5.9p1/auth.c.required-authentication 2012-11-26 15:27:28.134216999 +0100 +++ openssh-5.9p1/auth.c 2012-11-26 15:36:02.137986437 +0100 @@ -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, const char *method, + const char *submethod, const char *info) { void (*authlog) (const char *fmt,...) = verbose; char *authmsg; @@ -271,9 +272,10 @@ auth_log(Authctxt *authctxt, int authent 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 +305,7 @@ auth_log(Authctxt *authctxt, int authent * 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: @@ -694,3 +696,57 @@ fakepw(void) return (&fake); } + +int +auth_method_in_list(const char *list, const char *method) +{ + char *cp; + + cp = match_list(method, list, NULL); + if (cp != NULL) { + xfree(cp); + return 1; + } + + return 0; +} + +#define DELIM "," +int +auth_remove_from_list(char **list, const char *method) +{ + char *oldlist, *cp, *newlist = NULL; + u_int len = 0, ret = 0; + + if (list == NULL || *list == NULL) + return (0); + + oldlist = *list; + len = strlen(oldlist) + 1; + newlist = xmalloc(len); + memset(newlist, '\0', len); + + /* Remove method from list, if present */ + for (;;) { + if ((cp = strsep(&oldlist, DELIM)) == NULL) + break; + if (*cp == '\0') + continue; + if (strcmp(cp, method) != 0) { + if (*newlist != '\0') + strlcat(newlist, DELIM, len); + strlcat(newlist, cp, len); + } else + ret++; + } + + /* Return NULL instead of empty list */ + if (*newlist == '\0') { + xfree(newlist); + newlist = NULL; + } + xfree(*list); + *list = newlist; + + return (ret); +} diff -up openssh-5.9p1/auth.h.required-authentication openssh-5.9p1/auth.h --- openssh-5.9p1/auth.h.required-authentication 2011-05-29 13:39:38.000000000 +0200 +++ openssh-5.9p1/auth.h 2012-11-26 15:36:02.138986418 +0100 @@ -142,10 +142,11 @@ 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, 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); @@ -192,6 +193,11 @@ void auth_debug_send(void); void auth_debug_reset(void); struct passwd *fakepw(void); +int auth_method_in_list(const char *, const char *); +int auth_remove_from_list(char **, const char *); + +int auth1_check_required(const char *); +int auth2_check_required(const char *); int sys_auth_passwd(Authctxt *, const char *); diff -up openssh-5.9p1/monitor.c.required-authentication openssh-5.9p1/monitor.c --- openssh-5.9p1/monitor.c.required-authentication 2012-11-26 15:27:28.128217022 +0100 +++ openssh-5.9p1/monitor.c 2012-11-26 15:36:02.140986390 +0100 @@ -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; @@ -353,6 +354,7 @@ monitor_child_preauth(Authctxt *_authctx { struct mon_table *ent; int authenticated = 0; + char **req_auth; debug3("preauth child monitor started"); @@ -367,12 +369,14 @@ monitor_child_preauth(Authctxt *_authctx if (compat20) { mon_dispatch = mon_dispatch_proto20; + req_auth = &options.required_auth2; /* Permit requests for moduli and signatures */ monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); } else { mon_dispatch = mon_dispatch_proto15; + req_auth = &options.required_auth1; monitor_permit(mon_dispatch, MONITOR_REQ_SESSKEY, 1); } @@ -380,6 +384,7 @@ monitor_child_preauth(Authctxt *_authctx /* The first few requests do not require asynchronous access */ while (!authenticated) { auth_method = "unknown"; + auth_submethod = NULL; authenticated = (monitor_read(pmonitor, mon_dispatch, &ent) == 1); if (authenticated) { if (!(ent->flags & MON_AUTHDECIDE)) @@ -401,10 +406,19 @@ monitor_child_preauth(Authctxt *_authctx } #endif } + /* Loop until the required authmethods are done */ + if (authenticated && *req_auth != NULL) { + if (auth_remove_from_list(req_auth, auth_method) == 0) + fatal("INTERNAL ERROR: authenticated method " + "\"%s\" not in required list \"%s\"", + auth_method, *req_auth); + debug2("monitor_child_preauth: required list now: %s", + *req_auth == NULL ? "DONE" : *req_auth); + } if (ent->flags & (MON_AUTHDECIDE|MON_ALOG)) { auth_log(authctxt, authenticated, auth_method, - compat20 ? " ssh2" : ""); + auth_submethod, compat20 ? " ssh2" : ""); if (!authenticated) authctxt->failures++; } @@ -417,6 +431,8 @@ monitor_child_preauth(Authctxt *_authctx } } #endif + if (*req_auth != NULL) + authenticated = 0; } /* Drain any buffered messages from the child */ @@ -862,6 +878,7 @@ mm_answer_authpassword(int sock, Buffer auth_method = "none"; else auth_method = "password"; + auth_submethod = NULL; /* Causes monitor loop to terminate if authenticated */ return (authenticated); @@ -921,6 +938,7 @@ mm_answer_bsdauthrespond(int sock, Buffe mm_request_send(sock, MONITOR_ANS_BSDAUTHRESPOND, m); auth_method = "bsdauth"; + auth_submethod = NULL; return (authok != 0); } @@ -970,6 +988,7 @@ mm_answer_skeyrespond(int sock, Buffer * mm_request_send(sock, MONITOR_ANS_SKEYRESPOND, m); auth_method = "skey"; + auth_submethod = NULL; return (authok != 0); } @@ -1059,7 +1078,8 @@ 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); } @@ -1088,7 +1108,8 @@ mm_answer_pam_respond(int sock, Buffer * 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); @@ -1102,7 +1123,8 @@ mm_answer_pam_free_ctx(int sock, Buffer (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 @@ -1138,6 +1160,7 @@ mm_answer_keyallowed(int sock, Buffer *m allowed = options.pubkey_authentication && user_key_allowed(authctxt->pw, key); auth_method = "publickey"; + auth_submethod = NULL; if (options.pubkey_authentication && allowed != 1) auth_clear_options(); break; @@ -1146,6 +1169,7 @@ mm_answer_keyallowed(int sock, Buffer *m hostbased_key_allowed(authctxt->pw, cuser, chost, key); auth_method = "hostbased"; + auth_submethod = NULL; break; case MM_RSAHOSTKEY: key->type = KEY_RSA1; /* XXX */ @@ -1155,6 +1179,7 @@ mm_answer_keyallowed(int sock, Buffer *m if (options.rhosts_rsa_authentication && allowed != 1) auth_clear_options(); auth_method = "rsa"; + auth_submethod = NULL; break; default: fatal("%s: unknown key type %d", __func__, type); @@ -1180,7 +1205,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, auth_method, auth_submethod, + compat20 ? " ssh2" : ""); xfree(blob); xfree(cuser); xfree(chost); @@ -1356,6 +1382,7 @@ mm_answer_keyverify(int sock, Buffer *m) xfree(data); auth_method = key_blobtype == MM_USERKEY ? "publickey" : "hostbased"; + auth_submethod = NULL; monitor_reset_key_state(); @@ -1545,6 +1572,7 @@ mm_answer_rsa_keyallowed(int sock, Buffe debug3("%s entering", __func__); auth_method = "rsa"; + auth_submethod = NULL; if (options.rsa_authentication && authctxt->valid) { if ((client_n = BN_new()) == NULL) fatal("%s: BN_new", __func__); @@ -1650,6 +1678,7 @@ mm_answer_rsa_response(int sock, Buffer xfree(response); auth_method = key_blobtype == MM_RSAUSERKEY ? "rsa" : "rhosts-rsa"; + auth_submethod = NULL; /* reset state */ BN_clear_free(ssh1_challenge); @@ -2099,6 +2128,7 @@ mm_answer_gss_userok(int sock, Buffer *m mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m); auth_method = "gssapi-with-mic"; + auth_submethod = NULL; /* Monitor loop will terminate if authenticated */ return (authenticated); @@ -2303,6 +2333,7 @@ mm_answer_jpake_check_confirm(int sock, monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_STEP1, 1); auth_method = "jpake-01@openssh.com"; + auth_submethod = NULL; return authenticated; } diff -up openssh-5.9p1/servconf.c.required-authentication openssh-5.9p1/servconf.c --- openssh-5.9p1/servconf.c.required-authentication 2012-11-26 15:27:28.129217018 +0100 +++ openssh-5.9p1/servconf.c 2012-11-26 15:36:02.140986390 +0100 @@ -42,6 +42,8 @@ #include "key.h" #include "kex.h" #include "mac.h" +#include "hostfile.h" +#include "auth.h" #include "match.h" #include "channels.h" #include "groupaccess.h" @@ -129,6 +131,8 @@ initialize_server_options(ServerOptions options->num_authkeys_files = 0; options->num_accept_env = 0; options->permit_tun = -1; + options->required_auth1 = NULL; + options->required_auth2 = NULL; options->num_permitted_opens = -1; options->adm_forced_command = NULL; options->chroot_directory = NULL; @@ -319,6 +323,7 @@ typedef enum { sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, + sRequiredAuthentications1, sRequiredAuthentications2, sMatch, sPermitOpen, sForceCommand, sChrootDirectory, sUsePrivilegeSeparation, sAllowAgentForwarding, sZeroKnowledgePasswordAuthentication, sHostCertificate, @@ -447,6 +452,8 @@ static struct { { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL }, { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL }, { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL }, + { "requiredauthentications1", sRequiredAuthentications1, SSHCFG_ALL }, + { "requiredauthentications2", sRequiredAuthentications2, SSHCFG_ALL }, { "ipqos", sIPQoS, SSHCFG_ALL }, { NULL, sBadOption, 0 } }; @@ -1220,6 +1227,37 @@ process_server_config_line(ServerOptions options->max_startups = options->max_startups_begin; break; + + case sRequiredAuthentications1: + if (*activep && options->required_auth1 == NULL) { + charptr = &options->required_auth1; + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing argument.", + filename, linenum); + if (auth1_check_required(arg) != 0) + fatal("%.200s line %d: Invalid required authentication " + "list", filename, linenum); + if (*charptr == NULL) + *charptr = xstrdup(arg); + } + return 0; + + case sRequiredAuthentications2: + if (*activep && options->required_auth2 == NULL) { + charptr = &options->required_auth2; + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing argument.", + filename, linenum); + if (auth2_check_required(arg) != 0) + fatal("%.200s line %d: Invalid required authentication " + "list", filename, linenum); + if (*charptr == NULL) + *charptr = xstrdup(arg); + } + return 0; + case sMaxAuthTries: intptr = &options->max_authtries; goto parse_int; @@ -1776,6 +1814,7 @@ 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_string(sRequiredAuthentications2, o->required_auth2); /* other arguments */ for (i = 0; i < o->num_subsystems; i++) diff -up openssh-5.9p1/servconf.h.required-authentication openssh-5.9p1/servconf.h --- openssh-5.9p1/servconf.h.required-authentication 2011-06-23 00:30:03.000000000 +0200 +++ openssh-5.9p1/servconf.h 2012-11-26 15:40:11.694443938 +0100 @@ -154,6 +154,9 @@ typedef struct { u_int num_authkeys_files; /* Files containing public keys */ char *authorized_keys_files[MAX_AUTHKEYS_FILES]; + char *required_auth1; + char *required_auth2; + char *adm_forced_command; int use_pam; /* Enable auth via PAM */ @@ -180,6 +183,8 @@ typedef struct { M_CP_STROPT(revoked_keys_file); \ M_CP_STROPT(authorized_principals_file); \ M_CP_STRARRAYOPT(authorized_keys_files, num_authkeys_files); \ + M_CP_STROPT(required_auth1); \ + M_CP_STROPT(required_auth2); \ } while (0) void initialize_server_options(ServerOptions *); diff -up openssh-5.9p1/sshd_config.5.required-authentication openssh-5.9p1/sshd_config.5 --- openssh-5.9p1/sshd_config.5.required-authentication 2011-08-05 22:17:33.000000000 +0200 +++ openssh-5.9p1/sshd_config.5 2012-11-26 15:36:02.141986377 +0100 @@ -723,6 +723,8 @@ Available keywords are .Cm PermitOpen , .Cm PermitRootLogin , .Cm PermitTunnel , +.Cm RequiredAuthentications1, +.Cm RequiredAuthentications2, .Cm PubkeyAuthentication , .Cm RhostsRSAAuthentication , .Cm RSAAuthentication , @@ -920,6 +922,32 @@ Specifies a list of revoked public keys. Keys listed in this file will be refused for public key authentication. Note that if this file is not readable, then public key authentication will be refused for all users. +.It Cm RequiredAuthentications[12] +Specifies required methods of authentications that has to succeed before +authorizing the connection. (RequiredAuthentication1 for Protocol version 1, +and RequiredAuthentication2 for v2) +.Pp +.Bl -item -offset indent -compact +.It +RequiredAuthentications1 method[,method...] +.It +RequiredAuthentications2 method[,method...] +.El +.Pp +Example 1: +.Bl -item -offset indent -compact +RequiredAuthentications2 password,hostbased +.El +Example 2: +.Bl -item -offset indent -compact +RequiredAuthentications2 publickey,password +.El +.Pp +Available methods: +.Bl -item -offset indent -compact +.It +password, keyboard-interactive, publickey, hostbased, gssapi-keyex, gssapi-with-mic +.El .It Cm RhostsRSAAuthentication Specifies whether rhosts or /etc/hosts.equiv authentication together with successful RSA host authentication is allowed.