Пример #1
0
/*
 * "Blind" conversation function for password authentication.  Assumes that
 * echo-off prompts are for the password and stores messages for later
 * display.
 */
static int
sshpam_passwd_conv(int n, sshpam_const struct pam_message **msg,
    struct pam_response **resp, void *data)
{
	struct pam_response *reply;
	int r, i;
	size_t len;

	debug3("PAM: %s called with %d messages", __func__, n);

	*resp = NULL;

	if (n <= 0 || n > PAM_MAX_NUM_MSG)
		return (PAM_CONV_ERR);

	if ((reply = calloc(n, sizeof(*reply))) == NULL)
		return (PAM_CONV_ERR);

	for (i = 0; i < n; ++i) {
		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
		case PAM_PROMPT_ECHO_OFF:
			if (sshpam_password == NULL)
				goto fail;
			if ((reply[i].resp = strdup(sshpam_password)) == NULL)
				goto fail;
			reply[i].resp_retcode = PAM_SUCCESS;
			break;
		case PAM_ERROR_MSG:
		case PAM_TEXT_INFO:
			len = strlen(PAM_MSG_MEMBER(msg, i, msg));
			if (len > 0) {
				if ((r = sshbuf_putf(loginmsg, "%s\n",
				    PAM_MSG_MEMBER(msg, i, msg))) != 0)
					fatal("%s: buffer error: %s",
					    __func__, ssh_err(r));
			}
			if ((reply[i].resp = strdup("")) == NULL)
				goto fail;
			reply[i].resp_retcode = PAM_SUCCESS;
			break;
		default:
			goto fail;
		}
	}
	*resp = reply;
	return (PAM_SUCCESS);

 fail:
	for(i = 0; i < n; i++) {
		free(reply[i].resp);
	}
	free(reply);
	return (PAM_CONV_ERR);
}
Пример #2
0
int
buffer_consume_end_ret(Buffer *buffer, u_int bytes)
{
	int ret = sshbuf_consume_end(buffer, bytes);

	if (ret == 0)
		return 0;
	if (ret == SSH_ERR_MESSAGE_INCOMPLETE)
		return -1;
	fatal("%s: %s", __func__, ssh_err(ret));
}
Пример #3
0
/*
 * loop until authctxt->success == TRUE
 */
void
do_authentication2(struct ssh *ssh)
{
    struct authctxt *authctxt = ssh->authctxt;
    int r;

    ssh_dispatch_init(ssh, &dispatch_protocol_error);
    ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_REQUEST, &input_service_request);
    if ((r = ssh_dispatch_run(ssh, DISPATCH_BLOCK, &authctxt->success)) != 0)
        fatal("%s: ssh_dispatch_run failed: %s", __func__, ssh_err(r));
}
Пример #4
0
int
buffer_check_alloc(Buffer *buffer, u_int len)
{
	int ret = sshbuf_check_reserve(buffer, len);

	if (ret == 0)
		return 1;
	if (ret == SSH_ERR_NO_BUFFER_SPACE)
		return 0;
	fatal("%s: %s", __func__, ssh_err(ret));
}
Пример #5
0
int
buffer_get_ret(Buffer *buffer, void *buf, u_int len)
{
	int ret;

	if ((ret = sshbuf_get(buffer, buf, len)) != 0) {
		error("%s: %s", __func__, ssh_err(ret));
		return -1;
	}
	return 0;
}
Пример #6
0
int
buffer_get_short_ret(u_short *v, Buffer *buffer)
{
	int ret;

	if ((ret = sshbuf_get_u16(buffer, v)) != 0) {
		ssh_error("%s: %s", __func__, ssh_err(ret));
		return -1;
	}
	return 0;
}
Пример #7
0
int
buffer_get_bignum2_ret(Buffer *buffer, BIGNUM *value)
{
	int ret;

	if ((ret = sshbuf_get_bignum2(buffer, value)) != 0) {
		ssh_error("%s: %s", __func__, ssh_err(ret));
		return -1;
	}
	return 0;
}
Пример #8
0
int
buffer_get_char_ret(char *v, Buffer *buffer)
{
	int ret;

	if ((ret = sshbuf_get_u8(buffer, (u_char *)v)) != 0) {
		error("%s: %s", __func__, ssh_err(ret));
		return -1;
	}
	return 0;
}
Пример #9
0
int
buffer_get_int64_ret(u_int64_t *v, Buffer *buffer)
{
	int ret;

	if ((ret = sshbuf_get_u64(buffer, v)) != 0) {
		error("%s: %s", __func__, ssh_err(ret));
		return -1;
	}
	return 0;
}
Пример #10
0
int
buffer_put_bignum_ret(Buffer *buffer, const BIGNUM *value)
{
	int ret;

	if ((ret = sshbuf_put_bignum1(buffer, value)) != 0) {
		ssh_error("%s: %s", __func__, ssh_err(ret));
		return -1;
	}
	return 0;
}
Пример #11
0
/*
 * Computes the proper response to a RSA challenge, and sends the response to
 * the server.
 */
