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

Revision 18759, 8.3 KB checked in by zacheiss, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18758, 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.56 2002/06/10 16:53:06 stevesk 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 "mpaux.h"
27#include "uidswap.h"
28#include "match.h"
29#include "auth-options.h"
30#include "pathnames.h"
31#include "log.h"
32#include "servconf.h"
33#include "auth.h"
34#include "hostfile.h"
35#include "monitor_wrap.h"
36#include "ssh.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 8000 characters.  See the documentation 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[8192], *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                log("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 (fgets(line, sizeof(line), f)) {
206                char *cp;
207                char *options;
208
209                linenum++;
210
211                /* Skip leading whitespace, empty and comment lines. */
212                for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
213                        ;
214                if (!*cp || *cp == '\n' || *cp == '#')
215                        continue;
216
217                /*
218                 * Check if there are options for this key, and if so,
219                 * save their starting address and skip the option part
220                 * for now.  If there are no options, set the starting
221                 * address to NULL.
222                 */
223                if (*cp < '0' || *cp > '9') {
224                        int quoted = 0;
225                        options = cp;
226                        for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
227                                if (*cp == '\\' && cp[1] == '"')
228                                        cp++;   /* Skip both */
229                                else if (*cp == '"')
230                                        quoted = !quoted;
231                        }
232                } else
233                        options = NULL;
234
235                /* Parse the key from the line. */
236                if (hostfile_read_key(&cp, &bits, key) == 0) {
237                        debug("%.100s, line %lu: non ssh1 key syntax",
238                            file, linenum);
239                        continue;
240                }
241                /* cp now points to the comment part. */
242
243                /* Check if the we have found the desired key (identified by its modulus). */
244                if (BN_cmp(key->rsa->n, client_n) != 0)
245                        continue;
246
247                /* check the real bits  */
248                if (bits != BN_num_bits(key->rsa->n))
249                        log("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, 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(struct passwd *pw, BIGNUM *client_n)
288{
289        Key *key;
290        char *fp;
291
292        /* no user given */
293        if (pw == NULL)
294                return 0;
295
296        if (!PRIVSEP(auth_rsa_key_allowed(pw, client_n, &key))) {
297                auth_clear_options();
298                return (0);
299        }
300
301        /* Perform the challenge-response dialog for this key. */
302        if (!auth_rsa_challenge_dialog(key)) {
303                /* Wrong response. */
304                verbose("Wrong response to RSA authentication challenge.");
305                packet_send_debug("Wrong response to RSA authentication challenge.");
306                /*
307                 * Break out of the loop. Otherwise we might send
308                 * another challenge and break the protocol.
309                 */
310                key_free(key);
311                return (0);
312        }
313        /*
314         * Correct response.  The client has been successfully
315         * authenticated. Note that we have not yet processed the
316         * options; this will be reset if the options cause the
317         * authentication to be rejected.
318         */
319        fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
320        verbose("Found matching %s key: %s",
321            key_type(key), fp);
322        xfree(fp);
323        key_free(key);
324
325        packet_send_debug("RSA authentication accepted.");
326        return (1);
327}
Note: See TracBrowser for help on using the repository browser.