/* $Id: ssh_v1_auth_rsa_server.c,v 1.3 2001/02/10 19:45:31 thorpej Exp $ */ /* * Copyright 1999 RedBack Networks, Incorporated. * All rights reserved. * * This software is not in the public domain. It is distributed * under the terms of the license in the file LICENSE in the * same directory as this file. If you have received a copy of this * software without the LICENSE file (which means that whoever gave * you this software violated its license) you may obtain a copy from * http://www.panix.com/~tls/LICENSE.txt */ /* * SSHv1 RSA authentication: server-side routines. */ #include "options.h" #ifdef WITH_AUTH_RSA #include #include #include #include "sshd.h" #include "ssh_buffer.h" #include "ssh_event.h" #include "ssh_parse.h" #include "ssh_v1_messages.h" #include "ssh_util.h" #include "crypto/ssh_rsakeys.h" #include "ssh_v1_auth_rsa.h" int v1_auth_rsa_send_challenge(ssh_context_t *, struct ssh_buf *, size_t, char *); int v1_auth_rsa_recv_resp(ssh_context_t *, struct ssh_buf *, size_t, char *); /* * auth_user_rsa: Implements RSA authentication * * Returns: * RET_OK Partial success. * RET_NEXT_STATE Successful authetication completed * RET_FAIL Authentication failed. * RET_FATAL Fatal error, context->disconnect_msg set. * * Caller should send SUCCESS and FAILURE messages in response to * RET_NEXT_STATE and RET_FAIL. * DISCONNECT message with the disconnect_msg should be sent on FATAL errors. * No message should be sent on RET_OK. * * First time through this function sends a rsa challenge. * Second time through this function checks the response. * Third time same as first. * XXX Perhaps the static state information should go in the context somewhere? * * Assume: buf is discarded after we return. */ int v1_auth_user_rsa(ssh_context_t *context, struct ssh_buf *buf, size_t size) { FUNC_DECL(v1_auth_user_rsa); static u_int8_t calc_md5[16]; static int auth_state = 0; int ret; if (auth_state == 0) { auth_state = 1; memset(calc_md5, 0, sizeof(calc_md5)); ret = v1_auth_rsa_send_challenge(context, buf, size, calc_md5); return (ret); } else { auth_state = 0; ret = v1_auth_rsa_recv_resp(context, buf, size, calc_md5); memset(calc_md5, 0, sizeof(calc_md5)); return (ret); } } /* * v1_auth_rsa_send_challenge: Send an RSA challenge * * Returns: * RET_OK Send successful * RET_FAIL Failed. * RET_FATAL Fatal error, context->disconnect_msg set. */ int v1_auth_rsa_send_challenge(ssh_context_t * context, struct ssh_buf * buf, size_t size, char *calc_md5) { FUNC_DECL(v1_auth_rsa_send_challenge); int ret; char challenge[32]; char *encr_chal; int encr_len; ssh_MD5 md5ctx; ssh_BIGNUM *n, *challenge_as_bn; ssh_RSA *key; struct ssh_buf xbuf; struct ssh_event ev; struct ssh_ev_send *evsend; n = challenge_as_bn = NULL; key = NULL; encr_chal = NULL; memset(&xbuf, 0, sizeof(struct ssh_buf)); /* Grab the public key modulus that the client sent. */ if (buf_get_bignum(buf, &n) != 0) { SSH_DLOG(3, ("AUTH_RSA msg has bad n.\n")); context->disconnect_msg = "Bad SSH_AUTH_RSA message\n"; ret = RET_FATAL; goto out; } /* Make sure it's at least big enought to encrypt the challenge. */ if (bignum_num_bytes(n) < 32) { SSH_DLOG(3, ("key too short (%d bytes).\n", bignum_num_bytes(n))); ret = RET_FAIL; goto out; } /* See if it's in our list of valid keys. */ if (lookup_authorized_key(context, n, &key) != 0) { SSH_DLOG(3, ("key not found.\n")); ret = RET_FAIL; goto out; } /* Generate the challenge */ ssh_rand_bytes(32, challenge); /* Calculate the MD5 of the challenge+session id. */ ssh_md5_init(&md5ctx); ssh_md5_update(&md5ctx, challenge, 32); ssh_md5_update(&md5ctx, context->v1_ctx.session_id, 16); ssh_md5_final(calc_md5, &md5ctx); /* Encrypt the challenge and send it. */ encr_chal = malloc(bignum_num_bytes(ssh_rsa_npart(key))); if (encr_chal == NULL) { SSH_ERROR("Unable to alloc mem.\n"); context->disconnect_msg = "Unable to alloc mem.\n"; ret = RET_FATAL; goto out; } if ((encr_len = ssh_rsa_public_encrypt(32, challenge, encr_chal, key)) < 32) { SSH_ERROR("Unable to encrypt challenge.\n"); context->disconnect_msg = "Unable to encrypt challenge.\n"; ret = RET_FATAL; goto out; } /* Transmit the challenge as a mpint (bignum) */ if (bignum_bin2bn(&challenge_as_bn, encr_chal, encr_len) != 0) { SSH_ERROR("bin2bn on challenge failed.\n"); context->disconnect_msg = "bin2bn on challenge failed.\n"; ret = RET_FATAL; goto out; } /* encrypted len + extra to prevent realloc in buf_put_ */ if (buf_alloc(&xbuf, 1 + encr_len + 10) == NULL) { SSH_ERROR("Unable to alloc buf for xmit.\n"); context->disconnect_msg = "Unable to alloc buf for xmit.\n"; ret = RET_FATAL; goto out; } if (buf_put_int8(&xbuf, SSH_V1_SMSG_AUTH_RSA_CHALLENGE) != 0 || buf_put_bignum(&xbuf, challenge_as_bn) != 0) { SSH_ERROR("Unable to put challenge in buffer.\n"); context->disconnect_msg = "Unable to put challenge in buffer.\n"; ret = RET_FATAL; goto out; } /* * Build the event to send to the sendThread. * XXX This alloc, then immediate copy kinda sucks. */ ev.event_type = SSH_EVENT_SEND; evsend = (struct ssh_ev_send *) ev.event_data; evsend->dlen = buf_alllen(&xbuf); if (evsend->dlen > SSH_MAX_EV_SEND_DATA) { SSH_ERROR("Challenge too big for event buffer\n"); context->disconnect_msg = "Challenge too big for event buffer\n"; ret = RET_FATAL; goto out; } memcpy(evsend->data, buf_alldata(&xbuf), buf_alllen(&xbuf)); if (ssh_sys_sendevent(context, &ev) < 0) { if (context->send_pid == -1) SSH_DLOG(1, ("sendThread exited. Exiting recvThread\n")); else SSH_ERROR("Unable to send event (challenge).\n"); ssh_exit(context, 1, EXIT_NOW); } /* All ok */ SSH_DLOG(3, ("Challenge sent\n")); ret = RET_OK; out: memset(&ev, 0, sizeof(ev)); if (n) bignum_free(n); if (challenge_as_bn) bignum_free(challenge_as_bn); if (key) ssh_rsa_free(&key); if (encr_chal) free(encr_chal); return (ret); } /* * v1_auth_rsa_recv_resp: Parse client's response to rsa challenge. * * Returns: * RET_NEXT_STATE Successful authentication * RET_FAIL Failure authenticating * RET_FATAL Fatal error, context->disconnect_msg set. */ int v1_auth_rsa_recv_resp(ssh_context_t * context, struct ssh_buf * buf, size_t size, char *calc_md5) { FUNC_DECL(v1_auth_rsa_recv_resp); u_int8_t *recv_md5; int ret; recv_md5 = NULL; /* Grab the MD5 response from the client. */ if (buf_get_nbytes(buf, 16, &recv_md5) != 0) { SSH_DLOG(2, ("bad rsa response.\n")); ret = RET_FAIL; goto out; } /* Compare it to our calculated one. */ if (memcmp(recv_md5, calc_md5, 16) != 0) { SSH_DLOG(3, ("RSA response does not match\n")); ret = RET_FAIL; goto out; } /* All ok */ SSH_DLOG(3, ("authentication accepted.\n")); ret = RET_NEXT_STATE; out: if (recv_md5) { memset(recv_md5, 0, 16); free(recv_md5); } return (ret); } #endif /* WITH_AUTH_RSA */