Ejemplo n.º 1
0
void
userauth_finish(Authctxt *authctxt, char *method)
{
	int authenticated, partial;

	if (authctxt == NULL)
		fatal("%s: missing context", __func__);

	/* unknown method handling -- must elicit userauth failure msg */
	if (authctxt->method == NULL) {
		authenticated = 0;
		partial = 0;
		goto done_checking;
	}

#ifndef USE_PAM
	/* Special handling for root (done elsewhere for PAM) */
	if (authctxt->method->authenticated &&
	    authctxt->pw != NULL && authctxt->pw->pw_uid == 0 &&
	    !auth_root_allowed(method))
		authctxt->method->authenticated = 0;
#endif /* USE_PAM */

#ifdef _UNICOS
	if (authctxt->method->authenticated &&
	    cray_access_denied(authctxt->user)) {
		authctxt->method->authenticated = 0;
		fatal("Access denied for user %s.",authctxt->user);
	}
#endif /* _UNICOS */

	partial = userauth_check_partial_failure(authctxt);
	authenticated = authctxt->method->authenticated;

#ifdef USE_PAM
	/*
	 * If the userauth method failed to complete PAM work then force
	 * partial failure.
	 */
	if (authenticated && !AUTHPAM_DONE(authctxt))
		partial = 1;
#endif /* USE_PAM */

	/*
	 * To properly support invalid userauth method names we set
	 * authenticated=0, partial=0 above and know that
	 * authctxt->method == NULL.
	 *
	 * No unguarded reference to authctxt->method allowed from here.
	 * Checking authenticated != 0 is a valid guard; authctxt->method
	 * MUST NOT be NULL if authenticated.
	 */
done_checking:
	if (!authctxt->valid && authenticated) {
		/*
		 * We get here if the PreUserauthHook fails but the
		 * user is otherwise valid.
		 * An error in the PAM handling could also get us here
		 * but we need not panic, just treat as a failure.
		 */
		authctxt->method->authenticated = 0;
		authenticated = 0;
		log("Ignoring authenticated invalid user %s",
		    authctxt->user);
		auth_log(authctxt, 0, method, " ssh2");
	}

	/* Log before sending the reply */
	auth_log(authctxt, authenticated, method, " ssh2");

	if (authenticated && !partial) {

		/* 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 {
		char *methods;

		if (authctxt->method && authctxt->method->is_initial)
			authctxt->init_failures++;

		authctxt->method = NULL;

#ifdef USE_PAM
		/*
		 * Keep track of last PAM error (or PERM_DENIED) for BSM
		 * login failure auditing, which may run after the PAM
		 * state has been cleaned up.
		 */
		authctxt->pam_retval = AUTHPAM_ERROR(authctxt, PAM_PERM_DENIED);
#endif /* USE_PAM */

		if (authctxt->failures++ > options.max_auth_tries) {
#ifdef HAVE_BSM
			fatal_remove_cleanup(audit_failed_login_cleanup,
				authctxt);
			audit_sshd_login_failure(&ah, PAM_MAXTRIES,
			    authctxt->user);
#endif /* HAVE_BSM */
			packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
		}

#ifdef _UNICOS
		if (strcmp(method, "password") == 0)
			cray_login_failure(authctxt->user, IA_UDBERR);
#endif /* _UNICOS */
		packet_start(SSH2_MSG_USERAUTH_FAILURE);

		/*
		 * If (partial) then authmethods_get() will return only
		 * required methods, likely only "keyboard-interactive;"
		 * (methods == NULL) implies failure, even if (partial == 1)
		 */
		methods = authmethods_get();
		packet_put_cstring(methods);
		packet_put_char((authenticated && partial && methods) ? 1 : 0);
		if (methods)
			xfree(methods);
		packet_send();
		packet_write_wait();
	}
}
Ejemplo n.º 2
0
/*
 * read packets, try to authenticate the user and
 * return only if authentication is successful
 */