static void
respond_to_rsa_challenge(struct ssh *ssh, BIGNUM * challenge, RSA * prv)
{
    u_char buf[32], response[16];
    MD5_CTX md;
    int r, len;

    /* Decrypt the challenge using the private key. */
    /* XXX think about Bleichenbacher, too */
    if ((r = rsa_private_decrypt(challenge, challenge, prv)) != 0) {
        ssh_packet_disconnect(ssh,  "%s: rsa_private_decrypt: %s",
                              __func__, ssh_err(r));
    }

    /* Compute the response. */
    /* The response is MD5 of decrypted challenge plus session id. */
    len = BN_num_bytes(challenge);
    if (len <= 0 || (u_int)len > sizeof(buf))
        ssh_packet_disconnect(ssh,
                              "respond_to_rsa_challenge: bad challenge length %d", len);

    memset(buf, 0, sizeof(buf));
    BN_bn2bin(challenge, buf + sizeof(buf) - len);
    MD5_Init(&md);
    MD5_Update(&md, buf, 32);
    MD5_Update(&md, session_id, 16);
    MD5_Final(response, &md);

    debug("Sending response to host key RSA challenge.");

    /* Send the response back to the server. */
    if ((r = sshpkt_start(ssh, SSH_CMSG_AUTH_RSA_RESPONSE)) != 0 ||
            (r = sshpkt_put(ssh, &response, sizeof(response))) != 0 ||
            (r = sshpkt_send(ssh)) != 0)
        fatal("%s: %s", __func__, ssh_err(r));
    ssh_packet_write_wait(ssh);

    memset(buf, 0, sizeof(buf));
    memset(response, 0, sizeof(response));
    memset(&md, 0, sizeof(md));
}
Пример #12
0
static int
pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
    int padding)
{
	struct sshkey key;
	u_char *blob, *signature = NULL;
	size_t blen, slen = 0;
	int r, ret = -1;
	struct sshbuf *msg;

	if (padding != RSA_PKCS1_PADDING)
		return (-1);
	key.type = KEY_RSA;
	key.rsa = rsa;
	if ((r = sshkey_to_blob(&key, &blob, &blen)) != 0) {
		error("%s: sshkey_to_blob: %s", __func__, ssh_err(r));
		return -1;
	}
	if ((msg = sshbuf_new()) == NULL)
		fatal("%s: sshbuf_new failed", __func__);
	if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 ||
	    (r = sshbuf_put_string(msg, blob, blen)) != 0 ||
	    (r = sshbuf_put_string(msg, from, flen)) != 0 ||
	    (r = sshbuf_put_u32(msg, 0)) != 0)
		fatal("%s: buffer error: %s", __func__, ssh_err(r));
	xfree(blob);
	send_msg(msg);
	sshbuf_reset(msg);

	if (recv_msg(msg) == SSH2_AGENT_SIGN_RESPONSE) {
		if ((r = sshbuf_get_string(msg, &signature, &slen)) != 0)
			fatal("%s: buffer error: %s", __func__, ssh_err(r));
		if (slen <= (size_t)RSA_size(rsa)) {
			memcpy(to, signature, slen);
			ret = slen;
		}
		xfree(signature);
	}
	sshbuf_free(msg);
	return (ret);
}
Пример #13
0
int
key_certify(Key *k, Key *ca)
{
	int r;

	if ((r = sshkey_certify(k, ca)) != 0) {
		fatal_on_fatal_errors(r, __func__, 0);
		error("%s: %s", __func__, ssh_err(r));
		return -1;
	}
	return 0;
}
Пример #14
0
int
key_drop_cert(Key *k)
{
	int r;

	if ((r = sshkey_drop_cert(k)) != 0) {
		fatal_on_fatal_errors(r, __func__, 0);
		error("%s: %s", __func__, ssh_err(r));
		return -1;
	}
	return 0;
}
Пример #15
0
int
key_to_certified(Key *k, int legacy)
{
	int r;

	if ((r = sshkey_to_certified(k, legacy)) != 0) {
		fatal_on_fatal_errors(r, __func__, 0);
		error("%s: %s", __func__, ssh_err(r));
		return -1;
	}
	return 0;
}
Пример #16
0
static int
userauth_none(struct ssh *ssh)
{
    int r;

    none_enabled = 0;
    if ((r = sshpkt_get_end(ssh)) != 0)
        fatal("%s: %s", __func__, ssh_err(r));
    if (options.permit_empty_passwd && options.password_authentication)
        return (PRIVSEP(auth_password(ssh->authctxt, "")));
    return (0);
}
Пример #17
0
int
mm_answer_rsa_challenge(int sock, struct sshbuf *m)
{
	struct sshkey *key = NULL;
	u_char *blob;
	size_t blen;
	int r;

	debug3("%s entering", __func__);

	if (!authctxt->valid)
		fatal("%s: authctxt not valid", __func__);
	if ((r = sshbuf_get_string(m, &blob, &blen)) != 0)
		fatal("%s: buffer error: %s", __func__, ssh_err(r));
	if (!monitor_allowed_key(blob, blen))
		fatal("%s: bad key, not previously allowed", __func__);
	if (key_blobtype != MM_RSAUSERKEY && key_blobtype != MM_RSAHOSTKEY)
		fatal("%s: key type mismatch", __func__);
	if ((r = sshkey_from_blob(blob, blen, &key)) != 0)
		fatal("%s: received bad key: %s", __func__, ssh_err(r));
	if (key->type != KEY_RSA)
		fatal("%s: received bad key type %d", __func__, key->type);
	key->type = KEY_RSA1;
	if (ssh1_challenge)
		BN_clear_free(ssh1_challenge);
	ssh1_challenge = auth_rsa_generate_challenge(key);

	sshbuf_reset(m);
	if ((r = sshbuf_put_bignum2(m, ssh1_challenge)) != 0)
		fatal("%s: buffer error: %s", __func__, ssh_err(r));

	debug3("%s sending reply", __func__);
	mm_request_send(sock, MONITOR_ANS_RSACHALLENGE, m);

	monitor_permit(mon_dispatch, MONITOR_REQ_RSARESPONSE, 1);

	free(blob);
	sshkey_free(key);
	return (0);
}
Пример #18
0
static void
process_fsetstat(u_int32_t id)
{
	Attrib a;
	int handle, fd, r;
	int status = SSH2_FX_OK;

	if ((r = get_handle(iqueue, &handle)) != 0 ||
	    (r = decode_attrib(iqueue, &a)) != 0)
		fatal("%s: buffer error: %s", __func__, ssh_err(r));

	debug("request %u: fsetstat handle %d", id, handle);
	fd = handle_to_fd(handle);
	if (fd < 0)
		status = SSH2_FX_FAILURE;
	else {
		char *name = handle_to_name(handle);

		if (a.flags & SSH2_FILEXFER_ATTR_SIZE) {
			logit("set \"%s\" size %llu",
			    name, (unsigned long long)a.size);
			r = ftruncate(fd, a.size);
			if (r == -1)
				status = errno_to_portable(errno);
		}
		if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
			logit("set \"%s\" mode %04o", name, a.perm);
			r = fchmod(fd, a.perm & 07777);
			if (r == -1)
				status = errno_to_portable(errno);
		}
		if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
			char buf[64];
			time_t t = a.mtime;

			strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S",
			    localtime(&t));
			logit("set \"%s\" modtime %s", name, buf);
			r = futimes(fd, attrib_to_tv(&a));
			if (r == -1)
				status = errno_to_portable(errno);
		}
		if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) {
			logit("set \"%s\" owner %lu group %lu", name,
			    (u_long)a.uid, (u_long)a.gid);
			r = fchown(fd, a.uid, a.gid);
			if (r == -1)
				status = errno_to_portable(errno);
		}
	}
	send_status(id, status);
}
Пример #19
0
static void
process_rename(u_int32_t id)
{
	char *oldpath, *newpath;
	int r, status;
	struct stat sb;

	if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 ||
	    (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0)
		fatal("%s: buffer error: %s", __func__, ssh_err(r));

	debug3("request %u: rename", id);
	logit("rename old \"%s\" new \"%s\"", oldpath, newpath);
	status = SSH2_FX_FAILURE;
	if (lstat(oldpath, &sb) == -1)
		status = errno_to_portable(errno);
	else if (S_ISREG(sb.st_mode)) {
		/* Race-free rename of regular files */
		if (link(oldpath, newpath) == -1) {
			if (errno == EOPNOTSUPP) {
				struct stat st;

				/*
				 * fs doesn't support links, so fall back to
				 * stat+rename.  This is racy.
				 */
				if (stat(newpath, &st) == -1) {
					if (rename(oldpath, newpath) == -1)
						status =
						    errno_to_portable(errno);
					else
						status = SSH2_FX_OK;
				}
			} else {
				status = errno_to_portable(errno);
			}
		} else if (unlink(oldpath) == -1) {
			status = errno_to_portable(errno);
			/* clean spare link */
			unlink(newpath);
		} else
			status = SSH2_FX_OK;
	} else if (stat(newpath, &sb) == -1) {
		if (rename(oldpath, newpath) == -1)
			status = errno_to_portable(errno);
		else
			status = SSH2_FX_OK;
	}
	send_status(id, status);
	free(oldpath);
	free(newpath);
}
Пример #20
0
Key *
key_from_blob(const u_char *blob, u_int blen)
{
	int r;
	Key *ret = NULL;

	if ((r = sshkey_from_blob(blob, blen, &ret)) != 0) {
		fatal_on_fatal_errors(r, __func__, 0);
		error("%s: %s", __func__, ssh_err(r));
		return NULL;
	}
	return ret;
}
Пример #21
0
char *
ssh_packet_get_cstring(struct ssh *ssh, u_int *length_ptr)
{
	int r;
	size_t len;
	char *val;

	if ((r = sshpkt_get_cstring(ssh, &val, &len)) != 0)
		fatal("%s: %s", __func__, ssh_err(r));
	if (length_ptr != NULL)
		*length_ptr = (u_int)len;
	return val;
}
Пример #22
0
/*
 * Updates authctxt->session_info with details of authentication. Should be
 * whenever an authentication method succeeds.
 */
