Main Page | Namespace List | Class List | Directories | File List | Class Members | File Members

auth-rsa.c

Go to the documentation of this file.
00001 /*
00002  * Author: Tatu Ylonen <ylo@cs.hut.fi>
00003  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
00004  *                    All rights reserved
00005  * RSA-based authentication.  This code determines whether to admit a login
00006  * based on RSA authentication.  This file also contains functions to check
00007  * validity of the host key.
00008  *
00009  * As far as I am concerned, the code I have written for this software
00010  * can be used freely for any purpose.  Any derived versions of this
00011  * software must be clearly marked as such, and if the derived work is
00012  * incompatible with the protocol description in the RFC file, it must be
00013  * called by a name other than "ssh" or "Secure Shell".
00014  */
00015 
00016 #include "includes.h"
00017 RCSID("$OpenBSD: auth-rsa.c,v 1.63 2005/06/17 02:44:32 djm Exp $");
00018 
00019 #include <openssl/rsa.h>
00020 #include <openssl/md5.h>
00021 
00022 #include "rsa.h"
00023 #include "packet.h"
00024 #include "xmalloc.h"
00025 #include "ssh1.h"
00026 #include "uidswap.h"
00027 #include "match.h"
00028 #include "auth-options.h"
00029 #include "pathnames.h"
00030 #include "log.h"
00031 #include "servconf.h"
00032 #include "auth.h"
00033 #include "hostfile.h"
00034 #include "monitor_wrap.h"
00035 #include "ssh.h"
00036 #include "misc.h"
00037 
00038 /* import */
00039 extern ServerOptions options;
00040 
00041 /*
00042  * Session identifier that is used to bind key exchange and authentication
00043  * responses to a particular session.
00044  */
00045 extern u_char session_id[16];
00046 
00047 /*
00048  * The .ssh/authorized_keys file contains public keys, one per line, in the
00049  * following format:
00050  *   options bits e n comment
00051  * where bits, e and n are decimal numbers,
00052  * and comment is any string of characters up to newline.  The maximum
00053  * length of a line is SSH_MAX_PUBKEY_BYTES characters.  See sshd(8) for a
00054  * description of the options.
00055  */
00056 
00057 BIGNUM *
00058 auth_rsa_generate_challenge(Key *key)
00059 {
00060         BIGNUM *challenge;
00061         BN_CTX *ctx;
00062 
00063         if ((challenge = BN_new()) == NULL)
00064                 fatal("auth_rsa_generate_challenge: BN_new() failed");
00065         /* Generate a random challenge. */
00066         BN_rand(challenge, 256, 0, 0);
00067         if ((ctx = BN_CTX_new()) == NULL)
00068                 fatal("auth_rsa_generate_challenge: BN_CTX_new() failed");
00069         BN_mod(challenge, challenge, key->rsa->n, ctx);
00070         BN_CTX_free(ctx);
00071 
00072         return challenge;
00073 }
00074 
00075 int
00076 auth_rsa_verify_response(Key *key, BIGNUM *challenge, u_char response[16])
00077 {
00078         u_char buf[32], mdbuf[16];
00079         MD5_CTX md;
00080         int len;
00081 
00082         /* don't allow short keys */
00083         if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) {
00084                 error("auth_rsa_verify_response: RSA modulus too small: %d < minimum %d bits",
00085                     BN_num_bits(key->rsa->n), SSH_RSA_MINIMUM_MODULUS_SIZE);
00086                 return (0);
00087         }
00088 
00089         /* The response is MD5 of decrypted challenge plus session id. */
00090         len = BN_num_bytes(challenge);
00091         if (len <= 0 || len > 32)
00092                 fatal("auth_rsa_verify_response: bad challenge length %d", len);
00093         memset(buf, 0, 32);
00094         BN_bn2bin(challenge, buf + 32 - len);
00095         MD5_Init(&md);
00096         MD5_Update(&md, buf, 32);
00097         MD5_Update(&md, session_id, 16);
00098         MD5_Final(mdbuf, &md);
00099 
00100         /* Verify that the response is the original challenge. */
00101         if (memcmp(response, mdbuf, 16) != 0) {
00102                 /* Wrong answer. */
00103                 return (0);
00104         }
00105         /* Correct answer. */
00106         return (1);
00107 }
00108 
00109 /*
00110  * Performs the RSA authentication challenge-response dialog with the client,
00111  * and returns true (non-zero) if the client gave the correct answer to
00112  * our challenge; returns zero if the client gives a wrong answer.
00113  */
00114 
00115 int
00116 auth_rsa_challenge_dialog(Key *key)
00117 {
00118         BIGNUM *challenge, *encrypted_challenge;
00119         u_char response[16];
00120         int i, success;
00121 
00122         if ((encrypted_challenge = BN_new()) == NULL)
00123                 fatal("auth_rsa_challenge_dialog: BN_new() failed");
00124 
00125         challenge = PRIVSEP(auth_rsa_generate_challenge(key));
00126 
00127         /* Encrypt the challenge with the public key. */
00128         rsa_public_encrypt(encrypted_challenge, challenge, key->rsa);
00129 
00130         /* Send the encrypted challenge to the client. */
00131         packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE);
00132         packet_put_bignum(encrypted_challenge);
00133         packet_send();
00134         BN_clear_free(encrypted_challenge);
00135         packet_write_wait();
00136 
00137         /* Wait for a response. */
00138         packet_read_expect(SSH_CMSG_AUTH_RSA_RESPONSE);
00139         for (i = 0; i < 16; i++)
00140                 response[i] = packet_get_char();
00141         packet_check_eom();
00142 
00143         success = PRIVSEP(auth_rsa_verify_response(key, challenge, response));
00144         BN_clear_free(challenge);
00145         return (success);
00146 }
00147 
00148 /*
00149  * check if there's user key matching client_n,
00150  * return key if login is allowed, NULL otherwise
00151  */
00152 
00153 int
00154 auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey)
00155 {
00156         char line[SSH_MAX_PUBKEY_BYTES], *file;
00157         int allowed = 0;
00158         u_int bits;
00159         FILE *f;
00160         u_long linenum = 0;
00161         struct stat st;
00162         Key *key;
00163 
00164         /* Temporarily use the user's uid. */
00165         temporarily_use_uid(pw);
00166 
00167         /* The authorized keys. */
00168         file = authorized_keys_file(pw);
00169         debug("trying public RSA key file %s", file);
00170 
00171         /* Fail quietly if file does not exist */
00172         if (stat(file, &st) < 0) {
00173                 /* Restore the privileged uid. */
00174                 restore_uid();
00175                 xfree(file);
00176                 return (0);
00177         }
00178         /* Open the file containing the authorized keys. */
00179         f = fopen(file, "r");
00180         if (!f) {
00181                 /* Restore the privileged uid. */
00182                 restore_uid();
00183                 xfree(file);
00184                 return (0);
00185         }
00186         if (options.strict_modes &&
00187             secure_filename(f, file, pw, line, sizeof(line)) != 0) {
00188                 xfree(file);
00189                 fclose(f);
00190                 logit("Authentication refused: %s", line);
00191                 restore_uid();
00192                 return (0);
00193         }
00194 
00195         /* Flag indicating whether the key is allowed. */
00196         allowed = 0;
00197 
00198         key = key_new(KEY_RSA1);
00199 
00200         /*
00201          * Go though the accepted keys, looking for the current key.  If
00202          * found, perform a challenge-response dialog to verify that the
00203          * user really has the corresponding private key.
00204          */
00205         while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
00206                 char *cp;
00207                 char *key_options;
00208                 int keybits;
00209 
00210                 /* Skip leading whitespace, empty and comment lines. */
00211                 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
00212                         ;
00213                 if (!*cp || *cp == '\n' || *cp == '#')
00214                         continue;
00215 
00216                 /*
00217                  * Check if there are options for this key, and if so,
00218                  * save their starting address and skip the option part
00219                  * for now.  If there are no options, set the starting
00220                  * address to NULL.
00221                  */
00222                 if (*cp < '0' || *cp > '9') {
00223                         int quoted = 0;
00224                         key_options = cp;
00225                         for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
00226                                 if (*cp == '\\' && cp[1] == '"')
00227                                         cp++;   /* Skip both */
00228                                 else if (*cp == '"')
00229                                         quoted = !quoted;
00230                         }
00231                 } else
00232                         key_options = NULL;
00233 
00234                 /* Parse the key from the line. */
00235                 if (hostfile_read_key(&cp, &bits, key) == 0) {
00236                         debug("%.100s, line %lu: non ssh1 key syntax",
00237                             file, linenum);
00238                         continue;
00239                 }
00240                 /* cp now points to the comment part. */
00241 
00242                 /* Check if the we have found the desired key (identified by its modulus). */
00243                 if (BN_cmp(key->rsa->n, client_n) != 0)
00244                         continue;
00245 
00246                 /* check the real bits  */
00247                 keybits = BN_num_bits(key->rsa->n);
00248                 if (keybits < 0 || bits != (u_int)keybits)
00249                         logit("Warning: %s, line %lu: keysize mismatch: "
00250                             "actual %d vs. announced %d.",
00251                             file, linenum, BN_num_bits(key->rsa->n), bits);
00252 
00253                 /* We have found the desired key. */
00254                 /*
00255                  * If our options do not allow this key to be used,
00256                  * do not send challenge.
00257                  */
00258                 if (!auth_parse_options(pw, key_options, file, linenum))
00259                         continue;
00260 
00261                 /* break out, this key is allowed */
00262                 allowed = 1;
00263                 break;
00264         }
00265 
00266         /* Restore the privileged uid. */
00267         restore_uid();
00268 
00269         /* Close the file. */
00270         xfree(file);
00271         fclose(f);
00272 
00273         /* return key if allowed */
00274         if (allowed && rkey != NULL)
00275                 *rkey = key;
00276         else
00277                 key_free(key);
00278         return (allowed);
00279 }
00280 
00281 /*
00282  * Performs the RSA authentication dialog with the client.  This returns
00283  * 0 if the client could not be authenticated, and 1 if authentication was
00284  * successful.  This may exit if there is a serious protocol violation.
00285  */
00286 int
00287 auth_rsa(Authctxt *authctxt, BIGNUM *client_n)
00288 {
00289         Key *key;
00290         char *fp;
00291         struct passwd *pw = authctxt->pw;
00292 
00293         /* no user given */
00294         if (!authctxt->valid)
00295                 return 0;
00296 
00297         if (!PRIVSEP(auth_rsa_key_allowed(pw, client_n, &key))) {
00298                 auth_clear_options();
00299                 return (0);
00300         }
00301 
00302         /* Perform the challenge-response dialog for this key. */
00303         if (!auth_rsa_challenge_dialog(key)) {
00304                 /* Wrong response. */
00305                 verbose("Wrong response to RSA authentication challenge.");
00306                 packet_send_debug("Wrong response to RSA authentication challenge.");
00307                 /*
00308                  * Break out of the loop. Otherwise we might send
00309                  * another challenge and break the protocol.
00310                  */
00311                 key_free(key);
00312                 return (0);
00313         }
00314         /*
00315          * Correct response.  The client has been successfully
00316          * authenticated. Note that we have not yet processed the
00317          * options; this will be reset if the options cause the
00318          * authentication to be rejected.
00319          */
00320         fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
00321         verbose("Found matching %s key: %s",
00322             key_type(key), fp);
00323         xfree(fp);
00324         key_free(key);
00325 
00326         packet_send_debug("RSA authentication accepted.");
00327         return (1);
00328 }

© sourcejam.com 2005-2008