static void
do_authloop(Authctxt *authctxt)
{
	int authenticated = 0;
	u_int bits;
	Key *client_host_key;
	BIGNUM *n;
	char *client_user, *password;
	char info[1024];
	u_int dlen;
	u_int ulen;
	int type = 0;
	struct passwd *pw = authctxt->pw;

	debug("Attempting authentication for %s%.100s.",
	    authctxt->valid ? "" : "illegal user ", authctxt->user);

	/* If the user has no password, accept authentication immediately. */
	if (options.password_authentication &&
#if defined(KRB4) || defined(KRB5)
	    (!options.kerberos_authentication || options.kerberos_or_local_passwd) &&
#endif
	    PRIVSEP(auth_password(authctxt, ""))) {
		auth_log(authctxt, 1, "without authentication", "");
		return;
	}

	/* Indicate that authentication is needed. */
	packet_start(SSH_SMSG_FAILURE);
	packet_send();
	packet_write_wait();

	client_user = NULL;

	for ( ;; ) {
		/* default to fail */
		authenticated = 0;

		info[0] = '\0';

		/* Get a packet from the client. */
		authctxt->v1_auth_type = type = packet_read();
		authctxt->v1_auth_name = get_authname(type);

		authctxt->attempt++;

		/* Process the packet. */
		switch (type) {

#if defined(KRB4) || defined(KRB5)
		case SSH_CMSG_AUTH_KERBEROS:
			if (!options.kerberos_authentication) {
				verbose("Kerberos authentication disabled.");
			} else {
				char *kdata = packet_get_string(&dlen);
				packet_check_eom();

				if (kdata[0] == 4) { /* KRB_PROT_VERSION */
#ifdef KRB4
					KTEXT_ST tkt, reply;
					tkt.length = dlen;
					if (tkt.length < MAX_KTXT_LEN)
						memcpy(tkt.dat, kdata, tkt.length);

					if (PRIVSEP(auth_krb4(authctxt, &tkt,
					    &client_user, &reply))) {
						authenticated = 1;
						snprintf(info, sizeof(info),
						    " tktuser %.100s",
						    client_user);

						packet_start(
						    SSH_SMSG_AUTH_KERBEROS_RESPONSE);
						packet_put_string((char *)
						    reply.dat, reply.length);
						packet_send();
						packet_write_wait();
					}
#endif /* KRB4 */
				} else {
#ifdef KRB5
					krb5_data tkt, reply;
					tkt.length = dlen;
					tkt.data = kdata;

					if (PRIVSEP(auth_krb5(authctxt, &tkt,
					    &client_user, &reply))) {
						authenticated = 1;
						snprintf(info, sizeof(info),
						    " tktuser %.100s",
						    client_user);
 
 						/* Send response to client */
 						packet_start(
						    SSH_SMSG_AUTH_KERBEROS_RESPONSE);
 						packet_put_string((char *)
						    reply.data, reply.length);
 						packet_send();
 						packet_write_wait();

 						if (reply.length)
 							xfree(reply.data);
					}
#endif /* KRB5 */
				}
				xfree(kdata);
			}
			break;
#endif /* KRB4 || KRB5 */

#if defined(AFS) || defined(KRB5)
			/* XXX - punt on backward compatibility here. */
		case SSH_CMSG_HAVE_KERBEROS_TGT:
			packet_send_debug("Kerberos TGT passing disabled before authentication.");
			break;
#ifdef AFS
		case SSH_CMSG_HAVE_AFS_TOKEN:
			packet_send_debug("AFS token passing disabled before authentication.");
			break;
#endif /* AFS */
#endif /* AFS || KRB5 */

		case SSH_CMSG_AUTH_RHOSTS:
			if (!options.rhosts_authentication) {
				verbose("Rhosts authentication disabled.");
				break;
			}
			/*
			 * Get client user name.  Note that we just have to
			 * trust the client; this is one reason why rhosts
			 * authentication is insecure. (Another is
			 * IP-spoofing on a local network.)
			 */
			client_user = packet_get_string(&ulen);
			packet_check_eom();

			/* Try to authenticate using /etc/hosts.equiv and .rhosts. */
			authenticated = auth_rhosts(pw, client_user);

			snprintf(info, sizeof info, " ruser %.100s", client_user);
			break;

		case SSH_CMSG_AUTH_RHOSTS_RSA:
			if (!options.rhosts_rsa_authentication) {
				verbose("Rhosts with RSA authentication disabled.");
				break;
			}
			/*
			 * Get client user name.  Note that we just have to
			 * trust the client; root on the client machine can
			 * claim to be any user.
			 */
			client_user = packet_get_string(&ulen);

			/* Get the client host key. */
			client_host_key = key_new(KEY_RSA1);
			bits = packet_get_int();
			packet_get_bignum(client_host_key->rsa->e);
			packet_get_bignum(client_host_key->rsa->n);

			if (bits != BN_num_bits(client_host_key->rsa->n))
				verbose("Warning: keysize mismatch for client_host_key: "
				    "actual %d, announced %d",
				    BN_num_bits(client_host_key->rsa->n), bits);
			packet_check_eom();

			authenticated = auth_rhosts_rsa(pw, client_user,
			    client_host_key);
			key_free(client_host_key);

			snprintf(info, sizeof info, " ruser %.100s", client_user);
			break;

		case SSH_CMSG_AUTH_RSA:
			if (!options.rsa_authentication) {
				verbose("RSA authentication disabled.");
				break;
			}
			/* RSA authentication requested. */
			if ((n = BN_new()) == NULL)
				fatal("do_authloop: BN_new failed");
			packet_get_bignum(n);
			packet_check_eom();
			authenticated = auth_rsa(pw, n);
			BN_clear_free(n);
			break;

		case SSH_CMSG_AUTH_PASSWORD:
			authctxt->init_attempt++;

			if (!options.password_authentication) {
				verbose("Password authentication disabled.");
				break;
			}
			/*
			 * Read user password.  It is in plain text, but was
			 * transmitted over the encrypted channel so it is
			 * not visible to an outside observer.
			 */
			password = packet_get_string(&dlen);
			packet_check_eom();

			/* Try authentication with the password. */
			if (authctxt->init_failures <
				options.max_init_auth_tries)
				authenticated =
				    PRIVSEP(auth_password(authctxt,
						password));

			memset(password, 0, strlen(password));
			xfree(password);
			break;

		case SSH_CMSG_AUTH_TIS:
			debug("rcvd SSH_CMSG_AUTH_TIS");
			if (options.challenge_response_authentication == 1) {
				char *challenge = get_challenge(authctxt);
				if (challenge != NULL) {
					debug("sending challenge '%s'", challenge);
					packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE);
					packet_put_cstring(challenge);
					xfree(challenge);
					packet_send();
					packet_write_wait();
					continue;
				}
			}
			break;
		case SSH_CMSG_AUTH_TIS_RESPONSE:
			debug("rcvd SSH_CMSG_AUTH_TIS_RESPONSE");
			if (options.challenge_response_authentication == 1) {
				char *response = packet_get_string(&dlen);
				debug("got response '%s'", response);
				packet_check_eom();
				authenticated = verify_response(authctxt, response);
				memset(response, 'r', dlen);
				xfree(response);
			}
			break;

		default:
			/*
			 * Any unknown messages will be ignored (and failure
			 * returned) during authentication.
			 */
			log("Unknown message during authentication: type %d", type);
			break;
		}
#ifdef BSD_AUTH
		if (authctxt->as) {
			auth_close(authctxt->as);
			authctxt->as = NULL;
		}
#endif
		if (!authctxt->valid && authenticated) {
			authenticated = 0;
			log("Ignoring authenticated invalid user %s",
			    authctxt->user);
		}

#ifdef _UNICOS
		if (type == SSH_CMSG_AUTH_PASSWORD && !authenticated)
			cray_login_failure(authctxt->user, IA_UDBERR);
		if (authenticated && cray_access_denied(authctxt->user)) {
			authenticated = 0;
			fatal("Access denied for user %s.",authctxt->user);
		}
#endif /* _UNICOS */

#ifdef HAVE_CYGWIN
		if (authenticated &&
		    !check_nt_auth(type == SSH_CMSG_AUTH_PASSWORD, pw)) {
			packet_disconnect("Authentication rejected for uid %d.",
			pw == NULL ? -1 : pw->pw_uid);
			authenticated = 0;
		}
#else
		/* Special handling for root */
		if (!use_privsep &&
		    authenticated && authctxt->pw->pw_uid == 0 &&
		    !auth_root_allowed(get_authname(type)))
			authenticated = 0;
#endif
#ifdef USE_PAM
		/* XXX PAM and PRIVSEP don't mix */
		if (use_privsep && authenticated)
			fatal("Privsep is not supported");

		if (authenticated && type != SSH_CMSG_AUTH_PASSWORD)
			authenticated = do_pam_non_initial_userauth(authctxt);
		else if (authenticated && !AUTHPAM_DONE(authctxt))
			authenticated = 0;

		if (!authenticated)
			authctxt->pam_retval = AUTHPAM_ERROR(authctxt,
				PAM_PERM_DENIED);
#endif /* USE_PAM */

		/* Log before sending the reply */
		auth_log(authctxt, authenticated, get_authname(type), info);

		if (client_user != NULL) {
			xfree(client_user);
			client_user = NULL;
		}

		if (authenticated)
			return;

		if (type == SSH_CMSG_AUTH_PASSWORD)
			authctxt->init_failures++;

		if (authctxt->failures++ > options.max_auth_tries) {
#ifdef HAVE_BSM
			fatal_remove_cleanup(audit_failed_login_cleanup,
				authctxt);
			audit_sshd_login_failure(&ah, PAM_MAXTRIES);
#endif /* HAVE_BSM */
			packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
		}

		packet_start(SSH_SMSG_FAILURE);
		packet_send();
		packet_write_wait();
	}
}
Ejemplo n.º 3
0
/*
 * To be called from userauth methods, directly (as in keyboard-interactive) or
 * indirectly (from auth_pam_password() or from do_pam_non_initial_userauth().
 *
 * The caller is responsible for calling new_start_pam() first.
 *
 * PAM state is not cleaned up here on error.  This is left to subsequent calls
 * to new_start_pam() or to the cleanup function upon authentication error.
 */
