Home | History | Annotate | Download | only in dropbear
      1 /*
      2  * Dropbear - a SSH2 server
      3  *
      4  * Copyright (c) 2002,2003 Matt Johnston
      5  * All rights reserved.
      6  *
      7  * Permission is hereby granted, free of charge, to any person obtaining a copy
      8  * of this software and associated documentation files (the "Software"), to deal
      9  * in the Software without restriction, including without limitation the rights
     10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     11  * copies of the Software, and to permit persons to whom the Software is
     12  * furnished to do so, subject to the following conditions:
     13  *
     14  * The above copyright notice and this permission notice shall be included in
     15  * all copies or substantial portions of the Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     23  * SOFTWARE. */
     24 
     25 #include "includes.h"
     26 #include "dbutil.h"
     27 #include "signkey.h"
     28 #include "buffer.h"
     29 #include "ssh.h"
     30 
     31 /* malloc a new sign_key and set the dss and rsa keys to NULL */
     32 sign_key * new_sign_key() {
     33 
     34 	sign_key * ret;
     35 
     36 	ret = (sign_key*)m_malloc(sizeof(sign_key));
     37 #ifdef DROPBEAR_DSS
     38 	ret->dsskey = NULL;
     39 #endif
     40 #ifdef DROPBEAR_RSA
     41 	ret->rsakey = NULL;
     42 #endif
     43 	return ret;
     44 
     45 }
     46 
     47 /* Returns "ssh-dss" or "ssh-rsa" corresponding to the type. Exits fatally
     48  * if the type is invalid */
     49 const char* signkey_name_from_type(int type, int *namelen) {
     50 
     51 #ifdef DROPBEAR_RSA
     52 	if (type == DROPBEAR_SIGNKEY_RSA) {
     53 		*namelen = SSH_SIGNKEY_RSA_LEN;
     54 		return SSH_SIGNKEY_RSA;
     55 	}
     56 #endif
     57 #ifdef DROPBEAR_DSS
     58 	if (type == DROPBEAR_SIGNKEY_DSS) {
     59 		*namelen = SSH_SIGNKEY_DSS_LEN;
     60 		return SSH_SIGNKEY_DSS;
     61 	}
     62 #endif
     63 	dropbear_exit("bad key type %d", type);
     64 	return NULL; /* notreached */
     65 }
     66 
     67 /* Returns DROPBEAR_SIGNKEY_RSA, DROPBEAR_SIGNKEY_DSS,
     68  * or DROPBEAR_SIGNKEY_NONE */
     69 int signkey_type_from_name(const char* name, int namelen) {
     70 
     71 #ifdef DROPBEAR_RSA
     72 	if (namelen == SSH_SIGNKEY_RSA_LEN
     73 			&& memcmp(name, SSH_SIGNKEY_RSA, SSH_SIGNKEY_RSA_LEN) == 0) {
     74 		return DROPBEAR_SIGNKEY_RSA;
     75 	}
     76 #endif
     77 #ifdef DROPBEAR_DSS
     78 	if (namelen == SSH_SIGNKEY_DSS_LEN
     79 			&& memcmp(name, SSH_SIGNKEY_DSS, SSH_SIGNKEY_DSS_LEN) == 0) {
     80 		return DROPBEAR_SIGNKEY_DSS;
     81 	}
     82 #endif
     83 
     84 	return DROPBEAR_SIGNKEY_NONE;
     85 }
     86 
     87 /* returns DROPBEAR_SUCCESS on success, DROPBEAR_FAILURE on fail.
     88  * type should be set by the caller to specify the type to read, and
     89  * on return is set to the type read (useful when type = _ANY) */
     90 int buf_get_pub_key(buffer *buf, sign_key *key, int *type) {
     91 
     92 	unsigned char* ident;
     93 	unsigned int len;
     94 	int keytype;
     95 	int ret = DROPBEAR_FAILURE;
     96 
     97 	TRACE(("enter buf_get_pub_key"))
     98 
     99 	ident = buf_getstring(buf, &len);
    100 	keytype = signkey_type_from_name(ident, len);
    101 	m_free(ident);
    102 
    103 	if (*type != DROPBEAR_SIGNKEY_ANY && *type != keytype) {
    104 		return DROPBEAR_FAILURE;
    105 	}
    106 
    107 	*type = keytype;
    108 
    109 	/* Rewind the buffer back before "ssh-rsa" etc */
    110 	buf_incrpos(buf, -len - 4);
    111 
    112 #ifdef DROPBEAR_DSS
    113 	if (keytype == DROPBEAR_SIGNKEY_DSS) {
    114 		dss_key_free(key->dsskey);
    115 		key->dsskey = (dss_key*)m_malloc(sizeof(dss_key));
    116 		ret = buf_get_dss_pub_key(buf, key->dsskey);
    117 		if (ret == DROPBEAR_FAILURE) {
    118 			m_free(key->dsskey);
    119 		}
    120 	}
    121 #endif
    122 #ifdef DROPBEAR_RSA
    123 	if (keytype == DROPBEAR_SIGNKEY_RSA) {
    124 		rsa_key_free(key->rsakey);
    125 		key->rsakey = (rsa_key*)m_malloc(sizeof(rsa_key));
    126 		ret = buf_get_rsa_pub_key(buf, key->rsakey);
    127 		if (ret == DROPBEAR_FAILURE) {
    128 			m_free(key->rsakey);
    129 		}
    130 	}
    131 #endif
    132 
    133 	TRACE(("leave buf_get_pub_key"))
    134 
    135 	return ret;
    136 
    137 }
    138 
    139 /* returns DROPBEAR_SUCCESS on success, DROPBEAR_FAILURE on fail.
    140  * type should be set by the caller to specify the type to read, and
    141  * on return is set to the type read (useful when type = _ANY) */
    142 int buf_get_priv_key(buffer *buf, sign_key *key, int *type) {
    143 
    144 	unsigned char* ident;
    145 	unsigned int len;
    146 	int keytype;
    147 	int ret = DROPBEAR_FAILURE;
    148 
    149 	TRACE(("enter buf_get_priv_key"))
    150 
    151 	ident = buf_getstring(buf, &len);
    152 	keytype = signkey_type_from_name(ident, len);
    153 	m_free(ident);
    154 
    155 	if (*type != DROPBEAR_SIGNKEY_ANY && *type != keytype) {
    156 		TRACE(("wrong key type: %d %d", *type, keytype))
    157 		return DROPBEAR_FAILURE;
    158 	}
    159 
    160 	*type = keytype;
    161 
    162 	/* Rewind the buffer back before "ssh-rsa" etc */
    163 	buf_incrpos(buf, -len - 4);
    164 
    165 #ifdef DROPBEAR_DSS
    166 	if (keytype == DROPBEAR_SIGNKEY_DSS) {
    167 		dss_key_free(key->dsskey);
    168 		key->dsskey = (dss_key*)m_malloc(sizeof(dss_key));
    169 		ret = buf_get_dss_priv_key(buf, key->dsskey);
    170 		if (ret == DROPBEAR_FAILURE) {
    171 			m_free(key->dsskey);
    172 		}
    173 	}
    174 #endif
    175 #ifdef DROPBEAR_RSA
    176 	if (keytype == DROPBEAR_SIGNKEY_RSA) {
    177 		rsa_key_free(key->rsakey);
    178 		key->rsakey = (rsa_key*)m_malloc(sizeof(rsa_key));
    179 		ret = buf_get_rsa_priv_key(buf, key->rsakey);
    180 		if (ret == DROPBEAR_FAILURE) {
    181 			m_free(key->rsakey);
    182 		}
    183 	}
    184 #endif
    185 
    186 	TRACE(("leave buf_get_priv_key"))
    187 
    188 	return ret;
    189 
    190 }
    191 
    192 /* type is either DROPBEAR_SIGNKEY_DSS or DROPBEAR_SIGNKEY_RSA */
    193 void buf_put_pub_key(buffer* buf, sign_key *key, int type) {
    194 
    195 	buffer *pubkeys;
    196 
    197 	TRACE(("enter buf_put_pub_key"))
    198 	pubkeys = buf_new(MAX_PUBKEY_SIZE);
    199 
    200 #ifdef DROPBEAR_DSS
    201 	if (type == DROPBEAR_SIGNKEY_DSS) {
    202 		buf_put_dss_pub_key(pubkeys, key->dsskey);
    203 	}
    204 #endif
    205 #ifdef DROPBEAR_RSA
    206 	if (type == DROPBEAR_SIGNKEY_RSA) {
    207 		buf_put_rsa_pub_key(pubkeys, key->rsakey);
    208 	}
    209 #endif
    210 	if (pubkeys->len == 0) {
    211 		dropbear_exit("bad key types in buf_put_pub_key");
    212 	}
    213 
    214 	buf_setpos(pubkeys, 0);
    215 	buf_putstring(buf, buf_getptr(pubkeys, pubkeys->len),
    216 			pubkeys->len);
    217 
    218 	buf_free(pubkeys);
    219 	TRACE(("leave buf_put_pub_key"))
    220 }
    221 
    222 /* type is either DROPBEAR_SIGNKEY_DSS or DROPBEAR_SIGNKEY_RSA */
    223 void buf_put_priv_key(buffer* buf, sign_key *key, int type) {
    224 
    225 	TRACE(("enter buf_put_priv_key"))
    226 	TRACE(("type is %d", type))
    227 
    228 #ifdef DROPBEAR_DSS
    229 	if (type == DROPBEAR_SIGNKEY_DSS) {
    230 		buf_put_dss_priv_key(buf, key->dsskey);
    231 	TRACE(("leave buf_put_priv_key: dss done"))
    232 	return;
    233 	}
    234 #endif
    235 #ifdef DROPBEAR_RSA
    236 	if (type == DROPBEAR_SIGNKEY_RSA) {
    237 		buf_put_rsa_priv_key(buf, key->rsakey);
    238 	TRACE(("leave buf_put_priv_key: rsa done"))
    239 	return;
    240 	}
    241 #endif
    242 	dropbear_exit("bad key types in put pub key");
    243 }
    244 
    245 void sign_key_free(sign_key *key) {
    246 
    247 	TRACE(("enter sign_key_free"))
    248 
    249 #ifdef DROPBEAR_DSS
    250 	dss_key_free(key->dsskey);
    251 	key->dsskey = NULL;
    252 #endif
    253 #ifdef DROPBEAR_RSA
    254 	rsa_key_free(key->rsakey);
    255 	key->rsakey = NULL;
    256 #endif
    257 
    258 	m_free(key);
    259 	TRACE(("leave sign_key_free"))
    260 }
    261 
    262 static char hexdig(unsigned char x) {
    263 
    264 	if (x > 0xf)
    265 		return 'X';
    266 
    267 	if (x < 10)
    268 		return '0' + x;
    269 	else
    270 		return 'a' + x - 10;
    271 }
    272 
    273 /* Since we're not sure if we'll have md5 or sha1, we present both.
    274  * MD5 is used in preference, but sha1 could still be useful */
    275 #ifdef DROPBEAR_MD5_HMAC
    276 static char * sign_key_md5_fingerprint(unsigned char* keyblob,
    277 		unsigned int keybloblen) {
    278 
    279 	char * ret;
    280 	hash_state hs;
    281 	unsigned char hash[MD5_HASH_SIZE];
    282 	unsigned int i;
    283 	unsigned int buflen;
    284 
    285 	md5_init(&hs);
    286 
    287 	/* skip the size int of the string - this is a bit messy */
    288 	md5_process(&hs, keyblob, keybloblen);
    289 
    290 	md5_done(&hs, hash);
    291 
    292 	/* "md5 hexfingerprinthere\0", each hex digit is "AB:" etc */
    293 	buflen = 4 + 3*MD5_HASH_SIZE;
    294 	ret = (char*)m_malloc(buflen);
    295 
    296 	memset(ret, 'Z', buflen);
    297 	strcpy(ret, "md5 ");
    298 
    299 	for (i = 0; i < MD5_HASH_SIZE; i++) {
    300 		unsigned int pos = 4 + i*3;
    301 		ret[pos] = hexdig(hash[i] >> 4);
    302 		ret[pos+1] = hexdig(hash[i] & 0x0f);
    303 		ret[pos+2] = ':';
    304 	}
    305 	ret[buflen-1] = 0x0;
    306 
    307 	return ret;
    308 }
    309 
    310 #else /* use SHA1 rather than MD5 for fingerprint */
    311 static char * sign_key_sha1_fingerprint(unsigned char* keyblob,
    312 		unsigned int keybloblen) {
    313 
    314 	char * ret;
    315 	hash_state hs;
    316 	unsigned char hash[SHA1_HASH_SIZE];
    317 	unsigned int i;
    318 	unsigned int buflen;
    319 
    320 	sha1_init(&hs);
    321 
    322 	/* skip the size int of the string - this is a bit messy */
    323 	sha1_process(&hs, keyblob, keybloblen);
    324 
    325 	sha1_done(&hs, hash);
    326 
    327 	/* "sha1 hexfingerprinthere\0", each hex digit is "AB:" etc */
    328 	buflen = 5 + 3*SHA1_HASH_SIZE;
    329 	ret = (char*)m_malloc(buflen);
    330 
    331 	strcpy(ret, "sha1 ");
    332 
    333 	for (i = 0; i < SHA1_HASH_SIZE; i++) {
    334 		unsigned int pos = 5 + 3*i;
    335 		ret[pos] = hexdig(hash[i] >> 4);
    336 		ret[pos+1] = hexdig(hash[i] & 0x0f);
    337 		ret[pos+2] = ':';
    338 	}
    339 	ret[buflen-1] = 0x0;
    340 
    341 	return ret;
    342 }
    343 
    344 #endif /* MD5/SHA1 switch */
    345 
    346 /* This will return a freshly malloced string, containing a fingerprint
    347  * in either sha1 or md5 */
    348 char * sign_key_fingerprint(unsigned char* keyblob, unsigned int keybloblen) {
    349 
    350 #ifdef DROPBEAR_MD5_HMAC
    351 	return sign_key_md5_fingerprint(keyblob, keybloblen);
    352 #else
    353 	return sign_key_sha1_fingerprint(keyblob, keybloblen);
    354 #endif
    355 }
    356 
    357 void buf_put_sign(buffer* buf, sign_key *key, int type,
    358 		const unsigned char *data, unsigned int len) {
    359 
    360 	buffer *sigblob;
    361 
    362 	sigblob = buf_new(MAX_PUBKEY_SIZE);
    363 
    364 #ifdef DROPBEAR_DSS
    365 	if (type == DROPBEAR_SIGNKEY_DSS) {
    366 		buf_put_dss_sign(sigblob, key->dsskey, data, len);
    367 	}
    368 #endif
    369 #ifdef DROPBEAR_RSA
    370 	if (type == DROPBEAR_SIGNKEY_RSA) {
    371 		buf_put_rsa_sign(sigblob, key->rsakey, data, len);
    372 	}
    373 #endif
    374 	if (sigblob->len == 0) {
    375 		dropbear_exit("non-matching signing type");
    376 	}
    377 
    378 	buf_setpos(sigblob, 0);
    379 	buf_putstring(buf, buf_getptr(sigblob, sigblob->len),
    380 			sigblob->len);
    381 
    382 	buf_free(sigblob);
    383 
    384 }
    385 
    386 #ifdef DROPBEAR_SIGNKEY_VERIFY
    387 /* Return DROPBEAR_SUCCESS or DROPBEAR_FAILURE.
    388  * If FAILURE is returned, the position of
    389  * buf is undefined. If SUCCESS is returned, buf will be positioned after the
    390  * signature blob */
    391 int buf_verify(buffer * buf, sign_key *key, const unsigned char *data,
    392 		unsigned int len) {
    393 
    394 	unsigned int bloblen;
    395 	unsigned char * ident = NULL;
    396 	unsigned int identlen = 0;
    397 
    398 	TRACE(("enter buf_verify"))
    399 
    400 	bloblen = buf_getint(buf);
    401 	ident = buf_getstring(buf, &identlen);
    402 
    403 #ifdef DROPBEAR_DSS
    404 	if (bloblen == DSS_SIGNATURE_SIZE &&
    405 			memcmp(ident, SSH_SIGNKEY_DSS, identlen) == 0) {
    406 		m_free(ident);
    407 		if (key->dsskey == NULL) {
    408 			dropbear_exit("no dss key to verify signature");
    409 		}
    410 		return buf_dss_verify(buf, key->dsskey, data, len);
    411 	}
    412 #endif
    413 
    414 #ifdef DROPBEAR_RSA
    415 	if (memcmp(ident, SSH_SIGNKEY_RSA, identlen) == 0) {
    416 		m_free(ident);
    417 		if (key->rsakey == NULL) {
    418 			dropbear_exit("no rsa key to verify signature");
    419 		}
    420 		return buf_rsa_verify(buf, key->rsakey, data, len);
    421 	}
    422 #endif
    423 
    424 	m_free(ident);
    425 	dropbear_exit("non-matching signing type");
    426 	return DROPBEAR_FAILURE;
    427 }
    428 #endif /* DROPBEAR_SIGNKEY_VERIFY */
    429 
    430 #ifdef DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */
    431 
    432 /* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE when given a buffer containing
    433  * a key, a key, and a type. The buffer is positioned at the start of the
    434  * base64 data, and contains no trailing data */
    435 /* If fingerprint is non-NULL, it will be set to a malloc()ed fingerprint
    436    of the key if it is successfully decoded */
    437 int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen,
    438 					const unsigned char* algoname, unsigned int algolen,
    439 					buffer * line, char ** fingerprint) {
    440 
    441 	buffer * decodekey = NULL;
    442 	int ret = DROPBEAR_FAILURE;
    443 	unsigned int len, filealgolen;
    444 	unsigned long decodekeylen;
    445 	unsigned char* filealgo = NULL;
    446 
    447 	/* now we have the actual data */
    448 	len = line->len - line->pos;
    449 	decodekeylen = len * 2; /* big to be safe */
    450 	decodekey = buf_new(decodekeylen);
    451 
    452 	if (base64_decode(buf_getptr(line, len), len,
    453 				buf_getwriteptr(decodekey, decodekey->size),
    454 				&decodekeylen) != CRYPT_OK) {
    455 		TRACE(("checkpubkey: base64 decode failed"))
    456 		goto out;
    457 	}
    458 	TRACE(("checkpubkey: base64_decode success"))
    459 	buf_incrlen(decodekey, decodekeylen);
    460 
    461 	if (fingerprint) {
    462 		*fingerprint = sign_key_fingerprint(buf_getptr(decodekey, decodekeylen),
    463 											decodekeylen);
    464 	}
    465 
    466 	/* compare the keys */
    467 	if ( ( decodekeylen != keybloblen )
    468 			|| memcmp( buf_getptr(decodekey, decodekey->len),
    469 						keyblob, decodekey->len) != 0) {
    470 		TRACE(("checkpubkey: compare failed"))
    471 		goto out;
    472 	}
    473 
    474 	/* ... and also check that the algo specified and the algo in the key
    475 	 * itself match */
    476 	filealgolen = buf_getint(decodekey);
    477 	filealgo = buf_getptr(decodekey, filealgolen);
    478 	if (filealgolen != algolen || memcmp(filealgo, algoname, algolen) != 0) {
    479 		TRACE(("checkpubkey: algo match failed"))
    480 		goto out;
    481 	}
    482 
    483 	/* All checks passed */
    484 	ret = DROPBEAR_SUCCESS;
    485 
    486 out:
    487 	buf_free(decodekey);
    488 	decodekey = NULL;
    489 	return ret;
    490 }
    491 #endif
    492