source: trunk/third/openssh/auth-rsa.c @ 22570

Revision 22570, 8.5 KB checked in by ghudson, 18 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r22569, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Author: Tatu Ylonen <ylo@cs.hut.fi>
3 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
4 *                    All rights reserved
5 * RSA-based authentication.  This code determines whether to admit a login
6 * based on RSA authentication.  This file also contains functions to check
7 * validity of the host key.
8 *
9 * As far as I am concerned, the code I have written for this software
10 * can be used freely for any purpose.  Any derived versions of this
11 * software must be clearly marked as such, and if the derived work is
12 * incompatible with the protocol description in the RFC file, it must be
13 * called by a name other than "ssh" or "Secure Shell".
14 */
15
16#include "includes.h"
17RCSID("$OpenBSD: auth-rsa.c,v 1.63 2005/06/17 02:44:32 djm Exp $");
18
19#include <openssl/rsa.h>
20#include <openssl/md5.h>
21
22#include "rsa.h"
23#include "packet.h"
24#include "xmalloc.h"
25#include "ssh1.h"
26#include "uidswap.h"
27#include "match.h"
28#include "auth-options.h"
29#include "pathnames.h"
30#include "log.h"
31#include "servconf.h"
32#include "auth.h"
33#include "hostfile.h"
34#include "monitor_wrap.h"
35#include "ssh.h"
36#include "misc.h"
37
38/* import */
39extern ServerOptions options;
40
41/*
42 * Session identifier that is used to bind key exchange and authentication
43 * responses to a particular session.
44 */
45extern u_char session_id[16];
46
47/*
48 * The .ssh/authorized_keys file contains public keys, one per line, in the
49 * following format:
50 *   options bits e n comment
51 * where bits, e and n are decimal numbers,
52 * and comment is any string of characters up to newline.  The maximum
53 * length of a line is SSH_MAX_PUBKEY_BYTES characters.  See sshd(8) for a
54 * description of the options.
55 */
56
57BIGNUM *
58auth_rsa_generate_challenge(Key *key)
59{
60        BIGNUM *challenge;
61        BN_CTX *ctx;
62
63        if ((challenge = BN_new()) == NULL)
64                fatal("auth_rsa_generate_challenge: BN_new() failed");
65        /* Generate a random challenge. */
66        BN_rand(challenge, 256, 0, 0);
67        if ((ctx = BN_CTX_new()) == NULL)
68                fatal("auth_rsa_generate_challenge: BN_CTX_new() failed");
69        BN_mod(challenge, challenge, key->rsa->n, ctx);
70        BN_CTX_free(ctx);
71
72        return challenge;
73}
74
75int
76auth_rsa_verify_response(Key *key, BIGNUM *challenge, u_char response[16])
77{
78        u_char buf[32], mdbuf[16];
79        MD5_CTX md;
80        int len;
81
82        /* don't allow short keys */
83        if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) {
84                error("auth_rsa_verify_response: RSA modulus too small: %d < minimum %d bits",
85                    BN_num_bits(key->rsa->n), SSH_RSA_MINIMUM_MODULUS_SIZE);
86                return (0);
87        }
88
89        /* The response is MD5 of decrypted challenge plus session id. */
90        len = BN_num_bytes(challenge);
91        if (len <= 0 || len > 32)
92                fatal("auth_rsa_verify_response: bad challenge length %d", len);
93        memset(buf, 0, 32);
94        BN_bn2bin(challenge, buf + 32 - len);
95        MD5_Init(&md);
96        MD5_Update(&md, buf, 32);
97        MD5_Update(&md, session_id, 16);
98        MD5_Final(mdbuf, &md);
99
100        /* Verify that the response is the original challenge. */
101        if (memcmp(response, mdbuf, 16) != 0) {
102                /* Wrong answer. */
103                return (0);
104        }
105        /* Correct answer. */
106        return (1);
107}
108
109/*
110 * Performs the RSA authentication challenge-response dialog with the client,
111 * and returns true (non-zero) if the client gave the correct answer to
112 * our challenge; returns zero if the client gives a wrong answer.
113 */
114
115int
116auth_rsa_challenge_dialog(Key *key)
117{
118        BIGNUM *challenge, *encrypted_challenge;
119        u_char response[16];
120        int i, success;
121
122        if ((encrypted_challenge = BN_new()) == NULL)
123                fatal("auth_rsa_challenge_dialog: BN_new() failed");
124
125        challenge = PRIVSEP(auth_rsa_generate_challenge(key));
126
127        /* Encrypt the challenge with the public key. */
128        rsa_public_encrypt(encrypted_challenge, challenge, key->rsa);
129
130        /* Send the encrypted challenge to the client. */
131        packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE);
132        packet_put_bignum(encrypted_challenge);
133        packet_send();
134        BN_clear_free(encrypted_challenge);
135        packet_write_wait();
136
137        /* Wait for a response. */
138        packet_read_expect(SSH_CMSG_AUTH_RSA_RESPONSE);
139        for (i = 0; i < 16; i++)
140                response[i] = packet_get_char();
141        packet_check_eom();
142
143        success = PRIVSEP(auth_rsa_verify_response(key, challenge, response));
144        BN_clear_free(challenge);
145        return (success);
146}
147
148/*
149 * check if there's user key matching client_n,
150 * return key if login is allowed, NULL otherwise
151 */
152
153int
154auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey)
155{
156        char line[SSH_MAX_PUBKEY_BYTES], *file;
157        int allowed = 0;
158        u_int bits;
159        FILE *f;
160        u_long linenum = 0;
161        struct stat st;
162        Key *key;
163
164        /* Temporarily use the user's uid. */
165        temporarily_use_uid(pw);
166
167        /* The authorized keys. */
168        file = authorized_keys_file(pw);
169        debug("trying public RSA key file %s", file);
170
171        /* Fail quietly if file does not exist */
172        if (stat(file, &st) < 0) {
173                /* Restore the privileged uid. */
174                restore_uid();
175                xfree(file);
176                return (0);
177        }
178        /* Open the file containing the authorized keys. */
179        f = fopen(file, "r");
180        if (!f) {
181                /* Restore the privileged uid. */
182                restore_uid();
183                xfree(file);
184                return (0);
185        }
186        if (options.strict_modes &&
187            secure_filename(f, file, pw, line, sizeof(line)) != 0) {
188                xfree(file);
189                fclose(f);
190                logit("Authentication refused: %s", line);
191                restore_uid();
192                return (0);
193        }
194
195        /* Flag indicating whether the key is allowed. */
196        allowed = 0;
197
198        key = key_new(KEY_RSA1);
199
200        /*
201         * Go though the accepted keys, looking for the current key.  If
202         * found, perform a challenge-response dialog to verify that the
203         * user really has the corresponding private key.
204         */
205        while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
206                char *cp;
207                char *key_options;
208                int keybits;
209
210                /* Skip leading whitespace, empty and comment lines. */
211                for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
212                        ;
213                if (!*cp || *cp == '\n' || *cp == '#')
214                        continue;
215
216                /*
217                 * Check if there are options for this key, and if so,
218                 * save their starting address and skip the option part
219                 * for now.  If there are no options, set the starting
220                 * address to NULL.
221                 */
222                if (*cp < '0' || *cp > '9') {
223                        int quoted = 0;
224                        key_options = cp;
225                        for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
226                                if (*cp == '\\' && cp[1] == '"')
227                                        cp++;   /* Skip both */
228                                else if (*cp == '"')
229                                        quoted = !quoted;
230                        }
231                } else
232                        key_options = NULL;
233
234                /* Parse the key from the line. */
235                if (hostfile_read_key(&cp, &bits, key) == 0) {
236                        debug("%.100s, line %lu: non ssh1 key syntax",
237                            file, linenum);
238                        continue;
239                }
240                /* cp now points to the comment part. */
241
242                /* Check if the we have found the desired key (identified by its modulus). */
243                if (BN_cmp(key->rsa->n, client_n) != 0)
244                        continue;
245
246                /* check the real bits  */
247                keybits = BN_num_bits(key->rsa->n);
248                if (keybits < 0 || bits != (u_int)keybits)
249                        logit("Warning: %s, line %lu: keysize mismatch: "
250                            "actual %d vs. announced %d.",
251                            file, linenum, BN_num_bits(key->rsa->n), bits);
252
253                /* We have found the desired key. */
254                /*
255                 * If our options do not allow this key to be used,
256                 * do not send challenge.
257                 */
258                if (!auth_parse_options(pw, key_options, file, linenum))
259                        continue;
260
261                /* break out, this key is allowed */
262                allowed = 1;
263                break;
264        }
265
266        /* Restore the privileged uid. */
267        restore_uid();
268
269        /* Close the file. */
270        xfree(file);
271        fclose(f);
272
273        /* return key if allowed */
274        if (allowed && rkey != NULL)
275                *rkey = key;
276        else
277                key_free(key);
278        return (allowed);
279}
280
281/*
282 * Performs the RSA authentication dialog with the client.  This returns
283 * 0 if the client could not be authenticated, and 1 if authentication was
284 * successful.  This may exit if there is a serious protocol violation.
285 */
286int
287auth_rsa(Authctxt *authctxt, BIGNUM *client_n)
288{
289        Key *key;
290        char *fp;
291        struct passwd *pw = authctxt->pw;
292
293        /* no user given */
294        if (!authctxt->valid)
295                return 0;
296
297        if (!PRIVSEP(auth_rsa_key_allowed(pw, client_n, &key))) {
298                auth_clear_options();
299                return (0);
300        }
301
302        /* Perform the challenge-response dialog for this key. */
303        if (!auth_rsa_challenge_dialog(key)) {
304                /* Wrong response. */
305                verbose("Wrong response to RSA authentication challenge.");
306                packet_send_debug("Wrong response to RSA authentication challenge.");
307                /*
308                 * Break out of the loop. Otherwise we might send
309                 * another challenge and break the protocol.
310                 */
311                key_free(key);
312                return (0);
313        }
314        /*
315         * Correct response.  The client has been successfully
316         * authenticated. Note that we have not yet processed the
317         * options; this will be reset if the options cause the
318         * authentication to be rejected.
319         */
320        fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
321        verbose("Found matching %s key: %s",
322            key_type(key), fp);
323        xfree(fp);
324        key_free(key);
325
326        packet_send_debug("RSA authentication accepted.");
327        return (1);
328}
Note: See TracBrowser for help on using the repository browser.