int
finish_userauth_do_pam(Authctxt *authctxt)
{
	int retval;
	char *user, *method;

	/* Various checks; fail gracefully */
	if (authctxt == NULL || authctxt->pam == NULL)
		return PAM_SYSTEM_ERR;	/* shouldn't happen */

	if (compat20) {
		if (authctxt->method == NULL || authctxt->method->name == NULL)
			return PAM_SYSTEM_ERR;	/* shouldn't happen */
		method = authctxt->method->name;
	} else if ((method = authctxt->v1_auth_name) == NULL)
		return PAM_SYSTEM_ERR;	/* shouldn't happen */

	if (AUTHPAM_DONE(authctxt))
		return PAM_SYSTEM_ERR;	/* shouldn't happen */

	if (!(authctxt->pam->state & PAM_S_DONE_ACCT_MGMT)) {
		retval = pam_acct_mgmt(authctxt->pam->h, 0);
		authctxt->pam->last_pam_retval = retval;
		if (retval == PAM_NEW_AUTHTOK_REQD) {
			userauth_force_kbdint();
			return retval;
		}
		if (retval != PAM_SUCCESS)
			return retval;
		authctxt->pam->state |= PAM_S_DONE_ACCT_MGMT;
	}

	/*
	 * Handle PAM_USER change, if any.
	 *
	 * We do this before pam_open_session() because we need the PAM_USER's
	 * UID for:
	 *
	 * a) PermitRootLogin checking
	 * b) to get at the lastlog entry before pam_open_session() updates it.
	 */
	retval = pam_get_item(authctxt->pam->h, PAM_USER, (void **) &user);
	if (retval != PAM_SUCCESS) {
		fatal("PAM failure: pam_get_item(PAM_USER) "
		      "returned %d: %.200s", retval,
		      PAM_STRERROR(authctxt->pam->h, retval));
	}

	if (user == NULL || *user == '\0') {
		debug("PAM set NULL PAM_USER");
		return PAM_PERM_DENIED;
	}

	if (strcmp(user, authctxt->user) != 0) {
		log("PAM changed the SSH username");
		pwfree(&authctxt->pw);
		authctxt->pw = PRIVSEP(getpwnamallow(user));
		authctxt->valid = (authctxt->pw != NULL);
		xfree(authctxt->user);
		authctxt->user = xstrdup(user);
	}

	if (!authctxt->valid) {
		debug2("PAM set PAM_USER to unknown user");
		/*
		 * Return success, userauth_finish() will catch
		 * this and send back a failure message.
		 */
		return PAM_SUCCESS;
	}

	/* Check PermitRootLogin semantics */
	if (authctxt->pw->pw_uid == 0 && !auth_root_allowed(method))
		return PAM_PERM_DENIED;

	if (!(authctxt->pam->state & PAM_S_DONE_SETCRED)) {
		retval = pam_setcred(authctxt->pam->h,
				     PAM_ESTABLISH_CRED);
		authctxt->pam->last_pam_retval = retval;
		if (retval != PAM_SUCCESS)
			return retval;
		authctxt->pam->state |= PAM_S_DONE_SETCRED;

#ifdef GSSAPI
		/*
		 * Store GSS-API delegated creds after pam_setcred(), which may
		 * have set the current credential store.
		 */
		ssh_gssapi_storecreds(NULL, authctxt);
#endif /* GSSAPI */
	}

	/*
	 * On Solaris pam_unix_session.so updates the lastlog, but does
	 * not converse a PAM_TEXT_INFO message about it.  So we need to
	 * fetch the lastlog entry here and save it for use later.
	 */
	authctxt->last_login_time =
		get_last_login_time(authctxt->pw->pw_uid,
			authctxt->pw->pw_name,
			authctxt->last_login_host,
			sizeof(authctxt->last_login_host));

	if (!(authctxt->pam->state & PAM_S_DONE_OPEN_SESSION)) {
		retval = pam_open_session(authctxt->pam->h, 0);
		authctxt->pam->last_pam_retval = retval;
		if (retval != PAM_SUCCESS)
			return retval;
		authctxt->pam->state |= PAM_S_DONE_OPEN_SESSION;
	}

	/*
	 * All PAM work done successfully.
	 *
	 * PAM handle stays around so we can call pam_close_session() on
	 * it later.
	 */
	return PAM_SUCCESS;
}