source: trunk/third/openssh/authfile.c @ 18759

Revision 18759, 16.8 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 * This file contains functions for reading and writing identity files, and
6 * for reading the passphrase from the user.
7 *
8 * As far as I am concerned, the code I have written for this software
9 * can be used freely for any purpose.  Any derived versions of this
10 * software must be clearly marked as such, and if the derived work is
11 * incompatible with the protocol description in the RFC file, it must be
12 * called by a name other than "ssh" or "Secure Shell".
13 *
14 *
15 * Copyright (c) 2000 Markus Friedl.  All rights reserved.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
19 * are met:
20 * 1. Redistributions of source code must retain the above copyright
21 *    notice, this list of conditions and the following disclaimer.
22 * 2. Redistributions in binary form must reproduce the above copyright
23 *    notice, this list of conditions and the following disclaimer in the
24 *    documentation and/or other materials provided with the distribution.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#include "includes.h"
39RCSID("$OpenBSD: authfile.c,v 1.50 2002/06/24 14:55:38 markus Exp $");
40
41#include <openssl/err.h>
42#include <openssl/evp.h>
43#include <openssl/pem.h>
44
45#include "cipher.h"
46#include "xmalloc.h"
47#include "buffer.h"
48#include "bufaux.h"
49#include "key.h"
50#include "ssh.h"
51#include "log.h"
52#include "authfile.h"
53#include "rsa.h"
54
55/* Version identification string for SSH v1 identity files. */
56static const char authfile_id_string[] =
57    "SSH PRIVATE KEY FILE FORMAT 1.1\n";
58
59/*
60 * Saves the authentication (private) key in a file, encrypting it with
61 * passphrase.  The identification of the file (lowest 64 bits of n) will
62 * precede the key to provide identification of the key without needing a
63 * passphrase.
64 */
65
66static int
67key_save_private_rsa1(Key *key, const char *filename, const char *passphrase,
68    const char *comment)
69{
70        Buffer buffer, encrypted;
71        u_char buf[100], *cp;
72        int fd, i, cipher_num;
73        CipherContext ciphercontext;
74        Cipher *cipher;
75        u_int32_t rand;
76
77        /*
78         * If the passphrase is empty, use SSH_CIPHER_NONE to ease converting
79         * to another cipher; otherwise use SSH_AUTHFILE_CIPHER.
80         */
81        cipher_num = (strcmp(passphrase, "") == 0) ?
82            SSH_CIPHER_NONE : SSH_AUTHFILE_CIPHER;
83        if ((cipher = cipher_by_number(cipher_num)) == NULL)
84                fatal("save_private_key_rsa: bad cipher");
85
86        /* This buffer is used to built the secret part of the private key. */
87        buffer_init(&buffer);
88
89        /* Put checkbytes for checking passphrase validity. */
90        rand = arc4random();
91        buf[0] = rand & 0xff;
92        buf[1] = (rand >> 8) & 0xff;
93        buf[2] = buf[0];
94        buf[3] = buf[1];
95        buffer_append(&buffer, buf, 4);
96
97        /*
98         * Store the private key (n and e will not be stored because they
99         * will be stored in plain text, and storing them also in encrypted
100         * format would just give known plaintext).
101         */
102        buffer_put_bignum(&buffer, key->rsa->d);
103        buffer_put_bignum(&buffer, key->rsa->iqmp);
104        buffer_put_bignum(&buffer, key->rsa->q);        /* reverse from SSL p */
105        buffer_put_bignum(&buffer, key->rsa->p);        /* reverse from SSL q */
106
107        /* Pad the part to be encrypted until its size is a multiple of 8. */
108        while (buffer_len(&buffer) % 8 != 0)
109                buffer_put_char(&buffer, 0);
110
111        /* This buffer will be used to contain the data in the file. */
112        buffer_init(&encrypted);
113
114        /* First store keyfile id string. */
115        for (i = 0; authfile_id_string[i]; i++)
116                buffer_put_char(&encrypted, authfile_id_string[i]);
117        buffer_put_char(&encrypted, 0);
118
119        /* Store cipher type. */
120        buffer_put_char(&encrypted, cipher_num);
121        buffer_put_int(&encrypted, 0);  /* For future extension */
122
123        /* Store public key.  This will be in plain text. */
124        buffer_put_int(&encrypted, BN_num_bits(key->rsa->n));
125        buffer_put_bignum(&encrypted, key->rsa->n);
126        buffer_put_bignum(&encrypted, key->rsa->e);
127        buffer_put_cstring(&encrypted, comment);
128
129        /* Allocate space for the private part of the key in the buffer. */
130        cp = buffer_append_space(&encrypted, buffer_len(&buffer));
131
132        cipher_set_key_string(&ciphercontext, cipher, passphrase,
133            CIPHER_ENCRYPT);
134        cipher_crypt(&ciphercontext, cp,
135            buffer_ptr(&buffer), buffer_len(&buffer));
136        cipher_cleanup(&ciphercontext);
137        memset(&ciphercontext, 0, sizeof(ciphercontext));
138
139        /* Destroy temporary data. */
140        memset(buf, 0, sizeof(buf));
141        buffer_free(&buffer);
142
143        fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
144        if (fd < 0) {
145                error("open %s failed: %s.", filename, strerror(errno));
146                return 0;
147        }
148        if (write(fd, buffer_ptr(&encrypted), buffer_len(&encrypted)) !=
149            buffer_len(&encrypted)) {
150                error("write to key file %s failed: %s", filename,
151                    strerror(errno));
152                buffer_free(&encrypted);
153                close(fd);
154                unlink(filename);
155                return 0;
156        }
157        close(fd);
158        buffer_free(&encrypted);
159        return 1;
160}
161
162/* save SSH v2 key in OpenSSL PEM format */
163static int
164key_save_private_pem(Key *key, const char *filename, const char *_passphrase,
165    const char *comment)
166{
167        FILE *fp;
168        int fd;
169        int success = 0;
170        int len = strlen(_passphrase);
171        u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL;
172        const EVP_CIPHER *cipher = (len > 0) ? EVP_des_ede3_cbc() : NULL;
173
174        if (len > 0 && len <= 4) {
175                error("passphrase too short: have %d bytes, need > 4", len);
176                return 0;
177        }
178        fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
179        if (fd < 0) {
180                error("open %s failed: %s.", filename, strerror(errno));
181                return 0;
182        }
183        fp = fdopen(fd, "w");
184        if (fp == NULL ) {
185                error("fdopen %s failed: %s.", filename, strerror(errno));
186                close(fd);
187                return 0;
188        }
189        switch (key->type) {
190        case KEY_DSA:
191                success = PEM_write_DSAPrivateKey(fp, key->dsa,
192                    cipher, passphrase, len, NULL, NULL);
193                break;
194        case KEY_RSA:
195                success = PEM_write_RSAPrivateKey(fp, key->rsa,
196                    cipher, passphrase, len, NULL, NULL);
197                break;
198        }
199        fclose(fp);
200        return success;
201}
202
203int
204key_save_private(Key *key, const char *filename, const char *passphrase,
205    const char *comment)
206{
207        switch (key->type) {
208        case KEY_RSA1:
209                return key_save_private_rsa1(key, filename, passphrase,
210                    comment);
211                break;
212        case KEY_DSA:
213        case KEY_RSA:
214                return key_save_private_pem(key, filename, passphrase,
215                    comment);
216                break;
217        default:
218                break;
219        }
220        error("key_save_private: cannot save key type %d", key->type);
221        return 0;
222}
223
224/*
225 * Loads the public part of the ssh v1 key file.  Returns NULL if an error was
226 * encountered (the file does not exist or is not readable), and the key
227 * otherwise.
228 */
229
230static Key *
231key_load_public_rsa1(int fd, const char *filename, char **commentp)
232{
233        Buffer buffer;
234        Key *pub;
235        char *cp;
236        int i;
237        off_t len;
238
239        len = lseek(fd, (off_t) 0, SEEK_END);
240        lseek(fd, (off_t) 0, SEEK_SET);
241
242        buffer_init(&buffer);
243        cp = buffer_append_space(&buffer, len);
244
245        if (read(fd, cp, (size_t) len) != (size_t) len) {
246                debug("Read from key file %.200s failed: %.100s", filename,
247                    strerror(errno));
248                buffer_free(&buffer);
249                return NULL;
250        }
251
252        /* Check that it is at least big enough to contain the ID string. */
253        if (len < sizeof(authfile_id_string)) {
254                debug3("Not a RSA1 key file %.200s.", filename);
255                buffer_free(&buffer);
256                return NULL;
257        }
258        /*
259         * Make sure it begins with the id string.  Consume the id string
260         * from the buffer.
261         */
262        for (i = 0; i < sizeof(authfile_id_string); i++)
263                if (buffer_get_char(&buffer) != authfile_id_string[i]) {
264                        debug3("Not a RSA1 key file %.200s.", filename);
265                        buffer_free(&buffer);
266                        return NULL;
267                }
268        /* Skip cipher type and reserved data. */
269        (void) buffer_get_char(&buffer);        /* cipher type */
270        (void) buffer_get_int(&buffer);         /* reserved */
271
272        /* Read the public key from the buffer. */
273        (void) buffer_get_int(&buffer);
274        pub = key_new(KEY_RSA1);
275        buffer_get_bignum(&buffer, pub->rsa->n);
276        buffer_get_bignum(&buffer, pub->rsa->e);
277        if (commentp)
278                *commentp = buffer_get_string(&buffer, NULL);
279        /* The encrypted private part is not parsed by this function. */
280
281        buffer_free(&buffer);
282        return pub;
283}
284
285/* load public key from private-key file, works only for SSH v1 */
286Key *
287key_load_public_type(int type, const char *filename, char **commentp)
288{
289        Key *pub;
290        int fd;
291
292        if (type == KEY_RSA1) {
293                fd = open(filename, O_RDONLY);
294                if (fd < 0)
295                        return NULL;
296                pub = key_load_public_rsa1(fd, filename, commentp);
297                close(fd);
298                return pub;
299        }
300        return NULL;
301}
302
303/*
304 * Loads the private key from the file.  Returns 0 if an error is encountered
305 * (file does not exist or is not readable, or passphrase is bad). This
306 * initializes the private key.
307 * Assumes we are called under uid of the owner of the file.
308 */
309
310static Key *
311key_load_private_rsa1(int fd, const char *filename, const char *passphrase,
312    char **commentp)
313{
314        int i, check1, check2, cipher_type;
315        off_t len;
316        Buffer buffer, decrypted;
317        u_char *cp;
318        CipherContext ciphercontext;
319        Cipher *cipher;
320        Key *prv = NULL;
321
322        len = lseek(fd, (off_t) 0, SEEK_END);
323        lseek(fd, (off_t) 0, SEEK_SET);
324
325        buffer_init(&buffer);
326        cp = buffer_append_space(&buffer, len);
327
328        if (read(fd, cp, (size_t) len) != (size_t) len) {
329                debug("Read from key file %.200s failed: %.100s", filename,
330                    strerror(errno));
331                buffer_free(&buffer);
332                close(fd);
333                return NULL;
334        }
335
336        /* Check that it is at least big enough to contain the ID string. */
337        if (len < sizeof(authfile_id_string)) {
338                debug3("Not a RSA1 key file %.200s.", filename);
339                buffer_free(&buffer);
340                close(fd);
341                return NULL;
342        }
343        /*
344         * Make sure it begins with the id string.  Consume the id string
345         * from the buffer.
346         */
347        for (i = 0; i < sizeof(authfile_id_string); i++)
348                if (buffer_get_char(&buffer) != authfile_id_string[i]) {
349                        debug3("Not a RSA1 key file %.200s.", filename);
350                        buffer_free(&buffer);
351                        close(fd);
352                        return NULL;
353                }
354
355        /* Read cipher type. */
356        cipher_type = buffer_get_char(&buffer);
357        (void) buffer_get_int(&buffer); /* Reserved data. */
358
359        /* Read the public key from the buffer. */
360        (void) buffer_get_int(&buffer);
361        prv = key_new_private(KEY_RSA1);
362
363        buffer_get_bignum(&buffer, prv->rsa->n);
364        buffer_get_bignum(&buffer, prv->rsa->e);
365        if (commentp)
366                *commentp = buffer_get_string(&buffer, NULL);
367        else
368                xfree(buffer_get_string(&buffer, NULL));
369
370        /* Check that it is a supported cipher. */
371        cipher = cipher_by_number(cipher_type);
372        if (cipher == NULL) {
373                debug("Unsupported cipher %d used in key file %.200s.",
374                    cipher_type, filename);
375                buffer_free(&buffer);
376                goto fail;
377        }
378        /* Initialize space for decrypted data. */
379        buffer_init(&decrypted);
380        cp = buffer_append_space(&decrypted, buffer_len(&buffer));
381
382        /* Rest of the buffer is encrypted.  Decrypt it using the passphrase. */
383        cipher_set_key_string(&ciphercontext, cipher, passphrase,
384            CIPHER_DECRYPT);
385        cipher_crypt(&ciphercontext, cp,
386            buffer_ptr(&buffer), buffer_len(&buffer));
387        cipher_cleanup(&ciphercontext);
388        memset(&ciphercontext, 0, sizeof(ciphercontext));
389        buffer_free(&buffer);
390
391        check1 = buffer_get_char(&decrypted);
392        check2 = buffer_get_char(&decrypted);
393        if (check1 != buffer_get_char(&decrypted) ||
394            check2 != buffer_get_char(&decrypted)) {
395                if (strcmp(passphrase, "") != 0)
396                        debug("Bad passphrase supplied for key file %.200s.",
397                            filename);
398                /* Bad passphrase. */
399                buffer_free(&decrypted);
400                goto fail;
401        }
402        /* Read the rest of the private key. */
403        buffer_get_bignum(&decrypted, prv->rsa->d);
404        buffer_get_bignum(&decrypted, prv->rsa->iqmp);          /* u */
405        /* in SSL and SSH v1 p and q are exchanged */
406        buffer_get_bignum(&decrypted, prv->rsa->q);             /* p */
407        buffer_get_bignum(&decrypted, prv->rsa->p);             /* q */
408
409        /* calculate p-1 and q-1 */
410        rsa_generate_additional_parameters(prv->rsa);
411
412        buffer_free(&decrypted);
413        close(fd);
414        return prv;
415
416fail:
417        if (commentp)
418                xfree(*commentp);
419        close(fd);
420        key_free(prv);
421        return NULL;
422}
423
424Key *
425key_load_private_pem(int fd, int type, const char *passphrase,
426    char **commentp)
427{
428        FILE *fp;
429        EVP_PKEY *pk = NULL;
430        Key *prv = NULL;
431        char *name = "<no key>";
432
433        fp = fdopen(fd, "r");
434        if (fp == NULL) {
435                error("fdopen failed: %s", strerror(errno));
436                close(fd);
437                return NULL;
438        }
439        pk = PEM_read_PrivateKey(fp, NULL, NULL, (char *)passphrase);
440        if (pk == NULL) {
441                debug("PEM_read_PrivateKey failed");
442                (void)ERR_get_error();
443        } else if (pk->type == EVP_PKEY_RSA &&
444            (type == KEY_UNSPEC||type==KEY_RSA)) {
445                prv = key_new(KEY_UNSPEC);
446                prv->rsa = EVP_PKEY_get1_RSA(pk);
447                prv->type = KEY_RSA;
448                name = "rsa w/o comment";
449#ifdef DEBUG_PK
450                RSA_print_fp(stderr, prv->rsa, 8);
451#endif
452        } else if (pk->type == EVP_PKEY_DSA &&
453            (type == KEY_UNSPEC||type==KEY_DSA)) {
454                prv = key_new(KEY_UNSPEC);
455                prv->dsa = EVP_PKEY_get1_DSA(pk);
456                prv->type = KEY_DSA;
457                name = "dsa w/o comment";
458#ifdef DEBUG_PK
459                DSA_print_fp(stderr, prv->dsa, 8);
460#endif
461        } else {
462                error("PEM_read_PrivateKey: mismatch or "
463                    "unknown EVP_PKEY save_type %d", pk->save_type);
464        }
465        fclose(fp);
466        if (pk != NULL)
467                EVP_PKEY_free(pk);
468        if (prv != NULL && commentp)
469                *commentp = xstrdup(name);
470        debug("read PEM private key done: type %s",
471            prv ? key_type(prv) : "<unknown>");
472        return prv;
473}
474
475static int
476key_perm_ok(int fd, const char *filename)
477{
478        struct stat st;
479
480        if (fstat(fd, &st) < 0)
481                return 0;
482        /*
483         * if a key owned by the user is accessed, then we check the
484         * permissions of the file. if the key owned by a different user,
485         * then we don't care.
486         */
487#ifdef HAVE_CYGWIN
488        if (check_ntsec(filename))
489#endif
490        if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) {
491                error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
492                error("@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @");
493                error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
494                error("Permissions 0%3.3o for '%s' are too open.",
495                    st.st_mode & 0777, filename);
496                error("It is recommended that your private key files are NOT accessible by others.");
497                error("This private key will be ignored.");
498                return 0;
499        }
500        return 1;
501}
502
503Key *
504key_load_private_type(int type, const char *filename, const char *passphrase,
505    char **commentp)
506{
507        int fd;
508
509        fd = open(filename, O_RDONLY);
510        if (fd < 0)
511                return NULL;
512        if (!key_perm_ok(fd, filename)) {
513                error("bad permissions: ignore key: %s", filename);
514                close(fd);
515                return NULL;
516        }
517        switch (type) {
518        case KEY_RSA1:
519                return key_load_private_rsa1(fd, filename, passphrase,
520                    commentp);
521                /* closes fd */
522                break;
523        case KEY_DSA:
524        case KEY_RSA:
525        case KEY_UNSPEC:
526                return key_load_private_pem(fd, type, passphrase, commentp);
527                /* closes fd */
528                break;
529        default:
530                close(fd);
531                break;
532        }
533        return NULL;
534}
535
536Key *
537key_load_private(const char *filename, const char *passphrase,
538    char **commentp)
539{
540        Key *pub, *prv;
541        int fd;
542
543        fd = open(filename, O_RDONLY);
544        if (fd < 0)
545                return NULL;
546        if (!key_perm_ok(fd, filename)) {
547                error("bad permissions: ignore key: %s", filename);
548                close(fd);
549                return NULL;
550        }
551        pub = key_load_public_rsa1(fd, filename, commentp);
552        lseek(fd, (off_t) 0, SEEK_SET);         /* rewind */
553        if (pub == NULL) {
554                /* closes fd */
555                prv = key_load_private_pem(fd, KEY_UNSPEC, passphrase, NULL);
556                /* use the filename as a comment for PEM */
557                if (commentp && prv)
558                        *commentp = xstrdup(filename);
559        } else {
560                /* it's a SSH v1 key if the public key part is readable */
561                key_free(pub);
562                /* closes fd */
563                prv = key_load_private_rsa1(fd, filename, passphrase, NULL);
564        }
565        return prv;
566}
567
568static int
569key_try_load_public(Key *k, const char *filename, char **commentp)
570{
571        FILE *f;
572        char line[4096];
573        char *cp;
574
575        f = fopen(filename, "r");
576        if (f != NULL) {
577                while (fgets(line, sizeof(line), f)) {
578                        line[sizeof(line)-1] = '\0';
579                        cp = line;
580                        switch (*cp) {
581                        case '#':
582                        case '\n':
583                        case '\0':
584                                continue;
585                        }
586                        /* Skip leading whitespace. */
587                        for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
588                                ;
589                        if (*cp) {
590                                if (key_read(k, &cp) == 1) {
591                                        if (commentp)
592                                                *commentp=xstrdup(filename);
593                                        fclose(f);
594                                        return 1;
595                                }
596                        }
597                }
598                fclose(f);
599        }
600        return 0;
601}
602
603/* load public key from ssh v1 private or any pubkey file */
604Key *
605key_load_public(const char *filename, char **commentp)
606{
607        Key *pub;
608        char file[MAXPATHLEN];
609
610        pub = key_load_public_type(KEY_RSA1, filename, commentp);
611        if (pub != NULL)
612                return pub;
613        pub = key_new(KEY_UNSPEC);
614        if (key_try_load_public(pub, filename, commentp) == 1)
615                return pub;
616        if ((strlcpy(file, filename, sizeof file) < sizeof(file)) &&
617            (strlcat(file, ".pub", sizeof file) < sizeof(file)) &&
618            (key_try_load_public(pub, file, commentp) == 1))
619                return pub;
620        key_free(pub);
621        return NULL;
622}
Note: See TracBrowser for help on using the repository browser.