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

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