1295367Sdes/* $OpenBSD: auth-rsa.c,v 1.90 2015/01/28 22:36:00 djm Exp $ */ 257429Smarkm/* 357429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi> 457429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 557429Smarkm * All rights reserved 657429Smarkm * RSA-based authentication. This code determines whether to admit a login 757429Smarkm * based on RSA authentication. This file also contains functions to check 857429Smarkm * validity of the host key. 960576Skris * 1065674Skris * As far as I am concerned, the code I have written for this software 1165674Skris * can be used freely for any purpose. Any derived versions of this 1265674Skris * software must be clearly marked as such, and if the derived work is 1365674Skris * incompatible with the protocol description in the RFC file, it must be 1465674Skris * called by a name other than "ssh" or "Secure Shell". 1557429Smarkm */ 1657429Smarkm 1757429Smarkm#include "includes.h" 1857429Smarkm 19295367Sdes#ifdef WITH_SSH1 20295367Sdes 21162856Sdes#include <sys/types.h> 22162856Sdes#include <sys/stat.h> 23162856Sdes 2476262Sgreen#include <openssl/rsa.h> 2576262Sgreen 26162856Sdes#include <pwd.h> 27162856Sdes#include <stdio.h> 28162856Sdes#include <stdarg.h> 29162856Sdes#include <string.h> 30162856Sdes 31162856Sdes#include "xmalloc.h" 3257429Smarkm#include "rsa.h" 3357429Smarkm#include "packet.h" 3476262Sgreen#include "ssh1.h" 3557429Smarkm#include "uidswap.h" 3658585Skris#include "match.h" 37162856Sdes#include "buffer.h" 3876262Sgreen#include "pathnames.h" 3976262Sgreen#include "log.h" 40295367Sdes#include "misc.h" 4157429Smarkm#include "servconf.h" 42162856Sdes#include "key.h" 43215116Sdes#include "auth-options.h" 44162856Sdes#include "hostfile.h" 4576262Sgreen#include "auth.h" 46162856Sdes#ifdef GSSAPI 47162856Sdes#include "ssh-gss.h" 48162856Sdes#endif 4998684Sdes#include "monitor_wrap.h" 5098684Sdes#include "ssh.h" 5157429Smarkm 52264377Sdes#include "digest.h" 53264377Sdes 5469591Sgreen/* import */ 5569591Sgreenextern ServerOptions options; 5669591Sgreen 5757429Smarkm/* 5857429Smarkm * Session identifier that is used to bind key exchange and authentication 5957429Smarkm * responses to a particular session. 6057429Smarkm */ 6176262Sgreenextern u_char session_id[16]; 6257429Smarkm 6357429Smarkm/* 6457429Smarkm * The .ssh/authorized_keys file contains public keys, one per line, in the 6557429Smarkm * following format: 6657429Smarkm * options bits e n comment 6757429Smarkm * where bits, e and n are decimal numbers, 6857429Smarkm * and comment is any string of characters up to newline. The maximum 69147005Sdes * length of a line is SSH_MAX_PUBKEY_BYTES characters. See sshd(8) for a 7057429Smarkm * description of the options. 7157429Smarkm */ 7257429Smarkm 7398684SdesBIGNUM * 7498684Sdesauth_rsa_generate_challenge(Key *key) 7598684Sdes{ 7698684Sdes BIGNUM *challenge; 7798684Sdes BN_CTX *ctx; 7898684Sdes 7998684Sdes if ((challenge = BN_new()) == NULL) 8098684Sdes fatal("auth_rsa_generate_challenge: BN_new() failed"); 8198684Sdes /* Generate a random challenge. */ 82164149Sdes if (BN_rand(challenge, 256, 0, 0) == 0) 83164149Sdes fatal("auth_rsa_generate_challenge: BN_rand failed"); 8498684Sdes if ((ctx = BN_CTX_new()) == NULL) 85164149Sdes fatal("auth_rsa_generate_challenge: BN_CTX_new failed"); 86164149Sdes if (BN_mod(challenge, challenge, key->rsa->n, ctx) == 0) 87164149Sdes fatal("auth_rsa_generate_challenge: BN_mod failed"); 8898684Sdes BN_CTX_free(ctx); 8998684Sdes 9098684Sdes return challenge; 9198684Sdes} 9298684Sdes 9398684Sdesint 9498684Sdesauth_rsa_verify_response(Key *key, BIGNUM *challenge, u_char response[16]) 9598684Sdes{ 9698684Sdes u_char buf[32], mdbuf[16]; 97264377Sdes struct ssh_digest_ctx *md; 9898684Sdes int len; 9998684Sdes 10098684Sdes /* don't allow short keys */ 10198684Sdes if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { 102264377Sdes error("%s: RSA modulus too small: %d < minimum %d bits", 103264377Sdes __func__, 10498684Sdes BN_num_bits(key->rsa->n), SSH_RSA_MINIMUM_MODULUS_SIZE); 10598684Sdes return (0); 10698684Sdes } 10798684Sdes 10898684Sdes /* The response is MD5 of decrypted challenge plus session id. */ 10998684Sdes len = BN_num_bytes(challenge); 11098684Sdes if (len <= 0 || len > 32) 111264377Sdes fatal("%s: bad challenge length %d", __func__, len); 11298684Sdes memset(buf, 0, 32); 11398684Sdes BN_bn2bin(challenge, buf + 32 - len); 114264377Sdes if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL || 115264377Sdes ssh_digest_update(md, buf, 32) < 0 || 116264377Sdes ssh_digest_update(md, session_id, 16) < 0 || 117264377Sdes ssh_digest_final(md, mdbuf, sizeof(mdbuf)) < 0) 118264377Sdes fatal("%s: md5 failed", __func__); 119264377Sdes ssh_digest_free(md); 12098684Sdes 12198684Sdes /* Verify that the response is the original challenge. */ 122215116Sdes if (timingsafe_bcmp(response, mdbuf, 16) != 0) { 12398684Sdes /* Wrong answer. */ 12498684Sdes return (0); 12598684Sdes } 12698684Sdes /* Correct answer. */ 12798684Sdes return (1); 12898684Sdes} 12998684Sdes 13057429Smarkm/* 13157429Smarkm * Performs the RSA authentication challenge-response dialog with the client, 13257429Smarkm * and returns true (non-zero) if the client gave the correct answer to 13357429Smarkm * our challenge; returns zero if the client gives a wrong answer. 13457429Smarkm */ 13557429Smarkm 13657429Smarkmint 13798684Sdesauth_rsa_challenge_dialog(Key *key) 13857429Smarkm{ 13957429Smarkm BIGNUM *challenge, *encrypted_challenge; 14098684Sdes u_char response[16]; 14198684Sdes int i, success; 14257429Smarkm 14392559Sdes if ((encrypted_challenge = BN_new()) == NULL) 14492559Sdes fatal("auth_rsa_challenge_dialog: BN_new() failed"); 14557429Smarkm 14698684Sdes challenge = PRIVSEP(auth_rsa_generate_challenge(key)); 14757429Smarkm 14857429Smarkm /* Encrypt the challenge with the public key. */ 149295367Sdes if (rsa_public_encrypt(encrypted_challenge, challenge, key->rsa) != 0) 150295367Sdes fatal("%s: rsa_public_encrypt failed", __func__); 15157429Smarkm 15257429Smarkm /* Send the encrypted challenge to the client. */ 15357429Smarkm packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE); 15457429Smarkm packet_put_bignum(encrypted_challenge); 15557429Smarkm packet_send(); 15657429Smarkm BN_clear_free(encrypted_challenge); 15757429Smarkm packet_write_wait(); 15857429Smarkm 15957429Smarkm /* Wait for a response. */ 16092559Sdes packet_read_expect(SSH_CMSG_AUTH_RSA_RESPONSE); 16157429Smarkm for (i = 0; i < 16; i++) 162162856Sdes response[i] = (u_char)packet_get_char(); 16392559Sdes packet_check_eom(); 16457429Smarkm 16598684Sdes success = PRIVSEP(auth_rsa_verify_response(key, challenge, response)); 16657429Smarkm BN_clear_free(challenge); 16798684Sdes return (success); 16857429Smarkm} 16957429Smarkm 170226046Sdesstatic int 171226046Sdesrsa_key_allowed_in_file(struct passwd *pw, char *file, 172226046Sdes const BIGNUM *client_n, Key **rkey) 17357429Smarkm{ 174255767Sdes char *fp, line[SSH_MAX_PUBKEY_BYTES]; 175255767Sdes int allowed = 0, bits; 17657429Smarkm FILE *f; 17776262Sgreen u_long linenum = 0; 17892559Sdes Key *key; 17957429Smarkm 18092559Sdes debug("trying public RSA key file %s", file); 181226046Sdes if ((f = auth_openkeyfile(file, pw, options.strict_modes)) == NULL) 182226046Sdes return 0; 18357429Smarkm 18457429Smarkm /* 18557429Smarkm * Go though the accepted keys, looking for the current key. If 18657429Smarkm * found, perform a challenge-response dialog to verify that the 18757429Smarkm * user really has the corresponding private key. 18857429Smarkm */ 189226046Sdes key = key_new(KEY_RSA1); 190147005Sdes while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { 19157429Smarkm char *cp; 192137019Sdes char *key_options; 193149753Sdes int keybits; 19457429Smarkm 19557429Smarkm /* Skip leading whitespace, empty and comment lines. */ 19657429Smarkm for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 19757429Smarkm ; 19857429Smarkm if (!*cp || *cp == '\n' || *cp == '#') 19957429Smarkm continue; 20057429Smarkm 20157429Smarkm /* 20257429Smarkm * Check if there are options for this key, and if so, 20357429Smarkm * save their starting address and skip the option part 20457429Smarkm * for now. If there are no options, set the starting 20557429Smarkm * address to NULL. 20657429Smarkm */ 20757429Smarkm if (*cp < '0' || *cp > '9') { 20857429Smarkm int quoted = 0; 209137019Sdes key_options = cp; 21057429Smarkm for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { 21157429Smarkm if (*cp == '\\' && cp[1] == '"') 21257429Smarkm cp++; /* Skip both */ 21357429Smarkm else if (*cp == '"') 21457429Smarkm quoted = !quoted; 21557429Smarkm } 21657429Smarkm } else 217137019Sdes key_options = NULL; 21857429Smarkm 21957429Smarkm /* Parse the key from the line. */ 22092559Sdes if (hostfile_read_key(&cp, &bits, key) == 0) { 22192559Sdes debug("%.100s, line %lu: non ssh1 key syntax", 22276262Sgreen file, linenum); 22357429Smarkm continue; 22457429Smarkm } 22557429Smarkm /* cp now points to the comment part. */ 22657429Smarkm 227226046Sdes /* 228226046Sdes * Check if the we have found the desired key (identified 229226046Sdes * by its modulus). 230226046Sdes */ 23192559Sdes if (BN_cmp(key->rsa->n, client_n) != 0) 23257429Smarkm continue; 23357429Smarkm 23457429Smarkm /* check the real bits */ 235149753Sdes keybits = BN_num_bits(key->rsa->n); 236255767Sdes if (keybits < 0 || bits != keybits) 237124211Sdes logit("Warning: %s, line %lu: keysize mismatch: " 23857429Smarkm "actual %d vs. announced %d.", 23992559Sdes file, linenum, BN_num_bits(key->rsa->n), bits); 24057429Smarkm 241295367Sdes if ((fp = sshkey_fingerprint(key, options.fingerprint_hash, 242295367Sdes SSH_FP_DEFAULT)) == NULL) 243295367Sdes continue; 244255767Sdes debug("matching key found: file %s, line %lu %s %s", 245255767Sdes file, linenum, key_type(key), fp); 246255767Sdes free(fp); 247255767Sdes 248221420Sdes /* Never accept a revoked key */ 249221420Sdes if (auth_key_is_revoked(key)) 250221420Sdes break; 251221420Sdes 25257429Smarkm /* We have found the desired key. */ 25376262Sgreen /* 25476262Sgreen * If our options do not allow this key to be used, 25576262Sgreen * do not send challenge. 25676262Sgreen */ 257137019Sdes if (!auth_parse_options(pw, key_options, file, linenum)) 25876262Sgreen continue; 259215116Sdes if (key_is_cert_authority) 260215116Sdes continue; 26198684Sdes /* break out, this key is allowed */ 26298684Sdes allowed = 1; 26369591Sgreen break; 26457429Smarkm } 26557429Smarkm 26657429Smarkm /* Close the file. */ 26757429Smarkm fclose(f); 26857429Smarkm 26998684Sdes /* return key if allowed */ 27098684Sdes if (allowed && rkey != NULL) 27198684Sdes *rkey = key; 27298684Sdes else 27398684Sdes key_free(key); 274226046Sdes 275226046Sdes return allowed; 27698684Sdes} 27757429Smarkm 27898684Sdes/* 279226046Sdes * check if there's user key matching client_n, 280226046Sdes * return key if login is allowed, NULL otherwise 281226046Sdes */ 282226046Sdes 283226046Sdesint 284226046Sdesauth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) 285226046Sdes{ 286226046Sdes char *file; 287226046Sdes u_int i, allowed = 0; 288226046Sdes 289226046Sdes temporarily_use_uid(pw); 290226046Sdes 291226046Sdes for (i = 0; !allowed && i < options.num_authkeys_files; i++) { 292248619Sdes if (strcasecmp(options.authorized_keys_files[i], "none") == 0) 293248619Sdes continue; 294226046Sdes file = expand_authorized_keys( 295226046Sdes options.authorized_keys_files[i], pw); 296226046Sdes allowed = rsa_key_allowed_in_file(pw, file, client_n, rkey); 297255767Sdes free(file); 298226046Sdes } 299226046Sdes 300226046Sdes restore_uid(); 301226046Sdes 302226046Sdes return allowed; 303226046Sdes} 304226046Sdes 305226046Sdes/* 30698684Sdes * Performs the RSA authentication dialog with the client. This returns 30798684Sdes * 0 if the client could not be authenticated, and 1 if authentication was 30898684Sdes * successful. This may exit if there is a serious protocol violation. 30998684Sdes */ 31098684Sdesint 311126277Sdesauth_rsa(Authctxt *authctxt, BIGNUM *client_n) 31298684Sdes{ 31398684Sdes Key *key; 314126277Sdes struct passwd *pw = authctxt->pw; 31598684Sdes 31698684Sdes /* no user given */ 317126277Sdes if (!authctxt->valid) 31898684Sdes return 0; 31998684Sdes 32098684Sdes if (!PRIVSEP(auth_rsa_key_allowed(pw, client_n, &key))) { 32169591Sgreen auth_clear_options(); 32298684Sdes return (0); 32398684Sdes } 32457429Smarkm 32598684Sdes /* Perform the challenge-response dialog for this key. */ 32698684Sdes if (!auth_rsa_challenge_dialog(key)) { 32798684Sdes /* Wrong response. */ 32898684Sdes verbose("Wrong response to RSA authentication challenge."); 32998684Sdes packet_send_debug("Wrong response to RSA authentication challenge."); 33098684Sdes /* 33198684Sdes * Break out of the loop. Otherwise we might send 33298684Sdes * another challenge and break the protocol. 33398684Sdes */ 33498684Sdes key_free(key); 33598684Sdes return (0); 33698684Sdes } 33798684Sdes /* 33898684Sdes * Correct response. The client has been successfully 33998684Sdes * authenticated. Note that we have not yet processed the 34098684Sdes * options; this will be reset if the options cause the 34198684Sdes * authentication to be rejected. 34298684Sdes */ 343255767Sdes pubkey_auth_info(authctxt, key, NULL); 34498684Sdes 34598684Sdes packet_send_debug("RSA authentication accepted."); 34698684Sdes return (1); 34757429Smarkm} 348295367Sdes 349295367Sdes#endif /* WITH_SSH1 */ 350