/* * Copyright (c) 2000 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" RCSID("$OpenBSD: auth2.c,v 1.107 2004/07/28 09:40:29 markus Exp $"); #include "ssh2.h" #include "xmalloc.h" #include "packet.h" #include "log.h" #include "servconf.h" #include "compat.h" #include "auth.h" #include "dispatch.h" #include "pathnames.h" #include "monitor_wrap.h" #include "buffer.h" #ifdef GSSAPI #include "ssh-gss.h" #endif /* import */ extern ServerOptions options; extern u_char *session_id2; extern u_int session_id2_len; extern Buffer loginmsg; /* methods */ extern Authmethod method_none; extern Authmethod method_pubkey; extern Authmethod method_passwd; extern Authmethod method_kbdint; extern Authmethod method_hostbased; #ifdef GSSAPI extern Authmethod method_gssapi; #endif Authmethod *authmethods[] = { &method_none, &method_pubkey, #ifdef GSSAPI &method_gssapi, #endif &method_passwd, &method_kbdint, &method_hostbased, NULL }; /* protocol */ static void input_service_request(int, u_int32_t, void *); static void input_userauth_request(int, u_int32_t, void *); /* helper */ static Authmethod *authmethod_lookup(const char *); static char *authmethods_get(void); int user_key_allowed(struct passwd *, Key *); /* * loop until authctxt->success == TRUE */ void do_authentication2(Authctxt *authctxt) { /* challenge-response is implemented via keyboard interactive */ if (options.challenge_response_authentication) options.kbd_interactive_authentication = 1; dispatch_init(&dispatch_protocol_error); dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request); dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt); } static void input_service_request(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; u_int len; int acceptit = 0; char *service = packet_get_string(&len); packet_check_eom(); if (authctxt == NULL) fatal("input_service_request: no authctxt"); if (strcmp(service, "ssh-userauth") == 0) { if (!authctxt->success) { acceptit = 1; /* now we can handle user-auth requests */ dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request); } } /* XXX all other service requests are denied */ if (acceptit) { packet_start(SSH2_MSG_SERVICE_ACCEPT); packet_put_cstring(service); packet_send(); packet_write_wait(); } else { debug("bad service request %s", service); packet_disconnect("bad service request %s", service); } xfree(service); } 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)); 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); #ifdef USE_PAM if (options.use_pam) PRIVSEP(start_pam(authctxt)); #endif } else { logit("input_userauth_request: invalid user %s", user); authctxt->pw = fakepw(); #ifdef USE_PAM if (options.use_pam) PRIVSEP(start_pam(authctxt)); #endif #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_INVALID_USER)); #endif } setproctitle("%s%s", authctxt->valid ? user : "unknown", use_privsep ? " [net]" : ""); 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); #ifdef GSSAPI dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); #endif 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); } void userauth_finish(Authctxt *authctxt, int authenticated, char *method) { char *methods; if (!authctxt->valid && authenticated) fatal("INTERNAL ERROR: authenticated invalid user %s", authctxt->user); /* Special handling for root */ if (authenticated && authctxt->pw->pw_uid == 0 && !auth_root_allowed(method)) { authenticated = 0; #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED)); #endif } #ifdef USE_PAM if (options.use_pam && authenticated) { if (!PRIVSEP(do_pam_account())) { /* if PAM returned a message, send it to the user */ if (buffer_len(&loginmsg) > 0) { buffer_append(&loginmsg, "\0", 1); userauth_send_banner(buffer_ptr(&loginmsg)); packet_write_wait(); } fatal("Access denied for user %s by PAM account " "configuration", authctxt->user); } } #endif #ifdef _UNICOS if (authenticated && cray_access_denied(authctxt->user)) { authenticated = 0; fatal("Access denied for user %s.",authctxt->user); } #endif /* _UNICOS */ /* Log before sending the reply */ auth_log(authctxt, authenticated, method, " ssh2"); if (authctxt->postponed) return; /* XXX todo: check if multiple auth methods are needed */ if (authenticated == 1) { /* turn off userauth */ dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore); packet_start(SSH2_MSG_USERAUTH_SUCCESS); packet_send(); packet_write_wait(); /* now we can break out */ authctxt->success = 1; } else { if (authctxt->failures++ > options.max_authtries) { #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES)); #endif packet_disconnect(AUTH_FAIL_MSG, authctxt->user); } methods = authmethods_get(); packet_start(SSH2_MSG_USERAUTH_FAILURE); packet_put_cstring(methods); packet_put_char(0); /* XXX partial success, unused */ packet_send(); packet_write_wait(); xfree(methods); } } #define DELIM "," static char * authmethods_get(void) { Buffer b; char *list; int i; buffer_init(&b); for (i = 0; authmethods[i] != NULL; i++) { if (strcmp(authmethods[i]->name, "none") == 0) continue; if (authmethods[i]->enabled != NULL && *(authmethods[i]->enabled) != 0) { if (buffer_len(&b) > 0) buffer_append(&b, ",", 1); buffer_append(&b, authmethods[i]->name, strlen(authmethods[i]->name)); } } buffer_append(&b, "\0", 1); list = xstrdup(buffer_ptr(&b)); buffer_free(&b); return list; } static Authmethod * authmethod_lookup(const char *name) { int i; if (name != NULL) for (i = 0; authmethods[i] != NULL; i++) if (authmethods[i]->enabled != NULL && *(authmethods[i]->enabled) != 0 && strcmp(name, authmethods[i]->name) == 0) return authmethods[i]; debug2("Unrecognized authentication method name: %s", name ? name : "NULL"); return NULL; }