void
auth2_update_session_info(Authctxt *authctxt, const char *method,
    const char *submethod)
{
	int r;

	if (authctxt->session_info == NULL) {
		if ((authctxt->session_info = sshbuf_new()) == NULL)
			fatal("%s: sshbuf_new", __func__);
	}

	/* Append method[/submethod] */
	if ((r = sshbuf_putf(authctxt->session_info, "%s%s%s",
	    method, submethod == NULL ? "" : "/",
	    submethod == NULL ? "" : submethod)) != 0)
		fatal("%s: append method: %s", __func__, ssh_err(r));

	/* Append key if present */
	if (authctxt->auth_method_key != NULL) {
		if ((r = sshbuf_put_u8(authctxt->session_info, ' ')) != 0 ||
		    (r = sshkey_format_text(authctxt->auth_method_key,
		    authctxt->session_info)) != 0)
			fatal("%s: append key: %s", __func__, ssh_err(r));
	}

	if (authctxt->auth_method_info != NULL) {
		/* Ensure no ambiguity here */
		if (strchr(authctxt->auth_method_info, '\n') != NULL)
			fatal("%s: auth_method_info contains \\n", __func__);
		if ((r = sshbuf_put_u8(authctxt->session_info, ' ')) != 0 ||
		    (r = sshbuf_putf(authctxt->session_info, "%s",
		    authctxt->auth_method_info)) != 0) {
			fatal("%s: append method info: %s",
			    __func__, ssh_err(r));
		}
	}
	if ((r = sshbuf_put_u8(authctxt->session_info, '\n')) != 0)
		fatal("%s: append: %s", __func__, ssh_err(r));
}
Пример #23
0
static void
send_names(u_int32_t id, int count, const Stat *stats)
{
	struct sshbuf *msg;
	int i, r;

	if ((msg = sshbuf_new()) == NULL)
		fatal("%s: sshbuf_new failed", __func__);
	if ((r = sshbuf_put_u8(msg, SSH2_FXP_NAME)) != 0 ||
	    (r = sshbuf_put_u32(msg, id)) != 0 ||
	    (r = sshbuf_put_u32(msg, count)) != 0)
		fatal("%s: buffer error: %s", __func__, ssh_err(r));
	debug("request %u: sent names count %d", id, count);
	for (i = 0; i < count; i++) {
		if ((r = sshbuf_put_cstring(msg, stats[i].name)) != 0 ||
		    (r = sshbuf_put_cstring(msg, stats[i].long_name)) != 0 ||
		    (r = encode_attrib(msg, &stats[i].attrib)) != 0)
			fatal("%s: buffer error: %s", __func__, ssh_err(r));
	}
	send_msg(msg);
	sshbuf_free(msg);
}
Пример #24
0
int
mm_answer_moduli(int sock, struct sshbuf *m)
{
	DH *dh;
	int r;
	u_int min, want, max;

	if ((r = sshbuf_get_u32(m, &min)) != 0 ||
	    (r = sshbuf_get_u32(m, &want)) != 0 ||
	    (r = sshbuf_get_u32(m, &max)) != 0)
		fatal("%s: buffer error: %s", __func__, ssh_err(r));

	debug3("%s: got parameters: %d %d %d",
	    __func__, min, want, max);
	/* We need to check here, too, in case the child got corrupted */
	if (max < min || want < min || max < want)
		fatal("%s: bad parameters: %d %d %d",
		    __func__, min, want, max);

	sshbuf_reset(m);

	dh = choose_dh(min, want, max);
	if (dh == NULL) {
		if ((r = sshbuf_put_u8(m, 0)) != 0)
			fatal("%s: buffer error: %s", __func__, ssh_err(r));
		return (0);
	} else {
		/* Send first bignum */
		if ((r = sshbuf_put_u8(m, 1)) != 0 ||
		    (r = sshbuf_put_bignum2(m, dh->p)) != 0 ||
		    (r = sshbuf_put_bignum2(m, dh->g)) != 0)
			fatal("%s: buffer error: %s", __func__, ssh_err(r));

		DH_free(dh);
	}
	mm_request_send(sock, MONITOR_ANS_MODULI, m);
	return (0);
}
Пример #25
0
static void
process_close(u_int32_t id)
{
	int r, handle, ret, status = SSH2_FX_FAILURE;

	if ((r = get_handle(iqueue, &handle)) != 0)
		fatal("%s: buffer error: %s", __func__, ssh_err(r));

	debug3("request %u: close handle %u", id, handle);
	handle_log_close(handle, NULL);
	ret = handle_close(handle);
	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
	send_status(id, status);
}
Пример #26
0
/*
 * Caclulate a new key after a reconnect
 */
