1264377Sdes/* $OpenBSD: auth-rsa.c,v 1.86 2014/01/27 19:18:54 markus 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 19162856Sdes#include <sys/types.h> 20162856Sdes#include <sys/stat.h> 21162856Sdes 2276262Sgreen#include <openssl/rsa.h> 2376262Sgreen 24162856Sdes#include <pwd.h> 25162856Sdes#include <stdio.h> 26162856Sdes#include <stdarg.h> 27162856Sdes#include <string.h> 28162856Sdes 29162856Sdes#include "xmalloc.h" 3057429Smarkm#include "rsa.h" 3157429Smarkm#include "packet.h" 3276262Sgreen#include "ssh1.h" 3357429Smarkm#include "uidswap.h" 3458585Skris#include "match.h" 35162856Sdes#include "buffer.h" 3676262Sgreen#include "pathnames.h" 3776262Sgreen#include "log.h" 3857429Smarkm#include "servconf.h" 39162856Sdes#include "key.h" 40215116Sdes#include "auth-options.h" 41162856Sdes#include "hostfile.h" 4276262Sgreen#include "auth.h" 43162856Sdes#ifdef GSSAPI 44162856Sdes#include "ssh-gss.h" 45162856Sdes#endif 4698684Sdes#include "monitor_wrap.h" 4798684Sdes#include "ssh.h" 48147005Sdes#include "misc.h" 4957429Smarkm 50264377Sdes#include "digest.h" 51264377Sdes 5269591Sgreen/* import */ 5369591Sgreenextern ServerOptions options; 5469591Sgreen 5557429Smarkm/* 5657429Smarkm * Session identifier that is used to bind key exchange and authentication 5757429Smarkm * responses to a particular session. 5857429Smarkm */ 5976262Sgreenextern u_char session_id[16]; 6057429Smarkm 6157429Smarkm/* 6257429Smarkm * The .ssh/authorized_keys file contains public keys, one per line, in the 6357429Smarkm * following format: 6457429Smarkm * options bits e n comment 6557429Smarkm * where bits, e and n are decimal numbers, 6657429Smarkm * and comment is any string of characters up to newline. The maximum 67147005Sdes * length of a line is SSH_MAX_PUBKEY_BYTES characters. See sshd(8) for a 6857429Smarkm * description of the options. 6957429Smarkm */ 7057429Smarkm 7198684SdesBIGNUM * 7298684Sdesauth_rsa_generate_challenge(Key *key) 7398684Sdes{ 7498684Sdes BIGNUM *challenge; 7598684Sdes BN_CTX *ctx; 7698684Sdes 7798684Sdes if ((challenge = BN_new()) == NULL) 7898684Sdes fatal("auth_rsa_generate_challenge: BN_new() failed"); 7998684Sdes /* Generate a random challenge. */ 80164149Sdes if (BN_rand(challenge, 256, 0, 0) == 0) 81164149Sdes fatal("auth_rsa_generate_challenge: BN_rand failed"); 8298684Sdes if ((ctx = BN_CTX_new()) == NULL) 83164149Sdes fatal("auth_rsa_generate_challenge: BN_CTX_new failed"); 84164149Sdes if (BN_mod(challenge, challenge, key->rsa->n, ctx) == 0) 85164149Sdes fatal("auth_rsa_generate_challenge: BN_mod failed"); 8698684Sdes BN_CTX_free(ctx); 8798684Sdes 8898684Sdes return challenge; 8998684Sdes} 9098684Sdes 9198684Sdesint 9298684Sdesauth_rsa_verify_response(Key *key, BIGNUM *challenge, u_char response[16]) 9398684Sdes{ 9498684Sdes u_char buf[32], mdbuf[16]; 95264377Sdes struct ssh_digest_ctx *md; 9698684Sdes int len; 9798684Sdes 9898684Sdes /* don't allow short keys */ 9998684Sdes if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { 100264377Sdes error("%s: RSA modulus too small: %d < minimum %d bits", 101264377Sdes __func__, 10298684Sdes BN_num_bits(key->rsa->n), SSH_RSA_MINIMUM_MODULUS_SIZE); 10398684Sdes return (0); 10498684Sdes } 10598684Sdes 10698684Sdes /* The response is MD5 of decrypted challenge plus session id. */ 10798684Sdes len = BN_num_bytes(challenge); 10898684Sdes if (len <= 0 || len > 32) 109264377Sdes fatal("%s: bad challenge length %d", __func__, len); 11098684Sdes memset(buf, 0, 32); 11198684Sdes BN_bn2bin(challenge, buf + 32 - len); 112264377Sdes if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL || 113264377Sdes ssh_digest_update(md, buf, 32) < 0 || 114264377Sdes ssh_digest_update(md, session_id, 16) < 0 || 115264377Sdes ssh_digest_final(md, mdbuf, sizeof(mdbuf)) < 0) 116264377Sdes fatal("%s: md5 failed", __func__); 117264377Sdes ssh_digest_free(md); 11898684Sdes 11998684Sdes /* Verify that the response is the original challenge. */ 120215116Sdes if (timingsafe_bcmp(response, mdbuf, 16) != 0) { 12198684Sdes /* Wrong answer. */ 12298684Sdes return (0); 12398684Sdes } 12498684Sdes /* Correct answer. */ 12598684Sdes return (1); 12698684Sdes} 12798684Sdes 12857429Smarkm/* 12957429Smarkm * Performs the RSA authentication challenge-response dialog with the client, 13057429Smarkm * and returns true (non-zero) if the client gave the correct answer to 13157429Smarkm * our challenge; returns zero if the client gives a wrong answer. 13257429Smarkm */ 13357429Smarkm 13457429Smarkmint 13598684Sdesauth_rsa_challenge_dialog(Key *key) 13657429Smarkm{ 13757429Smarkm BIGNUM *challenge, *encrypted_challenge; 13898684Sdes u_char response[16]; 13998684Sdes int i, success; 14057429Smarkm 14192559Sdes if ((encrypted_challenge = BN_new()) == NULL) 14292559Sdes fatal("auth_rsa_challenge_dialog: BN_new() failed"); 14357429Smarkm 14498684Sdes challenge = PRIVSEP(auth_rsa_generate_challenge(key)); 14557429Smarkm 14657429Smarkm /* Encrypt the challenge with the public key. */ 14798684Sdes rsa_public_encrypt(encrypted_challenge, challenge, key->rsa); 14857429Smarkm 14957429Smarkm /* Send the encrypted challenge to the client. */ 15057429Smarkm packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE); 15157429Smarkm packet_put_bignum(encrypted_challenge); 15257429Smarkm packet_send(); 15357429Smarkm BN_clear_free(encrypted_challenge); 15457429Smarkm packet_write_wait(); 15557429Smarkm 15657429Smarkm /* Wait for a response. */ 15792559Sdes packet_read_expect(SSH_CMSG_AUTH_RSA_RESPONSE); 15857429Smarkm for (i = 0; i < 16; i++) 159162856Sdes response[i] = (u_char)packet_get_char(); 16092559Sdes packet_check_eom(); 16157429Smarkm 16298684Sdes success = PRIVSEP(auth_rsa_verify_response(key, challenge, response)); 16357429Smarkm BN_clear_free(challenge); 16498684Sdes return (success); 16557429Smarkm} 16657429Smarkm 167226046Sdesstatic int 168226046Sdesrsa_key_allowed_in_file(struct passwd *pw, char *file, 169226046Sdes const BIGNUM *client_n, Key **rkey) 17057429Smarkm{ 171255767Sdes char *fp, line[SSH_MAX_PUBKEY_BYTES]; 172255767Sdes int allowed = 0, bits; 17357429Smarkm FILE *f; 17476262Sgreen u_long linenum = 0; 17592559Sdes Key *key; 17657429Smarkm 17792559Sdes debug("trying public RSA key file %s", file); 178226046Sdes if ((f = auth_openkeyfile(file, pw, options.strict_modes)) == NULL) 179226046Sdes return 0; 18057429Smarkm 18157429Smarkm /* 18257429Smarkm * Go though the accepted keys, looking for the current key. If 18357429Smarkm * found, perform a challenge-response dialog to verify that the 18457429Smarkm * user really has the corresponding private key. 18557429Smarkm */ 186226046Sdes key = key_new(KEY_RSA1); 187147005Sdes while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { 18857429Smarkm char *cp; 189137019Sdes char *key_options; 190149753Sdes int keybits; 19157429Smarkm 19257429Smarkm /* Skip leading whitespace, empty and comment lines. */ 19357429Smarkm for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 19457429Smarkm ; 19557429Smarkm if (!*cp || *cp == '\n' || *cp == '#') 19657429Smarkm continue; 19757429Smarkm 19857429Smarkm /* 19957429Smarkm * Check if there are options for this key, and if so, 20057429Smarkm * save their starting address and skip the option part 20157429Smarkm * for now. If there are no options, set the starting 20257429Smarkm * address to NULL. 20357429Smarkm */ 20457429Smarkm if (*cp < '0' || *cp > '9') { 20557429Smarkm int quoted = 0; 206137019Sdes key_options = cp; 20757429Smarkm for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { 20857429Smarkm if (*cp == '\\' && cp[1] == '"') 20957429Smarkm cp++; /* Skip both */ 21057429Smarkm else if (*cp == '"') 21157429Smarkm quoted = !quoted; 21257429Smarkm } 21357429Smarkm } else 214137019Sdes key_options = NULL; 21557429Smarkm 21657429Smarkm /* Parse the key from the line. */ 21792559Sdes if (hostfile_read_key(&cp, &bits, key) == 0) { 21892559Sdes debug("%.100s, line %lu: non ssh1 key syntax", 21976262Sgreen file, linenum); 22057429Smarkm continue; 22157429Smarkm } 22257429Smarkm /* cp now points to the comment part. */ 22357429Smarkm 224226046Sdes /* 225226046Sdes * Check if the we have found the desired key (identified 226226046Sdes * by its modulus). 227226046Sdes */ 22892559Sdes if (BN_cmp(key->rsa->n, client_n) != 0) 22957429Smarkm continue; 23057429Smarkm 23157429Smarkm /* check the real bits */ 232149753Sdes keybits = BN_num_bits(key->rsa->n); 233255767Sdes if (keybits < 0 || bits != keybits) 234124211Sdes logit("Warning: %s, line %lu: keysize mismatch: " 23557429Smarkm "actual %d vs. announced %d.", 23692559Sdes file, linenum, BN_num_bits(key->rsa->n), bits); 23757429Smarkm 238255767Sdes fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); 239255767Sdes debug("matching key found: file %s, line %lu %s %s", 240255767Sdes file, linenum, key_type(key), fp); 241255767Sdes free(fp); 242255767Sdes 243221420Sdes /* Never accept a revoked key */ 244221420Sdes if (auth_key_is_revoked(key)) 245221420Sdes break; 246221420Sdes 24757429Smarkm /* We have found the desired key. */ 24876262Sgreen /* 24976262Sgreen * If our options do not allow this key to be used, 25076262Sgreen * do not send challenge. 25176262Sgreen */ 252137019Sdes if (!auth_parse_options(pw, key_options, file, linenum)) 25376262Sgreen continue; 254215116Sdes if (key_is_cert_authority) 255215116Sdes continue; 25698684Sdes /* break out, this key is allowed */ 25798684Sdes allowed = 1; 25869591Sgreen break; 25957429Smarkm } 26057429Smarkm 26157429Smarkm /* Close the file. */ 26257429Smarkm fclose(f); 26357429Smarkm 26498684Sdes /* return key if allowed */ 26598684Sdes if (allowed && rkey != NULL) 26698684Sdes *rkey = key; 26798684Sdes else 26898684Sdes key_free(key); 269226046Sdes 270226046Sdes return allowed; 27198684Sdes} 27257429Smarkm 27398684Sdes/* 274226046Sdes * check if there's user key matching client_n, 275226046Sdes * return key if login is allowed, NULL otherwise 276226046Sdes */ 277226046Sdes 278226046Sdesint 279226046Sdesauth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) 280226046Sdes{ 281226046Sdes char *file; 282226046Sdes u_int i, allowed = 0; 283226046Sdes 284226046Sdes temporarily_use_uid(pw); 285226046Sdes 286226046Sdes for (i = 0; !allowed && i < options.num_authkeys_files; i++) { 287248619Sdes if (strcasecmp(options.authorized_keys_files[i], "none") == 0) 288248619Sdes continue; 289226046Sdes file = expand_authorized_keys( 290226046Sdes options.authorized_keys_files[i], pw); 291226046Sdes allowed = rsa_key_allowed_in_file(pw, file, client_n, rkey); 292255767Sdes free(file); 293226046Sdes } 294226046Sdes 295226046Sdes restore_uid(); 296226046Sdes 297226046Sdes return allowed; 298226046Sdes} 299226046Sdes 300226046Sdes/* 30198684Sdes * Performs the RSA authentication dialog with the client. This returns 30298684Sdes * 0 if the client could not be authenticated, and 1 if authentication was 30398684Sdes * successful. This may exit if there is a serious protocol violation. 30498684Sdes */ 30598684Sdesint 306126277Sdesauth_rsa(Authctxt *authctxt, BIGNUM *client_n) 30798684Sdes{ 30898684Sdes Key *key; 309126277Sdes struct passwd *pw = authctxt->pw; 31098684Sdes 31198684Sdes /* no user given */ 312126277Sdes if (!authctxt->valid) 31398684Sdes return 0; 31498684Sdes 31598684Sdes if (!PRIVSEP(auth_rsa_key_allowed(pw, client_n, &key))) { 31669591Sgreen auth_clear_options(); 31798684Sdes return (0); 31898684Sdes } 31957429Smarkm 32098684Sdes /* Perform the challenge-response dialog for this key. */ 32198684Sdes if (!auth_rsa_challenge_dialog(key)) { 32298684Sdes /* Wrong response. */ 32398684Sdes verbose("Wrong response to RSA authentication challenge."); 32498684Sdes packet_send_debug("Wrong response to RSA authentication challenge."); 32598684Sdes /* 32698684Sdes * Break out of the loop. Otherwise we might send 32798684Sdes * another challenge and break the protocol. 32898684Sdes */ 32998684Sdes key_free(key); 33098684Sdes return (0); 33198684Sdes } 33298684Sdes /* 33398684Sdes * Correct response. The client has been successfully 33498684Sdes * authenticated. Note that we have not yet processed the 33598684Sdes * options; this will be reset if the options cause the 33698684Sdes * authentication to be rejected. 33798684Sdes */ 338255767Sdes pubkey_auth_info(authctxt, key, NULL); 33998684Sdes 34098684Sdes packet_send_debug("RSA authentication accepted."); 34198684Sdes return (1); 34257429Smarkm} 343