blob: f75c273fcefe5e04fc6c56c373d89ce89774375a [file] [log] [blame]
Damien Millera2327922010-12-01 12:01:21 +11001/* $OpenBSD: authfile.c,v 1.86 2010/11/21 10:57:07 djm Exp $ */
Damien Millerd4a8b7e1999-10-27 13:42:43 +10002/*
Damien Miller95def091999-11-25 00:26:21 +11003 * Author: Tatu Ylonen <ylo@cs.hut.fi>
Damien Miller95def091999-11-25 00:26:21 +11004 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5 * All rights reserved
Damien Miller95def091999-11-25 00:26:21 +11006 * This file contains functions for reading and writing identity files, and
7 * for reading the passphrase from the user.
Damien Miller4af51302000-04-16 11:18:38 +10008 *
Damien Millere4340be2000-09-16 13:29:08 +11009 * 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 * Copyright (c) 2000 Markus Friedl. All rights reserved.
17 *
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions
20 * are met:
21 * 1. Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
23 * 2. Redistributions in binary form must reproduce the above copyright
24 * notice, this list of conditions and the following disclaimer in the
25 * documentation and/or other materials provided with the distribution.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
28 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
29 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
30 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
32 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
36 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Damien Miller95def091999-11-25 00:26:21 +110037 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100038
39#include "includes.h"
Damien Millerf17883e2006-03-15 11:45:54 +110040
41#include <sys/types.h>
42#include <sys/stat.h>
Damien Miller8dbffe72006-08-05 11:02:17 +100043#include <sys/param.h>
Damien Millerd7834352006-08-05 12:39:39 +100044#include <sys/uio.h>
Damien Millerd4a8b7e1999-10-27 13:42:43 +100045
Damien Miller0bc1bd82000-11-13 22:57:25 +110046#include <openssl/err.h>
Damien Millereba71ba2000-04-29 23:57:08 +100047#include <openssl/evp.h>
Ben Lindstrom226cfa02001-01-22 05:34:40 +000048#include <openssl/pem.h>
Damien Millereba71ba2000-04-29 23:57:08 +100049
Darren Tuckere89ed1c2009-11-05 20:43:16 +110050/* compatibility with old or broken OpenSSL versions */
51#include "openbsd-compat/openssl-compat.h"
52
Darren Tuckerba724052006-07-12 22:24:22 +100053#include <errno.h>
Damien Miller57cf6382006-07-10 21:13:46 +100054#include <fcntl.h>
Damien Millerded319c2006-09-01 15:38:36 +100055#include <stdarg.h>
Damien Millera7a73ee2006-08-05 11:37:59 +100056#include <stdio.h>
Damien Millere7a1e5c2006-08-05 11:34:19 +100057#include <stdlib.h>
Damien Millere3476ed2006-07-24 14:13:33 +100058#include <string.h>
Damien Millere6b3b612006-07-24 14:01:23 +100059#include <unistd.h>
Damien Miller57cf6382006-07-10 21:13:46 +100060
Damien Millerd4a8b7e1999-10-27 13:42:43 +100061#include "xmalloc.h"
Damien Millerd7834352006-08-05 12:39:39 +100062#include "cipher.h"
Damien Millerd4a8b7e1999-10-27 13:42:43 +100063#include "buffer.h"
Damien Millereba71ba2000-04-29 23:57:08 +100064#include "key.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000065#include "ssh.h"
66#include "log.h"
Ben Lindstrom31ca54a2001-02-09 02:11:24 +000067#include "authfile.h"
Damien Miller040b64f2002-01-22 23:10:04 +110068#include "rsa.h"
Darren Tuckerf0f90982004-12-11 13:39:50 +110069#include "misc.h"
Damien Millereccb9de2005-06-17 12:59:34 +100070#include "atomicio.h"
Damien Millerd4a8b7e1999-10-27 13:42:43 +100071
Ben Lindstromd0fca422001-03-26 13:44:06 +000072/* Version identification string for SSH v1 identity files. */
Ben Lindstrom1170d712001-01-29 07:51:26 +000073static const char authfile_id_string[] =
74 "SSH PRIVATE KEY FILE FORMAT 1.1\n";
Damien Millerd4a8b7e1999-10-27 13:42:43 +100075
Damien Miller5428f641999-11-25 11:54:57 +110076/*
Damien Millera2327922010-12-01 12:01:21 +110077 * Serialises the authentication (private) key to a blob, encrypting it with
78 * passphrase. The identification of the blob (lowest 64 bits of n) will
Damien Miller5428f641999-11-25 11:54:57 +110079 * precede the key to provide identification of the key without needing a
80 * passphrase.
81 */
Ben Lindstrombba81212001-06-25 05:01:22 +000082static int
Damien Millera2327922010-12-01 12:01:21 +110083key_private_rsa1_to_blob(Key *key, Buffer *blob, const char *passphrase,
Ben Lindstromd0fca422001-03-26 13:44:06 +000084 const char *comment)
Damien Millerd4a8b7e1999-10-27 13:42:43 +100085{
Damien Miller95def091999-11-25 00:26:21 +110086 Buffer buffer, encrypted;
Damien Miller708d21c2002-01-22 23:18:15 +110087 u_char buf[100], *cp;
Damien Millera2327922010-12-01 12:01:21 +110088 int i, cipher_num;
Damien Miller874d77b2000-10-14 16:23:11 +110089 CipherContext ciphercontext;
90 Cipher *cipher;
Darren Tucker3f9fdc72004-06-22 12:56:01 +100091 u_int32_t rnd;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100092
Damien Miller5428f641999-11-25 11:54:57 +110093 /*
94 * If the passphrase is empty, use SSH_CIPHER_NONE to ease converting
95 * to another cipher; otherwise use SSH_AUTHFILE_CIPHER.
96 */
Damien Miller963f6b22002-02-19 15:21:23 +110097 cipher_num = (strcmp(passphrase, "") == 0) ?
98 SSH_CIPHER_NONE : SSH_AUTHFILE_CIPHER;
99 if ((cipher = cipher_by_number(cipher_num)) == NULL)
Damien Miller874d77b2000-10-14 16:23:11 +1100100 fatal("save_private_key_rsa: bad cipher");
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000101
Damien Miller95def091999-11-25 00:26:21 +1100102 /* This buffer is used to built the secret part of the private key. */
103 buffer_init(&buffer);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000104
Damien Miller95def091999-11-25 00:26:21 +1100105 /* Put checkbytes for checking passphrase validity. */
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000106 rnd = arc4random();
107 buf[0] = rnd & 0xff;
108 buf[1] = (rnd >> 8) & 0xff;
Damien Miller95def091999-11-25 00:26:21 +1100109 buf[2] = buf[0];
110 buf[3] = buf[1];
111 buffer_append(&buffer, buf, 4);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000112
Damien Miller5428f641999-11-25 11:54:57 +1100113 /*
114 * Store the private key (n and e will not be stored because they
115 * will be stored in plain text, and storing them also in encrypted
116 * format would just give known plaintext).
117 */
Ben Lindstromd0fca422001-03-26 13:44:06 +0000118 buffer_put_bignum(&buffer, key->rsa->d);
119 buffer_put_bignum(&buffer, key->rsa->iqmp);
120 buffer_put_bignum(&buffer, key->rsa->q); /* reverse from SSL p */
121 buffer_put_bignum(&buffer, key->rsa->p); /* reverse from SSL q */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000122
Damien Miller95def091999-11-25 00:26:21 +1100123 /* Pad the part to be encrypted until its size is a multiple of 8. */
124 while (buffer_len(&buffer) % 8 != 0)
125 buffer_put_char(&buffer, 0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000126
Damien Miller95def091999-11-25 00:26:21 +1100127 /* This buffer will be used to contain the data in the file. */
128 buffer_init(&encrypted);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000129
Damien Miller95def091999-11-25 00:26:21 +1100130 /* First store keyfile id string. */
Ben Lindstrom1170d712001-01-29 07:51:26 +0000131 for (i = 0; authfile_id_string[i]; i++)
132 buffer_put_char(&encrypted, authfile_id_string[i]);
Damien Miller95def091999-11-25 00:26:21 +1100133 buffer_put_char(&encrypted, 0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000134
Damien Miller95def091999-11-25 00:26:21 +1100135 /* Store cipher type. */
Damien Miller963f6b22002-02-19 15:21:23 +1100136 buffer_put_char(&encrypted, cipher_num);
Damien Miller95def091999-11-25 00:26:21 +1100137 buffer_put_int(&encrypted, 0); /* For future extension */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000138
Damien Miller95def091999-11-25 00:26:21 +1100139 /* Store public key. This will be in plain text. */
Ben Lindstromd0fca422001-03-26 13:44:06 +0000140 buffer_put_int(&encrypted, BN_num_bits(key->rsa->n));
141 buffer_put_bignum(&encrypted, key->rsa->n);
142 buffer_put_bignum(&encrypted, key->rsa->e);
Ben Lindstrom664408d2001-06-09 01:42:01 +0000143 buffer_put_cstring(&encrypted, comment);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000144
Damien Miller95def091999-11-25 00:26:21 +1100145 /* Allocate space for the private part of the key in the buffer. */
Damien Miller5a6b4fe2001-12-21 14:56:54 +1100146 cp = buffer_append_space(&encrypted, buffer_len(&buffer));
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000147
Damien Miller963f6b22002-02-19 15:21:23 +1100148 cipher_set_key_string(&ciphercontext, cipher, passphrase,
149 CIPHER_ENCRYPT);
150 cipher_crypt(&ciphercontext, cp,
Damien Miller708d21c2002-01-22 23:18:15 +1100151 buffer_ptr(&buffer), buffer_len(&buffer));
Damien Miller963f6b22002-02-19 15:21:23 +1100152 cipher_cleanup(&ciphercontext);
Damien Miller874d77b2000-10-14 16:23:11 +1100153 memset(&ciphercontext, 0, sizeof(ciphercontext));
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000154
Damien Miller95def091999-11-25 00:26:21 +1100155 /* Destroy temporary data. */
156 memset(buf, 0, sizeof(buf));
157 buffer_free(&buffer);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000158
Damien Millera2327922010-12-01 12:01:21 +1100159 buffer_append(blob, buffer_ptr(&encrypted), buffer_len(&encrypted));
Damien Miller95def091999-11-25 00:26:21 +1100160 buffer_free(&encrypted);
Damien Millera2327922010-12-01 12:01:21 +1100161
Damien Miller95def091999-11-25 00:26:21 +1100162 return 1;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000163}
164
Damien Millera2327922010-12-01 12:01:21 +1100165/* convert SSH v2 key in OpenSSL PEM format */
Ben Lindstrombba81212001-06-25 05:01:22 +0000166static int
Damien Millera2327922010-12-01 12:01:21 +1100167key_private_pem_to_blob(Key *key, Buffer *blob, const char *_passphrase,
Ben Lindstromd0fca422001-03-26 13:44:06 +0000168 const char *comment)
Damien Millereba71ba2000-04-29 23:57:08 +1000169{
Damien Miller0bc1bd82000-11-13 22:57:25 +1100170 int success = 0;
Damien Millera2327922010-12-01 12:01:21 +1100171 int blen, len = strlen(_passphrase);
Ben Lindstrom90fd8142002-02-26 18:09:42 +0000172 u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL;
Darren Tuckerdf6578b2009-11-07 16:03:14 +1100173#if (OPENSSL_VERSION_NUMBER < 0x00907000L)
174 const EVP_CIPHER *cipher = (len > 0) ? EVP_des_ede3_cbc() : NULL;
175#else
Darren Tuckerdfb9b712009-10-24 11:46:43 +1100176 const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL;
Darren Tuckerdf6578b2009-11-07 16:03:14 +1100177#endif
Damien Millera2327922010-12-01 12:01:21 +1100178 const u_char *bptr;
179 BIO *bio;
Damien Millereba71ba2000-04-29 23:57:08 +1000180
181 if (len > 0 && len <= 4) {
Ben Lindstrom15f33862001-04-16 02:00:02 +0000182 error("passphrase too short: have %d bytes, need > 4", len);
Damien Millereba71ba2000-04-29 23:57:08 +1000183 return 0;
184 }
Damien Millera2327922010-12-01 12:01:21 +1100185 if ((bio = BIO_new(BIO_s_mem())) == NULL) {
186 error("%s: BIO_new failed", __func__);
Damien Millereba71ba2000-04-29 23:57:08 +1000187 return 0;
188 }
Damien Miller0bc1bd82000-11-13 22:57:25 +1100189 switch (key->type) {
Ben Lindstromc1116602001-03-29 00:28:37 +0000190 case KEY_DSA:
Damien Millera2327922010-12-01 12:01:21 +1100191 success = PEM_write_bio_DSAPrivateKey(bio, key->dsa,
Ben Lindstromc1116602001-03-29 00:28:37 +0000192 cipher, passphrase, len, NULL, NULL);
193 break;
Damien Miller6af914a2010-09-10 11:39:26 +1000194#ifdef OPENSSL_HAS_ECC
Damien Millereb8b60e2010-08-31 22:41:14 +1000195 case KEY_ECDSA:
Damien Millera2327922010-12-01 12:01:21 +1100196 success = PEM_write_bio_ECPrivateKey(bio, key->ecdsa,
Damien Millereb8b60e2010-08-31 22:41:14 +1000197 cipher, passphrase, len, NULL, NULL);
198 break;
Damien Miller6af914a2010-09-10 11:39:26 +1000199#endif
Ben Lindstromc1116602001-03-29 00:28:37 +0000200 case KEY_RSA:
Damien Millera2327922010-12-01 12:01:21 +1100201 success = PEM_write_bio_RSAPrivateKey(bio, key->rsa,
Ben Lindstromc1116602001-03-29 00:28:37 +0000202 cipher, passphrase, len, NULL, NULL);
203 break;
Damien Millereba71ba2000-04-29 23:57:08 +1000204 }
Damien Millera2327922010-12-01 12:01:21 +1100205 if (success) {
206 if ((blen = BIO_get_mem_data(bio, &bptr)) <= 0)
207 success = 0;
208 else
209 buffer_append(blob, bptr, blen);
210 }
211 BIO_free(bio);
Damien Millereba71ba2000-04-29 23:57:08 +1000212 return success;
213}
214
Damien Millera2327922010-12-01 12:01:21 +1100215/* Save a key blob to a file */
216static int
217key_save_private_blob(Buffer *keybuf, const char *filename)
218{
219 int fd;
220
221 if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) {
222 error("open %s failed: %s.", filename, strerror(errno));
223 return 0;
224 }
225 if (atomicio(vwrite, fd, buffer_ptr(keybuf),
226 buffer_len(keybuf)) != buffer_len(keybuf)) {
227 error("write to key file %s failed: %s", filename,
228 strerror(errno));
229 close(fd);
230 unlink(filename);
231 return 0;
232 }
233 close(fd);
234 return 1;
235}
236
237/* Serialise "key" to buffer "blob" */
238static int
239key_private_to_blob(Key *key, Buffer *blob, const char *passphrase,
240 const char *comment)
241{
242 switch (key->type) {
243 case KEY_RSA1:
244 return key_private_rsa1_to_blob(key, blob, passphrase, comment);
245 case KEY_DSA:
246 case KEY_ECDSA:
247 case KEY_RSA:
248 return key_private_pem_to_blob(key, blob, passphrase, comment);
249 default:
250 error("%s: cannot save key type %d", __func__, key->type);
251 return 0;
252 }
253}
254
Damien Millereba71ba2000-04-29 23:57:08 +1000255int
Ben Lindstromd0fca422001-03-26 13:44:06 +0000256key_save_private(Key *key, const char *filename, const char *passphrase,
Damien Millereba71ba2000-04-29 23:57:08 +1000257 const char *comment)
258{
Damien Millera2327922010-12-01 12:01:21 +1100259 Buffer keyblob;
260 int success = 0;
261
262 buffer_init(&keyblob);
263 if (!key_private_to_blob(key, &keyblob, passphrase, comment))
264 goto out;
265 if (!key_save_private_blob(&keyblob, filename))
266 goto out;
267 success = 1;
268 out:
269 buffer_free(&keyblob);
270 return success;
271}
272
273/*
274 * Parse the public, unencrypted portion of a RSA1 key.
275 */
276static Key *
277key_parse_public_rsa1(Buffer *blob, char **commentp)
278{
279 Key *pub;
280
281 /* Check that it is at least big enough to contain the ID string. */
282 if (buffer_len(blob) < sizeof(authfile_id_string)) {
283 debug3("Truncated RSA1 identifier");
284 return NULL;
Damien Millereba71ba2000-04-29 23:57:08 +1000285 }
Damien Millera2327922010-12-01 12:01:21 +1100286
287 /*
288 * Make sure it begins with the id string. Consume the id string
289 * from the buffer.
290 */
291 if (memcmp(buffer_ptr(blob), authfile_id_string,
292 sizeof(authfile_id_string)) != 0) {
293 debug3("Incorrect RSA1 identifier");
294 return NULL;
295 }
296 buffer_consume(blob, sizeof(authfile_id_string));
297
298 /* Skip cipher type and reserved data. */
299 (void) buffer_get_char(blob); /* cipher type */
300 (void) buffer_get_int(blob); /* reserved */
301
302 /* Read the public key from the buffer. */
303 (void) buffer_get_int(blob);
304 pub = key_new(KEY_RSA1);
305 buffer_get_bignum(blob, pub->rsa->n);
306 buffer_get_bignum(blob, pub->rsa->e);
307 if (commentp)
308 *commentp = buffer_get_string(blob, NULL);
309 /* The encrypted private part is not parsed by this function. */
310 buffer_clear(blob);
311
312 return pub;
313}
314
315/* Load the contents of a key file into a buffer */
316static int
317key_load_file(int fd, const char *filename, Buffer *blob)
318{
319 size_t len;
320 u_char *cp;
321 struct stat st;
322
323 if (fstat(fd, &st) < 0) {
324 error("%s: fstat of key file %.200s%sfailed: %.100s", __func__,
325 filename == NULL ? "" : filename,
326 filename == NULL ? "" : " ",
327 strerror(errno));
328 close(fd);
329 return 0;
330 }
331 if (st.st_size > 1*1024*1024) {
332 error("%s: key file %.200s%stoo large", __func__,
333 filename == NULL ? "" : filename,
334 filename == NULL ? "" : " ");
335 close(fd);
336 return 0;
337 }
338 len = (size_t)st.st_size; /* truncated */
339
340 buffer_init(blob);
341 cp = buffer_append_space(blob, len);
342
343 if (atomicio(read, fd, cp, len) != len) {
344 debug("%s: read from key file %.200s%sfailed: %.100s", __func__,
345 filename == NULL ? "" : filename,
346 filename == NULL ? "" : " ",
347 strerror(errno));
348 buffer_clear(blob);
349 close(fd);
350 return 0;
351 }
352 return 1;
Damien Millereba71ba2000-04-29 23:57:08 +1000353}
354
Damien Miller5428f641999-11-25 11:54:57 +1100355/*
Ben Lindstromd0fca422001-03-26 13:44:06 +0000356 * Loads the public part of the ssh v1 key file. Returns NULL if an error was
357 * encountered (the file does not exist or is not readable), and the key
Damien Miller5428f641999-11-25 11:54:57 +1100358 * otherwise.
359 */
Ben Lindstrombba81212001-06-25 05:01:22 +0000360static Key *
Ben Lindstromd0fca422001-03-26 13:44:06 +0000361key_load_public_rsa1(int fd, const char *filename, char **commentp)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000362{
Damien Miller95def091999-11-25 00:26:21 +1100363 Buffer buffer;
Ben Lindstromd0fca422001-03-26 13:44:06 +0000364 Key *pub;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000365
Damien Miller95def091999-11-25 00:26:21 +1100366 buffer_init(&buffer);
Damien Millera2327922010-12-01 12:01:21 +1100367 if (!key_load_file(fd, filename, &buffer)) {
Damien Miller95def091999-11-25 00:26:21 +1100368 buffer_free(&buffer);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000369 return NULL;
Damien Miller95def091999-11-25 00:26:21 +1100370 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000371
Damien Millera2327922010-12-01 12:01:21 +1100372 pub = key_parse_public_rsa1(&buffer, commentp);
373 if (pub == NULL)
374 debug3("Could not load \"%s\" as a RSA1 public key", filename);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000375 buffer_free(&buffer);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000376 return pub;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000377}
378
Ben Lindstromd0fca422001-03-26 13:44:06 +0000379/* load public key from private-key file, works only for SSH v1 */
380Key *
381key_load_public_type(int type, const char *filename, char **commentp)
Damien Millereba71ba2000-04-29 23:57:08 +1000382{
Ben Lindstromd0fca422001-03-26 13:44:06 +0000383 Key *pub;
384 int fd;
385
386 if (type == KEY_RSA1) {
387 fd = open(filename, O_RDONLY);
388 if (fd < 0)
389 return NULL;
390 pub = key_load_public_rsa1(fd, filename, commentp);
391 close(fd);
392 return pub;
Damien Millereba71ba2000-04-29 23:57:08 +1000393 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000394 return NULL;
Damien Millereba71ba2000-04-29 23:57:08 +1000395}
396
Ben Lindstrombba81212001-06-25 05:01:22 +0000397static Key *
Damien Millera2327922010-12-01 12:01:21 +1100398key_parse_private_rsa1(Buffer *blob, const char *passphrase, char **commentp)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000399{
Damien Millereccb9de2005-06-17 12:59:34 +1000400 int check1, check2, cipher_type;
Damien Millera2327922010-12-01 12:01:21 +1100401 Buffer decrypted;
Damien Miller708d21c2002-01-22 23:18:15 +1100402 u_char *cp;
Damien Miller874d77b2000-10-14 16:23:11 +1100403 CipherContext ciphercontext;
404 Cipher *cipher;
Ben Lindstromd0fca422001-03-26 13:44:06 +0000405 Key *prv = NULL;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000406
Ben Lindstrom1170d712001-01-29 07:51:26 +0000407 /* Check that it is at least big enough to contain the ID string. */
Damien Millera2327922010-12-01 12:01:21 +1100408 if (buffer_len(blob) < sizeof(authfile_id_string)) {
409 debug3("Truncated RSA1 identifier");
Ben Lindstromd0fca422001-03-26 13:44:06 +0000410 return NULL;
Damien Miller95def091999-11-25 00:26:21 +1100411 }
Damien Millera2327922010-12-01 12:01:21 +1100412
Damien Miller5428f641999-11-25 11:54:57 +1100413 /*
414 * Make sure it begins with the id string. Consume the id string
415 * from the buffer.
416 */
Damien Millera2327922010-12-01 12:01:21 +1100417 if (memcmp(buffer_ptr(blob), authfile_id_string,
418 sizeof(authfile_id_string)) != 0) {
419 debug3("Incorrect RSA1 identifier");
420 return NULL;
421 }
422 buffer_consume(blob, sizeof(authfile_id_string));
Ben Lindstromb257cca2001-03-05 04:59:27 +0000423
Damien Miller95def091999-11-25 00:26:21 +1100424 /* Read cipher type. */
Damien Millera2327922010-12-01 12:01:21 +1100425 cipher_type = buffer_get_char(blob);
426 (void) buffer_get_int(blob); /* Reserved data. */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000427
Damien Miller95def091999-11-25 00:26:21 +1100428 /* Read the public key from the buffer. */
Damien Millera2327922010-12-01 12:01:21 +1100429 (void) buffer_get_int(blob);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000430 prv = key_new_private(KEY_RSA1);
431
Damien Millera2327922010-12-01 12:01:21 +1100432 buffer_get_bignum(blob, prv->rsa->n);
433 buffer_get_bignum(blob, prv->rsa->e);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000434 if (commentp)
Damien Millera2327922010-12-01 12:01:21 +1100435 *commentp = buffer_get_string(blob, NULL);
Damien Miller95def091999-11-25 00:26:21 +1100436 else
Damien Millera2327922010-12-01 12:01:21 +1100437 (void)buffer_get_string_ptr(blob, NULL);
Damien Miller95def091999-11-25 00:26:21 +1100438
439 /* Check that it is a supported cipher. */
Damien Miller874d77b2000-10-14 16:23:11 +1100440 cipher = cipher_by_number(cipher_type);
441 if (cipher == NULL) {
Damien Millera2327922010-12-01 12:01:21 +1100442 debug("Unsupported RSA1 cipher %d", cipher_type);
Damien Miller95def091999-11-25 00:26:21 +1100443 goto fail;
444 }
445 /* Initialize space for decrypted data. */
446 buffer_init(&decrypted);
Damien Millera2327922010-12-01 12:01:21 +1100447 cp = buffer_append_space(&decrypted, buffer_len(blob));
Damien Miller95def091999-11-25 00:26:21 +1100448
449 /* Rest of the buffer is encrypted. Decrypt it using the passphrase. */
Damien Miller963f6b22002-02-19 15:21:23 +1100450 cipher_set_key_string(&ciphercontext, cipher, passphrase,
451 CIPHER_DECRYPT);
452 cipher_crypt(&ciphercontext, cp,
Damien Millera2327922010-12-01 12:01:21 +1100453 buffer_ptr(blob), buffer_len(blob));
Damien Miller963f6b22002-02-19 15:21:23 +1100454 cipher_cleanup(&ciphercontext);
Damien Miller874d77b2000-10-14 16:23:11 +1100455 memset(&ciphercontext, 0, sizeof(ciphercontext));
Damien Millera2327922010-12-01 12:01:21 +1100456 buffer_clear(blob);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000457
Damien Miller95def091999-11-25 00:26:21 +1100458 check1 = buffer_get_char(&decrypted);
459 check2 = buffer_get_char(&decrypted);
460 if (check1 != buffer_get_char(&decrypted) ||
461 check2 != buffer_get_char(&decrypted)) {
462 if (strcmp(passphrase, "") != 0)
Damien Millera2327922010-12-01 12:01:21 +1100463 debug("Bad passphrase supplied for RSA1 key");
Damien Miller95def091999-11-25 00:26:21 +1100464 /* Bad passphrase. */
465 buffer_free(&decrypted);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000466 goto fail;
Damien Miller95def091999-11-25 00:26:21 +1100467 }
468 /* Read the rest of the private key. */
Ben Lindstromd0fca422001-03-26 13:44:06 +0000469 buffer_get_bignum(&decrypted, prv->rsa->d);
470 buffer_get_bignum(&decrypted, prv->rsa->iqmp); /* u */
471 /* in SSL and SSH v1 p and q are exchanged */
472 buffer_get_bignum(&decrypted, prv->rsa->q); /* p */
473 buffer_get_bignum(&decrypted, prv->rsa->p); /* q */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000474
Ben Lindstromd0fca422001-03-26 13:44:06 +0000475 /* calculate p-1 and q-1 */
Damien Millerda755162002-01-22 23:09:22 +1100476 rsa_generate_additional_parameters(prv->rsa);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000477
Damien Miller95def091999-11-25 00:26:21 +1100478 buffer_free(&decrypted);
Damien Millered33d3b2003-03-15 11:36:18 +1100479
480 /* enable blinding */
481 if (RSA_blinding_on(prv->rsa, NULL) != 1) {
Damien Millera2327922010-12-01 12:01:21 +1100482 error("%s: RSA_blinding_on failed", __func__);
Damien Millered33d3b2003-03-15 11:36:18 +1100483 goto fail;
484 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000485 return prv;
486
487fail:
488 if (commentp)
489 xfree(*commentp);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000490 key_free(prv);
491 return NULL;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000492}
Damien Millereba71ba2000-04-29 23:57:08 +1000493
Damien Millera2327922010-12-01 12:01:21 +1100494static Key *
495key_parse_private_pem(Buffer *blob, int type, const char *passphrase,
Ben Lindstromd0fca422001-03-26 13:44:06 +0000496 char **commentp)
Damien Millereba71ba2000-04-29 23:57:08 +1000497{
Damien Miller0bc1bd82000-11-13 22:57:25 +1100498 EVP_PKEY *pk = NULL;
Ben Lindstromd0fca422001-03-26 13:44:06 +0000499 Key *prv = NULL;
Damien Miller0bc1bd82000-11-13 22:57:25 +1100500 char *name = "<no key>";
Damien Millera2327922010-12-01 12:01:21 +1100501 BIO *bio;
Damien Millereba71ba2000-04-29 23:57:08 +1000502
Damien Millera2327922010-12-01 12:01:21 +1100503 if ((bio = BIO_new_mem_buf(buffer_ptr(blob),
504 buffer_len(blob))) == NULL) {
505 error("%s: BIO_new_mem_buf failed", __func__);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000506 return NULL;
Damien Millereba71ba2000-04-29 23:57:08 +1000507 }
Damien Millera2327922010-12-01 12:01:21 +1100508
509 pk = PEM_read_bio_PrivateKey(bio, NULL, NULL, (char *)passphrase);
510 BIO_free(bio);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100511 if (pk == NULL) {
Damien Millera2327922010-12-01 12:01:21 +1100512 debug("%s: PEM_read_PrivateKey failed", __func__);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100513 (void)ERR_get_error();
Ben Lindstromd0fca422001-03-26 13:44:06 +0000514 } else if (pk->type == EVP_PKEY_RSA &&
Damien Miller9f0f5c62001-12-21 14:45:46 +1100515 (type == KEY_UNSPEC||type==KEY_RSA)) {
Ben Lindstromd0fca422001-03-26 13:44:06 +0000516 prv = key_new(KEY_UNSPEC);
517 prv->rsa = EVP_PKEY_get1_RSA(pk);
518 prv->type = KEY_RSA;
519 name = "rsa w/o comment";
Damien Miller0bc1bd82000-11-13 22:57:25 +1100520#ifdef DEBUG_PK
Ben Lindstromd0fca422001-03-26 13:44:06 +0000521 RSA_print_fp(stderr, prv->rsa, 8);
Damien Millereba71ba2000-04-29 23:57:08 +1000522#endif
Damien Millered33d3b2003-03-15 11:36:18 +1100523 if (RSA_blinding_on(prv->rsa, NULL) != 1) {
Damien Millera2327922010-12-01 12:01:21 +1100524 error("%s: RSA_blinding_on failed", __func__);
Damien Millered33d3b2003-03-15 11:36:18 +1100525 key_free(prv);
526 prv = NULL;
527 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000528 } else if (pk->type == EVP_PKEY_DSA &&
Damien Miller9f0f5c62001-12-21 14:45:46 +1100529 (type == KEY_UNSPEC||type==KEY_DSA)) {
Ben Lindstromd0fca422001-03-26 13:44:06 +0000530 prv = key_new(KEY_UNSPEC);
531 prv->dsa = EVP_PKEY_get1_DSA(pk);
532 prv->type = KEY_DSA;
533 name = "dsa w/o comment";
Damien Miller0bc1bd82000-11-13 22:57:25 +1100534#ifdef DEBUG_PK
Ben Lindstromd0fca422001-03-26 13:44:06 +0000535 DSA_print_fp(stderr, prv->dsa, 8);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100536#endif
Damien Miller6af914a2010-09-10 11:39:26 +1000537#ifdef OPENSSL_HAS_ECC
Damien Millereb8b60e2010-08-31 22:41:14 +1000538 } else if (pk->type == EVP_PKEY_EC &&
539 (type == KEY_UNSPEC||type==KEY_ECDSA)) {
540 prv = key_new(KEY_UNSPEC);
541 prv->ecdsa = EVP_PKEY_get1_EC_KEY(pk);
542 prv->type = KEY_ECDSA;
Damien Millerb472a902010-11-05 10:19:49 +1100543 if ((prv->ecdsa_nid = key_ecdsa_key_to_nid(prv->ecdsa)) == -1 ||
544 key_curve_nid_to_name(prv->ecdsa_nid) == NULL ||
545 key_ec_validate_public(EC_KEY_get0_group(prv->ecdsa),
Damien Millereb8b60e2010-08-31 22:41:14 +1000546 EC_KEY_get0_public_key(prv->ecdsa)) != 0 ||
547 key_ec_validate_private(prv->ecdsa) != 0) {
548 error("%s: bad ECDSA key", __func__);
549 key_free(prv);
550 prv = NULL;
551 }
Damien Millerbf0423e2010-09-10 11:20:38 +1000552 name = "ecdsa w/o comment";
Damien Millereb8b60e2010-08-31 22:41:14 +1000553#ifdef DEBUG_PK
Damien Millerb472a902010-11-05 10:19:49 +1100554 if (prv != NULL && prv->ecdsa != NULL)
Damien Millereb8b60e2010-08-31 22:41:14 +1000555 key_dump_ec_key(prv->ecdsa);
556#endif
Damien Miller6af914a2010-09-10 11:39:26 +1000557#endif /* OPENSSL_HAS_ECC */
Damien Miller0bc1bd82000-11-13 22:57:25 +1100558 } else {
Damien Millera2327922010-12-01 12:01:21 +1100559 error("%s: PEM_read_PrivateKey: mismatch or "
560 "unknown EVP_PKEY save_type %d", __func__, pk->save_type);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100561 }
Damien Miller0bc1bd82000-11-13 22:57:25 +1100562 if (pk != NULL)
563 EVP_PKEY_free(pk);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000564 if (prv != NULL && commentp)
565 *commentp = xstrdup(name);
566 debug("read PEM private key done: type %s",
567 prv ? key_type(prv) : "<unknown>");
568 return prv;
Damien Millereba71ba2000-04-29 23:57:08 +1000569}
570
Damien Millera2327922010-12-01 12:01:21 +1100571Key *
572key_load_private_pem(int fd, int type, const char *passphrase,
573 char **commentp)
574{
575 Buffer buffer;
576 Key *prv;
577
578 buffer_init(&buffer);
579 if (!key_load_file(fd, NULL, &buffer)) {
580 buffer_free(&buffer);
581 return NULL;
582 }
583 prv = key_parse_private_pem(&buffer, type, passphrase, commentp);
584 buffer_free(&buffer);
585 return prv;
586}
587
Damien Miller8275fad2006-03-15 12:06:23 +1100588int
Ben Lindstromd0fca422001-03-26 13:44:06 +0000589key_perm_ok(int fd, const char *filename)
Damien Millereba71ba2000-04-29 23:57:08 +1000590{
Damien Millereba71ba2000-04-29 23:57:08 +1000591 struct stat st;
592
Ben Lindstrom7aff2612001-09-23 13:53:22 +0000593 if (fstat(fd, &st) < 0)
594 return 0;
595 /*
596 * if a key owned by the user is accessed, then we check the
597 * permissions of the file. if the key owned by a different user,
598 * then we don't care.
599 */
Damien Millerb70b61f2000-09-16 16:25:12 +1100600#ifdef HAVE_CYGWIN
Damien Millercb5e44a2000-09-29 12:12:36 +1100601 if (check_ntsec(filename))
Damien Millerb70b61f2000-09-16 16:25:12 +1100602#endif
Ben Lindstrom7aff2612001-09-23 13:53:22 +0000603 if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) {
Damien Millereba71ba2000-04-29 23:57:08 +1000604 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
605 error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @");
606 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
Ben Lindstrom7aff2612001-09-23 13:53:22 +0000607 error("Permissions 0%3.3o for '%s' are too open.",
Damien Miller04bd8b02003-05-25 14:38:33 +1000608 (u_int)st.st_mode & 0777, filename);
Damien Millereba71ba2000-04-29 23:57:08 +1000609 error("It is recommended that your private key files are NOT accessible by others.");
Ben Lindstromd0fca422001-03-26 13:44:06 +0000610 error("This private key will be ignored.");
Damien Millereba71ba2000-04-29 23:57:08 +1000611 return 0;
612 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000613 return 1;
614}
Ben Lindstromb257cca2001-03-05 04:59:27 +0000615
Damien Millera2327922010-12-01 12:01:21 +1100616static Key *
617key_parse_private_type(Buffer *blob, int type, const char *passphrase,
618 char **commentp)
619{
620 switch (type) {
621 case KEY_RSA1:
622 return key_parse_private_rsa1(blob, passphrase, commentp);
623 case KEY_DSA:
624 case KEY_ECDSA:
625 case KEY_RSA:
626 case KEY_UNSPEC:
627 return key_parse_private_pem(blob, type, passphrase, commentp);
628 default:
629 break;
630 }
631 return NULL;
632}
633
Ben Lindstromd0fca422001-03-26 13:44:06 +0000634Key *
635key_load_private_type(int type, const char *filename, const char *passphrase,
Darren Tucker232b76f2006-05-06 17:41:51 +1000636 char **commentp, int *perm_ok)
Ben Lindstromd0fca422001-03-26 13:44:06 +0000637{
638 int fd;
Damien Millera2327922010-12-01 12:01:21 +1100639 Key *ret;
640 Buffer buffer;
Ben Lindstromd0fca422001-03-26 13:44:06 +0000641
642 fd = open(filename, O_RDONLY);
Darren Tuckerd4c86b12010-01-12 19:41:22 +1100643 if (fd < 0) {
644 debug("could not open key file '%s': %s", filename,
645 strerror(errno));
646 if (perm_ok != NULL)
647 *perm_ok = 0;
Ben Lindstromd0fca422001-03-26 13:44:06 +0000648 return NULL;
Darren Tucker69c01b12010-01-12 19:42:29 +1100649 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000650 if (!key_perm_ok(fd, filename)) {
Darren Tucker232b76f2006-05-06 17:41:51 +1000651 if (perm_ok != NULL)
652 *perm_ok = 0;
Ben Lindstrom15f33862001-04-16 02:00:02 +0000653 error("bad permissions: ignore key: %s", filename);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000654 close(fd);
655 return NULL;
656 }
Darren Tucker232b76f2006-05-06 17:41:51 +1000657 if (perm_ok != NULL)
658 *perm_ok = 1;
Damien Millera2327922010-12-01 12:01:21 +1100659
660 buffer_init(&buffer);
661 if (!key_load_file(fd, filename, &buffer)) {
662 buffer_free(&buffer);
Ben Lindstromb257cca2001-03-05 04:59:27 +0000663 close(fd);
Damien Millera2327922010-12-01 12:01:21 +1100664 return NULL;
Damien Millereba71ba2000-04-29 23:57:08 +1000665 }
Damien Millera2327922010-12-01 12:01:21 +1100666 close(fd);
667 ret = key_parse_private_type(&buffer, type, passphrase, commentp);
668 buffer_free(&buffer);
669 return ret;
Ben Lindstromd0fca422001-03-26 13:44:06 +0000670}
671
672Key *
673key_load_private(const char *filename, const char *passphrase,
674 char **commentp)
675{
Ben Lindstrom322915d2001-06-05 20:46:32 +0000676 Key *pub, *prv;
Damien Millera2327922010-12-01 12:01:21 +1100677 Buffer buffer, pubcopy;
Ben Lindstromd0fca422001-03-26 13:44:06 +0000678 int fd;
679
680 fd = open(filename, O_RDONLY);
Darren Tuckerd4c86b12010-01-12 19:41:22 +1100681 if (fd < 0) {
682 debug("could not open key file '%s': %s", filename,
683 strerror(errno));
Ben Lindstromd0fca422001-03-26 13:44:06 +0000684 return NULL;
Darren Tuckerd4c86b12010-01-12 19:41:22 +1100685 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000686 if (!key_perm_ok(fd, filename)) {
Ben Lindstrom15f33862001-04-16 02:00:02 +0000687 error("bad permissions: ignore key: %s", filename);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000688 close(fd);
689 return NULL;
690 }
Damien Millera2327922010-12-01 12:01:21 +1100691
692 buffer_init(&buffer);
693 if (!key_load_file(fd, filename, &buffer)) {
694 buffer_free(&buffer);
695 close(fd);
696 return NULL;
697 }
698 close(fd);
699
700 buffer_init(&pubcopy);
701 buffer_append(&pubcopy, buffer_ptr(&buffer), buffer_len(&buffer));
702 /* it's a SSH v1 key if the public key part is readable */
703 pub = key_parse_public_rsa1(&pubcopy, commentp);
704 buffer_free(&pubcopy);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000705 if (pub == NULL) {
Damien Millera2327922010-12-01 12:01:21 +1100706 prv = key_parse_private_type(&buffer, KEY_UNSPEC,
707 passphrase, NULL);
Ben Lindstrom322915d2001-06-05 20:46:32 +0000708 /* use the filename as a comment for PEM */
709 if (commentp && prv)
Ben Lindstrom2d0356f2001-06-05 21:13:57 +0000710 *commentp = xstrdup(filename);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000711 } else {
Ben Lindstromd0fca422001-03-26 13:44:06 +0000712 key_free(pub);
Damien Millera2327922010-12-01 12:01:21 +1100713 prv = key_parse_private_type(&buffer, KEY_RSA1, passphrase,
714 commentp);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000715 }
Damien Millera2327922010-12-01 12:01:21 +1100716 buffer_free(&buffer);
Ben Lindstrom322915d2001-06-05 20:46:32 +0000717 return prv;
Damien Millereba71ba2000-04-29 23:57:08 +1000718}
Damien Millere4340be2000-09-16 13:29:08 +1100719
Ben Lindstrombba81212001-06-25 05:01:22 +0000720static int
Ben Lindstromd0fca422001-03-26 13:44:06 +0000721key_try_load_public(Key *k, const char *filename, char **commentp)
Damien Millere4340be2000-09-16 13:29:08 +1100722{
723 FILE *f;
Darren Tucker22cc7412004-12-06 22:47:41 +1100724 char line[SSH_MAX_PUBKEY_BYTES];
Damien Millere4340be2000-09-16 13:29:08 +1100725 char *cp;
Darren Tuckerf0f90982004-12-11 13:39:50 +1100726 u_long linenum = 0;
Damien Millere4340be2000-09-16 13:29:08 +1100727
728 f = fopen(filename, "r");
729 if (f != NULL) {
Darren Tucker22cc7412004-12-06 22:47:41 +1100730 while (read_keyfile_line(f, filename, line, sizeof(line),
731 &linenum) != -1) {
Damien Millere4340be2000-09-16 13:29:08 +1100732 cp = line;
Ben Lindstrom1c37c6a2001-12-06 18:00:18 +0000733 switch (*cp) {
Damien Millere4340be2000-09-16 13:29:08 +1100734 case '#':
735 case '\n':
736 case '\0':
737 continue;
738 }
739 /* Skip leading whitespace. */
740 for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
741 ;
742 if (*cp) {
Damien Miller0bc1bd82000-11-13 22:57:25 +1100743 if (key_read(k, &cp) == 1) {
Damien Millere4340be2000-09-16 13:29:08 +1100744 if (commentp)
745 *commentp=xstrdup(filename);
746 fclose(f);
747 return 1;
748 }
749 }
750 }
751 fclose(f);
752 }
753 return 0;
754}
755
Ben Lindstromd0fca422001-03-26 13:44:06 +0000756/* load public key from ssh v1 private or any pubkey file */
757Key *
758key_load_public(const char *filename, char **commentp)
Damien Millere4340be2000-09-16 13:29:08 +1100759{
Ben Lindstromd0fca422001-03-26 13:44:06 +0000760 Key *pub;
761 char file[MAXPATHLEN];
Damien Millere4340be2000-09-16 13:29:08 +1100762
Damien Millerdb274722003-05-14 13:45:22 +1000763 /* try rsa1 private key */
Ben Lindstromd0fca422001-03-26 13:44:06 +0000764 pub = key_load_public_type(KEY_RSA1, filename, commentp);
765 if (pub != NULL)
766 return pub;
Damien Millerdb274722003-05-14 13:45:22 +1000767
768 /* try rsa1 public key */
769 pub = key_new(KEY_RSA1);
770 if (key_try_load_public(pub, filename, commentp) == 1)
771 return pub;
772 key_free(pub);
773
774 /* try ssh2 public key */
Ben Lindstromd0fca422001-03-26 13:44:06 +0000775 pub = key_new(KEY_UNSPEC);
776 if (key_try_load_public(pub, filename, commentp) == 1)
777 return pub;
778 if ((strlcpy(file, filename, sizeof file) < sizeof(file)) &&
779 (strlcat(file, ".pub", sizeof file) < sizeof(file)) &&
780 (key_try_load_public(pub, file, commentp) == 1))
781 return pub;
782 key_free(pub);
783 return NULL;
Damien Millere4340be2000-09-16 13:29:08 +1100784}
Damien Miller1aed65e2010-03-04 21:53:35 +1100785
Damien Millerc1583312010-08-05 13:04:50 +1000786/* Load the certificate associated with the named private key */
787Key *
788key_load_cert(const char *filename)
789{
790 Key *pub;
Damien Miller5458c4d2010-08-05 13:05:15 +1000791 char *file;
Damien Millerc1583312010-08-05 13:04:50 +1000792
793 pub = key_new(KEY_UNSPEC);
Damien Miller5458c4d2010-08-05 13:05:15 +1000794 xasprintf(&file, "%s-cert.pub", filename);
795 if (key_try_load_public(pub, file, NULL) == 1) {
796 xfree(file);
Damien Millerc1583312010-08-05 13:04:50 +1000797 return pub;
Damien Miller5458c4d2010-08-05 13:05:15 +1000798 }
799 xfree(file);
Damien Millerc1583312010-08-05 13:04:50 +1000800 key_free(pub);
801 return NULL;
802}
803
804/* Load private key and certificate */
805Key *
806key_load_private_cert(int type, const char *filename, const char *passphrase,
807 int *perm_ok)
808{
809 Key *key, *pub;
810
811 switch (type) {
812 case KEY_RSA:
813 case KEY_DSA:
Damien Millereb8b60e2010-08-31 22:41:14 +1000814 case KEY_ECDSA:
Damien Millerc1583312010-08-05 13:04:50 +1000815 break;
816 default:
817 error("%s: unsupported key type", __func__);
818 return NULL;
819 }
820
821 if ((key = key_load_private_type(type, filename,
822 passphrase, NULL, perm_ok)) == NULL)
823 return NULL;
824
825 if ((pub = key_load_cert(filename)) == NULL) {
826 key_free(key);
827 return NULL;
828 }
829
830 /* Make sure the private key matches the certificate */
831 if (key_equal_public(key, pub) == 0) {
832 error("%s: certificate does not match private key %s",
833 __func__, filename);
834 } else if (key_to_certified(key, key_cert_is_legacy(pub)) != 0) {
835 error("%s: key_to_certified failed", __func__);
836 } else {
837 key_cert_copy(pub, key);
838 key_free(pub);
839 return key;
840 }
841
842 key_free(key);
843 key_free(pub);
844 return NULL;
845}
846
Damien Miller1aed65e2010-03-04 21:53:35 +1100847/*
848 * Returns 1 if the specified "key" is listed in the file "filename",
849 * 0 if the key is not listed or -1 on error.
850 * If strict_type is set then the key type must match exactly,
851 * otherwise a comparison that ignores certficiate data is performed.
852 */
853int
854key_in_file(Key *key, const char *filename, int strict_type)
855{
856 FILE *f;
857 char line[SSH_MAX_PUBKEY_BYTES];
858 char *cp;
859 u_long linenum = 0;
860 int ret = 0;
861 Key *pub;
862 int (*key_compare)(const Key *, const Key *) = strict_type ?
863 key_equal : key_equal_public;
864
865 if ((f = fopen(filename, "r")) == NULL) {
866 if (errno == ENOENT) {
867 debug("%s: keyfile \"%s\" missing", __func__, filename);
868 return 0;
869 } else {
870 error("%s: could not open keyfile \"%s\": %s", __func__,
871 filename, strerror(errno));
872 return -1;
873 }
874 }
875
876 while (read_keyfile_line(f, filename, line, sizeof(line),
877 &linenum) != -1) {
878 cp = line;
879
880 /* Skip leading whitespace. */
881 for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
882 ;
883
884 /* Skip comments and empty lines */
885 switch (*cp) {
886 case '#':
887 case '\n':
888 case '\0':
889 continue;
890 }
891
892 pub = key_new(KEY_UNSPEC);
893 if (key_read(pub, &cp) != 1) {
894 key_free(pub);
895 continue;
896 }
897 if (key_compare(key, pub)) {
898 ret = 1;
899 key_free(pub);
900 break;
901 }
902 key_free(pub);
903 }
904 fclose(f);
905 return ret;
906}
907