Annotation of src/usr.bin/gzsig/ssh.c, Revision 1.1
1.1 ! marius 1: /* $OpenBSD$ */
! 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:
! 56: struct des3_state {
! 57: des_key_schedule k1, k2, k3;
! 58: des_cblock iv1, iv2, iv3;
! 59: };
! 60:
! 61: static int
! 62: get_bn(BIGNUM *bn, u_char **pp, int *lenp)
! 63: {
! 64: short i;
! 65:
! 66: if (*lenp < 2) {
! 67: errno = EINVAL;
! 68: return (-1);
! 69: }
! 70: GETSHORT(i, *pp); *lenp -= 2;
! 71:
! 72: i = ((i + 7) / 8);
! 73:
! 74: if (*lenp < i) {
! 75: errno = EINVAL;
! 76: return (-1);
! 77: }
! 78: BN_bin2bn(*pp, i, bn);
! 79:
! 80: *pp += i; *lenp -= i;
! 81:
! 82: return (0);
! 83: }
! 84:
! 85: static int
! 86: get_string(char *dst, int len, u_char **pp, int *lenp)
! 87: {
! 88: long i;
! 89:
! 90: if (*lenp < 4) {
! 91: errno = EINVAL;
! 92: return (-1);
! 93: }
! 94: GETLONG(i, *pp); *lenp -= 4;
! 95:
! 96: if (*lenp < i || len < i) {
! 97: errno = EINVAL;
! 98: return (-1);
! 99: }
! 100: memcpy(dst, *pp, i);
! 101:
! 102: *pp += i; *lenp -= i;
! 103:
! 104: return (0);
! 105: }
! 106:
! 107: static int
! 108: read_ssh1_bn(BIGNUM *value, char **cpp)
! 109: {
! 110: char *cp = *cpp;
! 111: int old;
! 112:
! 113: /* Skip any leading whitespace. */
! 114: for (; *cp == ' ' || *cp == '\t'; cp++)
! 115: ;
! 116:
! 117: /* Check that it begins with a decimal digit. */
! 118: if (*cp < '0' || *cp > '9') {
! 119: errno = EINVAL;
! 120: return (-1);
! 121: }
! 122: /* Save starting position. */
! 123: *cpp = cp;
! 124:
! 125: /* Move forward until all decimal digits skipped. */
! 126: for (; *cp >= '0' && *cp <= '9'; cp++)
! 127: ;
! 128:
! 129: /* Save the old terminating character, and replace it by \0. */
! 130: old = *cp;
! 131: *cp = 0;
! 132:
! 133: /* Parse the number. */
! 134: if (BN_dec2bn(&value, *cpp) == 0)
! 135: return (-1);
! 136:
! 137: /* Restore old terminating character. */
! 138: *cp = old;
! 139:
! 140: /* Move beyond the number and return success. */
! 141: *cpp = cp;
! 142: return (0);
! 143: }
! 144:
! 145: /* XXX - SSH1's weirdo 3DES... */
! 146: static void *
! 147: des3_init(u_char *sesskey, int len)
! 148: {
! 149: struct des3_state *state;
! 150:
! 151: if ((state = malloc(sizeof(*state))) == NULL)
! 152: return (NULL);
! 153:
! 154: des_set_key((void *)sesskey, state->k1);
! 155: des_set_key((void *)(sesskey + 8), state->k2);
! 156:
! 157: if (len <= 16)
! 158: des_set_key((void *)sesskey, state->k3);
! 159: else
! 160: des_set_key((void *)(sesskey + 16), state->k3);
! 161:
! 162: memset(state->iv1, 0, 8);
! 163: memset(state->iv2, 0, 8);
! 164: memset(state->iv3, 0, 8);
! 165:
! 166: return (state);
! 167: }
! 168:
! 169: static void
! 170: des3_decrypt(u_char *src, u_char *dst, int len, void *state)
! 171: {
! 172: struct des3_state *dstate;
! 173:
! 174: dstate = (struct des3_state *)state;
! 175: memcpy(dstate->iv1, dstate->iv2, 8);
! 176:
! 177: des_ncbc_encrypt(src, dst, len, dstate->k3, &dstate->iv3, DES_DECRYPT);
! 178: des_ncbc_encrypt(dst, dst, len, dstate->k2, &dstate->iv2, DES_ENCRYPT);
! 179: des_ncbc_encrypt(dst, dst, len, dstate->k1, &dstate->iv1, DES_DECRYPT);
! 180: }
! 181:
! 182: static int
! 183: load_ssh1_public(RSA *rsa, struct iovec *iov)
! 184: {
! 185: char *p;
! 186: u_int bits;
! 187:
! 188: /* Skip leading whitespace. */
! 189: for (p = iov->iov_base; *p == ' ' || *p == '\t'; p++)
! 190: ;
! 191:
! 192: /* Get number of bits. */
! 193: if (*p < '0' || *p > '9')
! 194: return (-1);
! 195:
! 196: for (bits = 0; *p >= '0' && *p <= '9'; p++)
! 197: bits = 10 * bits + *p - '0';
! 198:
! 199: if (bits == 0)
! 200: return (-1);
! 201:
! 202: /* Get public exponent, public modulus. */
! 203: if (read_ssh1_bn(rsa->e, &p) < 0)
! 204: return (-1);
! 205:
! 206: if (read_ssh1_bn(rsa->n, &p) < 0)
! 207: return (-1);
! 208:
! 209: return (0);
! 210: }
! 211:
! 212: static int
! 213: load_ssh1_private(RSA *rsa, struct iovec *iov)
! 214: {
! 215: BN_CTX *ctx;
! 216: BIGNUM *aux;
! 217: MD5_CTX md;
! 218: char pass[128], prompt[128], comment[BUFSIZ];
! 219: u_char *p, cipher_type, digest[16];
! 220: void *dstate;
! 221: int i;
! 222:
! 223: i = strlen(SSH1_MAGIC) + 1;
! 224:
! 225: /* Make sure it begins with the id string. */
! 226: if (iov->iov_len < i || memcmp(iov->iov_base, SSH1_MAGIC, i) != 0)
! 227: return (-1);
! 228:
! 229: p = (u_char *)iov->iov_base + i;
! 230: i = iov->iov_len - i;
! 231:
! 232: /* Skip cipher_type, reserved data, bits. */
! 233: cipher_type = *p;
! 234: p += 1 + 4 + 4;
! 235: i -= 1 + 4 + 4;
! 236:
! 237: /* Read public key. */
! 238: if (get_bn(rsa->n, &p, &i) < 0 || get_bn(rsa->e, &p, &i) < 0)
! 239: return (-1);
! 240:
! 241: /* Read comment. */
! 242: if (get_string(comment, sizeof(comment), &p, &i) < 0)
! 243: return (-1);
! 244:
! 245: /* Decrypt private key. */
! 246: if (cipher_type != 0) {
! 247: sign_passwd_cb(pass, sizeof(pass), 0, NULL);
! 248:
! 249: MD5_Init(&md);
! 250: MD5_Update(&md, (const u_char *)pass, strlen(pass));
! 251: MD5_Final(digest, &md);
! 252:
! 253: memset(pass, 0, strlen(pass));
! 254:
! 255: if ((dstate = des3_init(digest, sizeof(digest))) == NULL)
! 256: return (-1);
! 257:
! 258: des3_decrypt(p, p, i, dstate);
! 259:
! 260: if (p[0] != p[2] || p[1] != p[3]) {
! 261: fprintf(stderr, "Bad passphrase for %s\n", comment);
! 262: return (-1);
! 263: }
! 264: }
! 265: else if (p[0] != p[2] || p[1] != p[3])
! 266: return (-1);
! 267:
! 268: p += 4;
! 269: i -= 4;
! 270:
! 271: /* Read the private key. */
! 272: if (get_bn(rsa->d, &p, &i) < 0 ||
! 273: get_bn(rsa->iqmp, &p, &i) < 0)
! 274: return (-1);
! 275:
! 276: /* In SSL and SSH v1 p and q are exchanged. */
! 277: if (get_bn(rsa->q, &p, &i) < 0 ||
! 278: get_bn(rsa->p, &p, &i) < 0)
! 279: return (-1);
! 280:
! 281: /* Calculate p-1 and q-1. */
! 282: ctx = BN_CTX_new();
! 283: aux = BN_new();
! 284:
! 285: BN_sub(aux, rsa->q, BN_value_one());
! 286: BN_mod(rsa->dmq1, rsa->d, aux, ctx);
! 287:
! 288: BN_sub(aux, rsa->p, BN_value_one());
! 289: BN_mod(rsa->dmp1, rsa->d, aux, ctx);
! 290:
! 291: BN_clear_free(aux);
! 292: BN_CTX_free(ctx);
! 293:
! 294: return (0);
! 295: }
! 296:
! 297: int
! 298: ssh_load_public(struct key *k, struct iovec *iov)
! 299: {
! 300: RSA *rsa;
! 301:
! 302: rsa = RSA_new();
! 303:
! 304: rsa->n = BN_new();
! 305: rsa->e = BN_new();
! 306:
! 307: if (load_ssh1_public(rsa, iov) < 0) {
! 308: RSA_free(rsa);
! 309: return (-1);
! 310: }
! 311: k->type = KEY_RSA;
! 312: k->data = (void *)rsa;
! 313:
! 314: return (0);
! 315: }
! 316:
! 317: int
! 318: ssh_load_private(struct key *k, struct iovec *iov)
! 319: {
! 320: RSA *rsa;
! 321:
! 322: rsa = RSA_new();
! 323:
! 324: rsa->n = BN_new();
! 325: rsa->e = BN_new();
! 326:
! 327: rsa->d = BN_new();
! 328: rsa->iqmp = BN_new();
! 329: rsa->q = BN_new();
! 330: rsa->p = BN_new();
! 331: rsa->dmq1 = BN_new();
! 332: rsa->dmp1 = BN_new();
! 333:
! 334: if (load_ssh1_private(rsa, iov) < 0) {
! 335: RSA_free(rsa);
! 336: return (-1);
! 337:
! 338: }
! 339: k->type = KEY_RSA;
! 340: k->data = (void *)rsa;
! 341:
! 342: return (0);
! 343: }