void
calculate_new_key(u_int64_t *key, u_int64_t cookie, u_int64_t challenge)
{
	u_char hash[SSH_DIGEST_MAX_LENGTH];
	struct sshbuf *b;
	int r;

	if ((b = sshbuf_new()) == NULL)
		fatal("%s: sshbuf_new failed", __func__);
	if ((r = sshbuf_put_u64(b, *key)) != 0 ||
	    (r = sshbuf_put_u64(b, cookie)) != 0 ||
	    (r = sshbuf_put_u64(b, challenge)) != 0)
		fatal("%s: buffer error: %s", __func__, ssh_err(r));

	if (ssh_digest_buffer(SSH_DIGEST_SHA1, b, hash, sizeof(hash)) != 0)
		fatal("%s: digest_buffer failed", __func__);

	sshbuf_reset(b);
	if ((r = sshbuf_put(b, hash, ssh_digest_bytes(SSH_DIGEST_SHA1))) != 0 ||
	    (r = sshbuf_get_u64(b, key)) != 0)
		fatal("%s: buffer error: %s", __func__, ssh_err(r));
	sshbuf_free(b);
}
Пример #27
0
void
ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
    const char *context)
{
	int r;

	sshbuf_reset(b);
	if ((r = sshbuf_put_string(b, session_id2, session_id2_len)) != 0 ||
	    (r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
	    (r = sshbuf_put_cstring(b, user)) != 0 ||
	    (r = sshbuf_put_cstring(b, service)) != 0 ||
	    (r = sshbuf_put_cstring(b, context)) != 0)
		fatal("%s: buffer error: %s", __func__, ssh_err(r));
}
Пример #28
0
int
key_cert_check_authority(const Key *k, int want_host, int require_principal,
    const char *name, const char **reason)
{
	int r;

	if ((r = sshkey_cert_check_authority(k, want_host, require_principal,
	    name, reason)) != 0) {
		fatal_on_fatal_errors(r, __func__, 0);
		error("%s: %s", __func__, ssh_err(r));
		return -1;
	}
	return 0;
}
Пример #29
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;
}
Пример #30
0
int
key_verify(const Key *key, const u_char *signature, u_int signaturelen,
    const u_char *data, u_int datalen)
{
	int r;

	if ((r = sshkey_verify(key, signature, signaturelen,
	    data, datalen, datafellows)) != 0) {
		fatal_on_fatal_errors(r, __func__, 0);
		error("%s: %s", __func__, ssh_err(r));
		return r == SSH_ERR_SIGNATURE_INVALID ? 0 : -1;
	}
	return 1;
}