xref: /onnv-gate/usr/src/cmd/ssh/sshd/auth-rsa.c (revision 5562:0f12179b71ab)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * Author: Tatu Ylonen <ylo@cs.hut.fi>
30Sstevel@tonic-gate  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
40Sstevel@tonic-gate  *                    All rights reserved
50Sstevel@tonic-gate  * RSA-based authentication.  This code determines whether to admit a login
60Sstevel@tonic-gate  * based on RSA authentication.  This file also contains functions to check
70Sstevel@tonic-gate  * validity of the host key.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * As far as I am concerned, the code I have written for this software
100Sstevel@tonic-gate  * can be used freely for any purpose.  Any derived versions of this
110Sstevel@tonic-gate  * software must be clearly marked as such, and if the derived work is
120Sstevel@tonic-gate  * incompatible with the protocol description in the RFC file, it must be
130Sstevel@tonic-gate  * called by a name other than "ssh" or "Secure Shell".
140Sstevel@tonic-gate  */
150Sstevel@tonic-gate 
160Sstevel@tonic-gate #include "includes.h"
170Sstevel@tonic-gate RCSID("$OpenBSD: auth-rsa.c,v 1.56 2002/06/10 16:53:06 stevesk Exp $");
180Sstevel@tonic-gate 
190Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
200Sstevel@tonic-gate 
210Sstevel@tonic-gate #include <openssl/rsa.h>
220Sstevel@tonic-gate #include <openssl/md5.h>
230Sstevel@tonic-gate 
240Sstevel@tonic-gate #include "rsa.h"
250Sstevel@tonic-gate #include "packet.h"
260Sstevel@tonic-gate #include "xmalloc.h"
270Sstevel@tonic-gate #include "ssh1.h"
280Sstevel@tonic-gate #include "mpaux.h"
290Sstevel@tonic-gate #include "uidswap.h"
300Sstevel@tonic-gate #include "match.h"
310Sstevel@tonic-gate #include "auth-options.h"
320Sstevel@tonic-gate #include "pathnames.h"
330Sstevel@tonic-gate #include "log.h"
340Sstevel@tonic-gate #include "servconf.h"
350Sstevel@tonic-gate #include "auth.h"
360Sstevel@tonic-gate #include "hostfile.h"
370Sstevel@tonic-gate #include "ssh.h"
380Sstevel@tonic-gate 
390Sstevel@tonic-gate /* import */
400Sstevel@tonic-gate extern ServerOptions options;
410Sstevel@tonic-gate 
420Sstevel@tonic-gate /*
430Sstevel@tonic-gate  * Session identifier that is used to bind key exchange and authentication
440Sstevel@tonic-gate  * responses to a particular session.
450Sstevel@tonic-gate  */
460Sstevel@tonic-gate extern u_char session_id[16];
470Sstevel@tonic-gate 
480Sstevel@tonic-gate /*
490Sstevel@tonic-gate  * The .ssh/authorized_keys file contains public keys, one per line, in the
500Sstevel@tonic-gate  * following format:
510Sstevel@tonic-gate  *   options bits e n comment
520Sstevel@tonic-gate  * where bits, e and n are decimal numbers,
530Sstevel@tonic-gate  * and comment is any string of characters up to newline.  The maximum
540Sstevel@tonic-gate  * length of a line is 8000 characters.  See the documentation for a
550Sstevel@tonic-gate  * description of the options.
560Sstevel@tonic-gate  */
570Sstevel@tonic-gate 
580Sstevel@tonic-gate BIGNUM *
auth_rsa_generate_challenge(Key * key)590Sstevel@tonic-gate auth_rsa_generate_challenge(Key *key)
600Sstevel@tonic-gate {
610Sstevel@tonic-gate 	BIGNUM *challenge;
620Sstevel@tonic-gate 	BN_CTX *ctx;
630Sstevel@tonic-gate 
640Sstevel@tonic-gate 	if ((challenge = BN_new()) == NULL)
650Sstevel@tonic-gate 		fatal("auth_rsa_generate_challenge: BN_new() failed");
660Sstevel@tonic-gate 	/* Generate a random challenge. */
670Sstevel@tonic-gate 	BN_rand(challenge, 256, 0, 0);
680Sstevel@tonic-gate 	if ((ctx = BN_CTX_new()) == NULL)
690Sstevel@tonic-gate 		fatal("auth_rsa_generate_challenge: BN_CTX_new() failed");
700Sstevel@tonic-gate 	BN_mod(challenge, challenge, key->rsa->n, ctx);
710Sstevel@tonic-gate 	BN_CTX_free(ctx);
720Sstevel@tonic-gate 
730Sstevel@tonic-gate 	return challenge;
740Sstevel@tonic-gate }
750Sstevel@tonic-gate 
760Sstevel@tonic-gate int
auth_rsa_verify_response(Key * key,BIGNUM * challenge,u_char response[16])770Sstevel@tonic-gate auth_rsa_verify_response(Key *key, BIGNUM *challenge, u_char response[16])
780Sstevel@tonic-gate {
790Sstevel@tonic-gate 	u_char buf[32], mdbuf[16];
800Sstevel@tonic-gate 	MD5_CTX md;
810Sstevel@tonic-gate 	int len;
820Sstevel@tonic-gate 
830Sstevel@tonic-gate 	/* don't allow short keys */
840Sstevel@tonic-gate 	if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) {
850Sstevel@tonic-gate 		error("auth_rsa_verify_response: RSA modulus too small: %d < minimum %d bits",
860Sstevel@tonic-gate 		    BN_num_bits(key->rsa->n), SSH_RSA_MINIMUM_MODULUS_SIZE);
870Sstevel@tonic-gate 		return (0);
880Sstevel@tonic-gate 	}
890Sstevel@tonic-gate 
900Sstevel@tonic-gate 	/* The response is MD5 of decrypted challenge plus session id. */
910Sstevel@tonic-gate 	len = BN_num_bytes(challenge);
920Sstevel@tonic-gate 	if (len <= 0 || len > 32)
930Sstevel@tonic-gate 		fatal("auth_rsa_verify_response: bad challenge length %d", len);
940Sstevel@tonic-gate 	memset(buf, 0, 32);
950Sstevel@tonic-gate 	BN_bn2bin(challenge, buf + 32 - len);
960Sstevel@tonic-gate 	MD5_Init(&md);
970Sstevel@tonic-gate 	MD5_Update(&md, buf, 32);
980Sstevel@tonic-gate 	MD5_Update(&md, session_id, 16);
990Sstevel@tonic-gate 	MD5_Final(mdbuf, &md);
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate 	/* Verify that the response is the original challenge. */
1020Sstevel@tonic-gate 	if (memcmp(response, mdbuf, 16) != 0) {
1030Sstevel@tonic-gate 		/* Wrong answer. */
1040Sstevel@tonic-gate 		return (0);
1050Sstevel@tonic-gate 	}
1060Sstevel@tonic-gate 	/* Correct answer. */
1070Sstevel@tonic-gate 	return (1);
1080Sstevel@tonic-gate }
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate /*
1110Sstevel@tonic-gate  * Performs the RSA authentication challenge-response dialog with the client,
1120Sstevel@tonic-gate  * and returns true (non-zero) if the client gave the correct answer to
1130Sstevel@tonic-gate  * our challenge; returns zero if the client gives a wrong answer.
1140Sstevel@tonic-gate  */
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate int
auth_rsa_challenge_dialog(Key * key)1170Sstevel@tonic-gate auth_rsa_challenge_dialog(Key *key)
1180Sstevel@tonic-gate {
1190Sstevel@tonic-gate 	BIGNUM *challenge, *encrypted_challenge;
1200Sstevel@tonic-gate 	u_char response[16];
1210Sstevel@tonic-gate 	int i, success;
1220Sstevel@tonic-gate 
1230Sstevel@tonic-gate 	if ((encrypted_challenge = BN_new()) == NULL)
1240Sstevel@tonic-gate 		fatal("auth_rsa_challenge_dialog: BN_new() failed");
1250Sstevel@tonic-gate 
126*5562Sjp161948 	challenge = auth_rsa_generate_challenge(key);
1270Sstevel@tonic-gate 
1280Sstevel@tonic-gate 	/* Encrypt the challenge with the public key. */
1290Sstevel@tonic-gate 	rsa_public_encrypt(encrypted_challenge, challenge, key->rsa);
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate 	/* Send the encrypted challenge to the client. */
1320Sstevel@tonic-gate 	packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE);
1330Sstevel@tonic-gate 	packet_put_bignum(encrypted_challenge);
1340Sstevel@tonic-gate 	packet_send();
1350Sstevel@tonic-gate 	BN_clear_free(encrypted_challenge);
1360Sstevel@tonic-gate 	packet_write_wait();
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate 	/* Wait for a response. */
1390Sstevel@tonic-gate 	packet_read_expect(SSH_CMSG_AUTH_RSA_RESPONSE);
1400Sstevel@tonic-gate 	for (i = 0; i < 16; i++)
1410Sstevel@tonic-gate 		response[i] = packet_get_char();
1420Sstevel@tonic-gate 	packet_check_eom();
1430Sstevel@tonic-gate 
144*5562Sjp161948 	success = auth_rsa_verify_response(key, challenge, response);
1450Sstevel@tonic-gate 	BN_clear_free(challenge);
1460Sstevel@tonic-gate 	return (success);
1470Sstevel@tonic-gate }
1480Sstevel@tonic-gate 
1490Sstevel@tonic-gate /*
1500Sstevel@tonic-gate  * check if there's user key matching client_n,
1510Sstevel@tonic-gate  * return key if login is allowed, NULL otherwise
1520Sstevel@tonic-gate  */
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate int
auth_rsa_key_allowed(struct passwd * pw,BIGNUM * client_n,Key ** rkey)1550Sstevel@tonic-gate auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey)
1560Sstevel@tonic-gate {
1570Sstevel@tonic-gate 	char line[8192], *file;
1580Sstevel@tonic-gate 	int allowed = 0;
1590Sstevel@tonic-gate 	u_int bits;
1600Sstevel@tonic-gate 	FILE *f;
1610Sstevel@tonic-gate 	u_long linenum = 0;
1620Sstevel@tonic-gate 	struct stat st;
1630Sstevel@tonic-gate 	Key *key;
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate 	/* Temporarily use the user's uid. */
1660Sstevel@tonic-gate 	temporarily_use_uid(pw);
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate 	/* The authorized keys. */
1690Sstevel@tonic-gate 	file = authorized_keys_file(pw);
1700Sstevel@tonic-gate 	debug("trying public RSA key file %s", file);
1710Sstevel@tonic-gate 
1720Sstevel@tonic-gate 	/* Fail quietly if file does not exist */
1730Sstevel@tonic-gate 	if (stat(file, &st) < 0) {
1740Sstevel@tonic-gate 		/* Restore the privileged uid. */
1750Sstevel@tonic-gate 		restore_uid();
1760Sstevel@tonic-gate 		xfree(file);
1770Sstevel@tonic-gate 		return (0);
1780Sstevel@tonic-gate 	}
1790Sstevel@tonic-gate 	/* Open the file containing the authorized keys. */
1800Sstevel@tonic-gate 	f = fopen(file, "r");
1810Sstevel@tonic-gate 	if (!f) {
1820Sstevel@tonic-gate 		/* Restore the privileged uid. */
1830Sstevel@tonic-gate 		restore_uid();
1840Sstevel@tonic-gate 		xfree(file);
1850Sstevel@tonic-gate 		return (0);
1860Sstevel@tonic-gate 	}
1870Sstevel@tonic-gate 	if (options.strict_modes &&
1880Sstevel@tonic-gate 	    secure_filename(f, file, pw, line, sizeof(line)) != 0) {
1890Sstevel@tonic-gate 		xfree(file);
1900Sstevel@tonic-gate 		fclose(f);
1910Sstevel@tonic-gate 		log("Authentication refused: %s", line);
1920Sstevel@tonic-gate 		restore_uid();
1930Sstevel@tonic-gate 		return (0);
1940Sstevel@tonic-gate 	}
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate 	/* Flag indicating whether the key is allowed. */
1970Sstevel@tonic-gate 	allowed = 0;
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 	key = key_new(KEY_RSA1);
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate 	/*
2020Sstevel@tonic-gate 	 * Go though the accepted keys, looking for the current key.  If
2030Sstevel@tonic-gate 	 * found, perform a challenge-response dialog to verify that the
2040Sstevel@tonic-gate 	 * user really has the corresponding private key.
2050Sstevel@tonic-gate 	 */
2060Sstevel@tonic-gate 	while (fgets(line, sizeof(line), f)) {
2070Sstevel@tonic-gate 		char *cp;
2080Sstevel@tonic-gate 		char *options;
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 		linenum++;
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate 		/* Skip leading whitespace, empty and comment lines. */
2130Sstevel@tonic-gate 		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
2140Sstevel@tonic-gate 			;
2150Sstevel@tonic-gate 		if (!*cp || *cp == '\n' || *cp == '#')
2160Sstevel@tonic-gate 			continue;
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate 		/*
2190Sstevel@tonic-gate 		 * Check if there are options for this key, and if so,
2200Sstevel@tonic-gate 		 * save their starting address and skip the option part
2210Sstevel@tonic-gate 		 * for now.  If there are no options, set the starting
2220Sstevel@tonic-gate 		 * address to NULL.
2230Sstevel@tonic-gate 		 */
2240Sstevel@tonic-gate 		if (*cp < '0' || *cp > '9') {
2250Sstevel@tonic-gate 			int quoted = 0;
2260Sstevel@tonic-gate 			options = cp;
2270Sstevel@tonic-gate 			for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
2280Sstevel@tonic-gate 				if (*cp == '\\' && cp[1] == '"')
2290Sstevel@tonic-gate 					cp++;	/* Skip both */
2300Sstevel@tonic-gate 				else if (*cp == '"')
2310Sstevel@tonic-gate 					quoted = !quoted;
2320Sstevel@tonic-gate 			}
2330Sstevel@tonic-gate 		} else
2340Sstevel@tonic-gate 			options = NULL;
2350Sstevel@tonic-gate 
2360Sstevel@tonic-gate 		/* Parse the key from the line. */
2370Sstevel@tonic-gate 		if (hostfile_read_key(&cp, &bits, key) == 0) {
2380Sstevel@tonic-gate 			debug("%.100s, line %lu: non ssh1 key syntax",
2390Sstevel@tonic-gate 			    file, linenum);
2400Sstevel@tonic-gate 			continue;
2410Sstevel@tonic-gate 		}
2420Sstevel@tonic-gate 		/* cp now points to the comment part. */
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate 		/* Check if the we have found the desired key (identified by its modulus). */
2450Sstevel@tonic-gate 		if (BN_cmp(key->rsa->n, client_n) != 0)
2460Sstevel@tonic-gate 			continue;
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate 		/* check the real bits  */
2490Sstevel@tonic-gate 		if (bits != BN_num_bits(key->rsa->n))
2500Sstevel@tonic-gate 			log("Warning: %s, line %lu: keysize mismatch: "
2510Sstevel@tonic-gate 			    "actual %d vs. announced %d.",
2520Sstevel@tonic-gate 			    file, linenum, BN_num_bits(key->rsa->n), bits);
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate 		/* We have found the desired key. */
2550Sstevel@tonic-gate 		/*
2560Sstevel@tonic-gate 		 * If our options do not allow this key to be used,
2570Sstevel@tonic-gate 		 * do not send challenge.
2580Sstevel@tonic-gate 		 */
2590Sstevel@tonic-gate 		if (!auth_parse_options(pw, options, file, linenum))
2600Sstevel@tonic-gate 			continue;
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate 		/* break out, this key is allowed */
2630Sstevel@tonic-gate 		allowed = 1;
2640Sstevel@tonic-gate 		break;
2650Sstevel@tonic-gate 	}
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate 	/* Restore the privileged uid. */
2680Sstevel@tonic-gate 	restore_uid();
2690Sstevel@tonic-gate 
2700Sstevel@tonic-gate 	/* Close the file. */
2710Sstevel@tonic-gate 	xfree(file);
2720Sstevel@tonic-gate 	fclose(f);
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 	/* return key if allowed */
2750Sstevel@tonic-gate 	if (allowed && rkey != NULL)
2760Sstevel@tonic-gate 		*rkey = key;
2770Sstevel@tonic-gate 	else
2780Sstevel@tonic-gate 		key_free(key);
2790Sstevel@tonic-gate 	return (allowed);
2800Sstevel@tonic-gate }
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate /*
2830Sstevel@tonic-gate  * Performs the RSA authentication dialog with the client.  This returns
2840Sstevel@tonic-gate  * 0 if the client could not be authenticated, and 1 if authentication was
2850Sstevel@tonic-gate  * successful.  This may exit if there is a serious protocol violation.
2860Sstevel@tonic-gate  */
2870Sstevel@tonic-gate int
auth_rsa(struct passwd * pw,BIGNUM * client_n)2880Sstevel@tonic-gate auth_rsa(struct passwd *pw, BIGNUM *client_n)
2890Sstevel@tonic-gate {
2900Sstevel@tonic-gate 	Key *key;
2910Sstevel@tonic-gate 	char *fp;
2920Sstevel@tonic-gate 
2930Sstevel@tonic-gate 	/* no user given */
2940Sstevel@tonic-gate 	if (pw == NULL)
2950Sstevel@tonic-gate 		return 0;
2960Sstevel@tonic-gate 
297*5562Sjp161948 	if (!auth_rsa_key_allowed(pw, client_n, &key)) {
2980Sstevel@tonic-gate 		auth_clear_options();
2990Sstevel@tonic-gate 		return (0);
3000Sstevel@tonic-gate 	}
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 	/* Perform the challenge-response dialog for this key. */
3030Sstevel@tonic-gate 	if (!auth_rsa_challenge_dialog(key)) {
3040Sstevel@tonic-gate 		/* Wrong response. */
3050Sstevel@tonic-gate 		verbose("Wrong response to RSA authentication challenge.");
3060Sstevel@tonic-gate 		packet_send_debug("Wrong response to RSA authentication challenge.");
3070Sstevel@tonic-gate 		/*
3080Sstevel@tonic-gate 		 * Break out of the loop. Otherwise we might send
3090Sstevel@tonic-gate 		 * another challenge and break the protocol.
3100Sstevel@tonic-gate 		 */
3110Sstevel@tonic-gate 		key_free(key);
3120Sstevel@tonic-gate 		return (0);
3130Sstevel@tonic-gate 	}
3140Sstevel@tonic-gate 	/*
3150Sstevel@tonic-gate 	 * Correct response.  The client has been successfully
3160Sstevel@tonic-gate 	 * authenticated. Note that we have not yet processed the
3170Sstevel@tonic-gate 	 * options; this will be reset if the options cause the
3180Sstevel@tonic-gate 	 * authentication to be rejected.
3190Sstevel@tonic-gate 	 */
3200Sstevel@tonic-gate 	fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
3210Sstevel@tonic-gate 	verbose("Found matching %s key: %s",
3220Sstevel@tonic-gate 	    key_type(key), fp);
3230Sstevel@tonic-gate 	xfree(fp);
3240Sstevel@tonic-gate 	key_free(key);
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 	packet_send_debug("RSA authentication accepted.");
3270Sstevel@tonic-gate 	return (1);
3280Sstevel@tonic-gate }
329