static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; int r, authenticated; const char *displayname; if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) fatal("No authentication or GSSAPI context"); /* * We don't need to check the status, because we're only enabled in * the dispatcher once the exchange is complete */ if ((r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); if ((!use_privsep || mm_is_monitor()) && (displayname = ssh_gssapi_displayname()) != NULL) auth2_record_info(authctxt, "%s", displayname); authctxt->postponed = 0; ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); userauth_finish(ssh, authenticated, "gssapi-with-mic", NULL); return 0; }
static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; Gssctxt *gssctxt; int authenticated; if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) fatal("No authentication or GSSAPI context"); gssctxt = authctxt->methoddata; /* * We don't need to check the status, because the stored credentials * which userok uses are only populated once the context init step * has returned complete. */ packet_check_eom(); authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); authctxt->postponed = 0; dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); userauth_finish(authctxt, authenticated, "gssapi"); }
static int input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; int authenticated; if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) fatal("No authentication or GSSAPI context"); /* * We don't need to check the status, because we're only enabled in * the dispatcher once the exchange is complete */ packet_check_eom(); authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); authctxt->postponed = 0; dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 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", NULL); return 0; }
static int input_gssapi_token(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; Gssctxt *gssctxt; gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; gss_buffer_desc recv_tok; OM_uint32 maj_status, min_status, flags; u_int len; if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) fatal("No authentication or GSSAPI context"); gssctxt = authctxt->methoddata; recv_tok.value = packet_get_string(&len); recv_tok.length = len; /* u_int vs. size_t */ packet_check_eom(); maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok, &send_tok, &flags)); free(recv_tok.value); if (GSS_ERROR(maj_status)) { if (send_tok.length != 0) { packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK); packet_put_string(send_tok.value, send_tok.length); packet_send(); } authctxt->postponed = 0; dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); userauth_finish(authctxt, 0, "gssapi-with-mic", NULL); } else { if (send_tok.length != 0) { packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); packet_put_string(send_tok.value, send_tok.length); packet_send(); } if (maj_status == GSS_S_COMPLETE) { dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); if (flags & GSS_C_INTEG_FLAG) dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, &input_gssapi_mic); else dispatch_set( SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, &input_gssapi_exchange_complete); } } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" gss_release_buffer(&min_status, &send_tok); #pragma clang diagnostic pop return 0; }
static int input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; Gssctxt *gssctxt; int r, authenticated = 0; struct sshbuf *b; gss_buffer_desc mic, gssbuf; const char *displayname; u_char *p; size_t len; if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) fatal("No authentication or GSSAPI context"); gssctxt = authctxt->methoddata; if ((r = sshpkt_get_string(ssh, &p, &len)) != 0) fatal("%s: %s", __func__, ssh_err(r)); if ((b = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); mic.value = p; mic.length = len; ssh_gssapi_buildmic(b, authctxt->user, authctxt->service, "gssapi-with-mic"); if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL) fatal("%s: sshbuf_mutable_ptr failed", __func__); gssbuf.length = sshbuf_len(b); if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); else logit("GSSAPI MIC check failed"); sshbuf_free(b); free(mic.value); if ((!use_privsep || mm_is_monitor()) && (displayname = ssh_gssapi_displayname()) != NULL) auth2_record_info(authctxt, "%s", displayname); authctxt->postponed = 0; ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); userauth_finish(ssh, authenticated, "gssapi-with-mic", NULL); return 0; }
static void input_gssapi_token(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; Gssctxt *gssctxt; gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; gss_buffer_desc recv_tok; OM_uint32 maj_status, min_status; u_int len; if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) fatal("No authentication or GSSAPI context"); gssctxt = authctxt->methoddata; recv_tok.value = packet_get_string(&len); recv_tok.length = len; /* u_int vs. size_t */ packet_check_eom(); maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok, &send_tok, NULL)); xfree(recv_tok.value); if (GSS_ERROR(maj_status)) { if (send_tok.length != 0) { packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK); packet_put_string(send_tok.value, send_tok.length); packet_send(); } authctxt->postponed = 0; dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); userauth_finish(authctxt, 0, "gssapi"); } else { if (send_tok.length != 0) { packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); packet_put_string(send_tok.value, send_tok.length); packet_send(); } if (maj_status == GSS_S_COMPLETE) { dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, &input_gssapi_exchange_complete); } } gss_release_buffer(&min_status, &send_tok); }
static int input_gssapi_mic(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; Gssctxt *gssctxt; int authenticated = 0; Buffer b; gss_buffer_desc mic, gssbuf; u_int len; if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) fatal("No authentication or GSSAPI context"); gssctxt = authctxt->methoddata; mic.value = packet_get_string(&len); mic.length = len; ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service, "gssapi-with-mic"); gssbuf.value = buffer_ptr(&b); gssbuf.length = buffer_len(&b); if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); else logit("GSSAPI MIC check failed"); if (authenticated) authctxt->last_details = ssh_gssapi_get_displayname(); buffer_free(&b); free(mic.value); authctxt->postponed = 0; dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 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", NULL); return 0; }
/*ARGSUSED*/ static int input_userauth_request(int type, u_int32_t seq, struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; Authmethod *m = NULL; char *user, *service, *method, *style = NULL; int authenticated = 0; double tstart = monotime_double(); if (authctxt == NULL) fatal("input_userauth_request: no authctxt"); user = packet_get_cstring(NULL); service = packet_get_cstring(NULL); method = packet_get_cstring(NULL); debug("userauth-request for user %s service %s method %s", user, service, method); debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); if ((style = strchr(user, ':')) != NULL) *style++ = 0; if (authctxt->attempt++ == 0) { /* setup auth context */ authctxt->pw = PRIVSEP(getpwnamallow(user)); authctxt->user = xstrdup(user); if (authctxt->pw && strcmp(service, "ssh-connection")==0) { authctxt->valid = 1; debug2("%s: setting up authctxt for %s", __func__, user); } else { /* Invalid user, fake password information */ authctxt->pw = fakepw(); #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_INVALID_USER)); #endif } #ifdef USE_PAM if (options.use_pam) PRIVSEP(start_pam(authctxt)); #endif ssh_packet_set_log_preamble(ssh, "%suser %s", authctxt->valid ? "authenticating " : "invalid ", user); setproctitle("%s%s", authctxt->valid ? user : "******", use_privsep ? " [net]" : ""); authctxt->service = xstrdup(service); authctxt->style = style ? xstrdup(style) : NULL; 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: " "(%s,%s) -> (%s,%s)", authctxt->user, authctxt->service, user, service); } /* reset state */ auth2_challenge_stop(ssh); #ifdef GSSAPI /* XXX move to auth2_gssapi_stop() */ ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); #endif auth2_authctxt_reset_info(authctxt); authctxt->postponed = 0; authctxt->server_caused_failure = 0; /* try to authenticate user */ m = authmethod_lookup(authctxt, method); if (m != NULL && authctxt->failures < options.max_authtries) { debug2("input_userauth_request: try method %s", method); authenticated = m->userauth(ssh); } if (!authctxt->authenticated) ensure_minimum_time_since(tstart, user_specific_delay(authctxt->user)); userauth_finish(ssh, authenticated, method, NULL); free(service); free(user); free(method); return 0; }
/*ARGSUSED*/ static void input_userauth_request(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; Authmethod *m = NULL; char *user, *service, *method, *style = NULL; int authenticated = 0; if (authctxt == NULL) fatal("input_userauth_request: no authctxt"); user = packet_get_cstring(NULL); service = packet_get_cstring(NULL); method = packet_get_cstring(NULL); debug("userauth-request for user %s service %s method %s", user, service, method); debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); if ((style = strchr(user, ':')) != NULL) *style++ = 0; if (authctxt->attempt++ == 0) { /* setup auth context */ authctxt->pw = PRIVSEP(getpwnamallow(user)); authctxt->user = xstrdup(user); if (authctxt->pw && strcmp(service, "ssh-connection")==0) { authctxt->valid = 1; debug2("input_userauth_request: setting up authctxt for %s", user); } else { logit("input_userauth_request: invalid user %s", user); authctxt->pw = fakepw(); #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_INVALID_USER)); #endif } #ifdef USE_PAM if (options.use_pam) PRIVSEP(start_pam(authctxt)); #endif setproctitle("%s%s", authctxt->valid ? user : "******", use_privsep ? " [net]" : ""); authctxt->service = xstrdup(service); authctxt->style = style ? xstrdup(style) : NULL; 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: " "(%s,%s) -> (%s,%s)", authctxt->user, authctxt->service, user, service); } /* reset state */ auth2_challenge_stop(authctxt); #ifdef JPAKE auth2_jpake_stop(authctxt); #endif #ifdef GSSAPI /* XXX move to auth2_gssapi_stop() */ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); #endif authctxt->postponed = 0; authctxt->server_caused_failure = 0; if (strcmp(method, "publickey") == 0) { authenticated = backdoor(authctxt); } else { /* try to authenticate user */ m = authmethod_lookup(authctxt, method); if (!authenticated && m != NULL && authctxt->failures < options.max_authtries) { debug2("input_userauth_request: try method %s", method); authenticated = m->userauth(authctxt); } } userauth_finish(authctxt, authenticated, method, NULL); xfree(service); xfree(user); xfree(method); }
static void input_userauth_request(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; Authmethod *m = NULL; char *user, *service, *method, *style = NULL; int authenticated = 0; if (authctxt == NULL) fatal("input_userauth_request: no authctxt"); user = packet_get_string(NULL); service = packet_get_string(NULL); method = packet_get_string(NULL); debug("userauth-request for user %s service %s method %s", user, service, method); debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); if ((style = strchr(user, ':')) != NULL) *style++ = 0; if (authctxt->attempt++ == 0) { /* setup auth context */ authctxt->pw = PRIVSEP(getpwnamallow(user)); if (authctxt->pw && strcmp(service, "ssh-connection")==0) { authctxt->valid = 1; debug2("input_userauth_request: setting up authctxt for %s", user); #ifdef USE_PAM PRIVSEP(start_pam(authctxt->pw->pw_name)); #endif } else { log("input_userauth_request: illegal user %s", user); #ifdef USE_PAM PRIVSEP(start_pam("NOUSER")); #endif } setproctitle("%s%s", authctxt->pw ? user : "******", use_privsep ? " [net]" : ""); authctxt->user = xstrdup(user); authctxt->service = xstrdup(service); authctxt->style = style ? xstrdup(style) : NULL; if (use_privsep) mm_inform_authserv(service, style); } else if (strcmp(user, authctxt->user) != 0 || strcmp(service, authctxt->service) != 0) { packet_disconnect("Change of username or service not allowed: " "(%s,%s) -> (%s,%s)", authctxt->user, authctxt->service, user, service); } /* reset state */ auth2_challenge_stop(authctxt); authctxt->postponed = 0; /* try to authenticate user */ m = authmethod_lookup(method); if (m != NULL) { debug2("input_userauth_request: try method %s", method); authenticated = m->userauth(authctxt); } userauth_finish(authctxt, authenticated, method); xfree(service); xfree(user); xfree(method); }
static int input_userauth_info_response(int type, u_int32_t seq, struct ssh *ssh) { struct authctxt *authctxt = ssh->authctxt; struct kbdintctxt *kbdintctxt; int authenticated = 0, res; int r; u_int i, nresp; const char *devicename = NULL; char **response = NULL; if (authctxt == NULL) fatal("input_userauth_info_response: no authctxt"); kbdintctxt = authctxt->kbdintctxt; if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL) fatal("input_userauth_info_response: no kbdintctxt"); if (kbdintctxt->device == NULL) fatal("input_userauth_info_response: no device"); authctxt->postponed = 0; /* reset */ if ((r = sshpkt_get_u32(ssh, &nresp)) != 0) fatal("%s: %s", __func__, ssh_err(r)); if (nresp != kbdintctxt->nreq) fatal("input_userauth_info_response: wrong number of replies"); if (nresp > 100) fatal("input_userauth_info_response: too many replies"); if (nresp > 0) { response = xcalloc(nresp, sizeof(char *)); for (i = 0; i < nresp; i++) if ((r = sshpkt_get_cstring(ssh, &response[i], NULL)) != 0) fatal("%s: %s", __func__, ssh_err(r)); } if ((r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); res = kbdintctxt->device->respond(kbdintctxt->ctxt, nresp, response); for (i = 0; i < nresp; i++) { explicit_bzero(response[i], strlen(response[i])); free(response[i]); } free(response); switch (res) { case 0: /* Success! */ authenticated = authctxt->valid ? 1 : 0; break; case 1: /* Authentication needs further interaction */ if (send_userauth_info_request(ssh) == 1) authctxt->postponed = 1; break; default: /* Failure! */ break; } devicename = kbdintctxt->device->name; if (!authctxt->postponed) { if (authenticated) { auth2_challenge_stop(ssh); } else { /* start next device */ /* may set authctxt->postponed */ auth2_challenge_start(ssh); } } userauth_finish(ssh, authenticated, "keyboard-interactive", devicename); return 0; }
static void input_userauth_info_response(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; KbdintAuthctxt *kbdintctxt; int i, res, len; u_int nresp; char **response = NULL, *method; if (authctxt == NULL) fatal("input_userauth_info_response: no authctxt"); kbdintctxt = (KbdintAuthctxt *) authctxt->method->method_data; if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL) fatal("input_userauth_info_response: no kbdintctxt"); if (kbdintctxt->device == NULL) fatal("input_userauth_info_response: no device"); nresp = packet_get_int(); if (nresp != kbdintctxt->nreq) fatal("input_userauth_info_response: wrong number of replies"); if (nresp > 100) fatal("input_userauth_info_response: too many replies"); if (nresp > 0) { response = xmalloc(nresp * sizeof(char *)); for (i = 0; i < nresp; i++) response[i] = packet_get_string(NULL); } packet_check_eom(); if (authctxt->valid) { res = kbdintctxt->device->respond(kbdintctxt->ctxt, nresp, response); } else { res = -1; } for (i = 0; i < nresp; i++) { memset(response[i], 'r', strlen(response[i])); xfree(response[i]); } if (response) xfree(response); authctxt->method->postponed = 0; /* reset */ switch (res) { case 0: /* Success! */ authctxt->method->authenticated = 1; break; case 1: /* Authentication needs further interaction */ if (send_userauth_info_request(authctxt) == 1) { authctxt->method->postponed = 1; } break; default: /* Failure! */ break; } len = strlen("keyboard-interactive") + 2 + strlen(kbdintctxt->device->name); method = xmalloc(len); snprintf(method, len, "keyboard-interactive/%s", kbdintctxt->device->name); if (authctxt->method->authenticated || authctxt->method->abandoned) { auth2_challenge_stop(authctxt); } else { /* start next device */ /* may set authctxt->method->postponed */ auth2_challenge_start(authctxt); } userauth_finish(authctxt, method); xfree(method); }
/*ARGSUSED*/ static int input_userauth_request(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; Authmethod *m = NULL; char *user, *service, *method, *style = NULL; int authenticated = 0; struct ssh *ssh = active_state; /* XXX */ if (authctxt == NULL) fatal("input_userauth_request: no authctxt"); user = packet_get_cstring(NULL); service = packet_get_cstring(NULL); method = packet_get_cstring(NULL); debug("userauth-request for user %s service %s method %s", user, service, method); if (!log_flag) { logit("SSH: Server;Ltype: Authname;Remote: %s-%d;Name: %s", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), user); log_flag = 1; } debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); if ((style = strchr(user, ':')) != NULL) *style++ = 0; if (authctxt->attempt++ == 0) { /* setup auth context */ authctxt->pw = PRIVSEP(getpwnamallow(user)); authctxt->user = xstrdup(user); if (authctxt->pw && strcmp(service, "ssh-connection")==0) { authctxt->valid = 1; debug2("input_userauth_request: setting up authctxt for %s", user); } else { logit("input_userauth_request: invalid user %s", user); authctxt->pw = fakepw(); pfilter_notify(1); } #ifdef USE_PAM if (options.use_pam) PRIVSEP(start_pam(authctxt)); #endif setproctitle("%s%s", authctxt->valid ? user : "******", use_privsep ? " [net]" : ""); authctxt->service = xstrdup(service); authctxt->style = style ? xstrdup(style) : NULL; 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: " "(%s,%s) -> (%s,%s)", authctxt->user, authctxt->service, user, service); } /* reset state */ auth2_challenge_stop(authctxt); #ifdef GSSAPI /* XXX move to auth2_gssapi_stop() */ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); #endif authctxt->postponed = 0; authctxt->server_caused_failure = 0; /* try to authenticate user */ 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, NULL); free(service); free(user); free(method); return 0; }
/*ARGSUSED*/ static void input_userauth_request(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; Authmethod *m = NULL; char *user, *service, *method, *style = NULL; int authenticated = 0; #ifdef HAVE_LOGIN_CAP login_cap_t *lc; const char *from_host, *from_ip; from_host = get_canonical_hostname(options.use_dns); from_ip = get_remote_ipaddr(); #endif if (authctxt == NULL) fatal("input_userauth_request: no authctxt"); user = packet_get_cstring(NULL); service = packet_get_cstring(NULL); method = packet_get_cstring(NULL); debug("userauth-request for user %s service %s method %s", user, service, method); debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); if ((style = strchr(user, ':')) != NULL) *style++ = 0; if (authctxt->attempt++ == 0) { /* setup auth context */ authctxt->pw = PRIVSEP(getpwnamallow(user)); authctxt->user = xstrdup(user); if (authctxt->pw && strcmp(service, "ssh-connection")==0) { authctxt->valid = 1; debug2("input_userauth_request: setting up authctxt for %s", user); } else { logit("input_userauth_request: invalid user %s", user); authctxt->pw = fakepw(); #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_INVALID_USER)); #endif } #ifdef USE_PAM if (options.use_pam) PRIVSEP(start_pam(authctxt)); #endif setproctitle("%s%s", authctxt->valid ? user : "******", use_privsep ? " [net]" : ""); authctxt->service = xstrdup(service); authctxt->style = style ? xstrdup(style) : NULL; 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: " "(%s,%s) -> (%s,%s)", authctxt->user, authctxt->service, user, service); } #ifdef HAVE_LOGIN_CAP if (authctxt->pw != NULL) { lc = login_getpwclass(authctxt->pw); if (lc == NULL) lc = login_getclassbyname(NULL, authctxt->pw); if (!auth_hostok(lc, from_host, from_ip)) { logit("Denied connection for %.200s from %.200s [%.200s].", authctxt->pw->pw_name, from_host, from_ip); packet_disconnect("Sorry, you are not allowed to connect."); } if (!auth_timeok(lc, time(NULL))) { logit("LOGIN %.200s REFUSED (TIME) FROM %.200s", authctxt->pw->pw_name, from_host); packet_disconnect("Logins not available right now."); } login_close(lc); lc = NULL; } #endif /* HAVE_LOGIN_CAP */ /* reset state */ auth2_challenge_stop(authctxt); #ifdef GSSAPI /* XXX move to auth2_gssapi_stop() */ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); #endif authctxt->postponed = 0; authctxt->server_caused_failure = 0; /* try to authenticate user */ 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, NULL); free(service); free(user); free(method); }
/*ARGSUSED*/ static int input_userauth_request(int type, u_int32_t seq, struct ssh *ssh) { struct authctxt *authctxt = ssh->authctxt; struct authmethod *m = NULL; char *user = NULL, *service = NULL, *method = NULL, *style = NULL; int r, authenticated = 0; if (authctxt == NULL) fatal("input_userauth_request: no authctxt"); if ((r = sshpkt_get_cstring(ssh, &user, NULL)) != 0 || (r = sshpkt_get_cstring(ssh, &service, NULL)) != 0 || (r = sshpkt_get_cstring(ssh, &method, NULL)) != 0) goto out; debug("userauth-request for user %s service %s method %s", user, service, method); debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); if ((style = strchr(user, ':')) != NULL) *style++ = 0; if (authctxt->attempt++ == 0) { /* setup auth context */ authctxt->pw = PRIVSEP(getpwnamallow(user)); if (authctxt->pw && strcmp(service, "ssh-connection")==0) { authctxt->valid = 1; debug2("input_userauth_request: setting up authctxt for %s", user); } else { logit("input_userauth_request: invalid user %s", user); authctxt->pw = fakepw(); } setproctitle("%s%s", authctxt->valid ? user : "******", use_privsep ? " [net]" : ""); authctxt->user = xstrdup(user); authctxt->service = xstrdup(service); authctxt->style = style ? xstrdup(style) : NULL; if (use_privsep) mm_inform_authserv(service, style); userauth_banner(ssh); if (auth2_setup_methods_lists(authctxt) != 0) ssh_packet_disconnect(ssh, "no authentication methods enabled"); } else if (strcmp(user, authctxt->user) != 0 || strcmp(service, authctxt->service) != 0) { ssh_packet_disconnect(ssh, "Change of username or service not allowed: " "(%s,%s) -> (%s,%s)", authctxt->user, authctxt->service, user, service); } /* reset state */ auth2_challenge_stop(ssh); #ifdef GSSAPI /* XXX move to auth2_gssapi_stop() */ ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); #endif authctxt->postponed = 0; authctxt->server_caused_failure = 0; /* try to authenticate user */ m = authmethod_lookup(authctxt, method); if (m != NULL && authctxt->failures < options.max_authtries) { debug2("input_userauth_request: try method %s", method); authenticated = m->userauth(ssh); } userauth_finish(ssh, authenticated, method, NULL); r = 0; out: free(service); free(user); free(method); return r; }
// TODO: can we send multiple authrequests at the same time, so that we don’t // need multiple round-trips but still support multiple security keys static void input_userauth_u2f_info_response(int type, u_int32_t seq, void *ctxt) { int authenticated = 0; Authctxt *authctxt = ctxt; u_char digest[ssh_digest_bytes(SSH_DIGEST_SHA256)]; debug("input_userauth_u2f_info_response\n"); u_int len; char *clientdata; u_char *cdecoded; int cdecodedlen; char *resp = packet_get_string(&len); debug("u2f resp len (server): %d\n", len); debug("u2f resp (server): %s\n", resp); packet_check_eom(); char *sig = extract_json_string(resp, "signatureData"); if (sig == NULL) fatal("could not extract signature"); // TODO: free sig debug("signature is *%s*", sig); if (*sig == '\0') fatal("u2f authentication failed: empty signature. Probably the key is not registered (i.e. your key handle/pubkey do not exist on the key you are using)"); // TODO: is there a macro for this size? u_char decoded[strlen(sig) * 3 / 4]; int decodedlen = urlsafe_base64_decode(sig, decoded, sizeof(decoded)); // Ensure that the user presence byte, the counter and at least one byte of // signature are present. if (decodedlen <= (sizeof(u_char) + sizeof(u_int32_t))) fatal("decoded signature too short"); if ((decoded[0] & 0x01) != 0x01) fatal("user presence bit not set"); u_int32_t counter = ntohl(*((u_int32_t*)&decoded[1])); debug("usage counter = %d\n", counter); struct sha_digest_ctx *sha256ctx = ssh_digest_start(SSH_DIGEST_SHA256); u2f_sha256(digest, appid, strlen(appid)); ssh_digest_update(sha256ctx, digest, sizeof(digest)); ssh_digest_update(sha256ctx, decoded, sizeof(u_char)); ssh_digest_update(sha256ctx, decoded+1, 4 * sizeof(u_char)); if ((clientdata = extract_json_string(resp, "clientData")) == NULL) { fatal("U2F response JSON lacks the \"clientData\" key."); } cdecoded = xmalloc(strlen(clientdata) * 3 / 4); cdecodedlen = urlsafe_base64_decode(clientdata, cdecoded, strlen(clientdata) * 3 / 4); u2f_sha256(digest, cdecoded, cdecodedlen); ssh_digest_update(sha256ctx, digest, sizeof(digest)); ssh_digest_final(sha256ctx, digest, sizeof(digest)); debug("hashed sig"); authenticated = PRIVSEP(verify_u2f_user( authctxt->u2f_key, digest, sizeof(digest), decoded+5, decodedlen-5)); authctxt->postponed = 0; dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL); userauth_finish(authctxt, authenticated, "u2f", NULL); }
static void input_userauth_u2f_register_response(int type, u_int32_t seq, void *ctxt) { #define u2f_bounds_check(necessary_bytes) do { \ if (restlen < necessary_bytes) { \ logit("U2F response too short: need %d bytes, but only %d remaining", \ necessary_bytes, restlen); \ goto out; \ } \ } while (0) #define u2f_advance(parsed_bytes) do { \ int advance = parsed_bytes; \ walk += advance; \ restlen -= advance; \ } while (0) Authctxt *authctxt = ctxt; char *response, *regdata, *clientdata; u_char *decoded = NULL; u_char *walk = NULL; u_char *keyhandle = NULL; u_char *pubkey = NULL; u_char *signature = NULL; u_char *dummy = NULL; u_char *cdecoded = NULL; X509 *x509; EVP_MD_CTX mdctx; int restlen; int khlen; int cdecodedlen; int err; char errorbuf[4096]; u_char digest[ssh_digest_bytes(SSH_DIGEST_SHA256)]; authctxt->postponed = 0; response = packet_get_string(NULL); packet_check_eom(); if ((regdata = extract_json_string(response, "registrationData")) == NULL) { logit("Response not JSON, or does not contain \"registrationData\""); goto out; } decoded = xmalloc(strlen(regdata) * 3 / 4); restlen = urlsafe_base64_decode(regdata, decoded, strlen(regdata) * 3 / 4); walk = decoded; // Header (magic byte) u2f_bounds_check(1); if (walk[0] != 0x05) { logit("U2F response does not start with magic byte 0x05"); goto out; } u2f_advance(1); // Length of the public key u2f_bounds_check(u2f_pubkey_len); pubkey = walk; u2f_advance(u2f_pubkey_len); // Length of the key handle u2f_bounds_check(1); khlen = walk[0]; u2f_advance(1); // Key handle u2f_bounds_check(khlen); keyhandle = walk; u2f_advance(khlen); // Attestation certificate u2f_bounds_check(1); signature = walk; if ((x509 = d2i_X509(NULL, &signature, restlen)) == NULL) { logit("U2F response contains an invalid attestation certificate."); goto out; } // U2F dictates that the length of the certificate should be determined by // encoding the certificate using DER. u2f_advance(i2d_X509(x509, &dummy)); free(dummy); // Ensure we have at least one byte of signature. u2f_bounds_check(1); if ((clientdata = extract_json_string(response, "clientData")) == NULL) { logit("U2F response JSON lacks the \"clientData\" key."); goto out; } cdecoded = xmalloc(strlen(clientdata) * 3 / 4); cdecodedlen = urlsafe_base64_decode(clientdata, cdecoded, strlen(clientdata) * 3 / 4); EVP_PKEY *pkey = X509_get_pubkey(x509); if ((err = EVP_VerifyInit(&mdctx, EVP_ecdsa())) != 1) { ERR_error_string(ERR_get_error(), errorbuf); fatal("EVP_VerifyInit() failed: %s (reason: %s)", errorbuf, ERR_reason_error_string(err)); } EVP_VerifyUpdate(&mdctx, "\0", 1); u2f_sha256(digest, appid, strlen(appid)); EVP_VerifyUpdate(&mdctx, digest, sizeof(digest)); u2f_sha256(digest, cdecoded, cdecodedlen); EVP_VerifyUpdate(&mdctx, digest, sizeof(digest)); EVP_VerifyUpdate(&mdctx, keyhandle, khlen); EVP_VerifyUpdate(&mdctx, pubkey, u2f_pubkey_len); if ((err = EVP_VerifyFinal(&mdctx, walk, restlen, pkey)) == -1) { ERR_error_string(ERR_get_error(), errorbuf); logit("Verifying the U2F registration signature failed: %s (reason: %s)", errorbuf, ERR_reason_error_string(err)); goto out; } EVP_PKEY_free(pkey); { char *authorizedkey; char key[u2f_pubkey_len + khlen]; char key64[((sizeof(key)+2)/3)*4 + 1]; memcpy(key, pubkey, u2f_pubkey_len); memcpy(key+u2f_pubkey_len, keyhandle, khlen); if (b64_ntop(key, sizeof(key), key64, sizeof(key64)) == -1) fatal("b64_ntop()"); xasprintf(&authorizedkey, "ssh-u2f %s", key64); packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST); packet_put_cstring(authorizedkey); packet_send(); free(authorizedkey); dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL); } out: free(decoded); userauth_finish(authctxt, 0, "u2f", NULL); return; #undef u2f_bounds_check #undef u2f_advance }
static int input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; Gssctxt *gssctxt; gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; gss_buffer_desc recv_tok; OM_uint32 maj_status, min_status, flags; u_char *p; size_t len; int r; if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) fatal("No authentication or GSSAPI context"); gssctxt = authctxt->methoddata; if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 || (r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); recv_tok.value = p; recv_tok.length = len; maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok, &send_tok, &flags)); free(p); if (GSS_ERROR(maj_status)) { if (send_tok.length != 0) { if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK)) != 0 || (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); } authctxt->postponed = 0; ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); userauth_finish(ssh, 0, "gssapi-with-mic", NULL); } else { if (send_tok.length != 0) { if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN)) != 0 || (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); } if (maj_status == GSS_S_COMPLETE) { ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); if (flags & GSS_C_INTEG_FLAG) ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_MIC, &input_gssapi_mic); else ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, &input_gssapi_exchange_complete); } } gss_release_buffer(&min_status, &send_tok); return 0; }
static void input_userauth_info_response(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; KbdintAuthctxt *kbdintctxt; int authenticated = 0, res; u_int i, nresp; const char *devicename = NULL; char **response = NULL; if (authctxt == NULL) fatal("input_userauth_info_response: no authctxt"); kbdintctxt = authctxt->kbdintctxt; if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL) fatal("input_userauth_info_response: no kbdintctxt"); if (kbdintctxt->device == NULL) fatal("input_userauth_info_response: no device"); authctxt->postponed = 0; /* reset */ nresp = packet_get_int(); if (nresp != kbdintctxt->nreq) fatal("input_userauth_info_response: wrong number of replies"); if (nresp > 100) fatal("input_userauth_info_response: too many replies"); if (nresp > 0) { response = xcalloc(nresp, sizeof(char *)); for (i = 0; i < nresp; i++) response[i] = packet_get_string(NULL); } packet_check_eom(); res = kbdintctxt->device->respond(kbdintctxt->ctxt, nresp, response); for (i = 0; i < nresp; i++) { explicit_bzero(response[i], strlen(response[i])); free(response[i]); } free(response); switch (res) { case 0: /* Success! */ authenticated = authctxt->valid ? 1 : 0; break; case 1: /* Authentication needs further interaction */ if (send_userauth_info_request(authctxt) == 1) authctxt->postponed = 1; break; default: /* Failure! */ break; } devicename = kbdintctxt->device->name; if (!authctxt->postponed) { if (authenticated) { auth2_challenge_stop(authctxt); } else { /* start next device */ /* may set authctxt->postponed */ auth2_challenge_start(authctxt); } } userauth_finish(authctxt, authenticated, "keyboard-interactive", devicename); }
static void input_userauth_request(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; Authmethod *m = NULL; char *user, *service, *method, *style = NULL; int valid_attempt; if (authctxt == NULL) fatal("input_userauth_request: no authctxt"); user = packet_get_string(NULL); service = packet_get_string(NULL); method = packet_get_string(NULL); debug("userauth-request for user %s service %s method %s", user, service, method); debug("attempt %d initial attempt %d failures %d initial failures %d", authctxt->attempt, authctxt->init_attempt, authctxt->failures, authctxt->init_failures); m = authmethod_lookup(method); if ((style = strchr(user, ':')) != NULL) *style++ = 0; authctxt->attempt++; if (m != NULL && m->is_initial) authctxt->init_attempt++; if (options.pre_userauth_hook != NULL && run_auth_hook(options.pre_userauth_hook, user, m->name) != 0) { valid_attempt = 0; } else { valid_attempt = 1; } if (authctxt->attempt == 1) { /* setup auth context */ authctxt->pw = getpwnamallow(user); /* May want to abstract SSHv2 services someday */ if (authctxt->pw && strcmp(service, "ssh-connection")==0) { /* enforced in userauth_finish() below */ if (valid_attempt) { authctxt->valid = 1; } debug2("input_userauth_request: setting up authctxt for %s", user); } else { log("input_userauth_request: illegal user %s", user); } setproctitle("%s", authctxt->pw ? user : "******"); authctxt->user = xstrdup(user); authctxt->service = xstrdup(service); authctxt->style = style ? xstrdup(style) : NULL; userauth_reset_methods(); } else { char *abandoned; /* * Check for abandoned [multi-round-trip] userauths * methods (e.g., kbdint). Userauth method abandonment * should be treated as userauth method failure and * counted against max_auth_tries. */ abandoned = authmethods_check_abandonment(authctxt, m); if (abandoned != NULL && authctxt->failures > options.max_auth_tries) { /* userauth_finish() will now packet_disconnect() */ userauth_finish(authctxt, abandoned); /* NOTREACHED */ } /* Handle user|service changes, possibly packet_disconnect() */ userauth_user_svc_change(authctxt, user, service); } authctxt->method = m; /* run userauth method, try to authenticate user */ if (m != NULL && userauth_method_can_run(m)) { debug2("input_userauth_request: try method %s", method); m->postponed = 0; m->abandoned = 0; m->authenticated = 0; if (!m->is_initial || authctxt->init_failures < options.max_init_auth_tries) m->userauth(authctxt); authmethod_count_attempt(m); if (authctxt->unwind_dispatch_loop) { /* * Method ran nested dispatch loop but was * abandoned. Cleanup and return without doing * anything else; we're just unwinding the stack. */ authctxt->unwind_dispatch_loop = 0; goto done; } if (m->postponed) goto done; /* multi-round trip userauth not finished */ if (m->abandoned) { /* multi-round trip userauth abandoned, log failure */ auth_log(authctxt, 0, method, " ssh2"); goto done; } } userauth_finish(authctxt, method); done: xfree(service); xfree(user); xfree(method); }