Annotation of src/usr.bin/gzsig/ssh.c, Revision 1.3
1.3 ! miod 1: /* $OpenBSD: ssh.c,v 1.2 2005/05/29 09:10:23 djm Exp $ */
1.1 marius 2:
3: /*
4: * ssh.c
5: *
6: * Copyright (c) 2001 Dug Song <dugsong@monkey.org>
7: * Copyright (c) 2000 Niels Provos <provos@monkey.org>
8: * Copyright (c) 2000 Markus Friedl <markus@monkey.org>
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: *
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
19: * 3. The names of the copyright holders may not be used to endorse or
20: * promote products derived from this software without specific
21: * prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
25: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
26: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33: *
34: * $Vendor: ssh.c,v 1.2 2005/04/01 16:47:31 dugsong Exp $
35: */
36:
37: #include <sys/types.h>
38: #include <sys/uio.h>
39:
40: #include <arpa/nameser.h>
41: #include <openssl/ssl.h>
42: #include <openssl/des.h>
43: #include <openssl/md5.h>
44:
45: #include <errno.h>
46: #include <stdio.h>
47: #include <stdlib.h>
48: #include <string.h>
49: #include <unistd.h>
50:
51: #include "key.h"
52: #include "ssh.h"
53:
54: #define SSH1_MAGIC "SSH PRIVATE KEY FILE FORMAT 1.1\n"
55:
1.2 djm 56: extern int sign_passwd_cb(char *, int, int, void *);
57:
1.1 marius 58: struct des3_state {
1.3 ! miod 59: DES_key_schedule k1, k2, k3;
! 60: DES_cblock iv1, iv2, iv3;
1.1 marius 61: };
62:
63: static int
64: get_bn(BIGNUM *bn, u_char **pp, int *lenp)
65: {
66: short i;
67:
68: if (*lenp < 2) {
69: errno = EINVAL;
70: return (-1);
71: }
72: GETSHORT(i, *pp); *lenp -= 2;
73:
74: i = ((i + 7) / 8);
75:
76: if (*lenp < i) {
77: errno = EINVAL;
78: return (-1);
79: }
80: BN_bin2bn(*pp, i, bn);
81:
82: *pp += i; *lenp -= i;
83:
84: return (0);
85: }
86:
87: static int
88: get_string(char *dst, int len, u_char **pp, int *lenp)
89: {
90: long i;
91:
92: if (*lenp < 4) {
93: errno = EINVAL;
94: return (-1);
95: }
96: GETLONG(i, *pp); *lenp -= 4;
97:
98: if (*lenp < i || len < i) {
99: errno = EINVAL;
100: return (-1);
101: }
102: memcpy(dst, *pp, i);
103:
104: *pp += i; *lenp -= i;
105:
106: return (0);
107: }
108:
109: static int
110: read_ssh1_bn(BIGNUM *value, char **cpp)
111: {
112: char *cp = *cpp;
113: int old;
114:
115: /* Skip any leading whitespace. */
116: for (; *cp == ' ' || *cp == '\t'; cp++)
117: ;
118:
119: /* Check that it begins with a decimal digit. */
120: if (*cp < '0' || *cp > '9') {
121: errno = EINVAL;
122: return (-1);
123: }
124: /* Save starting position. */
125: *cpp = cp;
126:
127: /* Move forward until all decimal digits skipped. */
128: for (; *cp >= '0' && *cp <= '9'; cp++)
129: ;
130:
131: /* Save the old terminating character, and replace it by \0. */
132: old = *cp;
133: *cp = 0;
134:
135: /* Parse the number. */
136: if (BN_dec2bn(&value, *cpp) == 0)
137: return (-1);
138:
139: /* Restore old terminating character. */
140: *cp = old;
141:
142: /* Move beyond the number and return success. */
143: *cpp = cp;
144: return (0);
145: }
146:
147: /* XXX - SSH1's weirdo 3DES... */
148: static void *
149: des3_init(u_char *sesskey, int len)
150: {
151: struct des3_state *state;
152:
153: if ((state = malloc(sizeof(*state))) == NULL)
154: return (NULL);
155:
1.3 ! miod 156: DES_set_key((const_DES_cblock *)sesskey, &state->k1);
! 157: DES_set_key((const_DES_cblock *)(sesskey + 8), &state->k2);
1.1 marius 158:
159: if (len <= 16)
1.3 ! miod 160: DES_set_key((const_DES_cblock *)sesskey, &state->k3);
1.1 marius 161: else
1.3 ! miod 162: DES_set_key((const_DES_cblock *)(sesskey + 16), &state->k3);
1.1 marius 163:
164: memset(state->iv1, 0, 8);
165: memset(state->iv2, 0, 8);
166: memset(state->iv3, 0, 8);
167:
168: return (state);
169: }
170:
171: static void
172: des3_decrypt(u_char *src, u_char *dst, int len, void *state)
173: {
174: struct des3_state *dstate;
175:
176: dstate = (struct des3_state *)state;
177: memcpy(dstate->iv1, dstate->iv2, 8);
178:
1.3 ! miod 179: DES_ncbc_encrypt(src, dst, len, &dstate->k3, &dstate->iv3, DES_DECRYPT);
! 180: DES_ncbc_encrypt(dst, dst, len, &dstate->k2, &dstate->iv2, DES_ENCRYPT);
! 181: DES_ncbc_encrypt(dst, dst, len, &dstate->k1, &dstate->iv1, DES_DECRYPT);
1.1 marius 182: }
183:
184: static int
185: load_ssh1_public(RSA *rsa, struct iovec *iov)
186: {
187: char *p;
188: u_int bits;
189:
190: /* Skip leading whitespace. */
191: for (p = iov->iov_base; *p == ' ' || *p == '\t'; p++)
192: ;
193:
194: /* Get number of bits. */
195: if (*p < '0' || *p > '9')
196: return (-1);
197:
198: for (bits = 0; *p >= '0' && *p <= '9'; p++)
199: bits = 10 * bits + *p - '0';
200:
201: if (bits == 0)
202: return (-1);
203:
204: /* Get public exponent, public modulus. */
205: if (read_ssh1_bn(rsa->e, &p) < 0)
206: return (-1);
207:
208: if (read_ssh1_bn(rsa->n, &p) < 0)
209: return (-1);
210:
211: return (0);
212: }
213:
214: static int
215: load_ssh1_private(RSA *rsa, struct iovec *iov)
216: {
217: BN_CTX *ctx;
218: BIGNUM *aux;
219: MD5_CTX md;
1.2 djm 220: char pass[128], comment[BUFSIZ];
1.1 marius 221: u_char *p, cipher_type, digest[16];
222: void *dstate;
223: int i;
224:
225: i = strlen(SSH1_MAGIC) + 1;
226:
227: /* Make sure it begins with the id string. */
228: if (iov->iov_len < i || memcmp(iov->iov_base, SSH1_MAGIC, i) != 0)
229: return (-1);
230:
231: p = (u_char *)iov->iov_base + i;
232: i = iov->iov_len - i;
233:
234: /* Skip cipher_type, reserved data, bits. */
235: cipher_type = *p;
236: p += 1 + 4 + 4;
237: i -= 1 + 4 + 4;
238:
239: /* Read public key. */
240: if (get_bn(rsa->n, &p, &i) < 0 || get_bn(rsa->e, &p, &i) < 0)
241: return (-1);
242:
243: /* Read comment. */
244: if (get_string(comment, sizeof(comment), &p, &i) < 0)
245: return (-1);
246:
247: /* Decrypt private key. */
248: if (cipher_type != 0) {
249: sign_passwd_cb(pass, sizeof(pass), 0, NULL);
250:
251: MD5_Init(&md);
252: MD5_Update(&md, (const u_char *)pass, strlen(pass));
253: MD5_Final(digest, &md);
254:
255: memset(pass, 0, strlen(pass));
256:
257: if ((dstate = des3_init(digest, sizeof(digest))) == NULL)
258: return (-1);
259:
260: des3_decrypt(p, p, i, dstate);
261:
262: if (p[0] != p[2] || p[1] != p[3]) {
263: fprintf(stderr, "Bad passphrase for %s\n", comment);
264: return (-1);
265: }
266: }
267: else if (p[0] != p[2] || p[1] != p[3])
268: return (-1);
269:
270: p += 4;
271: i -= 4;
272:
273: /* Read the private key. */
274: if (get_bn(rsa->d, &p, &i) < 0 ||
275: get_bn(rsa->iqmp, &p, &i) < 0)
276: return (-1);
277:
278: /* In SSL and SSH v1 p and q are exchanged. */
279: if (get_bn(rsa->q, &p, &i) < 0 ||
280: get_bn(rsa->p, &p, &i) < 0)
281: return (-1);
282:
283: /* Calculate p-1 and q-1. */
284: ctx = BN_CTX_new();
285: aux = BN_new();
286:
287: BN_sub(aux, rsa->q, BN_value_one());
288: BN_mod(rsa->dmq1, rsa->d, aux, ctx);
289:
290: BN_sub(aux, rsa->p, BN_value_one());
291: BN_mod(rsa->dmp1, rsa->d, aux, ctx);
292:
293: BN_clear_free(aux);
294: BN_CTX_free(ctx);
295:
296: return (0);
297: }
298:
299: int
300: ssh_load_public(struct key *k, struct iovec *iov)
301: {
302: RSA *rsa;
303:
304: rsa = RSA_new();
305:
306: rsa->n = BN_new();
307: rsa->e = BN_new();
308:
309: if (load_ssh1_public(rsa, iov) < 0) {
310: RSA_free(rsa);
311: return (-1);
312: }
313: k->type = KEY_RSA;
314: k->data = (void *)rsa;
315:
316: return (0);
317: }
318:
319: int
320: ssh_load_private(struct key *k, struct iovec *iov)
321: {
322: RSA *rsa;
323:
324: rsa = RSA_new();
325:
326: rsa->n = BN_new();
327: rsa->e = BN_new();
328:
329: rsa->d = BN_new();
330: rsa->iqmp = BN_new();
331: rsa->q = BN_new();
332: rsa->p = BN_new();
333: rsa->dmq1 = BN_new();
334: rsa->dmp1 = BN_new();
335:
336: if (load_ssh1_private(rsa, iov) < 0) {
337: RSA_free(rsa);
338: return (-1);
339:
340: }
341: k->type = KEY_RSA;
342: k->data = (void *)rsa;
343:
344: return (0);
345: }