1.13.1
by Colin Watson
Import upstream version 4.6p1 |
1 |
/* $OpenBSD: authfile.c,v 1.76 2006/08/03 03:34:41 deraadt Exp $ */
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
2 |
/*
|
3 |
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
|
4 |
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
|
5 |
* All rights reserved
|
|
6 |
* This file contains functions for reading and writing identity files, and
|
|
7 |
* for reading the passphrase from the user.
|
|
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 |
* 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.
|
|
37 |
*/
|
|
38 |
||
39 |
#include "includes.h" |
|
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
40 |
|
41 |
#include <sys/types.h> |
|
42 |
#include <sys/stat.h> |
|
43 |
#include <sys/param.h> |
|
44 |
#include <sys/uio.h> |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
45 |
|
46 |
#include <openssl/err.h> |
|
47 |
#include <openssl/evp.h> |
|
48 |
#include <openssl/pem.h> |
|
49 |
||
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
50 |
#include <errno.h> |
51 |
#include <fcntl.h> |
|
52 |
#include <stdarg.h> |
|
53 |
#include <stdio.h> |
|
54 |
#include <stdlib.h> |
|
55 |
#include <string.h> |
|
56 |
#include <unistd.h> |
|
57 |
||
58 |
#include "xmalloc.h" |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
59 |
#include "cipher.h" |
60 |
#include "buffer.h" |
|
61 |
#include "key.h" |
|
62 |
#include "ssh.h" |
|
63 |
#include "log.h" |
|
64 |
#include "authfile.h" |
|
65 |
#include "rsa.h" |
|
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
66 |
#include "misc.h" |
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
67 |
#include "atomicio.h" |
35
by Colin Watson
* Resynchronise with Debian. Remaining changes: |
68 |
#include "pathnames.h" |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
69 |
|
70 |
/* Version identification string for SSH v1 identity files. */
|
|
71 |
static const char authfile_id_string[] = |
|
72 |
"SSH PRIVATE KEY FILE FORMAT 1.1\n"; |
|
73 |
||
74 |
/*
|
|
75 |
* Saves the authentication (private) key in a file, encrypting it with
|
|
76 |
* passphrase. The identification of the file (lowest 64 bits of n) will
|
|
77 |
* precede the key to provide identification of the key without needing a
|
|
78 |
* passphrase.
|
|
79 |
*/
|
|
80 |
||
81 |
static int |
|
82 |
key_save_private_rsa1(Key *key, const char *filename, const char *passphrase, |
|
83 |
const char *comment) |
|
84 |
{
|
|
85 |
Buffer buffer, encrypted; |
|
86 |
u_char buf[100], *cp; |
|
87 |
int fd, i, cipher_num; |
|
88 |
CipherContext ciphercontext; |
|
89 |
Cipher *cipher; |
|
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
90 |
u_int32_t rnd; |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
91 |
|
92 |
/*
|
|
93 |
* If the passphrase is empty, use SSH_CIPHER_NONE to ease converting
|
|
94 |
* to another cipher; otherwise use SSH_AUTHFILE_CIPHER.
|
|
95 |
*/
|
|
96 |
cipher_num = (strcmp(passphrase, "") == 0) ? |
|
97 |
SSH_CIPHER_NONE : SSH_AUTHFILE_CIPHER; |
|
98 |
if ((cipher = cipher_by_number(cipher_num)) == NULL) |
|
99 |
fatal("save_private_key_rsa: bad cipher"); |
|
100 |
||
101 |
/* This buffer is used to built the secret part of the private key. */
|
|
102 |
buffer_init(&buffer); |
|
103 |
||
104 |
/* Put checkbytes for checking passphrase validity. */
|
|
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
105 |
rnd = arc4random(); |
106 |
buf[0] = rnd & 0xff; |
|
107 |
buf[1] = (rnd >> 8) & 0xff; |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
108 |
buf[2] = buf[0]; |
109 |
buf[3] = buf[1]; |
|
110 |
buffer_append(&buffer, buf, 4); |
|
111 |
||
112 |
/*
|
|
113 |
* Store the private key (n and e will not be stored because they
|
|
114 |
* will be stored in plain text, and storing them also in encrypted
|
|
115 |
* format would just give known plaintext).
|
|
116 |
*/
|
|
117 |
buffer_put_bignum(&buffer, key->rsa->d); |
|
118 |
buffer_put_bignum(&buffer, key->rsa->iqmp); |
|
119 |
buffer_put_bignum(&buffer, key->rsa->q); /* reverse from SSL p */ |
|
120 |
buffer_put_bignum(&buffer, key->rsa->p); /* reverse from SSL q */ |
|
121 |
||
122 |
/* Pad the part to be encrypted until its size is a multiple of 8. */
|
|
123 |
while (buffer_len(&buffer) % 8 != 0) |
|
124 |
buffer_put_char(&buffer, 0); |
|
125 |
||
126 |
/* This buffer will be used to contain the data in the file. */
|
|
127 |
buffer_init(&encrypted); |
|
128 |
||
129 |
/* First store keyfile id string. */
|
|
130 |
for (i = 0; authfile_id_string[i]; i++) |
|
131 |
buffer_put_char(&encrypted, authfile_id_string[i]); |
|
132 |
buffer_put_char(&encrypted, 0); |
|
133 |
||
134 |
/* Store cipher type. */
|
|
135 |
buffer_put_char(&encrypted, cipher_num); |
|
136 |
buffer_put_int(&encrypted, 0); /* For future extension */ |
|
137 |
||
138 |
/* Store public key. This will be in plain text. */
|
|
139 |
buffer_put_int(&encrypted, BN_num_bits(key->rsa->n)); |
|
140 |
buffer_put_bignum(&encrypted, key->rsa->n); |
|
141 |
buffer_put_bignum(&encrypted, key->rsa->e); |
|
142 |
buffer_put_cstring(&encrypted, comment); |
|
143 |
||
144 |
/* Allocate space for the private part of the key in the buffer. */
|
|
145 |
cp = buffer_append_space(&encrypted, buffer_len(&buffer)); |
|
146 |
||
147 |
cipher_set_key_string(&ciphercontext, cipher, passphrase, |
|
148 |
CIPHER_ENCRYPT); |
|
149 |
cipher_crypt(&ciphercontext, cp, |
|
150 |
buffer_ptr(&buffer), buffer_len(&buffer)); |
|
151 |
cipher_cleanup(&ciphercontext); |
|
152 |
memset(&ciphercontext, 0, sizeof(ciphercontext)); |
|
153 |
||
154 |
/* Destroy temporary data. */
|
|
155 |
memset(buf, 0, sizeof(buf)); |
|
156 |
buffer_free(&buffer); |
|
157 |
||
158 |
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); |
|
159 |
if (fd < 0) { |
|
160 |
error("open %s failed: %s.", filename, strerror(errno)); |
|
161 |
buffer_free(&encrypted); |
|
162 |
return 0; |
|
163 |
}
|
|
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
164 |
if (atomicio(vwrite, fd, buffer_ptr(&encrypted), |
165 |
buffer_len(&encrypted)) != buffer_len(&encrypted)) { |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
166 |
error("write to key file %s failed: %s", filename, |
167 |
strerror(errno)); |
|
168 |
buffer_free(&encrypted); |
|
169 |
close(fd); |
|
170 |
unlink(filename); |
|
171 |
return 0; |
|
172 |
}
|
|
173 |
close(fd); |
|
174 |
buffer_free(&encrypted); |
|
175 |
return 1; |
|
176 |
}
|
|
177 |
||
178 |
/* save SSH v2 key in OpenSSL PEM format */
|
|
179 |
static int |
|
180 |
key_save_private_pem(Key *key, const char *filename, const char *_passphrase, |
|
181 |
const char *comment) |
|
182 |
{
|
|
183 |
FILE *fp; |
|
184 |
int fd; |
|
185 |
int success = 0; |
|
186 |
int len = strlen(_passphrase); |
|
187 |
u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL; |
|
188 |
const EVP_CIPHER *cipher = (len > 0) ? EVP_des_ede3_cbc() : NULL; |
|
189 |
||
190 |
if (len > 0 && len <= 4) { |
|
191 |
error("passphrase too short: have %d bytes, need > 4", len); |
|
192 |
return 0; |
|
193 |
}
|
|
194 |
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); |
|
195 |
if (fd < 0) { |
|
196 |
error("open %s failed: %s.", filename, strerror(errno)); |
|
197 |
return 0; |
|
198 |
}
|
|
199 |
fp = fdopen(fd, "w"); |
|
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
200 |
if (fp == NULL) { |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
201 |
error("fdopen %s failed: %s.", filename, strerror(errno)); |
202 |
close(fd); |
|
203 |
return 0; |
|
204 |
}
|
|
205 |
switch (key->type) { |
|
206 |
case KEY_DSA: |
|
207 |
success = PEM_write_DSAPrivateKey(fp, key->dsa, |
|
208 |
cipher, passphrase, len, NULL, NULL); |
|
209 |
break; |
|
210 |
case KEY_RSA: |
|
211 |
success = PEM_write_RSAPrivateKey(fp, key->rsa, |
|
212 |
cipher, passphrase, len, NULL, NULL); |
|
213 |
break; |
|
214 |
}
|
|
215 |
fclose(fp); |
|
216 |
return success; |
|
217 |
}
|
|
218 |
||
219 |
int
|
|
220 |
key_save_private(Key *key, const char *filename, const char *passphrase, |
|
221 |
const char *comment) |
|
222 |
{
|
|
223 |
switch (key->type) { |
|
224 |
case KEY_RSA1: |
|
225 |
return key_save_private_rsa1(key, filename, passphrase, |
|
226 |
comment); |
|
227 |
case KEY_DSA: |
|
228 |
case KEY_RSA: |
|
229 |
return key_save_private_pem(key, filename, passphrase, |
|
230 |
comment); |
|
231 |
default: |
|
232 |
break; |
|
233 |
}
|
|
234 |
error("key_save_private: cannot save key type %d", key->type); |
|
235 |
return 0; |
|
236 |
}
|
|
237 |
||
238 |
/*
|
|
239 |
* Loads the public part of the ssh v1 key file. Returns NULL if an error was
|
|
240 |
* encountered (the file does not exist or is not readable), and the key
|
|
241 |
* otherwise.
|
|
242 |
*/
|
|
243 |
||
244 |
static Key * |
|
245 |
key_load_public_rsa1(int fd, const char *filename, char **commentp) |
|
246 |
{
|
|
247 |
Buffer buffer; |
|
248 |
Key *pub; |
|
249 |
struct stat st; |
|
250 |
char *cp; |
|
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
251 |
u_int i; |
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
252 |
size_t len; |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
253 |
|
254 |
if (fstat(fd, &st) < 0) { |
|
255 |
error("fstat for key file %.200s failed: %.100s", |
|
256 |
filename, strerror(errno)); |
|
257 |
return NULL; |
|
258 |
}
|
|
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
259 |
if (st.st_size > 1*1024*1024) { |
260 |
error("key file %.200s too large", filename); |
|
261 |
return NULL; |
|
262 |
}
|
|
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
263 |
len = (size_t)st.st_size; /* truncated */ |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
264 |
|
265 |
buffer_init(&buffer); |
|
266 |
cp = buffer_append_space(&buffer, len); |
|
267 |
||
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
268 |
if (atomicio(read, fd, cp, len) != len) { |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
269 |
debug("Read from key file %.200s failed: %.100s", filename, |
270 |
strerror(errno)); |
|
271 |
buffer_free(&buffer); |
|
272 |
return NULL; |
|
273 |
}
|
|
274 |
||
275 |
/* Check that it is at least big enough to contain the ID string. */
|
|
276 |
if (len < sizeof(authfile_id_string)) { |
|
277 |
debug3("Not a RSA1 key file %.200s.", filename); |
|
278 |
buffer_free(&buffer); |
|
279 |
return NULL; |
|
280 |
}
|
|
281 |
/*
|
|
282 |
* Make sure it begins with the id string. Consume the id string
|
|
283 |
* from the buffer.
|
|
284 |
*/
|
|
285 |
for (i = 0; i < sizeof(authfile_id_string); i++) |
|
286 |
if (buffer_get_char(&buffer) != authfile_id_string[i]) { |
|
287 |
debug3("Not a RSA1 key file %.200s.", filename); |
|
288 |
buffer_free(&buffer); |
|
289 |
return NULL; |
|
290 |
}
|
|
291 |
/* Skip cipher type and reserved data. */
|
|
292 |
(void) buffer_get_char(&buffer); /* cipher type */ |
|
293 |
(void) buffer_get_int(&buffer); /* reserved */ |
|
294 |
||
295 |
/* Read the public key from the buffer. */
|
|
296 |
(void) buffer_get_int(&buffer); |
|
297 |
pub = key_new(KEY_RSA1); |
|
298 |
buffer_get_bignum(&buffer, pub->rsa->n); |
|
299 |
buffer_get_bignum(&buffer, pub->rsa->e); |
|
300 |
if (commentp) |
|
301 |
*commentp = buffer_get_string(&buffer, NULL); |
|
302 |
/* The encrypted private part is not parsed by this function. */
|
|
303 |
||
304 |
buffer_free(&buffer); |
|
305 |
return pub; |
|
306 |
}
|
|
307 |
||
308 |
/* load public key from private-key file, works only for SSH v1 */
|
|
309 |
Key * |
|
310 |
key_load_public_type(int type, const char *filename, char **commentp) |
|
311 |
{
|
|
312 |
Key *pub; |
|
313 |
int fd; |
|
314 |
||
315 |
if (type == KEY_RSA1) { |
|
316 |
fd = open(filename, O_RDONLY); |
|
317 |
if (fd < 0) |
|
318 |
return NULL; |
|
319 |
pub = key_load_public_rsa1(fd, filename, commentp); |
|
320 |
close(fd); |
|
321 |
return pub; |
|
322 |
}
|
|
323 |
return NULL; |
|
324 |
}
|
|
325 |
||
326 |
/*
|
|
327 |
* Loads the private key from the file. Returns 0 if an error is encountered
|
|
328 |
* (file does not exist or is not readable, or passphrase is bad). This
|
|
329 |
* initializes the private key.
|
|
330 |
* Assumes we are called under uid of the owner of the file.
|
|
331 |
*/
|
|
332 |
||
333 |
static Key * |
|
334 |
key_load_private_rsa1(int fd, const char *filename, const char *passphrase, |
|
335 |
char **commentp) |
|
336 |
{
|
|
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
337 |
u_int i; |
338 |
int check1, check2, cipher_type; |
|
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
339 |
size_t len; |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
340 |
Buffer buffer, decrypted; |
341 |
u_char *cp; |
|
342 |
CipherContext ciphercontext; |
|
343 |
Cipher *cipher; |
|
344 |
Key *prv = NULL; |
|
345 |
struct stat st; |
|
346 |
||
347 |
if (fstat(fd, &st) < 0) { |
|
348 |
error("fstat for key file %.200s failed: %.100s", |
|
349 |
filename, strerror(errno)); |
|
350 |
close(fd); |
|
351 |
return NULL; |
|
352 |
}
|
|
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
353 |
if (st.st_size > 1*1024*1024) { |
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
354 |
error("key file %.200s too large", filename); |
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
355 |
close(fd); |
356 |
return (NULL); |
|
357 |
}
|
|
358 |
len = (size_t)st.st_size; /* truncated */ |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
359 |
|
360 |
buffer_init(&buffer); |
|
361 |
cp = buffer_append_space(&buffer, len); |
|
362 |
||
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
363 |
if (atomicio(read, fd, cp, len) != len) { |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
364 |
debug("Read from key file %.200s failed: %.100s", filename, |
365 |
strerror(errno)); |
|
366 |
buffer_free(&buffer); |
|
367 |
close(fd); |
|
368 |
return NULL; |
|
369 |
}
|
|
370 |
||
371 |
/* Check that it is at least big enough to contain the ID string. */
|
|
372 |
if (len < sizeof(authfile_id_string)) { |
|
373 |
debug3("Not a RSA1 key file %.200s.", filename); |
|
374 |
buffer_free(&buffer); |
|
375 |
close(fd); |
|
376 |
return NULL; |
|
377 |
}
|
|
378 |
/*
|
|
379 |
* Make sure it begins with the id string. Consume the id string
|
|
380 |
* from the buffer.
|
|
381 |
*/
|
|
382 |
for (i = 0; i < sizeof(authfile_id_string); i++) |
|
383 |
if (buffer_get_char(&buffer) != authfile_id_string[i]) { |
|
384 |
debug3("Not a RSA1 key file %.200s.", filename); |
|
385 |
buffer_free(&buffer); |
|
386 |
close(fd); |
|
387 |
return NULL; |
|
388 |
}
|
|
389 |
||
390 |
/* Read cipher type. */
|
|
391 |
cipher_type = buffer_get_char(&buffer); |
|
392 |
(void) buffer_get_int(&buffer); /* Reserved data. */ |
|
393 |
||
394 |
/* Read the public key from the buffer. */
|
|
395 |
(void) buffer_get_int(&buffer); |
|
396 |
prv = key_new_private(KEY_RSA1); |
|
397 |
||
398 |
buffer_get_bignum(&buffer, prv->rsa->n); |
|
399 |
buffer_get_bignum(&buffer, prv->rsa->e); |
|
400 |
if (commentp) |
|
401 |
*commentp = buffer_get_string(&buffer, NULL); |
|
402 |
else
|
|
403 |
xfree(buffer_get_string(&buffer, NULL)); |
|
404 |
||
405 |
/* Check that it is a supported cipher. */
|
|
406 |
cipher = cipher_by_number(cipher_type); |
|
407 |
if (cipher == NULL) { |
|
408 |
debug("Unsupported cipher %d used in key file %.200s.", |
|
409 |
cipher_type, filename); |
|
410 |
buffer_free(&buffer); |
|
411 |
goto fail; |
|
412 |
}
|
|
413 |
/* Initialize space for decrypted data. */
|
|
414 |
buffer_init(&decrypted); |
|
415 |
cp = buffer_append_space(&decrypted, buffer_len(&buffer)); |
|
416 |
||
417 |
/* Rest of the buffer is encrypted. Decrypt it using the passphrase. */
|
|
418 |
cipher_set_key_string(&ciphercontext, cipher, passphrase, |
|
419 |
CIPHER_DECRYPT); |
|
420 |
cipher_crypt(&ciphercontext, cp, |
|
421 |
buffer_ptr(&buffer), buffer_len(&buffer)); |
|
422 |
cipher_cleanup(&ciphercontext); |
|
423 |
memset(&ciphercontext, 0, sizeof(ciphercontext)); |
|
424 |
buffer_free(&buffer); |
|
425 |
||
426 |
check1 = buffer_get_char(&decrypted); |
|
427 |
check2 = buffer_get_char(&decrypted); |
|
428 |
if (check1 != buffer_get_char(&decrypted) || |
|
429 |
check2 != buffer_get_char(&decrypted)) { |
|
430 |
if (strcmp(passphrase, "") != 0) |
|
431 |
debug("Bad passphrase supplied for key file %.200s.", |
|
432 |
filename); |
|
433 |
/* Bad passphrase. */
|
|
434 |
buffer_free(&decrypted); |
|
435 |
goto fail; |
|
436 |
}
|
|
437 |
/* Read the rest of the private key. */
|
|
438 |
buffer_get_bignum(&decrypted, prv->rsa->d); |
|
439 |
buffer_get_bignum(&decrypted, prv->rsa->iqmp); /* u */ |
|
440 |
/* in SSL and SSH v1 p and q are exchanged */
|
|
441 |
buffer_get_bignum(&decrypted, prv->rsa->q); /* p */ |
|
442 |
buffer_get_bignum(&decrypted, prv->rsa->p); /* q */ |
|
443 |
||
444 |
/* calculate p-1 and q-1 */
|
|
445 |
rsa_generate_additional_parameters(prv->rsa); |
|
446 |
||
447 |
buffer_free(&decrypted); |
|
448 |
||
449 |
/* enable blinding */
|
|
450 |
if (RSA_blinding_on(prv->rsa, NULL) != 1) { |
|
451 |
error("key_load_private_rsa1: RSA_blinding_on failed"); |
|
452 |
goto fail; |
|
453 |
}
|
|
454 |
close(fd); |
|
455 |
return prv; |
|
456 |
||
457 |
fail: |
|
458 |
if (commentp) |
|
459 |
xfree(*commentp); |
|
460 |
close(fd); |
|
461 |
key_free(prv); |
|
462 |
return NULL; |
|
463 |
}
|
|
464 |
||
465 |
Key * |
|
466 |
key_load_private_pem(int fd, int type, const char *passphrase, |
|
467 |
char **commentp) |
|
468 |
{
|
|
469 |
FILE *fp; |
|
470 |
EVP_PKEY *pk = NULL; |
|
471 |
Key *prv = NULL; |
|
472 |
char *name = "<no key>"; |
|
473 |
||
474 |
fp = fdopen(fd, "r"); |
|
475 |
if (fp == NULL) { |
|
476 |
error("fdopen failed: %s", strerror(errno)); |
|
477 |
close(fd); |
|
478 |
return NULL; |
|
479 |
}
|
|
480 |
pk = PEM_read_PrivateKey(fp, NULL, NULL, (char *)passphrase); |
|
481 |
if (pk == NULL) { |
|
482 |
debug("PEM_read_PrivateKey failed"); |
|
483 |
(void)ERR_get_error(); |
|
484 |
} else if (pk->type == EVP_PKEY_RSA && |
|
485 |
(type == KEY_UNSPEC||type==KEY_RSA)) { |
|
486 |
prv = key_new(KEY_UNSPEC); |
|
487 |
prv->rsa = EVP_PKEY_get1_RSA(pk); |
|
488 |
prv->type = KEY_RSA; |
|
489 |
name = "rsa w/o comment"; |
|
490 |
#ifdef DEBUG_PK
|
|
491 |
RSA_print_fp(stderr, prv->rsa, 8); |
|
492 |
#endif
|
|
493 |
if (RSA_blinding_on(prv->rsa, NULL) != 1) { |
|
494 |
error("key_load_private_pem: RSA_blinding_on failed"); |
|
495 |
key_free(prv); |
|
496 |
prv = NULL; |
|
497 |
}
|
|
498 |
} else if (pk->type == EVP_PKEY_DSA && |
|
499 |
(type == KEY_UNSPEC||type==KEY_DSA)) { |
|
500 |
prv = key_new(KEY_UNSPEC); |
|
501 |
prv->dsa = EVP_PKEY_get1_DSA(pk); |
|
502 |
prv->type = KEY_DSA; |
|
503 |
name = "dsa w/o comment"; |
|
504 |
#ifdef DEBUG_PK
|
|
505 |
DSA_print_fp(stderr, prv->dsa, 8); |
|
506 |
#endif
|
|
507 |
} else { |
|
508 |
error("PEM_read_PrivateKey: mismatch or " |
|
509 |
"unknown EVP_PKEY save_type %d", pk->save_type); |
|
510 |
}
|
|
511 |
fclose(fp); |
|
512 |
if (pk != NULL) |
|
513 |
EVP_PKEY_free(pk); |
|
514 |
if (prv != NULL && commentp) |
|
515 |
*commentp = xstrdup(name); |
|
516 |
debug("read PEM private key done: type %s", |
|
517 |
prv ? key_type(prv) : "<unknown>"); |
|
518 |
return prv; |
|
519 |
}
|
|
520 |
||
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
521 |
int
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
522 |
key_perm_ok(int fd, const char *filename) |
523 |
{
|
|
524 |
struct stat st; |
|
525 |
||
526 |
if (fstat(fd, &st) < 0) |
|
527 |
return 0; |
|
528 |
/*
|
|
529 |
* if a key owned by the user is accessed, then we check the
|
|
530 |
* permissions of the file. if the key owned by a different user,
|
|
531 |
* then we don't care.
|
|
532 |
*/
|
|
533 |
#ifdef HAVE_CYGWIN
|
|
534 |
if (check_ntsec(filename)) |
|
535 |
#endif
|
|
536 |
if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) { |
|
537 |
error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); |
|
538 |
error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); |
|
539 |
error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); |
|
540 |
error("Permissions 0%3.3o for '%s' are too open.", |
|
541 |
(u_int)st.st_mode & 0777, filename); |
|
542 |
error("It is recommended that your private key files are NOT accessible by others."); |
|
543 |
error("This private key will be ignored."); |
|
544 |
return 0; |
|
545 |
}
|
|
546 |
return 1; |
|
547 |
}
|
|
548 |
||
549 |
Key * |
|
550 |
key_load_private_type(int type, const char *filename, const char *passphrase, |
|
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
551 |
char **commentp, int *perm_ok) |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
552 |
{
|
553 |
int fd; |
|
554 |
||
555 |
fd = open(filename, O_RDONLY); |
|
556 |
if (fd < 0) |
|
557 |
return NULL; |
|
558 |
if (!key_perm_ok(fd, filename)) { |
|
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
559 |
if (perm_ok != NULL) |
560 |
*perm_ok = 0; |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
561 |
error("bad permissions: ignore key: %s", filename); |
562 |
close(fd); |
|
563 |
return NULL; |
|
564 |
}
|
|
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
565 |
if (perm_ok != NULL) |
566 |
*perm_ok = 1; |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
567 |
switch (type) { |
568 |
case KEY_RSA1: |
|
569 |
return key_load_private_rsa1(fd, filename, passphrase, |
|
570 |
commentp); |
|
571 |
/* closes fd */
|
|
572 |
case KEY_DSA: |
|
573 |
case KEY_RSA: |
|
574 |
case KEY_UNSPEC: |
|
575 |
return key_load_private_pem(fd, type, passphrase, commentp); |
|
576 |
/* closes fd */
|
|
577 |
default: |
|
578 |
close(fd); |
|
579 |
break; |
|
580 |
}
|
|
581 |
return NULL; |
|
582 |
}
|
|
583 |
||
584 |
Key * |
|
585 |
key_load_private(const char *filename, const char *passphrase, |
|
586 |
char **commentp) |
|
587 |
{
|
|
588 |
Key *pub, *prv; |
|
589 |
int fd; |
|
590 |
||
591 |
fd = open(filename, O_RDONLY); |
|
592 |
if (fd < 0) |
|
593 |
return NULL; |
|
594 |
if (!key_perm_ok(fd, filename)) { |
|
595 |
error("bad permissions: ignore key: %s", filename); |
|
596 |
close(fd); |
|
597 |
return NULL; |
|
598 |
}
|
|
599 |
pub = key_load_public_rsa1(fd, filename, commentp); |
|
600 |
lseek(fd, (off_t) 0, SEEK_SET); /* rewind */ |
|
601 |
if (pub == NULL) { |
|
602 |
/* closes fd */
|
|
603 |
prv = key_load_private_pem(fd, KEY_UNSPEC, passphrase, NULL); |
|
604 |
/* use the filename as a comment for PEM */
|
|
605 |
if (commentp && prv) |
|
606 |
*commentp = xstrdup(filename); |
|
607 |
} else { |
|
608 |
/* it's a SSH v1 key if the public key part is readable */
|
|
609 |
key_free(pub); |
|
610 |
/* closes fd */
|
|
611 |
prv = key_load_private_rsa1(fd, filename, passphrase, NULL); |
|
612 |
}
|
|
613 |
return prv; |
|
614 |
}
|
|
615 |
||
616 |
static int |
|
617 |
key_try_load_public(Key *k, const char *filename, char **commentp) |
|
618 |
{
|
|
619 |
FILE *f; |
|
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
620 |
char line[SSH_MAX_PUBKEY_BYTES]; |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
621 |
char *cp; |
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
622 |
u_long linenum = 0; |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
623 |
|
624 |
f = fopen(filename, "r"); |
|
625 |
if (f != NULL) { |
|
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
626 |
while (read_keyfile_line(f, filename, line, sizeof(line), |
627 |
&linenum) != -1) { |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
628 |
cp = line; |
629 |
switch (*cp) { |
|
630 |
case '#': |
|
631 |
case '\n': |
|
632 |
case '\0': |
|
633 |
continue; |
|
634 |
}
|
|
635 |
/* Skip leading whitespace. */
|
|
636 |
for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) |
|
637 |
;
|
|
638 |
if (*cp) { |
|
639 |
if (key_read(k, &cp) == 1) { |
|
640 |
if (commentp) |
|
641 |
*commentp=xstrdup(filename); |
|
642 |
fclose(f); |
|
643 |
return 1; |
|
644 |
}
|
|
645 |
}
|
|
646 |
}
|
|
647 |
fclose(f); |
|
648 |
}
|
|
649 |
return 0; |
|
650 |
}
|
|
651 |
||
652 |
/* load public key from ssh v1 private or any pubkey file */
|
|
653 |
Key * |
|
654 |
key_load_public(const char *filename, char **commentp) |
|
655 |
{
|
|
656 |
Key *pub; |
|
657 |
char file[MAXPATHLEN]; |
|
658 |
||
659 |
/* try rsa1 private key */
|
|
660 |
pub = key_load_public_type(KEY_RSA1, filename, commentp); |
|
661 |
if (pub != NULL) |
|
662 |
return pub; |
|
663 |
||
664 |
/* try rsa1 public key */
|
|
665 |
pub = key_new(KEY_RSA1); |
|
666 |
if (key_try_load_public(pub, filename, commentp) == 1) |
|
667 |
return pub; |
|
668 |
key_free(pub); |
|
669 |
||
670 |
/* try ssh2 public key */
|
|
671 |
pub = key_new(KEY_UNSPEC); |
|
672 |
if (key_try_load_public(pub, filename, commentp) == 1) |
|
673 |
return pub; |
|
674 |
if ((strlcpy(file, filename, sizeof file) < sizeof(file)) && |
|
675 |
(strlcat(file, ".pub", sizeof file) < sizeof(file)) && |
|
676 |
(key_try_load_public(pub, file, commentp) == 1)) |
|
677 |
return pub; |
|
678 |
key_free(pub); |
|
679 |
return NULL; |
|
680 |
}
|
|
35
by Colin Watson
* Resynchronise with Debian. Remaining changes: |
681 |
|
37
by Colin Watson
* Resynchronise with Debian. Remaining changes: |
682 |
/* Scan a blacklist of known-vulnerable keys in blacklist_file. */
|
683 |
static int |
|
684 |
blacklisted_key_in_file(const Key *key, const char *blacklist_file, char **fp) |
|
685 |
{
|
|
35
by Colin Watson
* Resynchronise with Debian. Remaining changes: |
686 |
int fd = -1; |
687 |
char *dgst_hex = NULL; |
|
688 |
char *dgst_packed = NULL, *p; |
|
689 |
int i; |
|
690 |
size_t line_len; |
|
691 |
struct stat st; |
|
692 |
char buf[256]; |
|
693 |
off_t start, lower, upper; |
|
694 |
int ret = 0; |
|
695 |
||
696 |
debug("Checking blacklist file %s", blacklist_file); |
|
697 |
fd = open(blacklist_file, O_RDONLY); |
|
37
by Colin Watson
* Resynchronise with Debian. Remaining changes: |
698 |
if (fd < 0) { |
699 |
ret = -1; |
|
35
by Colin Watson
* Resynchronise with Debian. Remaining changes: |
700 |
goto out; |
37
by Colin Watson
* Resynchronise with Debian. Remaining changes: |
701 |
}
|
35
by Colin Watson
* Resynchronise with Debian. Remaining changes: |
702 |
|
703 |
dgst_hex = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); |
|
704 |
/* Remove all colons */
|
|
705 |
dgst_packed = xcalloc(1, strlen(dgst_hex) + 1); |
|
706 |
for (i = 0, p = dgst_packed; dgst_hex[i]; i++) |
|
707 |
if (dgst_hex[i] != ':') |
|
708 |
*p++ = dgst_hex[i]; |
|
709 |
/* Only compare least-significant 80 bits (to keep the blacklist
|
|
710 |
* size down)
|
|
711 |
*/
|
|
712 |
line_len = strlen(dgst_packed + 12); |
|
713 |
if (line_len > 32) |
|
714 |
goto out; |
|
715 |
||
716 |
/* Skip leading comments */
|
|
717 |
start = 0; |
|
718 |
for (;;) { |
|
719 |
ssize_t r; |
|
720 |
char *newline; |
|
721 |
||
37
by Colin Watson
* Resynchronise with Debian. Remaining changes: |
722 |
r = atomicio(read, fd, buf, sizeof(buf)); |
35
by Colin Watson
* Resynchronise with Debian. Remaining changes: |
723 |
if (r <= 0) |
724 |
goto out; |
|
725 |
if (buf[0] != '#') |
|
726 |
break; |
|
727 |
||
37
by Colin Watson
* Resynchronise with Debian. Remaining changes: |
728 |
newline = memchr(buf, '\n', sizeof(buf)); |
35
by Colin Watson
* Resynchronise with Debian. Remaining changes: |
729 |
if (!newline) |
730 |
goto out; |
|
731 |
start += newline + 1 - buf; |
|
732 |
if (lseek(fd, start, SEEK_SET) < 0) |
|
733 |
goto out; |
|
734 |
}
|
|
735 |
||
736 |
/* Initialise binary search record numbers */
|
|
737 |
if (fstat(fd, &st) < 0) |
|
738 |
goto out; |
|
739 |
lower = 0; |
|
740 |
upper = (st.st_size - start) / (line_len + 1); |
|
741 |
||
742 |
while (lower != upper) { |
|
743 |
off_t cur; |
|
744 |
int cmp; |
|
745 |
||
746 |
cur = lower + (upper - lower) / 2; |
|
747 |
||
748 |
/* Read this line and compare to digest; this is
|
|
749 |
* overflow-safe since cur < max(off_t) / (line_len + 1) */
|
|
750 |
if (lseek(fd, start + cur * (line_len + 1), SEEK_SET) < 0) |
|
751 |
break; |
|
752 |
if (atomicio(read, fd, buf, line_len) != line_len) |
|
753 |
break; |
|
754 |
cmp = memcmp(buf, dgst_packed + 12, line_len); |
|
755 |
if (cmp < 0) { |
|
756 |
if (cur == lower) |
|
757 |
break; |
|
758 |
lower = cur; |
|
759 |
} else if (cmp > 0) { |
|
760 |
if (cur == upper) |
|
761 |
break; |
|
762 |
upper = cur; |
|
763 |
} else { |
|
764 |
debug("Found %s in blacklist", dgst_hex); |
|
765 |
ret = 1; |
|
766 |
break; |
|
767 |
}
|
|
768 |
}
|
|
769 |
||
770 |
out: |
|
771 |
if (dgst_packed) |
|
772 |
xfree(dgst_packed); |
|
37
by Colin Watson
* Resynchronise with Debian. Remaining changes: |
773 |
if (ret != 1 && dgst_hex) { |
35
by Colin Watson
* Resynchronise with Debian. Remaining changes: |
774 |
xfree(dgst_hex); |
37
by Colin Watson
* Resynchronise with Debian. Remaining changes: |
775 |
dgst_hex = NULL; |
776 |
}
|
|
777 |
if (fp) |
|
778 |
*fp = dgst_hex; |
|
35
by Colin Watson
* Resynchronise with Debian. Remaining changes: |
779 |
if (fd >= 0) |
780 |
close(fd); |
|
37
by Colin Watson
* Resynchronise with Debian. Remaining changes: |
781 |
return ret; |
782 |
}
|
|
783 |
||
784 |
/*
|
|
785 |
* Scan blacklists of known-vulnerable keys. If a vulnerable key is found,
|
|
786 |
* its fingerprint is returned in *fp, unless fp is NULL.
|
|
787 |
*/
|
|
788 |
int
|
|
789 |
blacklisted_key(const Key *key, char **fp) |
|
790 |
{
|
|
791 |
Key *public; |
|
792 |
char *blacklist_file; |
|
793 |
int ret, ret2; |
|
794 |
||
795 |
public = key_demote(key); |
|
796 |
if (public->type == KEY_RSA1) |
|
797 |
public->type = KEY_RSA; |
|
798 |
||
799 |
xasprintf(&blacklist_file, "%s.%s-%u", |
|
800 |
_PATH_BLACKLIST, key_type(public), key_size(public)); |
|
801 |
ret = blacklisted_key_in_file(public, blacklist_file, fp); |
|
802 |
xfree(blacklist_file); |
|
803 |
if (ret > 0) { |
|
804 |
key_free(public); |
|
805 |
return ret; |
|
806 |
}
|
|
807 |
||
808 |
xasprintf(&blacklist_file, "%s.%s-%u", |
|
809 |
_PATH_BLACKLIST_CONFIG, key_type(public), key_size(public)); |
|
810 |
ret2 = blacklisted_key_in_file(public, blacklist_file, fp); |
|
811 |
xfree(blacklist_file); |
|
812 |
if (ret2 > ret) |
|
813 |
ret = ret2; |
|
814 |
||
815 |
key_free(public); |
|
35
by Colin Watson
* Resynchronise with Debian. Remaining changes: |
816 |
return ret; |
817 |
}
|