diff --git a/ssh/auth-options.c b/ssh/auth-options.c index c6116ec..b0c9fe6 100644 --- a/ssh/auth-options.c +++ b/ssh/auth-options.c @@ -574,7 +574,7 @@ parse_option_list(u_char *optblob, size_t optblob_len, struct passwd *pw, * options so this must be called after auth_parse_options(). */ int -auth_cert_options(Key *k, struct passwd *pw) +auth_cert_options(struct sshkey *k, struct passwd *pw) { int cert_no_port_forwarding_flag = 1; int cert_no_agent_forwarding_flag = 1; @@ -584,10 +584,10 @@ auth_cert_options(Key *k, struct passwd *pw) char *cert_forced_command = NULL; int cert_source_address_done = 0; - if (key_cert_is_legacy(k)) { + if (sshkey_cert_is_legacy(k)) { /* All options are in the one field for v00 certs */ - if (parse_option_list(buffer_ptr(&k->cert->critical), - buffer_len(&k->cert->critical), pw, + if (parse_option_list(buffer_ptr(k->cert->critical), + buffer_len(k->cert->critical), pw, OPTIONS_CRITICAL|OPTIONS_EXTENSIONS, 1, &cert_no_port_forwarding_flag, &cert_no_agent_forwarding_flag, @@ -599,14 +599,14 @@ auth_cert_options(Key *k, struct passwd *pw) return -1; } else { /* Separate options and extensions for v01 certs */ - if (parse_option_list(buffer_ptr(&k->cert->critical), - buffer_len(&k->cert->critical), pw, + if (parse_option_list(buffer_ptr(k->cert->critical), + buffer_len(k->cert->critical), pw, OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL, &cert_forced_command, &cert_source_address_done) == -1) return -1; - if (parse_option_list(buffer_ptr(&k->cert->extensions), - buffer_len(&k->cert->extensions), pw, + if (parse_option_list(buffer_ptr(k->cert->extensions), + buffer_len(k->cert->extensions), pw, OPTIONS_EXTENSIONS, 1, &cert_no_port_forwarding_flag, &cert_no_agent_forwarding_flag, diff --git a/ssh/auth-options.h b/ssh/auth-options.h index 7455c94..43fd240 100644 --- a/ssh/auth-options.h +++ b/ssh/auth-options.h @@ -35,6 +35,6 @@ extern char *authorized_principals; int auth_parse_options(struct passwd *, char *, char *, u_long); void auth_clear_options(void); -int auth_cert_options(Key *, struct passwd *); +int auth_cert_options(struct sshkey *, struct passwd *); #endif diff --git a/ssh/auth-rh-rsa.c b/ssh/auth-rh-rsa.c index d233e49..6892a41 100644 --- a/ssh/auth-rh-rsa.c +++ b/ssh/auth-rh-rsa.c @@ -38,7 +38,7 @@ extern ServerOptions options; int auth_rhosts_rsa_key_allowed(struct passwd *pw, char *cuser, char *chost, - Key *client_host_key) + struct sshkey *client_host_key) { HostStatus host_status; @@ -61,7 +61,8 @@ auth_rhosts_rsa_key_allowed(struct passwd *pw, char *cuser, char *chost, * its host key. Returns true if authentication succeeds. */ int -auth_rhosts_rsa(Authctxt *authctxt, char *cuser, Key *client_host_key) +auth_rhosts_rsa(Authctxt *authctxt, char *cuser, + struct sshkey *client_host_key) { char *chost; struct passwd *pw = authctxt->pw; diff --git a/ssh/auth-rsa.c b/ssh/auth-rsa.c index d9fc56e..468b24f 100644 --- a/ssh/auth-rsa.c +++ b/ssh/auth-rsa.c @@ -65,7 +65,7 @@ extern u_char session_id[16]; */ BIGNUM * -auth_rsa_generate_challenge(Key *key) +auth_rsa_generate_challenge(struct sshkey *key) { BIGNUM *challenge; BN_CTX *ctx; @@ -85,7 +85,8 @@ auth_rsa_generate_challenge(Key *key) } int -auth_rsa_verify_response(Key *key, BIGNUM *challenge, u_char response[16]) +auth_rsa_verify_response(struct sshkey *key, BIGNUM *challenge, + u_char response[16]) { u_char buf[32], mdbuf[16]; MD5_CTX md; @@ -125,7 +126,7 @@ auth_rsa_verify_response(Key *key, BIGNUM *challenge, u_char response[16]) */ int -auth_rsa_challenge_dialog(Key *key) +auth_rsa_challenge_dialog(struct sshkey *key) { BIGNUM *challenge, *encrypted_challenge; u_char response[16]; @@ -159,14 +160,14 @@ auth_rsa_challenge_dialog(Key *key) static int rsa_key_allowed_in_file(struct passwd *pw, char *file, - const BIGNUM *client_n, Key **rkey) + const BIGNUM *client_n, struct sshkey **rkey) { char line[SSH_MAX_PUBKEY_BYTES]; int allowed = 0; u_int bits; FILE *f; u_long linenum = 0; - Key *key; + struct sshkey *key; debug("trying public RSA key file %s", file); if ((f = auth_openkeyfile(file, pw, options.strict_modes)) == NULL) @@ -177,7 +178,8 @@ rsa_key_allowed_in_file(struct passwd *pw, char *file, * found, perform a challenge-response dialog to verify that the * user really has the corresponding private key. */ - key = key_new(KEY_RSA1); + if ((key = sshkey_new(KEY_RSA1)) == NULL) + fatal("%s: sshkey_new failed", __func__); while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { char *cp; char *key_options; @@ -254,7 +256,7 @@ rsa_key_allowed_in_file(struct passwd *pw, char *file, if (allowed && rkey != NULL) *rkey = key; else - key_free(key); + sshkey_free(key); return allowed; } @@ -265,7 +267,8 @@ rsa_key_allowed_in_file(struct passwd *pw, char *file, */ int -auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) +auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, + struct sshkey **rkey) { char *file; u_int i, allowed = 0; @@ -292,7 +295,7 @@ auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) int auth_rsa(Authctxt *authctxt, BIGNUM *client_n) { - Key *key; + struct sshkey *key; char *fp; struct passwd *pw = authctxt->pw; @@ -314,7 +317,7 @@ auth_rsa(Authctxt *authctxt, BIGNUM *client_n) * Break out of the loop. Otherwise we might send * another challenge and break the protocol. */ - key_free(key); + sshkey_free(key); return (0); } /* @@ -323,11 +326,11 @@ auth_rsa(Authctxt *authctxt, BIGNUM *client_n) * options; this will be reset if the options cause the * authentication to be rejected. */ - fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + fp = sshkey_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); verbose("Found matching %s key: %s", - key_type(key), fp); + sshkey_type(key), fp); xfree(fp); - key_free(key); + sshkey_free(key); packet_send_debug("RSA authentication accepted."); return (1); diff --git a/ssh/auth.c b/ssh/auth.c index d3663a4..cd015ef 100644 --- a/ssh/auth.c +++ b/ssh/auth.c @@ -274,7 +274,7 @@ authorized_principals_file(struct passwd *pw) /* return ok if key exists in sysfile or userfile */ HostStatus -check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host, +check_key_in_hostfiles(struct passwd *pw, struct sshkey *key, const char *host, const char *sysfile, const char *userfile) { char *user_hostfile; @@ -480,7 +480,7 @@ getpwnamallow(const char *user) /* Returns 1 if key is revoked by revoked_keys_file, 0 otherwise */ int -auth_key_is_revoked(Key *key) +auth_key_is_revoked(struct sshkey *key) { char *key_fp; @@ -498,9 +498,9 @@ auth_key_is_revoked(Key *key) return 1; case 1: /* Key revoked */ - key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + key_fp = sshkey_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); error("WARNING: authentication attempt with a revoked " - "%s key %s ", key_type(key), key_fp); + "%s key %s ", sshkey_type(key), key_fp); xfree(key_fp); return 1; } diff --git a/ssh/auth.h b/ssh/auth.h index 8d91bc1..b55fea8 100644 --- a/ssh/auth.h +++ b/ssh/auth.h @@ -100,17 +100,19 @@ int auth_rhosts(struct passwd *, const char *); int auth_rhosts2(struct passwd *, const char *, const char *, const char *); -int auth_rhosts_rsa(Authctxt *, char *, Key *); +int auth_rhosts_rsa(Authctxt *, char *, struct sshkey *); int auth_password(Authctxt *, const char *); int auth_rsa(Authctxt *, BIGNUM *); -int auth_rsa_challenge_dialog(Key *); -BIGNUM *auth_rsa_generate_challenge(Key *); -int auth_rsa_verify_response(Key *, BIGNUM *, u_char[]); -int auth_rsa_key_allowed(struct passwd *, BIGNUM *, Key **); +int auth_rsa_challenge_dialog(struct sshkey *); +BIGNUM *auth_rsa_generate_challenge(struct sshkey *); +int auth_rsa_verify_response(struct sshkey *, BIGNUM *, u_char[]); +int auth_rsa_key_allowed(struct passwd *, BIGNUM *, struct sshkey **); -int auth_rhosts_rsa_key_allowed(struct passwd *, char *, char *, Key *); -int hostbased_key_allowed(struct passwd *, const char *, char *, Key *); -int user_key_allowed(struct passwd *, Key *); +int auth_rhosts_rsa_key_allowed(struct passwd *, char *, char *, + struct sshkey *); +int hostbased_key_allowed(struct passwd *, const char *, char *, + struct sshkey *); +int user_key_allowed(struct passwd *, struct sshkey *); #ifdef KRB5 int auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client, krb5_data *); @@ -151,17 +153,17 @@ char *authorized_principals_file(struct passwd *); FILE *auth_openkeyfile(const char *, struct passwd *, int); FILE *auth_openprincipals(const char *, struct passwd *, int); -int auth_key_is_revoked(Key *); +int auth_key_is_revoked(struct sshkey *); HostStatus -check_key_in_hostfiles(struct passwd *, Key *, const char *, +check_key_in_hostfiles(struct passwd *, struct sshkey *, const char *, const char *, const char *); /* hostkey handling */ -Key *get_hostkey_by_index(int); -Key *get_hostkey_public_by_type(int, struct ssh *); -Key *get_hostkey_private_by_type(int, struct ssh *); -int get_hostkey_index(Key *); +struct sshkey *get_hostkey_by_index(int); +struct sshkey *get_hostkey_public_by_type(int, struct ssh *); +struct sshkey *get_hostkey_private_by_type(int, struct ssh *); +int get_hostkey_index(struct sshkey *); int ssh1_session_key(BIGNUM *); /* debug messages during authentication */ diff --git a/ssh/auth1.c b/ssh/auth1.c index 63fecc1..7db869b 100644 --- a/ssh/auth1.c +++ b/ssh/auth1.c @@ -153,7 +153,7 @@ auth1_process_rhosts_rsa(Authctxt *authctxt, char *info, size_t infolen) int keybits, authenticated = 0; u_int bits; char *client_user; - Key *client_host_key; + struct sshkey *client_host_key; u_int ulen; /* @@ -164,7 +164,7 @@ auth1_process_rhosts_rsa(Authctxt *authctxt, char *info, size_t infolen) client_user = packet_get_cstring(&ulen); /* Get the client host key. */ - client_host_key = key_new(KEY_RSA1); + client_host_key = sshkey_new(KEY_RSA1); bits = packet_get_int(); packet_get_bignum(client_host_key->rsa->e); packet_get_bignum(client_host_key->rsa->n); @@ -179,7 +179,7 @@ auth1_process_rhosts_rsa(Authctxt *authctxt, char *info, size_t infolen) authenticated = auth_rhosts_rsa(authctxt, client_user, client_host_key); - key_free(client_host_key); + sshkey_free(client_host_key); snprintf(info, infolen, " ruser %.100s", client_user); xfree(client_user); diff --git a/ssh/auth2-hostbased.c b/ssh/auth2-hostbased.c index 396026e..d73a241 100644 --- a/ssh/auth2-hostbased.c +++ b/ssh/auth2-hostbased.c @@ -46,6 +46,7 @@ #endif #include "monitor_wrap.h" #include "pathnames.h" +#include "err.h" /* import */ extern ServerOptions options; @@ -57,15 +58,14 @@ userauth_hostbased(struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; Buffer b; - Key *key = NULL; + struct sshkey *key = NULL; char *pkalg, *cuser, *chost, *service; u_char *pkblob, *sig; u_int alen, blen, slen; - int pktype; - int authenticated = 0; + int r, pktype, authenticated = 0; if (!authctxt->valid) { - debug2("userauth_hostbased: disabled because of invalid user"); + debug2("%s: disabled because of invalid user", __func__); return 0; } pkalg = ssh_packet_get_string(ssh, &alen); @@ -74,7 +74,7 @@ userauth_hostbased(struct ssh *ssh) cuser = ssh_packet_get_string(ssh, NULL); sig = ssh_packet_get_string(ssh, &slen); - debug("userauth_hostbased: cuser %s chost %s pkalg %s slen %d", + debug("%s: cuser %s chost %s pkalg %s slen %d", __func__, cuser, chost, pkalg, slen); #ifdef DEBUG_PK debug("signature:"); @@ -83,21 +83,24 @@ userauth_hostbased(struct ssh *ssh) buffer_dump(&b); buffer_free(&b); #endif - pktype = key_type_from_name(pkalg); + pktype = sshkey_type_from_name(pkalg); if (pktype == KEY_UNSPEC) { /* this is perfectly legal */ - logit("userauth_hostbased: unsupported " - "public key algorithm: %s", pkalg); + logit("%s: unsupported public key algorithm: %s", + __func__, pkalg); + goto done; + } + if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) { + error("%s: key_from_blob: %s", __func__, ssh_err(r)); goto done; } - key = key_from_blob(pkblob, blen); if (key == NULL) { - error("userauth_hostbased: cannot decode key: %s", pkalg); + error("%s: cannot decode key: %s", __func__, pkalg); goto done; } if (key->type != pktype) { - error("userauth_hostbased: type mismatch for decoded key " - "(received %d, expected %d)", key->type, pktype); + error("%s: type mismatch for decoded key " + "(received %d, expected %d)", __func__, key->type, pktype); goto done; } service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" : @@ -119,15 +122,15 @@ userauth_hostbased(struct ssh *ssh) /* test for allowed key and correct signature */ authenticated = 0; if (PRIVSEP(hostbased_key_allowed(authctxt->pw, cuser, chost, key)) && - PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), - buffer_len(&b))) == 1) + PRIVSEP(sshkey_verify(key, sig, slen, + buffer_ptr(&b), buffer_len(&b), datafellows)) == 0) authenticated = 1; buffer_free(&b); done: - debug2("userauth_hostbased: authenticated %d", authenticated); + debug2("%s: authenticated %d", __func__, authenticated); if (key != NULL) - key_free(key); + sshkey_free(key); xfree(pkalg); xfree(pkblob); xfree(cuser); @@ -139,7 +142,7 @@ done: /* return 1 if given hostkey is allowed */ int hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, - Key *key) + struct sshkey *key) { const char *resolvedname, *ipaddr, *lookup, *reason; HostStatus host_status; @@ -175,8 +178,8 @@ hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, } debug2("userauth_hostbased: access allowed by auth_rhosts2"); - if (key_is_cert(key) && - key_cert_check_authority(key, 1, 0, lookup, &reason)) { + if (sshkey_is_cert(key) && + sshkey_cert_check_authority(key, 1, 0, lookup, &reason)) { error("%s", reason); auth_debug_add("%s", reason); return 0; @@ -195,17 +198,17 @@ hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, } if (host_status == HOST_OK) { - if (key_is_cert(key)) { - fp = key_fingerprint(key->cert->signature_key, + if (sshkey_is_cert(key)) { + fp = sshkey_fingerprint(key->cert->signature_key, SSH_FP_MD5, SSH_FP_HEX); verbose("Accepted certificate ID \"%s\" signed by " "%s CA %s from %s@%s", key->cert->key_id, - key_type(key->cert->signature_key), fp, + sshkey_type(key->cert->signature_key), fp, cuser, lookup); } else { - fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + fp = sshkey_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); verbose("Accepted %s public key %s from %s@%s", - key_type(key), fp, cuser, lookup); + sshkey_type(key), fp, cuser, lookup); } xfree(fp); } diff --git a/ssh/auth2-jpake.c b/ssh/auth2-jpake.c index 93d5395..72684db 100644 --- a/ssh/auth2-jpake.c +++ b/ssh/auth2-jpake.c @@ -144,7 +144,7 @@ derive_rawsalt(const char *username, u_char *rawsalt, u_int len) u_char *digest; u_int digest_len; Buffer b; - Key *k; + struct sshkey *k; buffer_init(&b); buffer_put_cstring(&b, username); diff --git a/ssh/auth2-pubkey.c b/ssh/auth2-pubkey.c index 46cf30d..5beb104 100644 --- a/ssh/auth2-pubkey.c +++ b/ssh/auth2-pubkey.c @@ -57,6 +57,7 @@ #include "misc.h" #include "authfile.h" #include "match.h" +#include "err.h" /* import */ extern ServerOptions options; @@ -68,20 +69,20 @@ userauth_pubkey(struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; Buffer b; - Key *key = NULL; + struct sshkey *key = NULL; char *pkalg; u_char *pkblob, *sig; u_int alen, blen, slen; - int have_sig, pktype; + int r, have_sig, pktype; int authenticated = 0; if (!authctxt->valid) { - debug2("userauth_pubkey: disabled because of invalid user"); + debug2("%s: disabled because of invalid user", __func__); return 0; } have_sig = ssh_packet_get_char(ssh); if (datafellows & SSH_BUG_PKAUTH) { - debug2("userauth_pubkey: SSH_BUG_PKAUTH"); + debug2("%s: SSH_BUG_PKAUTH", __func__); /* no explicit pkalg given */ pkblob = ssh_packet_get_string(ssh, &blen); buffer_init(&b); @@ -93,21 +94,24 @@ userauth_pubkey(struct ssh *ssh) pkalg = ssh_packet_get_string(ssh, &alen); pkblob = ssh_packet_get_string(ssh, &blen); } - pktype = key_type_from_name(pkalg); + pktype = sshkey_type_from_name(pkalg); if (pktype == KEY_UNSPEC) { /* this is perfectly legal */ - logit("userauth_pubkey: unsupported public key algorithm: %s", + logit("%s: unsupported public key algorithm: %s", __func__, pkalg); goto done; } - key = key_from_blob(pkblob, blen); + if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) { + error("%s: could not parse key: %s", __func__, ssh_err(r)); + goto done; + } if (key == NULL) { - error("userauth_pubkey: cannot decode key: %s", pkalg); + error("%s: cannot decode key: %s", __func__, pkalg); goto done; } if (key->type != pktype) { - error("userauth_pubkey: type mismatch for decoded key " - "(received %d, expected %d)", key->type, pktype); + error("%s: type mismatch for decoded key " + "(received %d, expected %d)", __func__, key->type, pktype); goto done; } if (have_sig) { @@ -140,8 +144,8 @@ userauth_pubkey(struct ssh *ssh) /* test for correct signature */ authenticated = 0; if (PRIVSEP(user_key_allowed(authctxt->pw, key)) && - PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), - buffer_len(&b))) == 1) + PRIVSEP(sshkey_verify(key, sig, slen, buffer_ptr(&b), + buffer_len(&b), datafellows)) == 1) authenticated = 1; buffer_free(&b); xfree(sig); @@ -169,16 +173,16 @@ userauth_pubkey(struct ssh *ssh) if (authenticated != 1) auth_clear_options(); done: - debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg); + debug2("%s: authenticated %d pkalg %s", __func__, authenticated, pkalg); if (key != NULL) - key_free(key); + sshkey_free(key); xfree(pkalg); xfree(pkblob); return authenticated; } static int -match_principals_option(const char *principal_list, struct KeyCert *cert) +match_principals_option(const char *principal_list, struct sshkey_cert *cert) { char *result; u_int i; @@ -198,7 +202,7 @@ match_principals_option(const char *principal_list, struct KeyCert *cert) } static int -match_principals_file(char *file, struct passwd *pw, struct KeyCert *cert) +match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert) { FILE *f; char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts; @@ -257,14 +261,14 @@ match_principals_file(char *file, struct passwd *pw, struct KeyCert *cert) /* return 1 if user allows given key */ static int -user_key_allowed2(struct passwd *pw, Key *key, char *file) +user_key_allowed2(struct passwd *pw, struct sshkey *key, char *file) { char line[SSH_MAX_PUBKEY_BYTES]; const char *reason; int found_key = 0; FILE *f; u_long linenum = 0; - Key *found; + struct sshkey *found = NULL; char *fp; /* Temporarily use the user's uid. */ @@ -279,7 +283,9 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) } found_key = 0; - found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type); + found = sshkey_new(sshkey_is_cert(key) ? KEY_UNSPEC : key->type); + if (found == NULL) + goto done; while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { char *cp, *key_options = NULL; @@ -292,7 +298,7 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) if (!*cp || *cp == '\n' || *cp == '#') continue; - if (key_read(found, &cp) != 1) { + if (sshkey_read(found, &cp) != 0) { /* no key? check if there are options for this key */ int quoted = 0; debug2("user_key_allowed: check options: '%s'", cp); @@ -306,24 +312,24 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) /* Skip remaining whitespace. */ for (; *cp == ' ' || *cp == '\t'; cp++) ; - if (key_read(found, &cp) != 1) { + if (sshkey_read(found, &cp) != 0) { debug2("user_key_allowed: advance: '%s'", cp); /* still no key? advance to next line*/ continue; } } - if (key_is_cert(key)) { - if (!key_equal(found, key->cert->signature_key)) + if (sshkey_is_cert(key)) { + if (!sshkey_equal(found, key->cert->signature_key)) continue; if (auth_parse_options(pw, key_options, file, linenum) != 1) continue; if (!key_is_cert_authority) continue; - fp = key_fingerprint(found, SSH_FP_MD5, + fp = sshkey_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); debug("matching CA found: file %s, line %lu, %s %s", - file, linenum, key_type(found), fp); + file, linenum, sshkey_type(found), fp); /* * If the user has specified a list of principals as * a key option, then prefer that list to matching @@ -340,7 +346,7 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) auth_debug_add("%s", reason); continue; } - if (key_cert_check_authority(key, 0, 0, + if (sshkey_cert_check_authority(key, 0, 0, authorized_principals == NULL ? pw->pw_name : NULL, &reason) != 0) goto fail_reason; @@ -350,11 +356,11 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) } verbose("Accepted certificate ID \"%s\" " "signed by %s CA %s via %s", key->cert->key_id, - key_type(found), fp, file); + sshkey_type(found), fp, file); xfree(fp); found_key = 1; break; - } else if (key_equal(found, key)) { + } else if (sshkey_equal(found, key)) { if (auth_parse_options(pw, key_options, file, linenum) != 1) continue; @@ -363,16 +369,18 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) found_key = 1; debug("matching key found: file %s, line %lu", file, linenum); - fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); + fp = sshkey_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); verbose("Found matching %s key: %s", - key_type(found), fp); + sshkey_type(found), fp); xfree(fp); break; } } + done: restore_uid(); fclose(f); - key_free(found); + if (found != NULL) + sshkey_free(found); if (!found_key) debug2("key not found"); return found_key; @@ -380,22 +388,22 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) /* Authenticate a certificate key against TrustedUserCAKeys */ static int -user_cert_trusted_ca(struct passwd *pw, Key *key) +user_cert_trusted_ca(struct passwd *pw, struct sshkey *key) { char *ca_fp, *principals_file = NULL; const char *reason; int ret = 0; - if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL) + if (!sshkey_is_cert(key) || options.trusted_user_ca_keys == NULL) return 0; - ca_fp = key_fingerprint(key->cert->signature_key, + ca_fp = sshkey_fingerprint(key->cert->signature_key, SSH_FP_MD5, SSH_FP_HEX); if (key_in_file(key->cert->signature_key, options.trusted_user_ca_keys, 1) != 1) { debug2("%s: CA %s %s is not listed in %s", __func__, - key_type(key->cert->signature_key), ca_fp, + sshkey_type(key->cert->signature_key), ca_fp, options.trusted_user_ca_keys); goto out; } @@ -414,14 +422,14 @@ user_cert_trusted_ca(struct passwd *pw, Key *key) goto out; } } - if (key_cert_check_authority(key, 0, 1, + if (sshkey_cert_check_authority(key, 0, 1, principals_file == NULL ? pw->pw_name : NULL, &reason) != 0) goto fail_reason; if (auth_cert_options(key, pw) != 0) goto out; verbose("Accepted certificate ID \"%s\" signed by %s CA %s via %s", - key->cert->key_id, key_type(key->cert->signature_key), ca_fp, + key->cert->key_id, sshkey_type(key->cert->signature_key), ca_fp, options.trusted_user_ca_keys); ret = 1; @@ -435,14 +443,15 @@ user_cert_trusted_ca(struct passwd *pw, Key *key) /* check whether given key is in .ssh/authorized_keys* */ int -user_key_allowed(struct passwd *pw, Key *key) +user_key_allowed(struct passwd *pw, struct sshkey *key) { u_int success, i; char *file; if (auth_key_is_revoked(key)) return 0; - if (key_is_cert(key) && auth_key_is_revoked(key->cert->signature_key)) + if (sshkey_is_cert(key) && + auth_key_is_revoked(key->cert->signature_key)) return 0; success = user_cert_trusted_ca(pw, key); diff --git a/ssh/authfd.c b/ssh/authfd.c index c6807b7..88d7101 100644 --- a/ssh/authfd.c +++ b/ssh/authfd.c @@ -61,6 +61,7 @@ #include "log.h" #include "atomicio.h" #include "misc.h" +#include "err.h" static int agent_present = 0; @@ -299,7 +300,7 @@ ssh_get_num_identities(AuthenticationConnection *auth, int version) return auth->howmany; } -Key * +struct sshkey * ssh_get_first_identity(AuthenticationConnection *auth, char **comment, int version) { /* get number of identities and return the first entry (if any). */ @@ -308,14 +309,14 @@ ssh_get_first_identity(AuthenticationConnection *auth, char **comment, int versi return NULL; } -Key * +struct sshkey * ssh_get_next_identity(AuthenticationConnection *auth, char **comment, int version) { - int keybits; + int keybits, ret; u_int bits; u_char *blob; u_int blen; - Key *key = NULL; + struct sshkey *key = NULL; /* Return failure if no more entries. */ if (auth->howmany <= 0) @@ -327,7 +328,8 @@ ssh_get_next_identity(AuthenticationConnection *auth, char **comment, int versio */ switch (version) { case 1: - key = key_new(KEY_RSA1); + if ((key = sshkey_new(KEY_RSA1)) == NULL) + fatal("%s: sshkey_new failed", __func__); bits = buffer_get_int(&auth->identities); buffer_get_bignum(&auth->identities, key->rsa->e); buffer_get_bignum(&auth->identities, key->rsa->n); @@ -340,7 +342,9 @@ ssh_get_next_identity(AuthenticationConnection *auth, char **comment, int versio case 2: blob = buffer_get_string(&auth->identities, &blen); *comment = buffer_get_string(&auth->identities, NULL); - key = key_from_blob(blob, blen); + if ((ret = sshkey_from_blob(blob, blen, &key)) != 0) + fatal("%s: sshkey_from_blob: %s", + __func__, ssh_err(ret)); xfree(blob); break; default: @@ -361,7 +365,7 @@ ssh_get_next_identity(AuthenticationConnection *auth, char **comment, int versio int ssh_decrypt_challenge(AuthenticationConnection *auth, - Key* key, BIGNUM *challenge, + struct sshkey* key, BIGNUM *challenge, u_char session_id[16], u_int response_type, u_char response[16]) @@ -409,10 +413,10 @@ ssh_decrypt_challenge(AuthenticationConnection *auth, return success; } -/* ask agent to sign data, returns -1 on error, 0 on success */ +/* ask agent to sign data, returns err.h code on error, 0 on success */ int ssh_agent_sign(AuthenticationConnection *auth, - Key *key, + struct sshkey *key, u_char **sigp, u_int *lenp, u_char *data, u_int datalen) { @@ -421,10 +425,12 @@ ssh_agent_sign(AuthenticationConnection *auth, u_char *blob; u_int blen; int type, flags = 0; - int ret = -1; + int r, ret = SSH_ERR_AGENT_FAILURE; - if (key_to_blob(key, &blob, &blen) == 0) - return -1; + if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) { + error("%s: sshkey_to_blob: %s", __func__, ssh_err(r)); + return SSH_ERR_INVALID_ARGUMENT; + } if (datafellows & SSH_BUG_SIGBLOB) flags = SSH_AGENT_OLD_SIGNATURE; @@ -438,7 +444,7 @@ ssh_agent_sign(AuthenticationConnection *auth, if (ssh_request_reply(auth, &msg, &msg) == 0) { buffer_free(&msg); - return -1; + return SSH_ERR_AGENT_COMMUNICATION; } type = buffer_get_char(&msg); if (agent_failed(type)) { @@ -470,9 +476,9 @@ ssh_encode_identity_rsa1(Buffer *b, RSA *key, const char *comment) } static void -ssh_encode_identity_ssh2(Buffer *b, Key *key, const char *comment) +ssh_encode_identity_ssh2(Buffer *b, struct sshkey *key, const char *comment) { - buffer_put_cstring(b, key_ssh_name(key)); + buffer_put_cstring(b, sshkey_ssh_name(key)); switch (key->type) { case KEY_RSA: buffer_put_bignum2(b, key->rsa->n); @@ -484,10 +490,10 @@ ssh_encode_identity_ssh2(Buffer *b, Key *key, const char *comment) break; case KEY_RSA_CERT_V00: case KEY_RSA_CERT: - if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0) + if (key->cert == NULL || buffer_len(key->cert->certblob) == 0) fatal("%s: no cert/certblob", __func__); - buffer_put_string(b, buffer_ptr(&key->cert->certblob), - buffer_len(&key->cert->certblob)); + buffer_put_string(b, buffer_ptr(key->cert->certblob), + buffer_len(key->cert->certblob)); buffer_put_bignum2(b, key->rsa->d); buffer_put_bignum2(b, key->rsa->iqmp); buffer_put_bignum2(b, key->rsa->p); @@ -502,23 +508,23 @@ ssh_encode_identity_ssh2(Buffer *b, Key *key, const char *comment) break; case KEY_DSA_CERT_V00: case KEY_DSA_CERT: - if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0) + if (key->cert == NULL || buffer_len(key->cert->certblob) == 0) fatal("%s: no cert/certblob", __func__); - buffer_put_string(b, buffer_ptr(&key->cert->certblob), - buffer_len(&key->cert->certblob)); + buffer_put_string(b, buffer_ptr(key->cert->certblob), + buffer_len(key->cert->certblob)); buffer_put_bignum2(b, key->dsa->priv_key); break; case KEY_ECDSA: - buffer_put_cstring(b, key_curve_nid_to_name(key->ecdsa_nid)); + buffer_put_cstring(b, sshkey_curve_nid_to_name(key->ecdsa_nid)); buffer_put_ecpoint(b, EC_KEY_get0_group(key->ecdsa), EC_KEY_get0_public_key(key->ecdsa)); buffer_put_bignum2(b, EC_KEY_get0_private_key(key->ecdsa)); break; case KEY_ECDSA_CERT: - if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0) + if (key->cert == NULL || buffer_len(key->cert->certblob) == 0) fatal("%s: no cert/certblob", __func__); - buffer_put_string(b, buffer_ptr(&key->cert->certblob), - buffer_len(&key->cert->certblob)); + buffer_put_string(b, buffer_ptr(key->cert->certblob), + buffer_len(key->cert->certblob)); buffer_put_bignum2(b, EC_KEY_get0_private_key(key->ecdsa)); break; } @@ -531,7 +537,7 @@ ssh_encode_identity_ssh2(Buffer *b, Key *key, const char *comment) */ int -ssh_add_identity_constrained(AuthenticationConnection *auth, Key *key, +ssh_add_identity_constrained(AuthenticationConnection *auth, struct sshkey *key, const char *comment, u_int life, u_int confirm) { Buffer msg; @@ -588,10 +594,10 @@ ssh_add_identity_constrained(AuthenticationConnection *auth, Key *key, */ int -ssh_remove_identity(AuthenticationConnection *auth, Key *key) +ssh_remove_identity(AuthenticationConnection *auth, struct sshkey *key) { Buffer msg; - int type; + int r, type; u_char *blob; u_int blen; @@ -602,10 +608,13 @@ ssh_remove_identity(AuthenticationConnection *auth, Key *key) buffer_put_int(&msg, BN_num_bits(key->rsa->n)); buffer_put_bignum(&msg, key->rsa->e); buffer_put_bignum(&msg, key->rsa->n); - } else if (key_type_plain(key->type) == KEY_DSA || - key_type_plain(key->type) == KEY_RSA || - key_type_plain(key->type) == KEY_ECDSA) { - key_to_blob(key, &blob, &blen); + } else if (sshkey_type_plain(key->type) == KEY_DSA || + sshkey_type_plain(key->type) == KEY_RSA || + sshkey_type_plain(key->type) == KEY_ECDSA) { + if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) { + error("%s: sshkey_to_blob: %s", __func__, ssh_err(r)); + return 0; + } buffer_put_char(&msg, SSH2_AGENTC_REMOVE_IDENTITY); buffer_put_string(&msg, blob, blen); xfree(blob); diff --git a/ssh/authfd.h b/ssh/authfd.h index 2582a27..1ccc812 100644 --- a/ssh/authfd.h +++ b/ssh/authfd.h @@ -73,22 +73,21 @@ void ssh_close_authentication_socket(int); AuthenticationConnection *ssh_get_authentication_connection(void); void ssh_close_authentication_connection(AuthenticationConnection *); int ssh_get_num_identities(AuthenticationConnection *, int); -Key *ssh_get_first_identity(AuthenticationConnection *, char **, int); -Key *ssh_get_next_identity(AuthenticationConnection *, char **, int); -int ssh_add_identity_constrained(AuthenticationConnection *, Key *, - const char *, u_int, u_int); -int ssh_remove_identity(AuthenticationConnection *, Key *); +struct sshkey *ssh_get_first_identity(AuthenticationConnection *, char **, int); +struct sshkey *ssh_get_next_identity(AuthenticationConnection *, char **, int); +int ssh_add_identity_constrained(AuthenticationConnection *, + struct sshkey *, const char *, u_int, u_int); +int ssh_remove_identity(AuthenticationConnection *, struct sshkey *); int ssh_remove_all_identities(AuthenticationConnection *, int); int ssh_lock_agent(AuthenticationConnection *, int, const char *); int ssh_update_card(AuthenticationConnection *, int, const char *, const char *, u_int, u_int); -int -ssh_decrypt_challenge(AuthenticationConnection *, Key *, BIGNUM *, u_char[16], - u_int, u_char[16]); +int ssh_decrypt_challenge(AuthenticationConnection *, struct sshkey *, + BIGNUM *, u_char[16], u_int, u_char[16]); int -ssh_agent_sign(AuthenticationConnection *, Key *, u_char **, u_int *, u_char *, - u_int); +ssh_agent_sign(AuthenticationConnection *, struct sshkey *, + u_char **, u_int *, u_char *, u_int); #endif /* AUTHFD_H */ diff --git a/ssh/authfile.c b/ssh/authfile.c index 228934a..d7e8bc6 100644 --- a/ssh/authfile.c +++ b/ssh/authfile.c @@ -63,6 +63,7 @@ #include "rsa.h" #include "misc.h" #include "atomicio.h" +#include "err.h" #define MAX_KEY_FILE_SIZE (1024 * 1024) @@ -77,8 +78,8 @@ static const char authfile_id_string[] = * passphrase. */ static int -key_private_rsa1_to_blob(Key *key, Buffer *blob, const char *passphrase, - const char *comment) +key_private_rsa1_to_blob(struct sshkey *key, Buffer *blob, + const char *passphrase, const char *comment) { Buffer buffer, encrypted; u_char buf[100], *cp; @@ -161,8 +162,8 @@ key_private_rsa1_to_blob(Key *key, Buffer *blob, const char *passphrase, /* convert SSH v2 key in OpenSSL PEM format */ static int -key_private_pem_to_blob(Key *key, Buffer *blob, const char *_passphrase, - const char *comment) +key_private_pem_to_blob(struct sshkey *key, Buffer *blob, + const char *_passphrase, const char *comment) { int success = 0; int blen, len = strlen(_passphrase); @@ -227,7 +228,7 @@ key_save_private_blob(Buffer *keybuf, const char *filename) /* Serialise "key" to buffer "blob" */ static int -key_private_to_blob(Key *key, Buffer *blob, const char *passphrase, +key_private_to_blob(struct sshkey *key, Buffer *blob, const char *passphrase, const char *comment) { switch (key->type) { @@ -244,8 +245,8 @@ key_private_to_blob(Key *key, Buffer *blob, const char *passphrase, } int -key_save_private(Key *key, const char *filename, const char *passphrase, - const char *comment) +key_save_private(struct sshkey *key, const char *filename, + const char *passphrase, const char *comment) { Buffer keyblob; int success = 0; @@ -264,10 +265,10 @@ key_save_private(Key *key, const char *filename, const char *passphrase, /* * Parse the public, unencrypted portion of a RSA1 key. */ -static Key * +static struct sshkey * key_parse_public_rsa1(Buffer *blob, char **commentp) { - Key *pub; + struct sshkey *pub; Buffer copy; /* Check that it is at least big enough to contain the ID string. */ @@ -295,11 +296,14 @@ key_parse_public_rsa1(Buffer *blob, char **commentp) /* Read the public key from the buffer. */ (void) buffer_get_int(©); - pub = key_new(KEY_RSA1); + if ((pub = sshkey_new(KEY_RSA1)) == NULL) + goto out; buffer_get_bignum(©, pub->rsa->n); buffer_get_bignum(©, pub->rsa->e); if (commentp) *commentp = buffer_get_string(©, NULL); + + out: /* The encrypted private part is not parsed by this function. */ buffer_free(©); @@ -366,11 +370,11 @@ key_load_file(int fd, const char *filename, Buffer *blob) * encountered (the file does not exist or is not readable), and the key * otherwise. */ -static Key * +static struct sshkey * key_load_public_rsa1(int fd, const char *filename, char **commentp) { Buffer buffer; - Key *pub; + struct sshkey *pub; buffer_init(&buffer); if (!key_load_file(fd, filename, &buffer)) { @@ -386,10 +390,10 @@ key_load_public_rsa1(int fd, const char *filename, char **commentp) } /* load public key from private-key file, works only for SSH v1 */ -Key * +struct sshkey * key_load_public_type(int type, const char *filename, char **commentp) { - Key *pub; + struct sshkey *pub; int fd; if (type == KEY_RSA1) { @@ -403,7 +407,7 @@ key_load_public_type(int type, const char *filename, char **commentp) return NULL; } -static Key * +static struct sshkey * key_parse_private_rsa1(Buffer *blob, const char *passphrase, char **commentp) { int check1, check2, cipher_type; @@ -411,7 +415,7 @@ key_parse_private_rsa1(Buffer *blob, const char *passphrase, char **commentp) u_char *cp; CipherContext ciphercontext; Cipher *cipher; - Key *prv = NULL; + struct sshkey *prv = NULL; Buffer copy; /* Check that it is at least big enough to contain the ID string. */ @@ -439,7 +443,10 @@ key_parse_private_rsa1(Buffer *blob, const char *passphrase, char **commentp) /* Read the public key from the buffer. */ (void) buffer_get_int(©); - prv = key_new_private(KEY_RSA1); + if ((prv = sshkey_new_private(KEY_RSA1)) == NULL) { + buffer_free(©); + return NULL; + } buffer_get_bignum(©, prv->rsa->n); buffer_get_bignum(©, prv->rsa->e); @@ -500,16 +507,16 @@ key_parse_private_rsa1(Buffer *blob, const char *passphrase, char **commentp) fail: if (commentp) xfree(*commentp); - key_free(prv); + sshkey_free(prv); return NULL; } -static Key * +static struct sshkey * key_parse_private_pem(Buffer *blob, int type, const char *passphrase, char **commentp) { EVP_PKEY *pk = NULL; - Key *prv = NULL; + struct sshkey *prv = NULL; char *name = ""; BIO *bio; @@ -526,7 +533,10 @@ key_parse_private_pem(Buffer *blob, int type, const char *passphrase, (void)ERR_get_error(); } else if (pk->type == EVP_PKEY_RSA && (type == KEY_UNSPEC||type==KEY_RSA)) { - prv = key_new(KEY_UNSPEC); + if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { + error("%s: sshkey_new failed", __func__); + return NULL; + } prv->rsa = EVP_PKEY_get1_RSA(pk); prv->type = KEY_RSA; name = "rsa w/o comment"; @@ -535,12 +545,12 @@ key_parse_private_pem(Buffer *blob, int type, const char *passphrase, #endif if (RSA_blinding_on(prv->rsa, NULL) != 1) { error("%s: RSA_blinding_on failed", __func__); - key_free(prv); + sshkey_free(prv); prv = NULL; } } else if (pk->type == EVP_PKEY_DSA && (type == KEY_UNSPEC||type==KEY_DSA)) { - prv = key_new(KEY_UNSPEC); + prv = sshkey_new(KEY_UNSPEC); prv->dsa = EVP_PKEY_get1_DSA(pk); prv->type = KEY_DSA; name = "dsa w/o comment"; @@ -549,22 +559,23 @@ key_parse_private_pem(Buffer *blob, int type, const char *passphrase, #endif } else if (pk->type == EVP_PKEY_EC && (type == KEY_UNSPEC||type==KEY_ECDSA)) { - prv = key_new(KEY_UNSPEC); + prv = sshkey_new(KEY_UNSPEC); prv->ecdsa = EVP_PKEY_get1_EC_KEY(pk); prv->type = KEY_ECDSA; - if ((prv->ecdsa_nid = key_ecdsa_key_to_nid(prv->ecdsa)) == -1 || - key_curve_nid_to_name(prv->ecdsa_nid) == NULL || - key_ec_validate_public(EC_KEY_get0_group(prv->ecdsa), + prv->ecdsa_nid = sshkey_ecdsa_key_to_nid(prv->ecdsa); + if (prv->ecdsa_nid == -1 || + sshkey_curve_nid_to_name(prv->ecdsa_nid) == NULL || + sshkey_ec_validate_public(EC_KEY_get0_group(prv->ecdsa), EC_KEY_get0_public_key(prv->ecdsa)) != 0 || - key_ec_validate_private(prv->ecdsa) != 0) { + sshkey_ec_validate_private(prv->ecdsa) != 0) { error("%s: bad ECDSA key", __func__); - key_free(prv); + sshkey_free(prv); prv = NULL; } name = "ecdsa w/o comment"; #ifdef DEBUG_PK if (prv != NULL && prv->ecdsa != NULL) - key_dump_ec_key(prv->ecdsa); + sshkey_dump_ec_key(prv->ecdsa); #endif } else { error("%s: PEM_read_PrivateKey: mismatch or " @@ -575,16 +586,16 @@ key_parse_private_pem(Buffer *blob, int type, const char *passphrase, if (prv != NULL && commentp) *commentp = xstrdup(name); debug("read PEM private key done: type %s", - prv ? key_type(prv) : ""); + prv ? sshkey_type(prv) : ""); return prv; } -Key * +struct sshkey * key_load_private_pem(int fd, int type, const char *passphrase, char **commentp) { Buffer buffer; - Key *prv; + struct sshkey *prv; buffer_init(&buffer); if (!key_load_file(fd, NULL, &buffer)) { @@ -621,7 +632,7 @@ key_perm_ok(int fd, const char *filename) return 1; } -static Key * +static struct sshkey * key_parse_private_type(Buffer *blob, int type, const char *passphrase, char **commentp) { @@ -640,12 +651,12 @@ key_parse_private_type(Buffer *blob, int type, const char *passphrase, return NULL; } -Key * +struct sshkey * key_load_private_type(int type, const char *filename, const char *passphrase, char **commentp, int *perm_ok) { int fd; - Key *ret; + struct sshkey *ret; Buffer buffer; fd = open(filename, O_RDONLY); @@ -678,11 +689,11 @@ key_load_private_type(int type, const char *filename, const char *passphrase, return ret; } -Key * +struct sshkey * key_parse_private(Buffer *buffer, const char *filename, const char *passphrase, char **commentp) { - Key *pub, *prv; + struct sshkey *pub, *prv; /* it's a SSH v1 key if the public key part is readable */ pub = key_parse_public_rsa1(buffer, commentp); @@ -693,7 +704,7 @@ key_parse_private(Buffer *buffer, const char *filename, if (commentp && prv) *commentp = xstrdup(filename); } else { - key_free(pub); + sshkey_free(pub); /* key_parse_public_rsa1() has already loaded the comment */ prv = key_parse_private_type(buffer, KEY_RSA1, passphrase, NULL); @@ -701,11 +712,11 @@ key_parse_private(Buffer *buffer, const char *filename, return prv; } -Key * +struct sshkey * key_load_private(const char *filename, const char *passphrase, char **commentp) { - Key *prv; + struct sshkey *prv; Buffer buffer; int fd; @@ -735,7 +746,7 @@ key_load_private(const char *filename, const char *passphrase, } static int -key_try_load_public(Key *k, const char *filename, char **commentp) +key_try_load_public(struct sshkey *k, const char *filename, char **commentp) { FILE *f; char line[SSH_MAX_PUBKEY_BYTES]; @@ -760,7 +771,7 @@ key_try_load_public(Key *k, const char *filename, char **commentp) for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) ; if (*cp) { - if (key_read(k, &cp) == 1) { + if (sshkey_read(k, &cp) == 0) { cp[strcspn(cp, "\r\n")] = '\0'; if (commentp) { *commentp = xstrdup(*cp ? @@ -777,10 +788,10 @@ key_try_load_public(Key *k, const char *filename, char **commentp) } /* load public key from ssh v1 private or any pubkey file */ -Key * +struct sshkey * key_load_public(const char *filename, char **commentp) { - Key *pub; + struct sshkey *pub; char file[MAXPATHLEN]; /* try rsa1 private key */ @@ -789,47 +800,51 @@ key_load_public(const char *filename, char **commentp) return pub; /* try rsa1 public key */ - pub = key_new(KEY_RSA1); + if ((pub = sshkey_new(KEY_RSA1)) == NULL) + return NULL; if (key_try_load_public(pub, filename, commentp) == 1) return pub; - key_free(pub); + sshkey_free(pub); /* try ssh2 public key */ - pub = key_new(KEY_UNSPEC); + if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) + return NULL; if (key_try_load_public(pub, filename, commentp) == 1) return pub; if ((strlcpy(file, filename, sizeof file) < sizeof(file)) && (strlcat(file, ".pub", sizeof file) < sizeof(file)) && (key_try_load_public(pub, file, commentp) == 1)) return pub; - key_free(pub); + sshkey_free(pub); return NULL; } /* Load the certificate associated with the named private key */ -Key * +struct sshkey * key_load_cert(const char *filename) { - Key *pub; + struct sshkey *pub; char *file; - pub = key_new(KEY_UNSPEC); + if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) + return NULL; xasprintf(&file, "%s-cert.pub", filename); if (key_try_load_public(pub, file, NULL) == 1) { xfree(file); return pub; } xfree(file); - key_free(pub); + sshkey_free(pub); return NULL; } /* Load private key and certificate */ -Key * +struct sshkey * key_load_private_cert(int type, const char *filename, const char *passphrase, int *perm_ok) { - Key *key, *pub; + struct sshkey *key, *pub; + int ret; switch (type) { case KEY_RSA: @@ -846,24 +861,30 @@ key_load_private_cert(int type, const char *filename, const char *passphrase, return NULL; if ((pub = key_load_cert(filename)) == NULL) { - key_free(key); + sshkey_free(key); return NULL; } /* Make sure the private key matches the certificate */ - if (key_equal_public(key, pub) == 0) { + if (sshkey_equal_public(key, pub) == 0) { error("%s: certificate does not match private key %s", __func__, filename); - } else if (key_to_certified(key, key_cert_is_legacy(pub)) != 0) { - error("%s: key_to_certified failed", __func__); + } else if ((ret = sshkey_to_certified(key, + sshkey_cert_is_legacy(pub))) != 0) { + error("%s: key_to_certified failed: %s", + __func__, ssh_err(ret)); } else { - key_cert_copy(pub, key); - key_free(pub); - return key; + if ((ret = sshkey_cert_copy(pub, key)) != 0) + error("%s: sshkey_cert_copy failed: %s", + __func__, ssh_err(ret)); + else { + sshkey_free(pub); + return key; + } } - key_free(key); - key_free(pub); + sshkey_free(key); + sshkey_free(pub); return NULL; } @@ -874,16 +895,16 @@ key_load_private_cert(int type, const char *filename, const char *passphrase, * otherwise a comparison that ignores certficiate data is performed. */ int -key_in_file(Key *key, const char *filename, int strict_type) +key_in_file(struct sshkey *key, const char *filename, int strict_type) { FILE *f; char line[SSH_MAX_PUBKEY_BYTES]; char *cp; u_long linenum = 0; int ret = 0; - Key *pub; - int (*key_compare)(const Key *, const Key *) = strict_type ? - key_equal : key_equal_public; + struct sshkey *pub; + int (*key_compare)(const struct sshkey *, const struct sshkey *) = + strict_type ? sshkey_equal : sshkey_equal_public; if ((f = fopen(filename, "r")) == NULL) { if (errno == ENOENT) { @@ -912,17 +933,21 @@ key_in_file(Key *key, const char *filename, int strict_type) continue; } - pub = key_new(KEY_UNSPEC); - if (key_read(pub, &cp) != 1) { - key_free(pub); + if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) { + error("%s: sshkey_new failed", __func__); + fclose(f); + return -1; + } + if (sshkey_read(pub, &cp) != 0) { + sshkey_free(pub); continue; } if (key_compare(key, pub)) { ret = 1; - key_free(pub); + sshkey_free(pub); break; } - key_free(pub); + sshkey_free(pub); } fclose(f); return ret; diff --git a/ssh/authfile.h b/ssh/authfile.h index 78349be..a62f614 100644 --- a/ssh/authfile.h +++ b/ssh/authfile.h @@ -15,17 +15,23 @@ #ifndef AUTHFILE_H #define AUTHFILE_H -int key_save_private(Key *, const char *, const char *, const char *); -int key_load_file(int, const char *, Buffer *); -Key *key_load_cert(const char *); -Key *key_load_public(const char *, char **); -Key *key_load_public_type(int, const char *, char **); -Key *key_parse_private(Buffer *, const char *, const char *, char **); -Key *key_load_private(const char *, const char *, char **); -Key *key_load_private_cert(int, const char *, const char *, int *); -Key *key_load_private_type(int, const char *, const char *, char **, int *); -Key *key_load_private_pem(int, int, const char *, char **); -int key_perm_ok(int, const char *); -int key_in_file(Key *, const char *, int); +struct sshbuf; +struct sshkey; + +int key_save_private(struct sshkey *, const char *, + const char *, const char *); +int key_load_file(int, const char *, struct sshbuf *); +struct sshkey *key_load_cert(const char *); +struct sshkey *key_load_public(const char *, char **); +struct sshkey *key_load_public_type(int, const char *, char **); +struct sshkey *key_parse_private(struct sshbuf *, const char *, const char *, + char **); +struct sshkey *key_load_private(const char *, const char *, char **); +struct sshkey *key_load_private_cert(int, const char *, const char *, int *); +struct sshkey *key_load_private_type(int, const char *, const char *, + char **, int *); +struct sshkey *key_load_private_pem(int, int, const char *, char **); +int key_perm_ok(int, const char *); +int key_in_file(struct sshkey *, const char *, int); #endif diff --git a/ssh/bufaux.c b/ssh/bufaux.c index f5a5d88..dbbc8ce 100644 --- a/ssh/bufaux.c +++ b/ssh/bufaux.c @@ -48,20 +48,22 @@ #include "buffer.h" #include "log.h" #include "misc.h" +#include "err.h" /* * Returns integers from the buffer (msb first). */ int -buffer_get_short_ret(u_short *ret, Buffer *buffer) +buffer_get_short_ret(u_short *v, Buffer *buffer) { - u_char buf[2]; + int ret; - if (buffer_get_ret(buffer, (char *) buf, 2) == -1) - return (-1); - *ret = get_u16(buf); - return (0); + if ((ret = sshbuf_get_u16(buffer, v)) != 0) { + error("%s: %s", __func__, ssh_err(ret)); + return -1; + } + return 0; } u_short @@ -76,15 +78,15 @@ buffer_get_short(Buffer *buffer) } int -buffer_get_int_ret(u_int *ret, Buffer *buffer) +buffer_get_int_ret(u_int *v, Buffer *buffer) { - u_char buf[4]; + int ret; - if (buffer_get_ret(buffer, (char *) buf, 4) == -1) - return (-1); - if (ret != NULL) - *ret = get_u32(buf); - return (0); + if ((ret = sshbuf_get_u32(buffer, v)) != 0) { + error("%s: %s", __func__, ssh_err(ret)); + return -1; + } + return 0; } u_int @@ -99,15 +101,15 @@ buffer_get_int(Buffer *buffer) } int -buffer_get_int64_ret(u_int64_t *ret, Buffer *buffer) +buffer_get_int64_ret(u_int64_t *v, Buffer *buffer) { - u_char buf[8]; + int ret; - if (buffer_get_ret(buffer, (char *) buf, 8) == -1) - return (-1); - if (ret != NULL) - *ret = get_u64(buf); - return (0); + if ((ret = sshbuf_get_u64(buffer, v)) != 0) { + error("%s: %s", __func__, ssh_err(ret)); + return -1; + } + return 0; } u_int64_t @@ -127,28 +129,28 @@ buffer_get_int64(Buffer *buffer) void buffer_put_short(Buffer *buffer, u_short value) { - char buf[2]; + int ret; - put_u16(buf, value); - buffer_append(buffer, buf, 2); + if ((ret = sshbuf_put_u16(buffer, value)) != 0) + fatal("%s: %s", __func__, ssh_err(ret)); } void buffer_put_int(Buffer *buffer, u_int value) { - char buf[4]; + int ret; - put_u32(buf, value); - buffer_append(buffer, buf, 4); + if ((ret = sshbuf_put_u32(buffer, value)) != 0) + fatal("%s: %s", __func__, ssh_err(ret)); } void buffer_put_int64(Buffer *buffer, u_int64_t value) { - char buf[8]; + int ret; - put_u64(buf, value); - buffer_append(buffer, buf, 8); + if ((ret = sshbuf_put_u64(buffer, value)) != 0) + fatal("%s: %s", __func__, ssh_err(ret)); } /* @@ -162,32 +164,17 @@ buffer_put_int64(Buffer *buffer, u_int64_t value) void * buffer_get_string_ret(Buffer *buffer, u_int *length_ptr) { + size_t len; + int ret; u_char *value; - u_int len; - /* Get the length. */ - if (buffer_get_int_ret(&len, buffer) != 0) { - error("buffer_get_string_ret: cannot extract length"); - return (NULL); + if ((ret = sshbuf_get_string(buffer, &value, &len)) != 0) { + error("%s: %s", __func__, ssh_err(ret)); + return NULL; } - if (len > 256 * 1024) { - error("buffer_get_string_ret: bad string length %u", len); - return (NULL); - } - /* Allocate space for the string. Add one byte for a null character. */ - value = xmalloc(len + 1); - /* Get the string. */ - if (buffer_get_ret(buffer, value, len) == -1) { - error("buffer_get_string_ret: buffer_get failed"); - xfree(value); - return (NULL); - } - /* Append a null character to make processing easier. */ - value[len] = '\0'; - /* Optionally return the length of the string. */ - if (length_ptr) - *length_ptr = len; - return (value); + if (length_ptr != NULL) + *length_ptr = len; /* Safe: sshbuf never store len > 2^31 */ + return value; } void * @@ -203,24 +190,17 @@ buffer_get_string(Buffer *buffer, u_int *length_ptr) char * buffer_get_cstring_ret(Buffer *buffer, u_int *length_ptr) { - u_int length; - char *cp, *ret = buffer_get_string_ret(buffer, &length); + size_t len; + int ret; + char *value; - if (ret == NULL) + if ((ret = sshbuf_get_cstring(buffer, &value, &len)) != 0) { + error("%s: %s", __func__, ssh_err(ret)); return NULL; - if ((cp = memchr(ret, '\0', length)) != NULL) { - /* XXX allow \0 at end-of-string for a while, remove later */ - if (cp == ret + length - 1) - error("buffer_get_cstring_ret: string contains \\0"); - else { - bzero(ret, length); - xfree(ret); - return NULL; - } } if (length_ptr != NULL) - *length_ptr = length; - return ret; + *length_ptr = len; /* Safe: sshbuf never store len > 2^31 */ + return value; } char * @@ -233,29 +213,26 @@ buffer_get_cstring(Buffer *buffer, u_int *length_ptr) return ret; } -void * +const void * buffer_get_string_ptr_ret(Buffer *buffer, u_int *length_ptr) { - void *ptr; - u_int len; + size_t len; + int ret; + const u_char *value; - if (buffer_get_int_ret(&len, buffer) != 0) - return NULL; - if (len > 256 * 1024) { - error("buffer_get_string_ptr: bad string length %u", len); + if ((ret = sshbuf_get_string_direct(buffer, &value, &len)) != 0) { + error("%s: %s", __func__, ssh_err(ret)); return NULL; } - ptr = buffer_ptr(buffer); - buffer_consume(buffer, len); - if (length_ptr) - *length_ptr = len; - return (ptr); + if (length_ptr != NULL) + *length_ptr = len; /* Safe: sshbuf never store len > 2^31 */ + return value; } -void * +const void * buffer_get_string_ptr(Buffer *buffer, u_int *length_ptr) { - void *ret; + const void *ret; if ((ret = buffer_get_string_ptr_ret(buffer, length_ptr)) == NULL) fatal("buffer_get_string_ptr: buffer error"); @@ -268,28 +245,34 @@ buffer_get_string_ptr(Buffer *buffer, u_int *length_ptr) void buffer_put_string(Buffer *buffer, const void *buf, u_int len) { - buffer_put_int(buffer, len); - buffer_append(buffer, buf, len); + int ret; + + if ((ret = sshbuf_put_string(buffer, buf, len)) != 0) + fatal("%s: %s", __func__, ssh_err(ret)); } + void buffer_put_cstring(Buffer *buffer, const char *s) { - if (s == NULL) - fatal("buffer_put_cstring: s == NULL"); - buffer_put_string(buffer, s, strlen(s)); + int ret; + + if ((ret = sshbuf_put_cstring(buffer, s)) != 0) + fatal("%s: %s", __func__, ssh_err(ret)); } /* * Returns a character from the buffer (0 - 255). */ int -buffer_get_char_ret(char *ret, Buffer *buffer) +buffer_get_char_ret(char *v, Buffer *buffer) { - if (buffer_get_ret(buffer, ret, 1) == -1) { - error("buffer_get_char_ret: buffer_get_ret failed"); - return (-1); + int ret; + + if ((ret = sshbuf_get_u8(buffer, (u_char *)v)) != 0) { + error("%s: %s", __func__, ssh_err(ret)); + return -1; } - return (0); + return 0; } int @@ -308,7 +291,8 @@ buffer_get_char(Buffer *buffer) void buffer_put_char(Buffer *buffer, int value) { - char ch = value; + int ret; - buffer_append(buffer, &ch, 1); + if ((ret = sshbuf_put_u8(buffer, value)) != 0) + fatal("%s: %s", __func__, ssh_err(ret)); } diff --git a/ssh/bufbn.c b/ssh/bufbn.c index 18cdd70..03da27f 100644 --- a/ssh/bufbn.c +++ b/ssh/bufbn.c @@ -48,6 +48,7 @@ #include "buffer.h" #include "log.h" #include "misc.h" +#include "err.h" /* * Stores an BIGNUM in the buffer with a 2-byte msb first bit count, followed @@ -56,31 +57,13 @@ int buffer_put_bignum_ret(Buffer *buffer, const BIGNUM *value) { - int bits = BN_num_bits(value); - int bin_size = (bits + 7) / 8; - u_char *buf = xmalloc(bin_size); - int oi; - char msg[2]; + int ret; - /* Get the value of in binary */ - oi = BN_bn2bin(value, buf); - if (oi != bin_size) { - error("buffer_put_bignum_ret: BN_bn2bin() failed: oi %d != bin_size %d", - oi, bin_size); - xfree(buf); - return (-1); + if ((ret = sshbuf_put_bignum1(buffer, value)) != 0) { + error("%s: %s", __func__, ssh_err(ret)); + return -1; } - - /* Store the number of bits in the buffer in two bytes, msb first. */ - put_u16(msg, bits); - buffer_append(buffer, msg, 2); - /* Store the binary data. */ - buffer_append(buffer, buf, oi); - - memset(buf, 0, bin_size); - xfree(buf); - - return (0); + return 0; } void @@ -96,35 +79,13 @@ buffer_put_bignum(Buffer *buffer, const BIGNUM *value) int buffer_get_bignum_ret(Buffer *buffer, BIGNUM *value) { - u_int bits, bytes; - u_char buf[2], *bin; + int ret; - /* Get the number of bits. */ - if (buffer_get_ret(buffer, (char *) buf, 2) == -1) { - error("buffer_get_bignum_ret: invalid length"); - return (-1); + if ((ret = sshbuf_get_bignum1(buffer, value)) != 0) { + error("%s: %s", __func__, ssh_err(ret)); + return -1; } - bits = get_u16(buf); - /* Compute the number of binary bytes that follow. */ - bytes = (bits + 7) / 8; - if (bytes > 8 * 1024) { - error("buffer_get_bignum_ret: cannot handle BN of size %d", bytes); - return (-1); - } - if (buffer_len(buffer) < bytes) { - error("buffer_get_bignum_ret: input buffer too small"); - return (-1); - } - bin = buffer_ptr(buffer); - if (BN_bin2bn(bin, bytes, value) == NULL) { - error("buffer_get_bignum_ret: BN_bin2bn failed"); - return (-1); - } - if (buffer_consume_ret(buffer, bytes) == -1) { - error("buffer_get_bignum_ret: buffer_consume failed"); - return (-1); - } - return (0); + return 0; } void @@ -140,39 +101,13 @@ buffer_get_bignum(Buffer *buffer, BIGNUM *value) int buffer_put_bignum2_ret(Buffer *buffer, const BIGNUM *value) { - u_int bytes; - u_char *buf; - int oi; - u_int hasnohigh = 0; + int ret; - if (BN_is_zero(value)) { - buffer_put_int(buffer, 0); - return 0; + if ((ret = sshbuf_put_bignum2(buffer, value)) != 0) { + error("%s: %s", __func__, ssh_err(ret)); + return -1; } - if (value->neg) { - error("buffer_put_bignum2_ret: negative numbers not supported"); - return (-1); - } - bytes = BN_num_bytes(value) + 1; /* extra padding byte */ - if (bytes < 2) { - error("buffer_put_bignum2_ret: BN too small"); - return (-1); - } - buf = xmalloc(bytes); - buf[0] = 0x00; - /* Get the value of in binary */ - oi = BN_bn2bin(value, buf+1); - if (oi < 0 || (u_int)oi != bytes - 1) { - error("buffer_put_bignum2_ret: BN_bn2bin() failed: " - "oi %d != bin_size %d", oi, bytes); - xfree(buf); - return (-1); - } - hasnohigh = (buf[1] & 0x80) ? 0 : 1; - buffer_put_string(buffer, buf+hasnohigh, bytes-hasnohigh); - memset(buf, 0, bytes); - xfree(buf); - return (0); + return 0; } void @@ -185,32 +120,13 @@ buffer_put_bignum2(Buffer *buffer, const BIGNUM *value) int buffer_get_bignum2_ret(Buffer *buffer, BIGNUM *value) { - u_int len; - u_char *bin; + int ret; - if ((bin = buffer_get_string_ret(buffer, &len)) == NULL) { - error("buffer_get_bignum2_ret: invalid bignum"); - return (-1); + if ((ret = sshbuf_get_bignum2(buffer, value)) != 0) { + error("%s: %s", __func__, ssh_err(ret)); + return -1; } - - if (len > 0 && (bin[0] & 0x80)) { - error("buffer_get_bignum2_ret: negative numbers not supported"); - xfree(bin); - return (-1); - } - if (len > 8 * 1024) { - error("buffer_get_bignum2_ret: cannot handle BN of size %d", - len); - xfree(bin); - return (-1); - } - if (BN_bin2bn(bin, len, value) == NULL) { - error("buffer_get_bignum2_ret: BN_bin2bn failed"); - xfree(bin); - return (-1); - } - xfree(bin); - return (0); + return 0; } void diff --git a/ssh/bufec.c b/ssh/bufec.c index dff9c69..59fd54f 100644 --- a/ssh/bufec.c +++ b/ssh/bufec.c @@ -26,16 +26,7 @@ #include "buffer.h" #include "log.h" #include "misc.h" - -/* - * Maximum supported EC GFp field length is 528 bits. SEC1 uncompressed - * encoding represents this as two bitstring points that should each - * be no longer than the field length, SEC1 specifies a 1 byte - * point type header. - * Being paranoid here may insulate us to parsing problems in - * EC_POINT_oct2point. - */ -#define BUFFER_MAX_ECPOINT_LEN ((528*2 / 8) + 1) +#include "err.h" /* * Append an EC_POINT to the buffer as a string containing a SEC1 encoded @@ -45,38 +36,13 @@ int buffer_put_ecpoint_ret(Buffer *buffer, const EC_GROUP *curve, const EC_POINT *point) { - u_char *buf = NULL; - size_t len; - BN_CTX *bnctx; - int ret = -1; + int ret; - /* Determine length */ - if ((bnctx = BN_CTX_new()) == NULL) - fatal("%s: BN_CTX_new failed", __func__); - len = EC_POINT_point2oct(curve, point, POINT_CONVERSION_UNCOMPRESSED, - NULL, 0, bnctx); - if (len > BUFFER_MAX_ECPOINT_LEN) { - error("%s: giant EC point: len = %lu (max %u)", - __func__, (u_long)len, BUFFER_MAX_ECPOINT_LEN); - goto out; + if ((ret = sshbuf_put_ec(buffer, point, curve)) != 0) { + error("%s: %s", __func__, ssh_err(ret)); + return -1; } - /* Convert */ - buf = xmalloc(len); - if (EC_POINT_point2oct(curve, point, POINT_CONVERSION_UNCOMPRESSED, - buf, len, bnctx) != len) { - error("%s: EC_POINT_point2oct length mismatch", __func__); - goto out; - } - /* Append */ - buffer_put_string(buffer, buf, len); - ret = 0; - out: - if (buf != NULL) { - bzero(buf, len); - xfree(buf); - } - BN_CTX_free(bnctx); - return ret; + return 0; } void @@ -91,43 +57,13 @@ int buffer_get_ecpoint_ret(Buffer *buffer, const EC_GROUP *curve, EC_POINT *point) { - u_char *buf; - u_int len; - BN_CTX *bnctx; - int ret = -1; + int ret; - if ((buf = buffer_get_string_ret(buffer, &len)) == NULL) { - error("%s: invalid point", __func__); + if ((ret = sshbuf_get_ec(buffer, point, curve)) != 0) { + error("%s: %s", __func__, ssh_err(ret)); return -1; } - if ((bnctx = BN_CTX_new()) == NULL) - fatal("%s: BN_CTX_new failed", __func__); - if (len > BUFFER_MAX_ECPOINT_LEN) { - error("%s: EC_POINT too long: %u > max %u", __func__, - len, BUFFER_MAX_ECPOINT_LEN); - goto out; - } - if (len == 0) { - error("%s: EC_POINT buffer is empty", __func__); - goto out; - } - if (buf[0] != POINT_CONVERSION_UNCOMPRESSED) { - error("%s: EC_POINT is in an incorrect form: " - "0x%02x (want 0x%02x)", __func__, buf[0], - POINT_CONVERSION_UNCOMPRESSED); - goto out; - } - if (EC_POINT_oct2point(curve, point, buf, len, bnctx) != 1) { - error("buffer_get_bignum2_ret: BN_bin2bn failed"); - goto out; - } - /* EC_POINT_oct2point verifies that the point is on the curve for us */ - ret = 0; - out: - BN_CTX_free(bnctx); - bzero(buf, len); - xfree(buf); - return ret; + return 0; } void diff --git a/ssh/buffer.c b/ssh/buffer.c index 656454b..fb6ff1d 100644 --- a/ssh/buffer.c +++ b/ssh/buffer.c @@ -21,161 +21,52 @@ #include "xmalloc.h" #include "buffer.h" #include "log.h" - -#define BUFFER_MAX_CHUNK 0x100000 -#define BUFFER_MAX_LEN 0xa00000 -#define BUFFER_ALLOCSZ 0x008000 - -/* Initializes the buffer structure. */ - -void -buffer_init(Buffer *buffer) -{ - const u_int len = 4096; - - buffer->alloc = 0; - buffer->buf = xmalloc(len); - buffer->alloc = len; - buffer->offset = 0; - buffer->end = 0; -} - -/* Frees any memory used for the buffer. */ - -void -buffer_free(Buffer *buffer) -{ - if (buffer->alloc > 0) { - memset(buffer->buf, 0, buffer->alloc); - buffer->alloc = 0; - xfree(buffer->buf); - } -} - -/* - * Clears any data from the buffer, making it empty. This does not actually - * zero the memory. - */ - -void -buffer_clear(Buffer *buffer) -{ - buffer->offset = 0; - buffer->end = 0; -} +#include "err.h" /* Appends data to the buffer, expanding it if necessary. */ void buffer_append(Buffer *buffer, const void *data, u_int len) { - void *p; - p = buffer_append_space(buffer, len); - memcpy(p, data, len); -} + int ret; -static int -buffer_compact(Buffer *buffer) -{ - /* - * If the buffer is quite empty, but all data is at the end, move the - * data to the beginning. - */ - if (buffer->offset > MIN(buffer->alloc, BUFFER_MAX_CHUNK)) { - memmove(buffer->buf, buffer->buf + buffer->offset, - buffer->end - buffer->offset); - buffer->end -= buffer->offset; - buffer->offset = 0; - return (1); - } - return (0); + if ((ret = sshbuf_put(buffer, data, len)) != 0) + fatal("%s: %s", __func__, ssh_err(ret)); } -/* - * Appends space to the buffer, expanding the buffer if necessary. This does - * not actually copy the data into the buffer, but instead returns a pointer - * to the allocated region. - */ - void * buffer_append_space(Buffer *buffer, u_int len) { - u_int newlen; - void *p; + int ret; + u_char *p; - if (len > BUFFER_MAX_CHUNK) - fatal("buffer_append_space: len %u not supported", len); - - /* If the buffer is empty, start using it from the beginning. */ - if (buffer->offset == buffer->end) { - buffer->offset = 0; - buffer->end = 0; - } -restart: - /* If there is enough space to store all data, store it now. */ - if (buffer->end + len < buffer->alloc) { - p = buffer->buf + buffer->end; - buffer->end += len; - return p; - } - - /* Compact data back to the start of the buffer if necessary */ - if (buffer_compact(buffer)) - goto restart; - - /* Increase the size of the buffer and retry. */ - newlen = roundup(buffer->alloc + len, BUFFER_ALLOCSZ); - if (newlen > BUFFER_MAX_LEN) - fatal("buffer_append_space: alloc %u not supported", - newlen); - buffer->buf = xrealloc(buffer->buf, 1, newlen); - buffer->alloc = newlen; - goto restart; - /* NOTREACHED */ + if ((ret = sshbuf_reserve(buffer, len, &p)) != 0) + fatal("%s: %s", __func__, ssh_err(ret)); + return p; } -/* - * Check whether an allocation of 'len' will fit in the buffer - * This must follow the same math as buffer_append_space - */ int buffer_check_alloc(Buffer *buffer, u_int len) { - if (buffer->offset == buffer->end) { - buffer->offset = 0; - buffer->end = 0; - } - restart: - if (buffer->end + len < buffer->alloc) - return (1); - if (buffer_compact(buffer)) - goto restart; - if (roundup(buffer->alloc + len, BUFFER_ALLOCSZ) <= BUFFER_MAX_LEN) - return (1); - return (0); + int ret = sshbuf_check_reserve(buffer, len); + + if (ret == 0) + return 1; + if (ret == SSH_ERR_NO_BUFFER_SPACE) + return 0; + fatal("%s: %s", __func__, ssh_err(ret)); } -/* Returns the number of bytes of data in the buffer. */ - -u_int -buffer_len(const Buffer *buffer) -{ - return buffer->end - buffer->offset; -} - -/* Gets data from the beginning of the buffer. */ - int buffer_get_ret(Buffer *buffer, void *buf, u_int len) { - if (len > buffer->end - buffer->offset) { - error("buffer_get_ret: trying to get more bytes %d than in buffer %d", - len, buffer->end - buffer->offset); - return (-1); + int ret; + + if ((ret = sshbuf_get(buffer, buf, len)) != 0) { + error("%s: %s", __func__, ssh_err(ret)); + return -1; } - memcpy(buf, buffer->buf + buffer->offset, len); - buffer->offset += len; - return (0); + return 0; } void @@ -185,17 +76,16 @@ buffer_get(Buffer *buffer, void *buf, u_int len) fatal("buffer_get: buffer error"); } -/* Consumes the given number of bytes from the beginning of the buffer. */ - int buffer_consume_ret(Buffer *buffer, u_int bytes) { - if (bytes > buffer->end - buffer->offset) { - error("buffer_consume_ret: trying to get more bytes than in buffer"); - return (-1); - } - buffer->offset += bytes; - return (0); + int ret = sshbuf_consume(buffer, bytes); + + if (ret == 0) + return 0; + if (ret == SSH_ERR_MESSAGE_INCOMPLETE) + return -1; + fatal("%s: %s", __func__, ssh_err(ret)); } void @@ -205,46 +95,22 @@ buffer_consume(Buffer *buffer, u_int bytes) fatal("buffer_consume: buffer error"); } -/* Consumes the given number of bytes from the end of the buffer. */ - int buffer_consume_end_ret(Buffer *buffer, u_int bytes) { - if (bytes > buffer->end - buffer->offset) - return (-1); - buffer->end -= bytes; - return (0); + int ret = sshbuf_consume_end(buffer, bytes); + + if (ret == 0) + return 0; + if (ret == SSH_ERR_MESSAGE_INCOMPLETE) + return -1; + fatal("%s: %s", __func__, ssh_err(ret)); } void buffer_consume_end(Buffer *buffer, u_int bytes) { if (buffer_consume_end_ret(buffer, bytes) == -1) - fatal("buffer_consume_end: trying to get more bytes than in buffer"); + fatal("%s: buffer error", __func__); } -/* Returns a pointer to the first used byte in the buffer. */ - -void * -buffer_ptr(const Buffer *buffer) -{ - return buffer->buf + buffer->offset; -} - -/* Dumps the contents of the buffer to stderr. */ - -void -buffer_dump(const Buffer *buffer) -{ - u_int i; - u_char *ucp = buffer->buf; - - for (i = buffer->offset; i < buffer->end; i++) { - fprintf(stderr, "%02x", ucp[i]); - if ((i-buffer->offset)%16==15) - fprintf(stderr, "\r\n"); - else if ((i-buffer->offset)%2==1) - fprintf(stderr, " "); - } - fprintf(stderr, "\r\n"); -} diff --git a/ssh/buffer.h b/ssh/buffer.h index 1fb3f16..61fcf0f 100644 --- a/ssh/buffer.h +++ b/ssh/buffer.h @@ -16,31 +16,27 @@ #ifndef BUFFER_H #define BUFFER_H -typedef struct { - u_char *buf; /* Buffer for data. */ - u_int alloc; /* Number of bytes allocated for data. */ - u_int offset; /* Offset of first byte containing data. */ - u_int end; /* Offset of last byte containing data. */ -} Buffer; +#include "sshbuf.h" -void buffer_init(Buffer *); -void buffer_clear(Buffer *); -void buffer_free(Buffer *); +typedef struct sshbuf Buffer; -u_int buffer_len(const Buffer *); -void *buffer_ptr(const Buffer *); +#define buffer_init(b) sshbuf_init(b) +#define buffer_clear(b) sshbuf_reset(b) +#define buffer_free(b) sshbuf_free(b) +#define buffer_dump(b) sshbuf_dump(b, stderr) + +/* XXX cast is safe: sshbuf never stores more than len 2^31 */ +#define buffer_len(b) ((u_int) sshbuf_len(b)) +#define buffer_ptr(b) sshbuf_ptr(b) void buffer_append(Buffer *, const void *, u_int); void *buffer_append_space(Buffer *, u_int); - int buffer_check_alloc(Buffer *, u_int); - void buffer_get(Buffer *, void *, u_int); void buffer_consume(Buffer *, u_int); void buffer_consume_end(Buffer *, u_int); -void buffer_dump(const Buffer *); int buffer_get_ret(Buffer *, void *, u_int); int buffer_consume_ret(Buffer *, u_int); @@ -66,13 +62,12 @@ int buffer_get_char(Buffer *); void buffer_put_char(Buffer *, int); void *buffer_get_string(Buffer *, u_int *); -void *buffer_get_string_ptr(Buffer *, u_int *); +const void *buffer_get_string_ptr(Buffer *, u_int *); void buffer_put_string(Buffer *, const void *, u_int); char *buffer_get_cstring(Buffer *, u_int *); void buffer_put_cstring(Buffer *, const char *); -#define buffer_skip_string(b) \ - do { u_int l = buffer_get_int(b); buffer_consume(b, l); } while (0) +#define buffer_skip_string(b) (void)buffer_get_string_ptr(b, NULL); int buffer_put_bignum_ret(Buffer *, const BIGNUM *); int buffer_get_bignum_ret(Buffer *, BIGNUM *); @@ -83,7 +78,7 @@ int buffer_get_int_ret(u_int *, Buffer *); int buffer_get_int64_ret(u_int64_t *, Buffer *); void *buffer_get_string_ret(Buffer *, u_int *); char *buffer_get_cstring_ret(Buffer *, u_int *); -void *buffer_get_string_ptr_ret(Buffer *, u_int *); +const void *buffer_get_string_ptr_ret(Buffer *, u_int *); int buffer_get_char_ret(char *, Buffer *); #include diff --git a/ssh/channels.c b/ssh/channels.c index 668ec64..4229d06 100644 --- a/ssh/channels.c +++ b/ssh/channels.c @@ -2248,7 +2248,7 @@ void channel_input_data(int type, u_int32_t seq, struct ssh *ssh) { int id; - char *data; + const char *data; u_int data_len, win_len; Channel *c; diff --git a/ssh/dns.c b/ssh/dns.c index b4be46a..6767881 100644 --- a/ssh/dns.c +++ b/ssh/dns.c @@ -72,7 +72,7 @@ dns_result_totext(unsigned int res) */ static int dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, - u_char **digest, u_int *digest_len, Key *key) + u_char **digest, u_int *digest_len, struct sshkey *key) { int success = 0; @@ -90,9 +90,9 @@ dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, if (*algorithm) { *digest_type = SSHFP_HASH_SHA1; - *digest = key_fingerprint_raw(key, SSH_FP_SHA1, digest_len); + *digest = sshkey_fingerprint_raw(key, SSH_FP_SHA1, digest_len); if (*digest == NULL) - fatal("dns_read_key: null from key_fingerprint_raw()"); + fatal("%s: null from sshkey_fingerprint_raw", __func__); success = 1; } else { *digest_type = SSHFP_HASH_RESERVED; @@ -170,7 +170,7 @@ is_numeric_hostname(const char *hostname) */ int verify_host_key_dns(const char *hostname, struct sockaddr *address, - Key *hostkey, int *flags) + struct sshkey *hostkey, int *flags) { u_int counter; int result; @@ -269,7 +269,7 @@ verify_host_key_dns(const char *hostname, struct sockaddr *address, * Export the fingerprint of a key as a DNS resource record */ int -export_dns_rr(const char *hostname, Key *key, FILE *f, int generic) +export_dns_rr(const char *hostname, struct sshkey *key, FILE *f, int generic) { u_int8_t rdata_pubkey_algorithm = 0; u_int8_t rdata_digest_type = SSHFP_HASH_SHA1; diff --git a/ssh/dns.h b/ssh/dns.h index 90cfd7b..a4fd3f1 100644 --- a/ssh/dns.h +++ b/ssh/dns.h @@ -46,7 +46,8 @@ enum sshfp_hashes { #define DNS_VERIFY_MATCH 0x00000002 #define DNS_VERIFY_SECURE 0x00000004 -int verify_host_key_dns(const char *, struct sockaddr *, Key *, int *); -int export_dns_rr(const char *, Key *, FILE *, int); +int verify_host_key_dns(const char *, struct sockaddr *, + struct sshkey *, int *); +int export_dns_rr(const char *, struct sshkey *, FILE *, int); #endif /* DNS_H */ diff --git a/ssh/err.c b/ssh/err.c new file mode 100644 index 0000000..d0dc91d --- /dev/null +++ b/ssh/err.c @@ -0,0 +1,85 @@ +/* $OpenBSD$ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include "err.h" + +const char * +ssh_err(int n) +{ + switch (n) { + case SSH_ERR_SUCCESS: + return "success"; + case SSH_ERR_INTERNAL_ERROR: + return "internal buffer error"; + case SSH_ERR_ALLOC_FAIL: + return "memory allocation failed"; + case SSH_ERR_MESSAGE_INCOMPLETE: + return "incomplete message"; + case SSH_ERR_INVALID_FORMAT: + return "invalid format"; + case SSH_ERR_BIGNUM_IS_NEGATIVE: + return "bignum is negative"; + case SSH_ERR_BIGNUM_TOO_LARGE: + return "bignum is too large"; + case SSH_ERR_ECPOINT_TOO_LARGE: + return "elliptic curve point is too large"; + case SSH_ERR_NO_BUFFER_SPACE: + return "insufficient buffer space"; + case SSH_ERR_INVALID_ARGUMENT: + return "invalid argument"; + case SSH_ERR_KEY_BITS_MISMATCH: + return "key bits do not match"; + case SSH_ERR_EC_CURVE_INVALID: + return "invalid elliptic curve"; + case SSH_ERR_KEY_TYPE_MISMATCH: + return "key type does not match"; + case SSH_ERR_KEY_TYPE_UNKNOWN: + return "unknown or unsupported key type"; + case SSH_ERR_EC_CURVE_MISMATCH: + return "elliptic curve does not match"; + case SSH_ERR_EXPECTED_CERT: + return "plain key provided where certificate required"; + case SSH_ERR_KEY_LACKS_CERTBLOB: + return "key lacks certificate data"; + case SSH_ERR_KEY_CERT_UNKNOWN_TYPE: + return "unknown/unsupported certificate type"; + case SSH_ERR_KEY_CERT_INVALID_SIGN_KEY: + return "invalid certificate signing key"; + case SSH_ERR_KEY_INVALID_EC_VALUE: + return "invalid elliptic curve value"; + case SSH_ERR_SIGNATURE_INVALID: + return "incorrect signature"; + case SSH_ERR_LIBCRYPTO_ERROR: + return "error in libcrypto"; /* XXX fetch and return */ + case SSH_ERR_UNEXPECTED_TRAILING_DATA: + return "unexpected bytes remain after decoding"; + case SSH_ERR_SYSTEM_ERROR: + return strerror(errno); + case SSH_ERR_KEY_CERT_INVALID: + return "invalid certificate"; + case SSH_ERR_AGENT_COMMUNICATION: + return "communication with agent failed"; + case SSH_ERR_AGENT_FAILURE: + return "agent refused operation"; + default: + return "unknown error"; + } +} + + diff --git a/ssh/err.h b/ssh/err.h new file mode 100644 index 0000000..ccee7c3 --- /dev/null +++ b/ssh/err.h @@ -0,0 +1,55 @@ +/* $OpenBSD$ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SSH_ERR_ERR_H +#define _SSH_ERR_ERR_H + +/* XXX are these too granular? not granular enough? I can't decide - djm */ + +/* Error codes */ +#define SSH_ERR_SUCCESS 0 +#define SSH_ERR_INTERNAL_ERROR -1 +#define SSH_ERR_ALLOC_FAIL -2 +#define SSH_ERR_MESSAGE_INCOMPLETE -3 +#define SSH_ERR_INVALID_FORMAT -4 +#define SSH_ERR_BIGNUM_IS_NEGATIVE -5 +#define SSH_ERR_BIGNUM_TOO_LARGE -6 +#define SSH_ERR_ECPOINT_TOO_LARGE -7 +#define SSH_ERR_NO_BUFFER_SPACE -8 +#define SSH_ERR_INVALID_ARGUMENT -9 +#define SSH_ERR_KEY_BITS_MISMATCH -10 +#define SSH_ERR_EC_CURVE_INVALID -11 +#define SSH_ERR_KEY_TYPE_MISMATCH -12 +#define SSH_ERR_KEY_TYPE_UNKNOWN -13 /* XXX UNSUPPORTED? */ +#define SSH_ERR_EC_CURVE_MISMATCH -14 +#define SSH_ERR_EXPECTED_CERT -15 +#define SSH_ERR_KEY_LACKS_CERTBLOB -16 +#define SSH_ERR_KEY_CERT_UNKNOWN_TYPE -17 +#define SSH_ERR_KEY_CERT_INVALID_SIGN_KEY -18 +#define SSH_ERR_KEY_INVALID_EC_VALUE -19 +#define SSH_ERR_SIGNATURE_INVALID -20 +#define SSH_ERR_LIBCRYPTO_ERROR -21 +#define SSH_ERR_UNEXPECTED_TRAILING_DATA -22 +#define SSH_ERR_SYSTEM_ERROR -23 +#define SSH_ERR_KEY_CERT_INVALID -24 +#define SSH_ERR_AGENT_COMMUNICATION -25 +#define SSH_ERR_AGENT_FAILURE -26 + +/* Translate a numeric error code to a human-readable error string */ +const char *ssh_err(int n); + +#endif /* _SSH_ERR_ERR_H */ diff --git a/ssh/hostfile.c b/ssh/hostfile.c index 4f47281..4b819de 100644 --- a/ssh/hostfile.c +++ b/ssh/hostfile.c @@ -54,6 +54,7 @@ #include "hostfile.h" #include "log.h" #include "misc.h" +#include "err.h" struct hostkeys { struct hostkey_entry *entries; @@ -150,15 +151,16 @@ host_hash(const char *host, const char *name_from_hostfile, u_int src_len) */ int -hostfile_read_key(char **cpp, u_int *bitsp, Key *ret) +hostfile_read_key(char **cpp, u_int *bitsp, struct sshkey *ret) { char *cp; + int r; /* Skip leading whitespace. */ for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) ; - if (key_read(ret, &cp) != 1) + if ((r = sshkey_read(ret, &cp)) != 0) return 0; /* Skip trailing whitespace. */ @@ -168,12 +170,12 @@ hostfile_read_key(char **cpp, u_int *bitsp, Key *ret) /* Return results. */ *cpp = cp; if (bitsp != NULL) - *bitsp = key_size(ret); + *bitsp = sshkey_size(ret); return 1; } static int -hostfile_check_key(int bits, const Key *key, const char *host, +hostfile_check_key(int bits, const struct sshkey *key, const char *host, const char *filename, u_long linenum) { if (key == NULL || key->type != KEY_RSA1 || key->rsa == NULL) @@ -240,7 +242,7 @@ load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path) u_long linenum = 0, num_loaded = 0; char *cp, *cp2, *hashed_host; HostkeyMarker marker; - Key *key; + struct sshkey *key; int kbits; if ((f = fopen(path, "r")) == NULL) @@ -287,12 +289,18 @@ load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path) * Extract the key from the line. This will skip any leading * whitespace. Ignore badly formatted lines. */ - key = key_new(KEY_UNSPEC); + if ((key = sshkey_new(KEY_UNSPEC)) == NULL) { + error("%s: sshkey_new failed", __func__); + break; + } if (!hostfile_read_key(&cp, &kbits, key)) { - key_free(key); - key = key_new(KEY_RSA1); + sshkey_free(key); + if ((key = sshkey_new(KEY_RSA1)) == NULL) { + error("%s: sshkey_new failed", __func__); + break; + } if (!hostfile_read_key(&cp, &kbits, key)) { - key_free(key); + sshkey_free(key); continue; } } @@ -302,7 +310,7 @@ load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path) debug3("%s: found %skey type %s in file %s:%lu", __func__, marker == MRK_NONE ? "" : (marker == MRK_CA ? "ca " : "revoked "), - key_type(key), path, linenum); + sshkey_type(key), path, linenum); hostkeys->entries = xrealloc(hostkeys->entries, hostkeys->num_entries + 1, sizeof(*hostkeys->entries)); hostkeys->entries[hostkeys->num_entries].host = xstrdup(host); @@ -326,7 +334,7 @@ free_hostkeys(struct hostkeys *hostkeys) for (i = 0; i < hostkeys->num_entries; i++) { xfree(hostkeys->entries[i].host); xfree(hostkeys->entries[i].file); - key_free(hostkeys->entries[i].key); + sshkey_free(hostkeys->entries[i].key); bzero(hostkeys->entries + i, sizeof(*hostkeys->entries)); } if (hostkeys->entries != NULL) @@ -337,18 +345,18 @@ free_hostkeys(struct hostkeys *hostkeys) } static int -check_key_not_revoked(struct hostkeys *hostkeys, Key *k) +check_key_not_revoked(struct hostkeys *hostkeys, struct sshkey *k) { - int is_cert = key_is_cert(k); + int is_cert = sshkey_is_cert(k); u_int i; for (i = 0; i < hostkeys->num_entries; i++) { if (hostkeys->entries[i].marker != MRK_REVOKE) continue; - if (key_equal_public(k, hostkeys->entries[i].key)) + if (sshkey_equal_public(k, hostkeys->entries[i].key)) return -1; if (is_cert && - key_equal_public(k->cert->signature_key, + sshkey_equal_public(k->cert->signature_key, hostkeys->entries[i].key)) return -1; } @@ -372,11 +380,11 @@ check_key_not_revoked(struct hostkeys *hostkeys, Key *k) */ static HostStatus check_hostkeys_by_key_or_type(struct hostkeys *hostkeys, - Key *k, int keytype, const struct hostkey_entry **found) + struct sshkey *k, int keytype, const struct hostkey_entry **found) { u_int i; HostStatus end_return = HOST_NEW; - int want_cert = key_is_cert(k); + int want_cert = sshkey_is_cert(k); HostkeyMarker want_marker = want_cert ? MRK_CA : MRK_NONE; int proto = (k ? k->type : keytype) == KEY_RSA1 ? 1 : 2; @@ -400,7 +408,7 @@ check_hostkeys_by_key_or_type(struct hostkeys *hostkeys, break; } if (want_cert) { - if (key_equal_public(k->cert->signature_key, + if (sshkey_equal_public(k->cert->signature_key, hostkeys->entries[i].key)) { /* A matching CA exists */ end_return = HOST_OK; @@ -409,7 +417,7 @@ check_hostkeys_by_key_or_type(struct hostkeys *hostkeys, break; } } else { - if (key_equal(k, hostkeys->entries[i].key)) { + if (sshkey_equal(k, hostkeys->entries[i].key)) { end_return = HOST_OK; if (found != NULL) *found = hostkeys->entries + i; @@ -430,7 +438,7 @@ check_hostkeys_by_key_or_type(struct hostkeys *hostkeys, } HostStatus -check_key_in_hostkeys(struct hostkeys *hostkeys, Key *key, +check_key_in_hostkeys(struct hostkeys *hostkeys, struct sshkey *key, const struct hostkey_entry **found) { if (key == NULL) @@ -452,11 +460,11 @@ lookup_key_in_hostkeys_by_type(struct hostkeys *hostkeys, int keytype, */ int -add_host_to_hostfile(const char *filename, const char *host, const Key *key, - int store_hash) +add_host_to_hostfile(const char *filename, const char *host, + const struct sshkey *key, int store_hash) { FILE *f; - int success = 0; + int r, success = 0; char *hashed_host = NULL; if (key == NULL) @@ -474,12 +482,12 @@ add_host_to_hostfile(const char *filename, const char *host, const Key *key, } fprintf(f, "%s ", store_hash ? hashed_host : host); - if (key_write(key, f)) { + if ((r = sshkey_write(key, f)) != 0) { + error("%s: saving key in %s failed: %s", + __func__, filename, ssh_err(r)); + } else success = 1; - } else { - error("add_host_to_hostfile: saving key in %s failed", filename); - } - fprintf(f, "\n"); + fputs("\n", f); fclose(f); return success; } diff --git a/ssh/hostfile.h b/ssh/hostfile.h index d84d422..d48156f 100644 --- a/ssh/hostfile.h +++ b/ssh/hostfile.h @@ -26,7 +26,7 @@ struct hostkey_entry { char *host; char *file; u_long line; - Key *key; + struct sshkey *key; HostkeyMarker marker; }; struct hostkeys; @@ -35,13 +35,14 @@ struct hostkeys *init_hostkeys(void); void load_hostkeys(struct hostkeys *, const char *, const char *); void free_hostkeys(struct hostkeys *); -HostStatus check_key_in_hostkeys(struct hostkeys *, Key *, +HostStatus check_key_in_hostkeys(struct hostkeys *, struct sshkey *, const struct hostkey_entry **); int lookup_key_in_hostkeys_by_type(struct hostkeys *, int, const struct hostkey_entry **); -int hostfile_read_key(char **, u_int *, Key *); -int add_host_to_hostfile(const char *, const char *, const Key *, int); +int hostfile_read_key(char **, u_int *, struct sshkey *); +int add_host_to_hostfile(const char *, const char *, + const struct sshkey *, int); #define HASH_MAGIC "|1|" #define HASH_DELIM '|' diff --git a/ssh/kex.c b/ssh/kex.c index 7d675ef..593596e 100644 --- a/ssh/kex.c +++ b/ssh/kex.c @@ -46,6 +46,7 @@ #include "dispatch.h" #include "monitor.h" #include "roaming.h" +#include "err.h" /* prototype */ static void kex_kexinit_finish(struct ssh *); @@ -370,7 +371,7 @@ choose_hostkeyalg(Kex *k, char *client, char *server) char *hostkeyalg = match_list(client, server, NULL); if (hostkeyalg == NULL) fatal("no hostkey alg"); - k->hostkey_type = key_type_from_name(hostkeyalg); + k->hostkey_type = sshkey_type_from_name(hostkeyalg); if (k->hostkey_type == KEY_UNSPEC) fatal("bad hostkey alg '%s'", hostkeyalg); xfree(hostkeyalg); diff --git a/ssh/kex.h b/ssh/kex.h index 5736a8f..6b82c37 100644 --- a/ssh/kex.h +++ b/ssh/kex.h @@ -131,10 +131,10 @@ struct Kex { const EVP_MD *evp_md; char *client_version_string; char *server_version_string; - int (*verify_host_key)(Key *, struct ssh *); - Key *(*load_host_public_key)(int, struct ssh *); - Key *(*load_host_private_key)(int, struct ssh *); - int (*host_key_index)(Key *); + int (*verify_host_key)(struct sshkey *, struct ssh *); + struct sshkey *(*load_host_public_key)(int, struct ssh *); + struct sshkey *(*load_host_private_key)(int, struct ssh *); + int (*host_key_index)(struct sshkey *); void (*kex[KEX_MAX])(struct ssh *); void *state; }; diff --git a/ssh/kexdhc.c b/ssh/kexdhc.c index 89dcaed..5399d5d 100644 --- a/ssh/kexdhc.c +++ b/ssh/kexdhc.c @@ -41,6 +41,8 @@ #include "dh.h" #include "ssh2.h" #include "dispatch.h" +#include "compat.h" +#include "err.h" struct kexdhc_state { DH *dh; @@ -93,17 +95,17 @@ input_kex_dh(int type, u_int32_t seq, struct ssh *ssh) Kex *kex = ssh->kex; struct kexdhc_state *kexdhc_state = kex->state; BIGNUM *dh_server_pub = NULL, *shared_secret = NULL; - Key *server_host_key; + struct sshkey *server_host_key; u_char *server_host_key_blob = NULL, *signature = NULL; u_char *kbuf, *hash; u_int klen, slen, sbloblen, hashlen; - int kout; + int kout, r; /* key, cert */ server_host_key_blob = ssh_packet_get_string(ssh, &sbloblen); - server_host_key = key_from_blob(server_host_key_blob, sbloblen); - if (server_host_key == NULL) - fatal("cannot decode server_host_key_blob"); + if ((r = sshkey_from_blob(server_host_key_blob, sbloblen, + &server_host_key)) != 0) + fatal("cannot decode server_host_key_blob: %s", ssh_err(r)); if (server_host_key->type != kex->hostkey_type) fatal("type mismatch for decoded server_host_key_blob"); if (kex->verify_host_key == NULL) @@ -161,9 +163,10 @@ input_kex_dh(int type, u_int32_t seq, struct ssh *ssh) BN_clear_free(dh_server_pub); DH_free(kexdhc_state->dh); - if (key_verify(server_host_key, signature, slen, hash, hashlen) != 1) + if (sshkey_verify(server_host_key, signature, slen, hash, hashlen, + datafellows) != 1) fatal("key_verify failed for server_host_key"); - key_free(server_host_key); + sshkey_free(server_host_key); xfree(signature); /* save session id */ diff --git a/ssh/kexdhs.c b/ssh/kexdhs.c index 1c9d638..1d2a5d7 100644 --- a/ssh/kexdhs.c +++ b/ssh/kexdhs.c @@ -44,6 +44,8 @@ #endif #include "monitor_wrap.h" #include "dispatch.h" +#include "compat.h" +#include "err.h" struct kexdhs_state { DH *dh; @@ -86,10 +88,10 @@ input_kex_dh_init(int type, u_int32_t seq, struct ssh *ssh) struct kexdhs_state *kexdhs_state = kex->state; BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; DH *dh; - Key *server_host_public, *server_host_private; + struct sshkey *server_host_public, *server_host_private; u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; u_int sbloblen, klen, hashlen, slen; - int kout; + int kout, r; dh = kexdhs_state->dh; @@ -141,7 +143,9 @@ input_kex_dh_init(int type, u_int32_t seq, struct ssh *ssh) memset(kbuf, 0, klen); xfree(kbuf); - key_to_blob(server_host_public, &server_host_key_blob, &sbloblen); + if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob, + &sbloblen)) != 0) + fatal("%s: sshkey_to_blob: %s", __func__, ssh_err(r)); /* calc H */ kex_dh_hash( @@ -165,9 +169,9 @@ input_kex_dh_init(int type, u_int32_t seq, struct ssh *ssh) } /* sign H */ - if (PRIVSEP(key_sign(server_host_private, &signature, &slen, hash, - hashlen)) < 0) - fatal("kexdh_server: key_sign failed"); + if (PRIVSEP(sshkey_sign(server_host_private, &signature, &slen, hash, + hashlen, datafellows)) < 0) + fatal("kexdh_server: sshkey_sign failed"); /* destroy_sensitive_data(); */ diff --git a/ssh/kexecdh.c b/ssh/kexecdh.c index 7c1cb3e..08544ff 100644 --- a/ssh/kexecdh.c +++ b/ssh/kexecdh.c @@ -46,7 +46,8 @@ kex_ecdh_name_to_nid(const char *kexname) { if (strlen(kexname) < sizeof(KEX_ECDH_SHA2_STEM) - 1) fatal("%s: kexname too short \"%s\"", __func__, kexname); - return key_curve_name_to_nid(kexname + sizeof(KEX_ECDH_SHA2_STEM) - 1); + return sshkey_curve_name_to_nid(kexname + + sizeof(KEX_ECDH_SHA2_STEM) - 1); } const EVP_MD * @@ -56,7 +57,7 @@ kex_ecdh_name_to_evpmd(const char *kexname) if (nid == -1) fatal("%s: unsupported ECDH curve \"%s\"", __func__, kexname); - return key_ec_nid_to_evpmd(nid); + return sshkey_ec_nid_to_evpmd(nid); } void diff --git a/ssh/kexecdhc.c b/ssh/kexecdhc.c index 9511bc9..e70f8d5 100644 --- a/ssh/kexecdhc.c +++ b/ssh/kexecdhc.c @@ -42,6 +42,8 @@ #include "dh.h" #include "ssh2.h" #include "dispatch.h" +#include "compat.h" +#include "err.h" struct kexecdhc_state { EC_KEY *client_key; @@ -74,7 +76,7 @@ kexecdh_client(struct ssh *ssh) #ifdef DEBUG_KEXECDH fputs("client private key:\n", stderr); - key_dump_ec_key(client_key); + sshkey_dump_ec_key(client_key); #endif kexecdhc_state = xcalloc(1, sizeof(*kexecdhc_state)); @@ -95,17 +97,21 @@ input_kex_ecdh_reply(int type, u_int32_t seq, struct ssh *ssh) EC_POINT *server_public; EC_KEY *client_key; BIGNUM *shared_secret; - Key *server_host_key; + struct sshkey *server_host_key; u_char *server_host_key_blob = NULL, *signature = NULL; u_char *kbuf, *hash; u_int klen, slen, sbloblen, hashlen; + int r; group = kexecdhc_state->group; client_key = kexecdhc_state->client_key; /* hostkey */ server_host_key_blob = ssh_packet_get_string(ssh, &sbloblen); - server_host_key = key_from_blob(server_host_key_blob, sbloblen); + if ((r = sshkey_from_blob(server_host_key_blob, sbloblen, + &server_host_key)) != 0) + fatal("%s: could not parse server host key: %s", + __func__, ssh_err(r)); if (server_host_key == NULL) fatal("cannot decode server_host_key_blob"); if (server_host_key->type != kex->hostkey_type) @@ -120,12 +126,12 @@ input_kex_ecdh_reply(int type, u_int32_t seq, struct ssh *ssh) fatal("%s: EC_POINT_new failed", __func__); ssh_packet_get_ecpoint(ssh, group, server_public); - if (key_ec_validate_public(group, server_public) != 0) + if (sshkey_ec_validate_public(group, server_public) != 0) fatal("%s: invalid server public key", __func__); #ifdef DEBUG_KEXECDH fputs("server public key:\n", stderr); - key_dump_ec_point(group, server_public); + sshkey_dump_ec_point(group, server_public); #endif /* signed H */ @@ -166,9 +172,10 @@ input_kex_ecdh_reply(int type, u_int32_t seq, struct ssh *ssh) EC_POINT_clear_free(server_public); EC_KEY_free(client_key); - if (key_verify(server_host_key, signature, slen, hash, hashlen) != 1) - fatal("key_verify failed for server_host_key"); - key_free(server_host_key); + if ((r = sshkey_verify(server_host_key, signature, slen, hash, + hashlen, datafellows)) != 0) + fatal("key_verify failed for server_host_key: %s", ssh_err(r)); + sshkey_free(server_host_key); xfree(signature); /* save session id */ diff --git a/ssh/kexecdhs.c b/ssh/kexecdhs.c index 34bcdc3..ef138db 100644 --- a/ssh/kexecdhs.c +++ b/ssh/kexecdhs.c @@ -44,6 +44,8 @@ #endif #include "monitor_wrap.h" #include "dispatch.h" +#include "compat.h" +#include "err.h" static void input_kex_ecdh_init(int, u_int32_t, struct ssh *); @@ -62,11 +64,11 @@ input_kex_ecdh_init(int type, u_int32_t seq, struct ssh *ssh) EC_KEY *server_key; const EC_GROUP *group; BIGNUM *shared_secret; - Key *server_host_private, *server_host_public; + struct sshkey *server_host_private, *server_host_public; u_char *server_host_key_blob = NULL, *signature = NULL; u_char *kbuf, *hash; u_int klen, slen, sbloblen, hashlen; - int curve_nid; + int curve_nid, r; if ((curve_nid = kex_ecdh_name_to_nid(kex->name)) == -1) fatal("%s: unsupported ECDH curve \"%s\"", __func__, kex->name); @@ -78,7 +80,7 @@ input_kex_ecdh_init(int type, u_int32_t seq, struct ssh *ssh) #ifdef DEBUG_KEXECDH fputs("server private key:\n", stderr); - key_dump_ec_key(server_key); + sshkey_dump_ec_key(server_key); #endif if (kex->load_host_public_key == NULL || @@ -97,12 +99,12 @@ input_kex_ecdh_init(int type, u_int32_t seq, struct ssh *ssh) ssh_packet_get_ecpoint(ssh, group, client_public); ssh_packet_check_eom(ssh); - if (key_ec_validate_public(group, client_public) != 0) + if (sshkey_ec_validate_public(group, client_public) != 0) fatal("%s: invalid client public key", __func__); #ifdef DEBUG_KEXECDH fputs("client public key:\n", stderr); - key_dump_ec_point(group, client_public); + sshkey_dump_ec_point(group, client_public); #endif /* Calculate shared_secret */ @@ -123,7 +125,9 @@ input_kex_ecdh_init(int type, u_int32_t seq, struct ssh *ssh) xfree(kbuf); /* calc H */ - key_to_blob(server_host_public, &server_host_key_blob, &sbloblen); + if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob, + &sbloblen)) != 0) + fatal("%s: sshkey_to_blob: %s", __func__, ssh_err(r)); kex_ecdh_hash( kex->evp_md, group, @@ -147,9 +151,9 @@ input_kex_ecdh_init(int type, u_int32_t seq, struct ssh *ssh) } /* sign H */ - if (PRIVSEP(key_sign(server_host_private, &signature, &slen, - hash, hashlen)) < 0) - fatal("kexdh_server: key_sign failed"); + if (PRIVSEP(sshkey_sign(server_host_private, &signature, &slen, + hash, hashlen, datafellows)) < 0) + fatal("kexdh_server: sshkey_sign failed"); /* destroy_sensitive_data(); */ diff --git a/ssh/kexgexc.c b/ssh/kexgexc.c index f0f6c2b..5552e37 100644 --- a/ssh/kexgexc.c +++ b/ssh/kexgexc.c @@ -43,6 +43,7 @@ #include "ssh2.h" #include "compat.h" #include "dispatch.h" +#include "err.h" struct kexgexc_state { int min, max, nbits; @@ -150,10 +151,10 @@ input_kex_dh_gex_reply(int type, u_int32_t seq, struct ssh *ssh) Kex *kex = ssh->kex; struct kexgexc_state *kexgexc_state = kex->state; BIGNUM *dh_server_pub = NULL, *shared_secret = NULL; - Key *server_host_key; + struct sshkey *server_host_key; u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; u_int klen, slen, sbloblen, hashlen; - int kout, min, max, nbits; + int kout, min, max, nbits, r; DH *dh; debug("%s %p %p", __func__, kex, kexgexc_state); @@ -166,9 +167,9 @@ input_kex_dh_gex_reply(int type, u_int32_t seq, struct ssh *ssh) /* key, cert */ server_host_key_blob = ssh_packet_get_string(ssh, &sbloblen); - server_host_key = key_from_blob(server_host_key_blob, sbloblen); - if (server_host_key == NULL) - fatal("cannot decode server_host_key_blob"); + if ((r = sshkey_from_blob(server_host_key_blob, sbloblen, + &server_host_key)) != 0) + fatal("cannot decode server_host_key_blob: %s", ssh_err(r)); if (server_host_key->type != kex->hostkey_type) fatal("type mismatch for decoded server_host_key_blob"); if (kex->verify_host_key == NULL) @@ -234,9 +235,10 @@ input_kex_dh_gex_reply(int type, u_int32_t seq, struct ssh *ssh) xfree(server_host_key_blob); BN_clear_free(dh_server_pub); - if (key_verify(server_host_key, signature, slen, hash, hashlen) != 1) + if (sshkey_verify(server_host_key, signature, slen, hash, + hashlen, datafellows) != 1) fatal("key_verify failed for server_host_key"); - key_free(server_host_key); + sshkey_free(server_host_key); xfree(signature); /* save session id */ diff --git a/ssh/kexgexs.c b/ssh/kexgexs.c index c1e5137..8b03dd2 100644 --- a/ssh/kexgexs.c +++ b/ssh/kexgexs.c @@ -47,6 +47,7 @@ #endif #include "monitor_wrap.h" #include "dispatch.h" +#include "err.h" struct kexgexs_state { int omin, min, omax, max, onbits; @@ -129,12 +130,11 @@ input_kex_dh_gex_init(int type, u_int32_t seq, struct ssh *ssh) Kex *kex = ssh->kex; struct kexgexs_state *kexgexs_state = kex->state; BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; - Key *server_host_public, *server_host_private; + struct sshkey *server_host_public, *server_host_private; DH *dh; u_int sbloblen, klen, slen, hashlen; u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; - int omin, min, omax, max, onbits; - int kout; + int omin, min, omax, max, onbits, kout, r; dh = kexgexs_state->dh; omin = kexgexs_state->omin; @@ -191,7 +191,9 @@ input_kex_dh_gex_init(int type, u_int32_t seq, struct ssh *ssh) memset(kbuf, 0, klen); xfree(kbuf); - key_to_blob(server_host_public, &server_host_key_blob, &sbloblen); + if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob, + &sbloblen)) != 0) + fatal("%s: sshkey_to_blob failed: %s", __func__, ssh_err(r)); if (type == SSH2_MSG_KEX_DH_GEX_REQUEST_OLD) omin = min = omax = max = -1; @@ -221,9 +223,9 @@ input_kex_dh_gex_init(int type, u_int32_t seq, struct ssh *ssh) } /* sign H */ - if (PRIVSEP(key_sign(server_host_private, &signature, &slen, hash, - hashlen)) < 0) - fatal("kexgex_server: key_sign failed"); + if (PRIVSEP(sshkey_sign(server_host_private, &signature, &slen, hash, + hashlen, datafellows)) < 0) + fatal("kexgex_server: sshkey_sign failed"); /* destroy_sensitive_data(); */ diff --git a/ssh/key.c b/ssh/key.c index 5761320..c317adb 100644 --- a/ssh/key.c +++ b/ssh/key.c @@ -1,17 +1,8 @@ -/* $OpenBSD: key.c,v 1.98 2011/10/18 04:58:26 djm Exp $ */ +/* $OpenBSD: key.c,v 1.97 2011/05/17 07:13:31 djm Exp $ */ /* - * read_bignum(): - * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - * - * * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2008 Alexander von Gernler. All rights reserved. + * Copyright (c) 2010,2011 Damien Miller. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,40 +30,243 @@ #include +#include #include #include -#include "xmalloc.h" -#include "key.h" -#include "rsa.h" -#include "uuencode.h" -#include "buffer.h" -#include "log.h" -#include "misc.h" #include "ssh2.h" +#include "err.h" +#include "sshbuf.h" +#define SSHKEY_INTERNAL +#include "key.h" -static struct KeyCert * +struct keytype { + int type; + char *name; + char *ssh2name; + int plain_type; + int nid; +}; + +static const struct keytype key_types[] = { + { KEY_RSA1, "RSA1", NULL, KEY_RSA, -1 }, + { KEY_RSA, "RSA", "ssh-rsa", KEY_RSA, -1 }, + { KEY_DSA, "DSA", "ssh-dss", KEY_DSA, -1 }, + { KEY_RSA_CERT_V00, "RSA-CERT-V00", "ssh-rsa-cert-v00@openssh.com", + KEY_RSA, -1 }, + { KEY_DSA_CERT_V00, "DSA-CERT-V00", "ssh-dss-cert-v00@openssh.com", + KEY_DSA, -1 }, + { KEY_RSA_CERT, "RSA-CERT", "ssh-rsa-cert-v01@openssh.com", + KEY_RSA, -1 }, + { KEY_DSA_CERT, "DSA-CERT", "ssh-dss-cert-v01@openssh.com", + KEY_DSA, -1 }, + { KEY_ECDSA, "ECDSA", "ecdsa-sha2-nistp256", + KEY_ECDSA, NID_X9_62_prime256v1 }, + { KEY_ECDSA, "ECDSA", "ecdsa-sha2-nistp384", KEY_ECDSA, NID_secp384r1 }, + { KEY_ECDSA, "ECDSA", "ecdsa-sha2-nistp521", KEY_ECDSA, NID_secp521r1 }, + { KEY_ECDSA_CERT, "ECDSA-CERT", + "ecdsa-sha2-nistp256-cert-v01@openssh.com", + KEY_ECDSA, NID_X9_62_prime256v1 }, + { KEY_ECDSA_CERT, "ECDSA-CERT", + "ecdsa-sha2-nistp384-cert-v01@openssh.com", + KEY_ECDSA, NID_secp384r1 }, + { KEY_ECDSA_CERT, "ECDSA-CERT", + "ecdsa-sha2-nistp521-cert-v01@openssh.com", + KEY_ECDSA, NID_secp521r1 }, + { -1, NULL, NULL, -1, -1 } +}; + +int +sshkey_type_from_name(char *name) +{ + const struct keytype *kt; + + for (kt = key_types; kt->name != NULL; kt++) { + if (strcasecmp(name, kt->name) == 0 || + (kt->ssh2name != NULL && strcmp(name, kt->ssh2name) == 0)) + return kt->type; + } + return KEY_UNSPEC; +} + +const char * +sshkey_type(const struct sshkey *k) +{ + u_int i; + + for (i = 0; key_types[i].type != -1; i++) { + if (key_types[i].type == k->type) + return key_types[i].name; + } + return "unknown"; +} + +const char * +sshkey_cert_type(const struct sshkey *k) +{ + switch (k->cert->type) { + case SSH2_CERT_TYPE_USER: + return "user"; + case SSH2_CERT_TYPE_HOST: + return "host"; + default: + return "unknown"; + } +} + +static const char * +sshkey_ssh_name_from_type_nid(int type, int nid, int plain) +{ + const struct keytype *kt; + + for (kt = key_types; kt->name != NULL; kt++) { + if (type == (plain ? kt->plain_type : kt->type) && + (kt->nid == -1 || kt->nid == nid)) + return kt->ssh2name; + } + return "ssh-unknown"; +} + +const char * +sshkey_ssh_name(const struct sshkey *k) +{ + return sshkey_ssh_name_from_type_nid(k->type, k->ecdsa_nid, 0); +} + +const char * +sshkey_ssh_name_plain(const struct sshkey *k) +{ + return sshkey_ssh_name_from_type_nid(k->type, k->ecdsa_nid, 1); +} + +int +sshkey_ecdsa_bits_to_nid(int bits) +{ + switch (bits) { + case 256: + return NID_X9_62_prime256v1; + case 384: + return NID_secp384r1; + case 521: + return NID_secp521r1; + default: + return -1; + } +} + +int +sshkey_ecdsa_nid_from_name(const char *name) +{ + const struct keytype *kt; + + for (kt = key_types; kt->name != NULL; kt++) { + if (kt->ssh2name != NULL && strcmp(kt->ssh2name, name) == 0) + return kt->nid; + } + return -1; +} + +int +sshkey_cert_is_legacy(struct sshkey *k) +{ + switch (k->type) { + case KEY_DSA_CERT_V00: + case KEY_RSA_CERT_V00: + return 1; + default: + return 0; + } +} + +/* XXX: these are really begging for a table-driven approach */ +int +sshkey_curve_name_to_nid(const char *name) +{ + if (strcmp(name, "nistp256") == 0) + return NID_X9_62_prime256v1; + else if (strcmp(name, "nistp384") == 0) + return NID_secp384r1; + else if (strcmp(name, "nistp521") == 0) + return NID_secp521r1; + else + return -1; +} + +u_int +sshkey_curve_nid_to_bits(int nid) +{ + switch (nid) { + case NID_X9_62_prime256v1: + return 256; + case NID_secp384r1: + return 384; + case NID_secp521r1: + return 521; + default: + return 0; + } +} + +const char * +sshkey_curve_nid_to_name(int nid) +{ + switch (nid) { + case NID_X9_62_prime256v1: + return "nistp256"; + case NID_secp384r1: + return "nistp384"; + case NID_secp521r1: + return "nistp521"; + default: + return NULL; + } +} + +const EVP_MD * +sshkey_ec_nid_to_evpmd(int nid) +{ + int kbits = sshkey_curve_nid_to_bits(nid); + + if (kbits <= 0) + return NULL; + + /* RFC5656 section 6.2.1 */ + if (kbits <= 256) + return EVP_sha256(); + else if (kbits <= 384) + return EVP_sha384(); + else + return EVP_sha512(); +} + +static struct sshkey_cert * cert_new(void) { - struct KeyCert *cert; + struct sshkey_cert *cert; - cert = xcalloc(1, sizeof(*cert)); - buffer_init(&cert->certblob); - buffer_init(&cert->critical); - buffer_init(&cert->extensions); + if ((cert = calloc(1, sizeof(*cert))) == NULL) + return NULL; + if ((cert->certblob = sshbuf_new()) == NULL || + (cert->critical = sshbuf_new()) == NULL || + (cert->extensions = sshbuf_new()) == NULL) { + free(cert); + return NULL; + } cert->key_id = NULL; cert->principals = NULL; cert->signature_key = NULL; return cert; } -Key * -key_new(int type) +struct sshkey * +sshkey_new(int type) { - Key *k; + struct sshkey *k; RSA *rsa; DSA *dsa; - k = xcalloc(1, sizeof(*k)); + + if ((k = calloc(1, sizeof(*k))) == NULL) + return NULL; k->type = type; k->ecdsa = NULL; k->ecdsa_nid = -1; @@ -84,27 +278,29 @@ key_new(int type) case KEY_RSA: case KEY_RSA_CERT_V00: case KEY_RSA_CERT: - if ((rsa = RSA_new()) == NULL) - fatal("key_new: RSA_new failed"); - if ((rsa->n = BN_new()) == NULL) - fatal("key_new: BN_new failed"); - if ((rsa->e = BN_new()) == NULL) - fatal("key_new: BN_new failed"); + if ((rsa = RSA_new()) == NULL || + (rsa->n = BN_new()) == NULL || + (rsa->e = BN_new()) == NULL) { + if (rsa != NULL) + RSA_free(rsa); + free(k); + return NULL; + } k->rsa = rsa; break; case KEY_DSA: case KEY_DSA_CERT_V00: case KEY_DSA_CERT: - if ((dsa = DSA_new()) == NULL) - fatal("key_new: DSA_new failed"); - if ((dsa->p = BN_new()) == NULL) - fatal("key_new: BN_new failed"); - if ((dsa->q = BN_new()) == NULL) - fatal("key_new: BN_new failed"); - if ((dsa->g = BN_new()) == NULL) - fatal("key_new: BN_new failed"); - if ((dsa->pub_key = BN_new()) == NULL) - fatal("key_new: BN_new failed"); + if ((dsa = DSA_new()) == NULL || + (dsa->p = BN_new()) == NULL || + (dsa->q = BN_new()) == NULL || + (dsa->g = BN_new()) == NULL || + (dsa->pub_key = BN_new()) == NULL) { + if (dsa != NULL) + DSA_free(dsa); + free(k); + return NULL; + } k->dsa = dsa; break; case KEY_ECDSA: @@ -114,42 +310,42 @@ key_new(int type) case KEY_UNSPEC: break; default: - fatal("key_new: bad key type %d", k->type); + free(k); + return NULL; break; } - if (key_is_cert(k)) - k->cert = cert_new(); + if (sshkey_is_cert(k)) { + if ((k->cert = cert_new()) == NULL) { + sshkey_free(k); + return NULL; + } + } return k; } -void -key_add_private(Key *k) +int +sshkey_add_private(struct sshkey *k) { switch (k->type) { case KEY_RSA1: case KEY_RSA: case KEY_RSA_CERT_V00: case KEY_RSA_CERT: - if ((k->rsa->d = BN_new()) == NULL) - fatal("key_new_private: BN_new failed"); - if ((k->rsa->iqmp = BN_new()) == NULL) - fatal("key_new_private: BN_new failed"); - if ((k->rsa->q = BN_new()) == NULL) - fatal("key_new_private: BN_new failed"); - if ((k->rsa->p = BN_new()) == NULL) - fatal("key_new_private: BN_new failed"); - if ((k->rsa->dmq1 = BN_new()) == NULL) - fatal("key_new_private: BN_new failed"); - if ((k->rsa->dmp1 = BN_new()) == NULL) - fatal("key_new_private: BN_new failed"); + if ((k->rsa->d = BN_new()) == NULL || + (k->rsa->iqmp = BN_new()) == NULL || + (k->rsa->q = BN_new()) == NULL || + (k->rsa->p = BN_new()) == NULL || + (k->rsa->dmq1 = BN_new()) == NULL || + (k->rsa->dmp1 = BN_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; break; case KEY_DSA: case KEY_DSA_CERT_V00: case KEY_DSA_CERT: if ((k->dsa->priv_key = BN_new()) == NULL) - fatal("key_new_private: BN_new failed"); + return SSH_ERR_ALLOC_FAIL; break; case KEY_ECDSA: case KEY_ECDSA_CERT: @@ -158,42 +354,54 @@ key_add_private(Key *k) case KEY_UNSPEC: break; default: - break; + return SSH_ERR_INVALID_ARGUMENT; } + return 0; } -Key * -key_new_private(int type) +struct sshkey * +sshkey_new_private(int type) { - Key *k = key_new(type); + struct sshkey *k = sshkey_new(type); - key_add_private(k); + if (k == NULL) + return NULL; + if (sshkey_add_private(k) != 0) { + sshkey_free(k); + return NULL; + } return k; } static void -cert_free(struct KeyCert *cert) +cert_free(struct sshkey_cert *cert) { u_int i; - buffer_free(&cert->certblob); - buffer_free(&cert->critical); - buffer_free(&cert->extensions); + if (cert == NULL) + return; + if (cert->certblob != NULL) + sshbuf_free(cert->certblob); + if (cert->critical != NULL) + sshbuf_free(cert->critical); + if (cert->extensions != NULL) + sshbuf_free(cert->extensions); if (cert->key_id != NULL) - xfree(cert->key_id); + free(cert->key_id); for (i = 0; i < cert->nprincipals; i++) - xfree(cert->principals[i]); + free(cert->principals[i]); if (cert->principals != NULL) - xfree(cert->principals); + free(cert->principals); if (cert->signature_key != NULL) - key_free(cert->signature_key); + sshkey_free(cert->signature_key); + bzero(cert, sizeof(*cert)); } void -key_free(Key *k) +sshkey_free(struct sshkey *k) { if (k == NULL) - fatal("key_free: key is NULL"); + return; switch (k->type) { case KEY_RSA1: case KEY_RSA: @@ -219,29 +427,25 @@ key_free(Key *k) case KEY_UNSPEC: break; default: - fatal("key_free: bad key type %d", k->type); break; } - if (key_is_cert(k)) { - if (k->cert != NULL) - cert_free(k->cert); - k->cert = NULL; - } - - xfree(k); + if (sshkey_is_cert(k)) + cert_free(k->cert); + bzero(k, sizeof(*k)); + free(k); } static int -cert_compare(struct KeyCert *a, struct KeyCert *b) +cert_compare(struct sshkey_cert *a, struct sshkey_cert *b) { if (a == NULL && b == NULL) return 1; if (a == NULL || b == NULL) return 0; - if (buffer_len(&a->certblob) != buffer_len(&b->certblob)) + if (sshbuf_len(a->certblob) != sshbuf_len(b->certblob)) return 0; - if (timingsafe_bcmp(buffer_ptr(&a->certblob), buffer_ptr(&b->certblob), - buffer_len(&a->certblob)) != 0) + if (timingsafe_bcmp(sshbuf_ptr(a->certblob), sshbuf_ptr(b->certblob), + sshbuf_len(a->certblob)) != 0) return 0; return 1; } @@ -251,12 +455,12 @@ cert_compare(struct KeyCert *a, struct KeyCert *b) * certificates and plain keys too. */ int -key_equal_public(const Key *a, const Key *b) +sshkey_equal_public(const struct sshkey *a, const struct sshkey *b) { BN_CTX *bnctx; if (a == NULL || b == NULL || - key_type_plain(a->type) != key_type_plain(b->type)) + sshkey_type_plain(a->type) != sshkey_type_plain(b->type)) return 0; switch (a->type) { @@ -282,7 +486,7 @@ key_equal_public(const Key *a, const Key *b) EC_KEY_get0_public_key(b->ecdsa) == NULL) return 0; if ((bnctx = BN_CTX_new()) == NULL) - fatal("%s: BN_CTX_new failed", __func__); + return 0; if (EC_GROUP_cmp(EC_KEY_get0_group(a->ecdsa), EC_KEY_get0_group(b->ecdsa), bnctx) != 0 || EC_POINT_cmp(EC_KEY_get0_group(a->ecdsa), @@ -294,29 +498,30 @@ key_equal_public(const Key *a, const Key *b) BN_CTX_free(bnctx); return 1; default: - fatal("key_equal: bad key type %d", a->type); + return 0; } /* NOTREACHED */ } int -key_equal(const Key *a, const Key *b) +sshkey_equal(const struct sshkey *a, const struct sshkey *b) { if (a == NULL || b == NULL || a->type != b->type) return 0; - if (key_is_cert(a)) { + if (sshkey_is_cert(a)) { if (!cert_compare(a->cert, b->cert)) return 0; } - return key_equal_public(a, b); + return sshkey_equal_public(a, b); } u_char* -key_fingerprint_raw(Key *k, enum fp_type dgst_type, u_int *dgst_raw_length) +sshkey_fingerprint_raw(struct sshkey *k, enum sshkey_fp_type dgst_type, + u_int *dgst_raw_length) { const EVP_MD *md = NULL; EVP_MD_CTX ctx; - u_char *blob = NULL; + u_char *blob; u_char *retval = NULL; u_int len = 0; int nlen, elen, otype; @@ -331,22 +536,23 @@ key_fingerprint_raw(Key *k, enum fp_type dgst_type, u_int *dgst_raw_length) md = EVP_sha1(); break; default: - fatal("key_fingerprint_raw: bad digest type %d", - dgst_type); + return NULL; } switch (k->type) { case KEY_RSA1: nlen = BN_num_bytes(k->rsa->n); elen = BN_num_bytes(k->rsa->e); len = nlen + elen; - blob = xmalloc(len); + if ((blob = malloc(len)) == NULL) + return NULL; BN_bn2bin(k->rsa->n, blob); BN_bn2bin(k->rsa->e, blob + nlen); break; case KEY_DSA: case KEY_ECDSA: case KEY_RSA: - key_to_blob(k, &blob, &len); + if (sshkey_to_blob(k, &blob, &len) == -1) + return NULL; break; case KEY_DSA_CERT_V00: case KEY_RSA_CERT_V00: @@ -355,36 +561,38 @@ key_fingerprint_raw(Key *k, enum fp_type dgst_type, u_int *dgst_raw_length) case KEY_RSA_CERT: /* We want a fingerprint of the _key_ not of the cert */ otype = k->type; - k->type = key_type_plain(k->type); - key_to_blob(k, &blob, &len); + k->type = sshkey_type_plain(k->type); + if (sshkey_to_blob(k, &blob, &len) == -1) { + k->type = otype; + return NULL; + } k->type = otype; break; case KEY_UNSPEC: - return retval; default: - fatal("key_fingerprint_raw: bad key type %d", k->type); - break; + return NULL; } - if (blob != NULL) { - retval = xmalloc(EVP_MAX_MD_SIZE); - EVP_DigestInit(&ctx, md); - EVP_DigestUpdate(&ctx, blob, len); - EVP_DigestFinal(&ctx, retval, dgst_raw_length); - memset(blob, 0, len); - xfree(blob); - } else { - fatal("key_fingerprint_raw: blob is null"); + if ((retval = malloc(EVP_MAX_MD_SIZE)) == NULL) { + bzero(blob, len); + free(blob); + return NULL; } + EVP_DigestInit(&ctx, md); + EVP_DigestUpdate(&ctx, blob, len); + EVP_DigestFinal(&ctx, retval, dgst_raw_length); + bzero(blob, len); + free(blob); return retval; } static char * -key_fingerprint_hex(u_char *dgst_raw, u_int dgst_raw_len) +fingerprint_hex(u_char *dgst_raw, u_int dgst_raw_len) { char *retval; u_int i; - retval = xcalloc(1, dgst_raw_len * 3 + 1); + if ((retval = calloc(1, dgst_raw_len * 3 + 1)) == NULL) + return NULL; for (i = 0; i < dgst_raw_len; i++) { char hex[4]; snprintf(hex, sizeof(hex), "%02x:", dgst_raw[i]); @@ -397,7 +605,7 @@ key_fingerprint_hex(u_char *dgst_raw, u_int dgst_raw_len) } static char * -key_fingerprint_bubblebabble(u_char *dgst_raw, u_int dgst_raw_len) +fingerprint_bubblebabble(u_char *dgst_raw, u_int dgst_raw_len) { char vowels[] = { 'a', 'e', 'i', 'o', 'u', 'y' }; char consonants[] = { 'b', 'c', 'd', 'f', 'g', 'h', 'k', 'l', 'm', @@ -406,7 +614,8 @@ key_fingerprint_bubblebabble(u_char *dgst_raw, u_int dgst_raw_len) char *retval; rounds = (dgst_raw_len / 2) + 1; - retval = xcalloc((rounds * 6), sizeof(char)); + if ((retval = calloc(rounds, 6)) == NULL) + return NULL; retval[j++] = 'x'; for (i = 0; i < rounds; i++) { u_int idx0, idx1, idx2, idx3, idx4; @@ -477,7 +686,8 @@ key_fingerprint_bubblebabble(u_char *dgst_raw, u_int dgst_raw_len) #define FLDSIZE_Y (FLDBASE + 1) #define FLDSIZE_X (FLDBASE * 2 + 1) static char * -key_fingerprint_randomart(u_char *dgst_raw, u_int dgst_raw_len, const Key *k) +fingerprint_randomart(u_char *dgst_raw, u_int dgst_raw_len, + const struct sshkey *k) { /* * Chars to be used after each other every time the worm @@ -490,7 +700,8 @@ key_fingerprint_randomart(u_char *dgst_raw, u_int dgst_raw_len, const Key *k) int x, y; size_t len = strlen(augmentation_string) - 1; - retval = xcalloc(1, (FLDSIZE_X + 3) * (FLDSIZE_Y + 2)); + if ((retval = calloc((FLDSIZE_X + 3), (FLDSIZE_Y + 2))) == NULL) + return NULL; /* initialize field */ memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char)); @@ -525,7 +736,8 @@ key_fingerprint_randomart(u_char *dgst_raw, u_int dgst_raw_len, const Key *k) field[x][y] = len; /* fill in retval */ - snprintf(retval, FLDSIZE_X, "+--[%4s %4u]", key_type(k), key_size(k)); + snprintf(retval, FLDSIZE_X, "+--[%4s %4u]", + sshkey_type(k), sshkey_size(k)); p = strchr(retval, '\0'); /* output upper border */ @@ -553,32 +765,33 @@ key_fingerprint_randomart(u_char *dgst_raw, u_int dgst_raw_len, const Key *k) } char * -key_fingerprint(Key *k, enum fp_type dgst_type, enum fp_rep dgst_rep) +sshkey_fingerprint(struct sshkey *k, enum sshkey_fp_type dgst_type, + enum sshkey_fp_rep dgst_rep) { char *retval = NULL; u_char *dgst_raw; u_int dgst_raw_len; - dgst_raw = key_fingerprint_raw(k, dgst_type, &dgst_raw_len); - if (!dgst_raw) - fatal("key_fingerprint: null from key_fingerprint_raw()"); + if ((dgst_raw = sshkey_fingerprint_raw(k, dgst_type, + &dgst_raw_len)) == NULL) + return NULL; switch (dgst_rep) { case SSH_FP_HEX: - retval = key_fingerprint_hex(dgst_raw, dgst_raw_len); + retval = fingerprint_hex(dgst_raw, dgst_raw_len); break; case SSH_FP_BUBBLEBABBLE: - retval = key_fingerprint_bubblebabble(dgst_raw, dgst_raw_len); + retval = fingerprint_bubblebabble(dgst_raw, dgst_raw_len); break; case SSH_FP_RANDOMART: - retval = key_fingerprint_randomart(dgst_raw, dgst_raw_len, k); + retval = fingerprint_randomart(dgst_raw, dgst_raw_len, k); break; default: - fatal("key_fingerprint: bad digest representation %d", - dgst_rep); - break; + bzero(dgst_raw, dgst_raw_len); + free(dgst_raw); + return NULL; } - memset(dgst_raw, 0, dgst_raw_len); - xfree(dgst_raw); + bzero(dgst_raw, dgst_raw_len); + free(dgst_raw); return retval; } @@ -586,94 +799,61 @@ key_fingerprint(Key *k, enum fp_type dgst_type, enum fp_rep dgst_rep) * Reads a multiple-precision integer in decimal from the buffer, and advances * the pointer. The integer must already be initialized. This function is * permitted to modify the buffer. This leaves *cpp to point just beyond the - * last processed (and maybe modified) character. Note that this may modify - * the buffer containing the number. + * last processed character. */ static int -read_bignum(char **cpp, BIGNUM * value) +read_decimal_bignum(char **cpp, BIGNUM *v) { - char *cp = *cpp; - int old; + char *cp; + size_t e; - /* Skip any leading whitespace. */ - for (; *cp == ' ' || *cp == '\t'; cp++) - ; - - /* Check that it begins with a decimal digit. */ - if (*cp < '0' || *cp > '9') - return 0; - - /* Save starting position. */ - *cpp = cp; - - /* Move forward until all decimal digits skipped. */ - for (; *cp >= '0' && *cp <= '9'; cp++) - ; - - /* Save the old terminating character, and replace it by \0. */ - old = *cp; - *cp = 0; - - /* Parse the number. */ - if (BN_dec2bn(&value, *cpp) == 0) - return 0; - - /* Restore old terminating character. */ - *cp = old; - - /* Move beyond the number and return success. */ - *cpp = cp; - return 1; -} - -static int -write_bignum(FILE *f, BIGNUM *num) -{ - char *buf = BN_bn2dec(num); - if (buf == NULL) { - error("write_bignum: BN_bn2dec() failed"); - return 0; - } - fprintf(f, " %s", buf); - OPENSSL_free(buf); - return 1; + cp = *cpp; + while (*cp == ' ' || *cp == '\t') + cp++; + e = strspn(cp, "0123456789"); + if (e == 0) + return SSH_ERR_INVALID_FORMAT; + if (e > SSHBUF_MAX_BIGNUM * 3) + return SSH_ERR_BIGNUM_TOO_LARGE; + if (cp[e] != '\0' && index(" \t\r\n", cp[e]) != NULL) + return SSH_ERR_INVALID_FORMAT; + cp[e] = '\0'; + if (BN_dec2bn(&v, cp) <= 0) + return SSH_ERR_INVALID_FORMAT; + *cpp = cp + e; + return 0; } /* returns 1 ok, -1 error */ int -key_read(Key *ret, char **cpp) +sshkey_read(struct sshkey *ret, char **cpp) { - Key *k; - int success = -1; - char *cp, *space; - int len, n, type, curve_nid = -1; - u_int bits; - u_char *blob; + struct sshkey *k; + int retval = SSH_ERR_INVALID_FORMAT; + char *cp, *ep, *space; + int r, type, curve_nid = -1; + u_long bits; + struct sshbuf *blob; cp = *cpp; switch (ret->type) { case KEY_RSA1: /* Get number of bits. */ - if (*cp < '0' || *cp > '9') - return -1; /* Bad bit count... */ - for (bits = 0; *cp >= '0' && *cp <= '9'; cp++) - bits = 10 * bits + *cp - '0'; - if (bits == 0) - return -1; - *cpp = cp; + bits = strtoul(cp, &ep, 10); + if (*cp == '\0' || index(" \t\r\n", *ep) != NULL || + bits == 0 || bits > SSHBUF_MAX_BIGNUM * 8) + return SSH_ERR_INVALID_FORMAT; /* Bad bit count... */ /* Get public exponent, public modulus. */ - if (!read_bignum(cpp, ret->rsa->e)) - return -1; - if (!read_bignum(cpp, ret->rsa->n)) - return -1; + if ((r = read_decimal_bignum(&ep, ret->rsa->e)) < 0) + return r; + if ((r = read_decimal_bignum(&ep, ret->rsa->n)) < 0) + return r; + *cpp = ep; /* validate the claimed number of bits */ - if ((u_int)BN_num_bits(ret->rsa->n) != bits) { - verbose("key_read: claimed key size %d does not match " - "actual %d", bits, BN_num_bits(ret->rsa->n)); - return -1; - } - success = 1; + if (BN_num_bits(ret->rsa->n) != (int)bits) + return SSH_ERR_KEY_BITS_MISMATCH; + retval = 0; break; case KEY_UNSPEC: case KEY_RSA: @@ -685,72 +865,54 @@ key_read(Key *ret, char **cpp) case KEY_ECDSA_CERT: case KEY_RSA_CERT: space = strchr(cp, ' '); - if (space == NULL) { - debug3("key_read: missing whitespace"); - return -1; - } + if (space == NULL) + return SSH_ERR_INVALID_FORMAT; *space = '\0'; - type = key_type_from_name(cp); - if (key_type_plain(type) == KEY_ECDSA && - (curve_nid = key_ecdsa_nid_from_name(cp)) == -1) { - debug("key_read: invalid curve"); - return -1; - } + type = sshkey_type_from_name(cp); + if (sshkey_type_plain(type) == KEY_ECDSA && + (curve_nid = sshkey_ecdsa_nid_from_name(cp)) == -1) + return SSH_ERR_EC_CURVE_INVALID; *space = ' '; - if (type == KEY_UNSPEC) { - debug3("key_read: missing keytype"); - return -1; - } + if (type == KEY_UNSPEC) + return SSH_ERR_INVALID_FORMAT; cp = space+1; - if (*cp == '\0') { - debug3("key_read: short string"); - return -1; - } + if (*cp == '\0') + return SSH_ERR_INVALID_FORMAT; if (ret->type == KEY_UNSPEC) { ret->type = type; - } else if (ret->type != type) { - /* is a key, but different type */ - debug3("key_read: type mismatch"); - return -1; - } - len = 2*strlen(cp); - blob = xmalloc(len); - n = uudecode(cp, blob, len); - if (n < 0) { - error("key_read: uudecode %s failed", cp); - xfree(blob); - return -1; - } - k = key_from_blob(blob, (u_int)n); - xfree(blob); - if (k == NULL) { - error("key_read: key_from_blob %s failed", cp); - return -1; + } else if (ret->type != type) + return SSH_ERR_KEY_TYPE_MISMATCH; + if ((blob = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_b64tod(blob, cp)) != 0) + return r; + if ((r = sshkey_from_blob(sshbuf_ptr(blob), + sshbuf_len(blob), &k)) != 0) { + sshbuf_free(blob); + return r; } + sshbuf_free(blob); if (k->type != type) { - error("key_read: type mismatch: encoding error"); - key_free(k); - return -1; + sshkey_free(k); + return SSH_ERR_KEY_TYPE_MISMATCH; } - if (key_type_plain(type) == KEY_ECDSA && + if (sshkey_type_plain(type) == KEY_ECDSA && curve_nid != k->ecdsa_nid) { - error("key_read: type mismatch: EC curve mismatch"); - key_free(k); - return -1; + sshkey_free(k); + return SSH_ERR_EC_CURVE_MISMATCH; } /*XXXX*/ - if (key_is_cert(ret)) { - if (!key_is_cert(k)) { - error("key_read: loaded key is not a cert"); - key_free(k); - return -1; + if (sshkey_is_cert(ret)) { + if (!sshkey_is_cert(k)) { + sshkey_free(k); + return SSH_ERR_EXPECTED_CERT; } if (ret->cert != NULL) cert_free(ret->cert); ret->cert = k->cert; k->cert = NULL; } - if (key_type_plain(ret->type) == KEY_RSA) { + if (sshkey_type_plain(ret->type) == KEY_RSA) { if (ret->rsa != NULL) RSA_free(ret->rsa); ret->rsa = k->rsa; @@ -759,7 +921,7 @@ key_read(Key *ret, char **cpp) RSA_print_fp(stderr, ret->rsa, 8); #endif } - if (key_type_plain(ret->type) == KEY_DSA) { + if (sshkey_type_plain(ret->type) == KEY_DSA) { if (ret->dsa != NULL) DSA_free(ret->dsa); ret->dsa = k->dsa; @@ -768,7 +930,7 @@ key_read(Key *ret, char **cpp) DSA_print_fp(stderr, ret->dsa, 8); #endif } - if (key_type_plain(ret->type) == KEY_ECDSA) { + if (sshkey_type_plain(ret->type) == KEY_ECDSA) { if (ret->ecdsa != NULL) EC_KEY_free(ret->ecdsa); ret->ecdsa = k->ecdsa; @@ -776,13 +938,13 @@ key_read(Key *ret, char **cpp) k->ecdsa = NULL; k->ecdsa_nid = -1; #ifdef DEBUG_PK - key_dump_ec_key(ret->ecdsa); + sshkey_dump_ec_key(ret->ecdsa); #endif } - success = 1; + retval = 0; /*XXXX*/ - key_free(k); - if (success != 1) + sshkey_free(k); + if (retval != 0) break; /* advance cp: skip whitespace and data */ while (*cp == ' ' || *cp == '\t') @@ -792,175 +954,97 @@ key_read(Key *ret, char **cpp) *cpp = cp; break; default: - fatal("key_read: bad key type: %d", ret->type); - break; + return SSH_ERR_INVALID_ARGUMENT; } - return success; + return retval; } int -key_write(const Key *key, FILE *f) +sshkey_write(const struct sshkey *key, FILE *f) { - int n, success = 0; - u_int len, bits = 0; - u_char *blob; - char *uu; + int ret = SSH_ERR_INTERNAL_ERROR; + u_int bits = 0; + struct sshbuf *b = NULL, *bb = NULL; + char *uu = NULL, *dec_e = NULL, *dec_n = NULL; - if (key_is_cert(key)) { - if (key->cert == NULL) { - error("%s: no cert data", __func__); - return 0; - } - if (buffer_len(&key->cert->certblob) == 0) { - error("%s: no signed certificate blob", __func__); - return 0; - } + if (sshkey_is_cert(key)) { + if (key->cert == NULL) + return SSH_ERR_EXPECTED_CERT; + if (sshbuf_len(key->cert->certblob) == 0) + return SSH_ERR_KEY_LACKS_CERTBLOB; } - + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; switch (key->type) { case KEY_RSA1: - if (key->rsa == NULL) - return 0; + if (key->rsa == NULL || key->rsa->e == NULL || + key->rsa->n == NULL) { + ret = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((dec_e = BN_bn2dec(key->rsa->e)) == NULL || + (dec_n = BN_bn2dec(key->rsa->n)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } /* size of modulus 'n' */ - bits = BN_num_bits(key->rsa->n); - fprintf(f, "%u", bits); - if (write_bignum(f, key->rsa->e) && - write_bignum(f, key->rsa->n)) - return 1; - error("key_write: failed for RSA key"); - return 0; + if ((bits = BN_num_bits(key->rsa->n)) <= 0) { + ret = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((ret = sshbuf_putf(b, "%u %s %s", bits, dec_e, dec_n)) != 0) + goto out; + break; case KEY_DSA: case KEY_DSA_CERT_V00: case KEY_DSA_CERT: - if (key->dsa == NULL) - return 0; - break; case KEY_ECDSA: case KEY_ECDSA_CERT: - if (key->ecdsa == NULL) - return 0; - break; case KEY_RSA: case KEY_RSA_CERT_V00: case KEY_RSA_CERT: - if (key->rsa == NULL) - return 0; + if ((bb = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((ret = sshkey_to_blob_buf(key, bb)) != 0) + goto out; + if ((uu = sshbuf_dtob16(bb)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((ret = sshbuf_putf(b, "%s ", sshkey_ssh_name(key))) != 0) + goto out; + if ((ret = sshbuf_put(b, uu, strlen(uu))) != 0) + goto out; break; default: - return 0; + ret = SSH_ERR_KEY_TYPE_UNKNOWN; + goto out; } - - key_to_blob(key, &blob, &len); - uu = xmalloc(2*len); - n = uuencode(blob, len, uu, 2*len); - if (n > 0) { - fprintf(f, "%s %s", key_ssh_name(key), uu); - success = 1; + if (fwrite(sshbuf_ptr(b), sshbuf_len(b), 1, f) != 1) { + if (feof(f)) + errno = EPIPE; + ret = SSH_ERR_SYSTEM_ERROR; + goto out; } - xfree(blob); - xfree(uu); - - return success; -} - -const char * -key_type(const Key *k) -{ - switch (k->type) { - case KEY_RSA1: - return "RSA1"; - case KEY_RSA: - return "RSA"; - case KEY_DSA: - return "DSA"; - case KEY_ECDSA: - return "ECDSA"; - case KEY_RSA_CERT_V00: - return "RSA-CERT-V00"; - case KEY_DSA_CERT_V00: - return "DSA-CERT-V00"; - case KEY_RSA_CERT: - return "RSA-CERT"; - case KEY_DSA_CERT: - return "DSA-CERT"; - case KEY_ECDSA_CERT: - return "ECDSA-CERT"; - } - return "unknown"; -} - -const char * -key_cert_type(const Key *k) -{ - switch (k->cert->type) { - case SSH2_CERT_TYPE_USER: - return "user"; - case SSH2_CERT_TYPE_HOST: - return "host"; - default: - return "unknown"; - } -} - -static const char * -key_ssh_name_from_type_nid(int type, int nid) -{ - switch (type) { - case KEY_RSA: - return "ssh-rsa"; - case KEY_DSA: - return "ssh-dss"; - case KEY_RSA_CERT_V00: - return "ssh-rsa-cert-v00@openssh.com"; - case KEY_DSA_CERT_V00: - return "ssh-dss-cert-v00@openssh.com"; - case KEY_RSA_CERT: - return "ssh-rsa-cert-v01@openssh.com"; - case KEY_DSA_CERT: - return "ssh-dss-cert-v01@openssh.com"; - case KEY_ECDSA: - switch (nid) { - case NID_X9_62_prime256v1: - return "ecdsa-sha2-nistp256"; - case NID_secp384r1: - return "ecdsa-sha2-nistp384"; - case NID_secp521r1: - return "ecdsa-sha2-nistp521"; - default: - break; - } - break; - case KEY_ECDSA_CERT: - switch (nid) { - case NID_X9_62_prime256v1: - return "ecdsa-sha2-nistp256-cert-v01@openssh.com"; - case NID_secp384r1: - return "ecdsa-sha2-nistp384-cert-v01@openssh.com"; - case NID_secp521r1: - return "ecdsa-sha2-nistp521-cert-v01@openssh.com"; - default: - break; - } - break; - } - return "ssh-unknown"; -} - -const char * -key_ssh_name(const Key *k) -{ - return key_ssh_name_from_type_nid(k->type, k->ecdsa_nid); -} - -const char * -key_ssh_name_plain(const Key *k) -{ - return key_ssh_name_from_type_nid(key_type_plain(k->type), - k->ecdsa_nid); + ret = 0; + out: + if (b != NULL) + sshbuf_free(b); + if (bb != NULL) + sshbuf_free(bb); + if (uu != NULL) + free(uu); + if (dec_e != NULL) + OPENSSL_free(dec_e); + if (dec_n != NULL) + OPENSSL_free(dec_n); + return ret; } u_int -key_size(const Key *k) +sshkey_size(const struct sshkey *k) { switch (k->type) { case KEY_RSA1: @@ -974,61 +1058,68 @@ key_size(const Key *k) return BN_num_bits(k->dsa->p); case KEY_ECDSA: case KEY_ECDSA_CERT: - return key_curve_nid_to_bits(k->ecdsa_nid); + return sshkey_curve_nid_to_bits(k->ecdsa_nid); } return 0; } -static RSA * -rsa_generate_private_key(u_int bits) +static int +rsa_generate_private_key(u_int bits, RSA **rsap) { - RSA *private = RSA_new(); - BIGNUM *f4 = BN_new(); + RSA *private = NULL; + BIGNUM *f4 = NULL; + int ret = SSH_ERR_INTERNAL_ERROR; - if (private == NULL) - fatal("%s: RSA_new failed", __func__); - if (f4 == NULL) - fatal("%s: BN_new failed", __func__); - if (!BN_set_word(f4, RSA_F4)) - fatal("%s: BN_new failed", __func__); - if (!RSA_generate_key_ex(private, bits, f4, NULL)) - fatal("%s: key generation failed.", __func__); - BN_free(f4); - return private; + if (rsap == NULL || bits == 0 || bits > SSHBUF_MAX_BIGNUM * 8) + return SSH_ERR_INVALID_ARGUMENT; + if ((private = RSA_new()) == NULL || (f4 = BN_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (!BN_set_word(f4, RSA_F4) || + !RSA_generate_key_ex(private, bits, f4, NULL)) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + *rsap = private; + private = NULL; + ret = 0; + out: + if (private != NULL) + RSA_free(private); + if (f4 != NULL) + BN_free(f4); + return ret; } -static DSA* -dsa_generate_private_key(u_int bits) +static int +dsa_generate_private_key(u_int bits, DSA **dsap) { DSA *private = DSA_new(); + int ret = SSH_ERR_INTERNAL_ERROR; - if (private == NULL) - fatal("%s: DSA_new failed", __func__); - if (!DSA_generate_parameters_ex(private, bits, NULL, 0, NULL, - NULL, NULL)) - fatal("%s: DSA_generate_parameters failed", __func__); - if (!DSA_generate_key(private)) - fatal("%s: DSA_generate_key failed.", __func__); - return private; -} - -int -key_ecdsa_bits_to_nid(int bits) -{ - switch (bits) { - case 256: - return NID_X9_62_prime256v1; - case 384: - return NID_secp384r1; - case 521: - return NID_secp521r1; - default: - return -1; + if (dsap == NULL || bits != 1024) + return SSH_ERR_INVALID_ARGUMENT; + if ((private = DSA_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; } + if (!DSA_generate_parameters_ex(private, bits, NULL, 0, NULL, + NULL, NULL) || !DSA_generate_key(private)) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + *dsap = private; + private = NULL; + ret = 0; + out: + if (private != NULL) + DSA_free(private); + return ret; } int -key_ecdsa_key_to_nid(EC_KEY *k) +sshkey_ecdsa_key_to_nid(EC_KEY *k) { EC_GROUP *eg; int nids[] = { @@ -1053,74 +1144,99 @@ key_ecdsa_key_to_nid(EC_KEY *k) if ((nid = EC_GROUP_get_curve_name(g)) > 0) return nid; if ((bnctx = BN_CTX_new()) == NULL) - fatal("%s: BN_CTX_new() failed", __func__); + return -1; for (i = 0; nids[i] != -1; i++) { - if ((eg = EC_GROUP_new_by_curve_name(nids[i])) == NULL) - fatal("%s: EC_GROUP_new_by_curve_name failed", - __func__); + if ((eg = EC_GROUP_new_by_curve_name(nids[i])) == NULL) { + BN_CTX_free(bnctx); + return -1; + } if (EC_GROUP_cmp(g, eg, bnctx) == 0) break; EC_GROUP_free(eg); } BN_CTX_free(bnctx); - debug3("%s: nid = %d", __func__, nids[i]); if (nids[i] != -1) { /* Use the group with the NID attached */ EC_GROUP_set_asn1_flag(eg, OPENSSL_EC_NAMED_CURVE); - if (EC_KEY_set_group(k, eg) != 1) - fatal("%s: EC_KEY_set_group", __func__); + if (EC_KEY_set_group(k, eg) != 1) { + EC_GROUP_free(eg); + return -1; + } } return nids[i]; } -static EC_KEY* -ecdsa_generate_private_key(u_int bits, int *nid) +static int +ecdsa_generate_private_key(u_int bits, int *nid, EC_KEY **ecdsap) { EC_KEY *private; + int ret = SSH_ERR_INTERNAL_ERROR; - if ((*nid = key_ecdsa_bits_to_nid(bits)) == -1) - fatal("%s: invalid key length", __func__); - if ((private = EC_KEY_new_by_curve_name(*nid)) == NULL) - fatal("%s: EC_KEY_new_by_curve_name failed", __func__); - if (EC_KEY_generate_key(private) != 1) - fatal("%s: EC_KEY_generate_key failed", __func__); + if (nid == NULL || ecdsap == NULL || + (*nid = sshkey_ecdsa_bits_to_nid(bits)) == -1) + return SSH_ERR_INVALID_ARGUMENT; + if ((private = EC_KEY_new_by_curve_name(*nid)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (EC_KEY_generate_key(private) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE); - return private; + *ecdsap = private; + private = NULL; + ret = 0; + out: + if (private != NULL) + EC_KEY_free(private); + return ret; } -Key * -key_generate(int type, u_int bits) +int +sshkey_generate(int type, u_int bits, struct sshkey **keyp) { - Key *k = key_new(KEY_UNSPEC); + struct sshkey *k; + int ret; + + if (keyp == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((k = sshkey_new(KEY_UNSPEC)) == NULL) + return SSH_ERR_ALLOC_FAIL; switch (type) { case KEY_DSA: - k->dsa = dsa_generate_private_key(bits); + ret = dsa_generate_private_key(bits, &k->dsa); break; case KEY_ECDSA: - k->ecdsa = ecdsa_generate_private_key(bits, &k->ecdsa_nid); + ret = ecdsa_generate_private_key(bits, &k->ecdsa_nid, + &k->ecdsa); break; case KEY_RSA: case KEY_RSA1: - k->rsa = rsa_generate_private_key(bits); + ret = rsa_generate_private_key(bits, &k->rsa); break; case KEY_RSA_CERT_V00: case KEY_DSA_CERT_V00: case KEY_RSA_CERT: case KEY_DSA_CERT: - fatal("key_generate: cert keys cannot be generated directly"); default: - fatal("key_generate: unknown type %d", type); + ret = SSH_ERR_INVALID_ARGUMENT; } - k->type = type; - return k; + if (ret == 0) { + k->type = type; + *keyp = k; + } else + sshkey_free(k); + return ret; } -void -key_cert_copy(const Key *from_key, struct Key *to_key) +int +sshkey_cert_copy(const struct sshkey *from_key, struct sshkey *to_key) { u_int i; - const struct KeyCert *from; - struct KeyCert *to; + const struct sshkey_cert *from; + struct sshkey_cert *to; + int ret = SSH_ERR_INTERNAL_ERROR; if (to_key->cert != NULL) { cert_free(to_key->cert); @@ -1128,322 +1244,307 @@ key_cert_copy(const Key *from_key, struct Key *to_key) } if ((from = from_key->cert) == NULL) - return; + return SSH_ERR_INVALID_ARGUMENT; - to = to_key->cert = cert_new(); + if ((to = to_key->cert = cert_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; - buffer_append(&to->certblob, buffer_ptr(&from->certblob), - buffer_len(&from->certblob)); - - buffer_append(&to->critical, - buffer_ptr(&from->critical), buffer_len(&from->critical)); - buffer_append(&to->extensions, - buffer_ptr(&from->extensions), buffer_len(&from->extensions)); + if ((ret = sshbuf_putb(to->certblob, from->certblob)) != 0 || + (ret = sshbuf_putb(to->critical, from->critical)) != 0 || + (ret = sshbuf_putb(to->extensions, from->extensions) != 0)) + return ret; to->serial = from->serial; to->type = from->type; - to->key_id = from->key_id == NULL ? NULL : xstrdup(from->key_id); + if (from->key_id == NULL) + to->key_id = NULL; + else if ((to->key_id = strdup(from->key_id)) == NULL) + return SSH_ERR_ALLOC_FAIL; to->valid_after = from->valid_after; to->valid_before = from->valid_before; - to->signature_key = from->signature_key == NULL ? - NULL : key_from_private(from->signature_key); + if (from->signature_key == NULL) + to->signature_key = NULL; + else if ((ret = sshkey_from_private(from->signature_key, + &to->signature_key)) != 0) + return ret; - to->nprincipals = from->nprincipals; - if (to->nprincipals > CERT_MAX_PRINCIPALS) - fatal("%s: nprincipals (%u) > CERT_MAX_PRINCIPALS (%u)", - __func__, to->nprincipals, CERT_MAX_PRINCIPALS); - if (to->nprincipals > 0) { - to->principals = xcalloc(from->nprincipals, - sizeof(*to->principals)); - for (i = 0; i < to->nprincipals; i++) - to->principals[i] = xstrdup(from->principals[i]); + if (from->nprincipals > SSHKEY_CERT_MAX_PRINCIPALS) + return SSH_ERR_INVALID_ARGUMENT; + if (from->nprincipals > 0) { + if ((to->principals = calloc(from->nprincipals, + sizeof(*to->principals))) == NULL) + return SSH_ERR_ALLOC_FAIL; + for (i = 0; i < from->nprincipals; i++) { + to->principals[i] = strdup(from->principals[i]); + if (to->principals[i] == NULL) { + to->nprincipals = i; + return SSH_ERR_ALLOC_FAIL; + } + } } + to->nprincipals = from->nprincipals; + return 0; } -Key * -key_from_private(const Key *k) +int +sshkey_from_private(const struct sshkey *k, struct sshkey **pkp) { - Key *n = NULL; + struct sshkey *n = NULL; + int ret; + switch (k->type) { case KEY_DSA: case KEY_DSA_CERT_V00: case KEY_DSA_CERT: - n = key_new(k->type); + if ((n = sshkey_new(k->type)) == NULL) + return SSH_ERR_ALLOC_FAIL; if ((BN_copy(n->dsa->p, k->dsa->p) == NULL) || (BN_copy(n->dsa->q, k->dsa->q) == NULL) || (BN_copy(n->dsa->g, k->dsa->g) == NULL) || - (BN_copy(n->dsa->pub_key, k->dsa->pub_key) == NULL)) - fatal("key_from_private: BN_copy failed"); + (BN_copy(n->dsa->pub_key, k->dsa->pub_key) == NULL)) { + sshkey_free(n); + return SSH_ERR_ALLOC_FAIL; + } break; case KEY_ECDSA: case KEY_ECDSA_CERT: - n = key_new(k->type); + if ((n = sshkey_new(k->type)) == NULL) + return SSH_ERR_ALLOC_FAIL; n->ecdsa_nid = k->ecdsa_nid; - if ((n->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid)) == NULL) - fatal("%s: EC_KEY_new_by_curve_name failed", __func__); + n->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid); + if (n->ecdsa == NULL) { + sshkey_free(n); + return SSH_ERR_ALLOC_FAIL; + } if (EC_KEY_set_public_key(n->ecdsa, - EC_KEY_get0_public_key(k->ecdsa)) != 1) - fatal("%s: EC_KEY_set_public_key failed", __func__); + EC_KEY_get0_public_key(k->ecdsa)) != 1) { + sshkey_free(n); + return SSH_ERR_LIBCRYPTO_ERROR; + } break; case KEY_RSA: case KEY_RSA1: case KEY_RSA_CERT_V00: case KEY_RSA_CERT: - n = key_new(k->type); + if ((n = sshkey_new(k->type)) == NULL) + return SSH_ERR_ALLOC_FAIL; if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) || - (BN_copy(n->rsa->e, k->rsa->e) == NULL)) - fatal("key_from_private: BN_copy failed"); + (BN_copy(n->rsa->e, k->rsa->e) == NULL)) { + sshkey_free(n); + return SSH_ERR_ALLOC_FAIL; + } break; default: - fatal("key_from_private: unknown type %d", k->type); - break; + return SSH_ERR_KEY_TYPE_UNKNOWN; } - if (key_is_cert(k)) - key_cert_copy(k, n); - return n; + if (sshkey_is_cert(k)) { + if ((ret = sshkey_cert_copy(k, n)) != 0) { + sshkey_free(n); + return ret; + } + } + *pkp = n; + return 0; } int -key_type_from_name(char *name) -{ - if (strcmp(name, "rsa1") == 0) { - return KEY_RSA1; - } else if (strcmp(name, "rsa") == 0) { - return KEY_RSA; - } else if (strcmp(name, "dsa") == 0) { - return KEY_DSA; - } else if (strcmp(name, "ssh-rsa") == 0) { - return KEY_RSA; - } else if (strcmp(name, "ssh-dss") == 0) { - return KEY_DSA; - } else if (strcmp(name, "ecdsa") == 0 || - strcmp(name, "ecdsa-sha2-nistp256") == 0 || - strcmp(name, "ecdsa-sha2-nistp384") == 0 || - strcmp(name, "ecdsa-sha2-nistp521") == 0) { - return KEY_ECDSA; - } else if (strcmp(name, "ssh-rsa-cert-v00@openssh.com") == 0) { - return KEY_RSA_CERT_V00; - } else if (strcmp(name, "ssh-dss-cert-v00@openssh.com") == 0) { - return KEY_DSA_CERT_V00; - } else if (strcmp(name, "ssh-rsa-cert-v01@openssh.com") == 0) { - return KEY_RSA_CERT; - } else if (strcmp(name, "ssh-dss-cert-v01@openssh.com") == 0) { - return KEY_DSA_CERT; - } else if (strcmp(name, "ecdsa-sha2-nistp256-cert-v01@openssh.com") == 0 || - strcmp(name, "ecdsa-sha2-nistp384-cert-v01@openssh.com") == 0 || - strcmp(name, "ecdsa-sha2-nistp521-cert-v01@openssh.com") == 0) - return KEY_ECDSA_CERT; - - debug2("key_type_from_name: unknown key type '%s'", name); - return KEY_UNSPEC; -} - -int -key_ecdsa_nid_from_name(const char *name) -{ - if (strcmp(name, "ecdsa-sha2-nistp256") == 0 || - strcmp(name, "ecdsa-sha2-nistp256-cert-v01@openssh.com") == 0) - return NID_X9_62_prime256v1; - if (strcmp(name, "ecdsa-sha2-nistp384") == 0 || - strcmp(name, "ecdsa-sha2-nistp384-cert-v01@openssh.com") == 0) - return NID_secp384r1; - if (strcmp(name, "ecdsa-sha2-nistp521") == 0 || - strcmp(name, "ecdsa-sha2-nistp521-cert-v01@openssh.com") == 0) - return NID_secp521r1; - - debug2("%s: unknown/non-ECDSA key type '%s'", __func__, name); - return -1; -} - -int -key_names_valid2(const char *names) +sshkey_names_valid2(const char *names) { char *s, *cp, *p; if (names == NULL || strcmp(names, "") == 0) return 0; - s = cp = xstrdup(names); + if ((s = cp = strdup(names)) == NULL) + return 0; for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { - switch (key_type_from_name(p)) { + switch (sshkey_type_from_name(p)) { case KEY_RSA1: case KEY_UNSPEC: - xfree(s); + free(s); return 0; } } - debug3("key names ok: [%s]", names); - xfree(s); + free(s); return 1; } static int -cert_parse(Buffer *b, Key *key, const u_char *blob, u_int blen) +cert_parse(struct sshbuf *b, struct sshkey *key, const u_char *blob, u_int blen) { - u_char *principals, *critical, *exts, *sig_key, *sig; - u_int signed_len, plen, clen, sklen, slen, kidlen, elen; - Buffer tmp; + u_char *principals = NULL, *critical = NULL, *exts = NULL; + u_char *sig_key = NULL, *sig = NULL; + size_t signed_len, plen, clen, sklen, slen, kidlen, elen; + struct sshbuf *tmp; char *principal; - int ret = -1; + int ret; int v00 = key->type == KEY_DSA_CERT_V00 || key->type == KEY_RSA_CERT_V00; + char **oprincipals; - buffer_init(&tmp); + if ((tmp = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; /* Copy the entire key blob for verification and later serialisation */ - buffer_append(&key->cert->certblob, blob, blen); + if ((ret = sshbuf_put(key->cert->certblob, blob, blen)) != 0) + return ret; elen = 0; /* Not touched for v00 certs */ principals = exts = critical = sig_key = sig = NULL; - if ((!v00 && buffer_get_int64_ret(&key->cert->serial, b) != 0) || - buffer_get_int_ret(&key->cert->type, b) != 0 || - (key->cert->key_id = buffer_get_cstring_ret(b, &kidlen)) == NULL || - (principals = buffer_get_string_ret(b, &plen)) == NULL || - buffer_get_int64_ret(&key->cert->valid_after, b) != 0 || - buffer_get_int64_ret(&key->cert->valid_before, b) != 0 || - (critical = buffer_get_string_ret(b, &clen)) == NULL || - (!v00 && (exts = buffer_get_string_ret(b, &elen)) == NULL) || - (v00 && buffer_get_string_ptr_ret(b, NULL) == NULL) || /* nonce */ - buffer_get_string_ptr_ret(b, NULL) == NULL || /* reserved */ - (sig_key = buffer_get_string_ret(b, &sklen)) == NULL) { - error("%s: parse error", __func__); + if ((!v00 && (ret = sshbuf_get_u64(b, &key->cert->serial)) != 0) || + (ret = sshbuf_get_u32(b, &key->cert->type)) != 0 || + (ret = sshbuf_get_cstring(b, &key->cert->key_id, &kidlen)) != 0 || + (ret = sshbuf_get_string(b, &principals, &plen)) != 0 || + (ret = sshbuf_get_u64(b, &key->cert->valid_after)) != 0 || + (ret = sshbuf_get_u64(b, &key->cert->valid_before)) != 0 || + (ret = sshbuf_get_string(b, &critical, &clen)) != 0 || + (!v00 && (ret = sshbuf_get_string(b, &exts, &elen)) != 0) || + (v00 && (ret = sshbuf_get_string_direct(b, NULL, NULL)) != 0) || + (ret = sshbuf_get_string_direct(b, NULL, NULL)) != 0 || + (ret = sshbuf_get_string(b, &sig_key, &sklen)) != 0) { + /* XXX debug print error for ret */ + ret = SSH_ERR_INVALID_FORMAT; goto out; } /* Signature is left in the buffer so we can calculate this length */ - signed_len = buffer_len(&key->cert->certblob) - buffer_len(b); + signed_len = sshbuf_len(key->cert->certblob) - sshbuf_len(b); - if ((sig = buffer_get_string_ret(b, &slen)) == NULL) { - error("%s: parse error", __func__); + if ((ret = sshbuf_get_string(b, &sig, &slen)) != 0) { + ret = SSH_ERR_INVALID_FORMAT; goto out; } if (key->cert->type != SSH2_CERT_TYPE_USER && key->cert->type != SSH2_CERT_TYPE_HOST) { - error("Unknown certificate type %u", key->cert->type); + ret = SSH_ERR_KEY_CERT_UNKNOWN_TYPE; goto out; } - buffer_append(&tmp, principals, plen); - while (buffer_len(&tmp) > 0) { - if (key->cert->nprincipals >= CERT_MAX_PRINCIPALS) { - error("%s: Too many principals", __func__); + if ((ret = sshbuf_put(tmp, principals, plen)) != 0) + goto out; + while (sshbuf_len(tmp) > 0) { + if (key->cert->nprincipals >= SSHKEY_CERT_MAX_PRINCIPALS) { + ret = SSH_ERR_INVALID_FORMAT; goto out; } - if ((principal = buffer_get_cstring_ret(&tmp, &plen)) == NULL) { - error("%s: Principals data invalid", __func__); + if ((ret = sshbuf_get_cstring(tmp, &principal, &plen)) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + oprincipals = key->cert->principals; + key->cert->principals = realloc(key->cert->principals, + (key->cert->nprincipals + 1) * + sizeof(*key->cert->principals)); + if (key->cert->principals == NULL) { + free(principal); + key->cert->principals = oprincipals; + ret = SSH_ERR_ALLOC_FAIL; goto out; } - key->cert->principals = xrealloc(key->cert->principals, - key->cert->nprincipals + 1, sizeof(*key->cert->principals)); key->cert->principals[key->cert->nprincipals++] = principal; } - buffer_clear(&tmp); + sshbuf_reset(tmp); + + if ((ret = sshbuf_put(key->cert->critical, critical, clen)) != 0 || + (ret = sshbuf_put(tmp, critical, clen)) != 0) + goto out; - buffer_append(&key->cert->critical, critical, clen); - buffer_append(&tmp, critical, clen); /* validate structure */ - while (buffer_len(&tmp) != 0) { - if (buffer_get_string_ptr_ret(&tmp, NULL) == NULL || - buffer_get_string_ptr_ret(&tmp, NULL) == NULL) { - error("%s: critical option data invalid", __func__); + while (sshbuf_len(tmp) != 0) { + if ((ret = sshbuf_get_string_direct(tmp, NULL, NULL)) != 0 || + (ret = sshbuf_get_string_direct(tmp, NULL, NULL)) != 0) { + ret = SSH_ERR_INVALID_FORMAT; goto out; } } - buffer_clear(&tmp); + sshbuf_reset(tmp); + + if ((ret = sshbuf_put(key->cert->extensions, exts, elen)) != 0 || + (ret = sshbuf_put(tmp, exts, elen)) != 0) + goto out; - buffer_append(&key->cert->extensions, exts, elen); - buffer_append(&tmp, exts, elen); /* validate structure */ - while (buffer_len(&tmp) != 0) { - if (buffer_get_string_ptr_ret(&tmp, NULL) == NULL || - buffer_get_string_ptr_ret(&tmp, NULL) == NULL) { - error("%s: extension data invalid", __func__); + while (sshbuf_len(tmp) != 0) { + if ((ret = sshbuf_get_string_direct(tmp, NULL, NULL)) != 0 || + (ret = sshbuf_get_string_direct(tmp, NULL, NULL)) != 0) { + ret = SSH_ERR_INVALID_FORMAT; goto out; } } - buffer_clear(&tmp); + sshbuf_reset(tmp); - if ((key->cert->signature_key = key_from_blob(sig_key, - sklen)) == NULL) { - error("%s: Signature key invalid", __func__); + if (sshkey_from_blob(sig_key, sklen, &key->cert->signature_key) != 0) { + ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; goto out; } if (key->cert->signature_key->type != KEY_RSA && key->cert->signature_key->type != KEY_DSA && key->cert->signature_key->type != KEY_ECDSA) { - error("%s: Invalid signature key type %s (%d)", __func__, - key_type(key->cert->signature_key), - key->cert->signature_key->type); + ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; goto out; } - switch (key_verify(key->cert->signature_key, sig, slen, - buffer_ptr(&key->cert->certblob), signed_len)) { - case 1: - ret = 0; - break; /* Good signature */ - case 0: - error("%s: Invalid signature on certificate", __func__); + if ((ret = sshkey_verify(key->cert->signature_key, sig, slen, + sshbuf_ptr(key->cert->certblob), signed_len, 0)) != 0) goto out; - case -1: - error("%s: Certificate signature verification failed", - __func__); - goto out; - } + ret = 0; out: - buffer_free(&tmp); - if (principals != NULL) - xfree(principals); - if (critical != NULL) - xfree(critical); - if (exts != NULL) - xfree(exts); - if (sig_key != NULL) - xfree(sig_key); - if (sig != NULL) - xfree(sig); + sshbuf_free(tmp); + free(principals); + free(critical); + free(exts); + free(sig_key); + free(sig); return ret; } -Key * -key_from_blob(const u_char *blob, u_int blen) +int +sshkey_from_blob(const u_char *blob, u_int blen, struct sshkey **keyp) { - Buffer b; - int rlen, type, nid = -1; + struct sshbuf *b; + int type, nid = -1, ret = SSH_ERR_INTERNAL_ERROR; char *ktype = NULL, *curve = NULL; - Key *key = NULL; + struct sshkey *key = NULL; EC_POINT *q = NULL; -#ifdef DEBUG_PK +#ifdef DEBUG_PK /* XXX */ dump_base64(stderr, blob, blen); #endif - buffer_init(&b); - buffer_append(&b, blob, blen); - if ((ktype = buffer_get_cstring_ret(&b, NULL)) == NULL) { - error("key_from_blob: can't read key type"); + *keyp = NULL; + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((ret = sshbuf_put(b, blob, blen)) != 0) + goto out; + if (sshbuf_get_cstring(b, &ktype, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; goto out; } - type = key_type_from_name(ktype); - if (key_type_plain(type) == KEY_ECDSA) - nid = key_ecdsa_nid_from_name(ktype); + type = sshkey_type_from_name(ktype); + if (sshkey_type_plain(type) == KEY_ECDSA) + nid = sshkey_ecdsa_nid_from_name(ktype); switch (type) { case KEY_RSA_CERT: - (void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */ + if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } /* FALLTHROUGH */ case KEY_RSA: case KEY_RSA_CERT_V00: - key = key_new(type); - if (buffer_get_bignum2_ret(&b, key->rsa->e) == -1 || - buffer_get_bignum2_ret(&b, key->rsa->n) == -1) { - error("key_from_blob: can't read rsa key"); + key = sshkey_new(type); + if (sshbuf_get_bignum2(b, key->rsa->e) == -1 || + sshbuf_get_bignum2(b, key->rsa->n) == -1) { + ret = SSH_ERR_INVALID_FORMAT; badkey: - key_free(key); - key = NULL; + if (key != NULL) { + sshkey_free(key); + key = NULL; + } goto out; } #ifdef DEBUG_PK @@ -1451,16 +1552,19 @@ key_from_blob(const u_char *blob, u_int blen) #endif break; case KEY_DSA_CERT: - (void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */ + if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } /* FALLTHROUGH */ case KEY_DSA: case KEY_DSA_CERT_V00: - key = key_new(type); - if (buffer_get_bignum2_ret(&b, key->dsa->p) == -1 || - buffer_get_bignum2_ret(&b, key->dsa->q) == -1 || - buffer_get_bignum2_ret(&b, key->dsa->g) == -1 || - buffer_get_bignum2_ret(&b, key->dsa->pub_key) == -1) { - error("key_from_blob: can't read dsa key"); + key = sshkey_new(type); + if (sshbuf_get_bignum2(b, key->dsa->p) == -1 || + sshbuf_get_bignum2(b, key->dsa->q) == -1 || + sshbuf_get_bignum2(b, key->dsa->g) == -1 || + sshbuf_get_bignum2(b, key->dsa->pub_key) == -1) { + ret = SSH_ERR_INVALID_FORMAT; goto badkey; } #ifdef DEBUG_PK @@ -1468,76 +1572,88 @@ key_from_blob(const u_char *blob, u_int blen) #endif break; case KEY_ECDSA_CERT: - (void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */ + if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } /* FALLTHROUGH */ case KEY_ECDSA: - key = key_new(type); + key = sshkey_new(type); key->ecdsa_nid = nid; - if ((curve = buffer_get_string_ret(&b, NULL)) == NULL) { - error("key_from_blob: can't read ecdsa curve"); + if (sshbuf_get_cstring(b, &curve, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; goto badkey; } - if (key->ecdsa_nid != key_curve_name_to_nid(curve)) { - error("key_from_blob: ecdsa curve doesn't match type"); + if (key->ecdsa_nid != sshkey_curve_name_to_nid(curve)) { + ret = SSH_ERR_EC_CURVE_MISMATCH; goto badkey; } if (key->ecdsa != NULL) EC_KEY_free(key->ecdsa); if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) - == NULL) - fatal("key_from_blob: EC_KEY_new_by_curve_name failed"); - if ((q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL) - fatal("key_from_blob: EC_POINT_new failed"); - if (buffer_get_ecpoint_ret(&b, EC_KEY_get0_group(key->ecdsa), - q) == -1) { - error("key_from_blob: can't read ecdsa key point"); + == NULL) { + ret = SSH_ERR_EC_CURVE_INVALID; goto badkey; } - if (key_ec_validate_public(EC_KEY_get0_group(key->ecdsa), - q) != 0) + if ((q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; goto badkey; - if (EC_KEY_set_public_key(key->ecdsa, q) != 1) - fatal("key_from_blob: EC_KEY_set_public_key failed"); + } + if (sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa)) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto badkey; + } + if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), + q) != 0) { + ret = SSH_ERR_KEY_INVALID_EC_VALUE; + goto badkey; + } + if (EC_KEY_set_public_key(key->ecdsa, q) != 1) { + /* XXX assume it is a allocation error */ + ret = SSH_ERR_ALLOC_FAIL; + goto badkey; + } #ifdef DEBUG_PK - key_dump_ec_point(EC_KEY_get0_group(key->ecdsa), q); + sshkey_dump_ec_point(EC_KEY_get0_group(key->ecdsa), q); #endif break; case KEY_UNSPEC: - key = key_new(type); + key = sshkey_new(type); break; default: - error("key_from_blob: cannot handle type %s", ktype); + ret = SSH_ERR_KEY_TYPE_UNKNOWN; goto out; } - if (key_is_cert(key) && cert_parse(&b, key, blob, blen) == -1) { - error("key_from_blob: can't parse cert data"); + + /* Parse certificate potion */ + if (sshkey_is_cert(key) && + (ret = cert_parse(b, key, blob, blen)) != 0) goto badkey; + + if (key != NULL && sshbuf_len(b) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; } - rlen = buffer_len(&b); - if (key != NULL && rlen != 0) - error("key_from_blob: remaining bytes in key blob %d", rlen); + ret = 0; + *keyp = key; out: if (ktype != NULL) - xfree(ktype); + free(ktype); if (curve != NULL) - xfree(curve); + free(curve); if (q != NULL) EC_POINT_free(q); - buffer_free(&b); - return key; + sshbuf_free(b); + return ret; } int -key_to_blob(const Key *key, u_char **blobp, u_int *lenp) +sshkey_to_blob_buf(const struct sshkey *key, struct sshbuf *b) { - Buffer b; - int len; + int ret = SSH_ERR_INTERNAL_ERROR; - if (key == NULL) { - error("key_to_blob: key == NULL"); - return 0; - } - buffer_init(&b); + if (key == NULL) + return SSH_ERR_INVALID_ARGUMENT; switch (key->type) { case KEY_DSA_CERT_V00: case KEY_RSA_CERT_V00: @@ -1545,65 +1661,89 @@ key_to_blob(const Key *key, u_char **blobp, u_int *lenp) case KEY_ECDSA_CERT: case KEY_RSA_CERT: /* Use the existing blob */ - buffer_append(&b, buffer_ptr(&key->cert->certblob), - buffer_len(&key->cert->certblob)); + /* XXX modified flag? */ + if ((ret = sshbuf_putb(b, key->cert->certblob)) != 0) + return ret; break; case KEY_DSA: - buffer_put_cstring(&b, key_ssh_name(key)); - buffer_put_bignum2(&b, key->dsa->p); - buffer_put_bignum2(&b, key->dsa->q); - buffer_put_bignum2(&b, key->dsa->g); - buffer_put_bignum2(&b, key->dsa->pub_key); + if (key->dsa == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name(key))) != 0 || + (ret = sshbuf_put_bignum2(b, key->dsa->p)) != 0 || + (ret = sshbuf_put_bignum2(b, key->dsa->q)) != 0 || + (ret = sshbuf_put_bignum2(b, key->dsa->g)) != 0 || + (ret = sshbuf_put_bignum2(b, key->dsa->pub_key)) != 0) + return ret; break; case KEY_ECDSA: - buffer_put_cstring(&b, key_ssh_name(key)); - buffer_put_cstring(&b, key_curve_nid_to_name(key->ecdsa_nid)); - buffer_put_ecpoint(&b, EC_KEY_get0_group(key->ecdsa), - EC_KEY_get0_public_key(key->ecdsa)); + if (key->ecdsa == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name(key))) != 0 || + (ret = sshbuf_put_cstring(b, + sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 || + (ret = sshbuf_put_eckey(b, key->ecdsa)) != 0) + return ret; break; case KEY_RSA: - buffer_put_cstring(&b, key_ssh_name(key)); - buffer_put_bignum2(&b, key->rsa->e); - buffer_put_bignum2(&b, key->rsa->n); + if (key->rsa == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name(key))) != 0 || + (ret = sshbuf_put_bignum2(b, key->rsa->e)) != 0 || + (ret = sshbuf_put_bignum2(b, key->rsa->n)) != 0) + return ret; break; default: - error("key_to_blob: unsupported key type %d", key->type); - buffer_free(&b); - return 0; + return SSH_ERR_KEY_TYPE_UNKNOWN; } - len = buffer_len(&b); - if (lenp != NULL) - *lenp = len; - if (blobp != NULL) { - *blobp = xmalloc(len); - memcpy(*blobp, buffer_ptr(&b), len); - } - memset(buffer_ptr(&b), 0, len); - buffer_free(&b); - return len; + return 0; } int -key_sign( - const Key *key, +sshkey_to_blob(const struct sshkey *key, u_char **blobp, u_int *lenp) +{ + int ret = SSH_ERR_INTERNAL_ERROR; + size_t len; + struct sshbuf *b = NULL; + + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((ret = sshkey_to_blob_buf(key, b)) != 0) + goto out; + len = sshbuf_len(b); + if (lenp != NULL) + *lenp = len; + if (blobp != NULL) { + if ((*blobp = malloc(len)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(*blobp, sshbuf_ptr(b), len); + } + ret = 0; + out: + sshbuf_free(b); + return ret; +} + +int +sshkey_sign(const struct sshkey *key, u_char **sigp, u_int *lenp, - const u_char *data, u_int datalen) + const u_char *data, u_int datalen, u_int compat) { switch (key->type) { case KEY_DSA_CERT_V00: case KEY_DSA_CERT: case KEY_DSA: - return ssh_dss_sign(key, sigp, lenp, data, datalen); + return ssh_dss_sign(key, sigp, lenp, data, datalen, compat); case KEY_ECDSA_CERT: case KEY_ECDSA: - return ssh_ecdsa_sign(key, sigp, lenp, data, datalen); + return ssh_ecdsa_sign(key, sigp, lenp, data, datalen, compat); case KEY_RSA_CERT_V00: case KEY_RSA_CERT: case KEY_RSA: - return ssh_rsa_sign(key, sigp, lenp, data, datalen); + return ssh_rsa_sign(key, sigp, lenp, data, datalen, compat); default: - error("key_sign: invalid key type %d", key->type); - return -1; + return SSH_ERR_KEY_TYPE_UNKNOWN; } } @@ -1612,39 +1752,39 @@ key_sign( * and -1 on error. */ int -key_verify( - const Key *key, - const u_char *signature, u_int signaturelen, - const u_char *data, u_int datalen) +sshkey_verify(const struct sshkey *key, + const u_char *sig, u_int siglen, + const u_char *data, u_int dlen, u_int compat) { - if (signaturelen == 0) + if (siglen == 0) return -1; switch (key->type) { case KEY_DSA_CERT_V00: case KEY_DSA_CERT: case KEY_DSA: - return ssh_dss_verify(key, signature, signaturelen, data, datalen); + return ssh_dss_verify(key, sig, siglen, data, dlen, compat); case KEY_ECDSA_CERT: case KEY_ECDSA: - return ssh_ecdsa_verify(key, signature, signaturelen, data, datalen); + return ssh_ecdsa_verify(key, sig, siglen, data, dlen, compat); case KEY_RSA_CERT_V00: case KEY_RSA_CERT: case KEY_RSA: - return ssh_rsa_verify(key, signature, signaturelen, data, datalen); + return ssh_rsa_verify(key, sig, siglen, data, dlen, compat); default: - error("key_verify: invalid key type %d", key->type); - return -1; + return SSH_ERR_KEY_TYPE_UNKNOWN; } } /* Converts a private to a public key */ -Key * -key_demote(const Key *k) +int +sshkey_demote(const struct sshkey *k, struct sshkey **dkp) { - Key *pk; + struct sshkey *pk; + int ret = SSH_ERR_INTERNAL_ERROR; - pk = xcalloc(1, sizeof(*pk)); + if ((pk = calloc(1, sizeof(*pk))) == NULL) + return SSH_ERR_ALLOC_FAIL; pk->type = k->type; pk->flags = k->flags; pk->ecdsa_nid = k->ecdsa_nid; @@ -1655,53 +1795,61 @@ key_demote(const Key *k) switch (k->type) { case KEY_RSA_CERT_V00: case KEY_RSA_CERT: - key_cert_copy(k, pk); + if ((ret = sshkey_cert_copy(k, pk)) != 0) + goto fail; /* FALLTHROUGH */ case KEY_RSA1: case KEY_RSA: - if ((pk->rsa = RSA_new()) == NULL) - fatal("key_demote: RSA_new failed"); - if ((pk->rsa->e = BN_dup(k->rsa->e)) == NULL) - fatal("key_demote: BN_dup failed"); - if ((pk->rsa->n = BN_dup(k->rsa->n)) == NULL) - fatal("key_demote: BN_dup failed"); + if ((pk->rsa = RSA_new()) == NULL || + (pk->rsa->e = BN_dup(k->rsa->e)) == NULL || + (pk->rsa->n = BN_dup(k->rsa->n)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto fail; + } break; case KEY_DSA_CERT_V00: case KEY_DSA_CERT: - key_cert_copy(k, pk); + if ((ret = sshkey_cert_copy(k, pk)) != 0) + goto fail; /* FALLTHROUGH */ case KEY_DSA: - if ((pk->dsa = DSA_new()) == NULL) - fatal("key_demote: DSA_new failed"); - if ((pk->dsa->p = BN_dup(k->dsa->p)) == NULL) - fatal("key_demote: BN_dup failed"); - if ((pk->dsa->q = BN_dup(k->dsa->q)) == NULL) - fatal("key_demote: BN_dup failed"); - if ((pk->dsa->g = BN_dup(k->dsa->g)) == NULL) - fatal("key_demote: BN_dup failed"); - if ((pk->dsa->pub_key = BN_dup(k->dsa->pub_key)) == NULL) - fatal("key_demote: BN_dup failed"); + if ((pk->dsa = DSA_new()) == NULL || + (pk->dsa->p = BN_dup(k->dsa->p)) == NULL || + (pk->dsa->q = BN_dup(k->dsa->q)) == NULL || + (pk->dsa->g = BN_dup(k->dsa->g)) == NULL || + (pk->dsa->pub_key = BN_dup(k->dsa->pub_key)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto fail; + } break; case KEY_ECDSA_CERT: - key_cert_copy(k, pk); + if ((ret = sshkey_cert_copy(k, pk)) != 0) + goto fail; /* FALLTHROUGH */ case KEY_ECDSA: - if ((pk->ecdsa = EC_KEY_new_by_curve_name(pk->ecdsa_nid)) == NULL) - fatal("key_demote: EC_KEY_new_by_curve_name failed"); + pk->ecdsa = EC_KEY_new_by_curve_name(pk->ecdsa_nid); + if (pk->ecdsa == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto fail; + } if (EC_KEY_set_public_key(pk->ecdsa, - EC_KEY_get0_public_key(k->ecdsa)) != 1) - fatal("key_demote: EC_KEY_set_public_key failed"); + EC_KEY_get0_public_key(k->ecdsa)) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto fail; + } break; default: - fatal("key_free: bad key type %d", k->type); - break; + ret = SSH_ERR_KEY_TYPE_UNKNOWN; + fail: + sshkey_free(pk); + return ret; } - - return (pk); + *dkp = pk; + return 0; } int -key_is_cert(const Key *k) +sshkey_is_cert(const struct sshkey *k) { if (k == NULL) return 0; @@ -1719,7 +1867,7 @@ key_is_cert(const Key *k) /* Return the cert-less equivalent to a certified key type */ int -key_type_plain(int type) +sshkey_type_plain(int type) { switch (type) { case KEY_RSA_CERT_V00: @@ -1737,33 +1885,34 @@ key_type_plain(int type) /* Convert a KEY_RSA or KEY_DSA to their _CERT equivalent */ int -key_to_certified(Key *k, int legacy) +sshkey_to_certified(struct sshkey *k, int legacy) { switch (k->type) { case KEY_RSA: - k->cert = cert_new(); + if ((k->cert = cert_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; k->type = legacy ? KEY_RSA_CERT_V00 : KEY_RSA_CERT; return 0; case KEY_DSA: - k->cert = cert_new(); + if ((k->cert = cert_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; k->type = legacy ? KEY_DSA_CERT_V00 : KEY_DSA_CERT; return 0; case KEY_ECDSA: if (legacy) - fatal("%s: legacy ECDSA certificates are not supported", - __func__); - k->cert = cert_new(); + return SSH_ERR_INVALID_ARGUMENT; + if ((k->cert = cert_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; k->type = KEY_ECDSA_CERT; return 0; default: - error("%s: key has incorrect type %s", __func__, key_type(k)); - return -1; + return SSH_ERR_KEY_TYPE_UNKNOWN; } } /* Convert a KEY_RSA_CERT or KEY_DSA_CERT to their raw key equivalent */ int -key_drop_cert(Key *k) +sshkey_drop_cert(struct sshkey *k) { switch (k->type) { case KEY_RSA_CERT_V00: @@ -1781,8 +1930,7 @@ key_drop_cert(Key *k) k->type = KEY_ECDSA; return 0; default: - error("%s: key has incorrect type %s", __func__, key_type(k)); - return -1; + return SSH_ERR_KEY_TYPE_UNKNOWN; } } @@ -1791,117 +1939,129 @@ key_drop_cert(Key *k) * the signed certblob */ int -key_certify(Key *k, Key *ca) +sshkey_certify(struct sshkey *k, struct sshkey *ca) { - Buffer principals; - u_char *ca_blob, *sig_blob, nonce[32]; + struct sshbuf *principals = NULL; + u_char *ca_blob = NULL, *sig_blob = NULL, nonce[32]; u_int i, ca_len, sig_len; + int ret; + struct sshbuf *cert; - if (k->cert == NULL) { - error("%s: key lacks cert info", __func__); - return -1; - } - - if (!key_is_cert(k)) { - error("%s: certificate has unknown type %d", __func__, - k->cert->type); - return -1; - } - + if (k == NULL || k->cert == NULL || k->cert->certblob == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (!sshkey_is_cert(k)) + return SSH_ERR_KEY_TYPE_UNKNOWN; if (ca->type != KEY_RSA && ca->type != KEY_DSA && - ca->type != KEY_ECDSA) { - error("%s: CA key has unsupported type %s", __func__, - key_type(ca)); - return -1; - } + ca->type != KEY_ECDSA) + return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; - key_to_blob(ca, &ca_blob, &ca_len); + if ((ret = sshkey_to_blob(ca, &ca_blob, &ca_len)) != 0) + return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; - buffer_clear(&k->cert->certblob); - buffer_put_cstring(&k->cert->certblob, key_ssh_name(k)); + cert = k->cert->certblob; /* for readability */ + sshbuf_reset(cert); + if ((ret = sshbuf_put_cstring(cert, sshkey_ssh_name(k))) != 0) + goto out; /* -v01 certs put nonce first */ arc4random_buf(&nonce, sizeof(nonce)); - if (!key_cert_is_legacy(k)) - buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce)); + if (!sshkey_cert_is_legacy(k)) { + if ((ret = sshbuf_put_string(cert, nonce, sizeof(nonce))) != 0) + goto out; + } switch (k->type) { case KEY_DSA_CERT_V00: case KEY_DSA_CERT: - buffer_put_bignum2(&k->cert->certblob, k->dsa->p); - buffer_put_bignum2(&k->cert->certblob, k->dsa->q); - buffer_put_bignum2(&k->cert->certblob, k->dsa->g); - buffer_put_bignum2(&k->cert->certblob, k->dsa->pub_key); + if ((ret = sshbuf_put_bignum2(cert, k->dsa->p)) != 0 || + (ret = sshbuf_put_bignum2(cert, k->dsa->q)) != 0 || + (ret = sshbuf_put_bignum2(cert, k->dsa->g)) != 0 || + (ret = sshbuf_put_bignum2(cert, k->dsa->pub_key)) != 0) + goto out; break; case KEY_ECDSA_CERT: - buffer_put_cstring(&k->cert->certblob, - key_curve_nid_to_name(k->ecdsa_nid)); - buffer_put_ecpoint(&k->cert->certblob, - EC_KEY_get0_group(k->ecdsa), - EC_KEY_get0_public_key(k->ecdsa)); + if ((ret = sshbuf_put_cstring(cert, + sshkey_curve_nid_to_name(k->ecdsa_nid))) != 0 || + (ret = sshbuf_put_ec(cert, + EC_KEY_get0_public_key(k->ecdsa), + EC_KEY_get0_group(k->ecdsa))) != 0) + goto out; break; case KEY_RSA_CERT_V00: case KEY_RSA_CERT: - buffer_put_bignum2(&k->cert->certblob, k->rsa->e); - buffer_put_bignum2(&k->cert->certblob, k->rsa->n); + if ((ret = sshbuf_put_bignum2(cert, k->rsa->e)) != 0 || + (sshbuf_put_bignum2(cert, k->rsa->n)) != 0) + goto out; break; default: - error("%s: key has incorrect type %s", __func__, key_type(k)); - buffer_clear(&k->cert->certblob); - xfree(ca_blob); - return -1; + ret = SSH_ERR_INVALID_ARGUMENT; } /* -v01 certs have a serial number next */ - if (!key_cert_is_legacy(k)) - buffer_put_int64(&k->cert->certblob, k->cert->serial); + if (!sshkey_cert_is_legacy(k)) { + if ((ret = sshbuf_put_u64(cert, k->cert->serial)) != 0) + goto out; + } - buffer_put_int(&k->cert->certblob, k->cert->type); - buffer_put_cstring(&k->cert->certblob, k->cert->key_id); + if ((ret = sshbuf_put_u32(cert, k->cert->type)) != 0 || + (ret = sshbuf_put_cstring(cert, k->cert->key_id)) != 0) + goto out; - buffer_init(&principals); - for (i = 0; i < k->cert->nprincipals; i++) - buffer_put_cstring(&principals, k->cert->principals[i]); - buffer_put_string(&k->cert->certblob, buffer_ptr(&principals), - buffer_len(&principals)); - buffer_free(&principals); - - buffer_put_int64(&k->cert->certblob, k->cert->valid_after); - buffer_put_int64(&k->cert->certblob, k->cert->valid_before); - buffer_put_string(&k->cert->certblob, - buffer_ptr(&k->cert->critical), buffer_len(&k->cert->critical)); + if ((principals = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + for (i = 0; i < k->cert->nprincipals; i++) { + if ((ret = sshbuf_put_cstring(principals, + k->cert->principals[i])) != 0) + goto out; + } + if ((ret = sshbuf_put_stringb(cert, principals)) != 0 || + (ret = sshbuf_put_u64(cert, k->cert->valid_after)) != 0 || + (ret = sshbuf_put_u64(cert, k->cert->valid_before)) != 0 || + (ret = sshbuf_put_stringb(cert, k->cert->critical)) != 0) + goto out; /* -v01 certs have non-critical options here */ - if (!key_cert_is_legacy(k)) { - buffer_put_string(&k->cert->certblob, - buffer_ptr(&k->cert->extensions), - buffer_len(&k->cert->extensions)); + if (!sshkey_cert_is_legacy(k)) { + if ((ret = sshbuf_put_stringb(cert, k->cert->extensions)) != 0) + goto out; } /* -v00 certs put the nonce at the end */ - if (key_cert_is_legacy(k)) - buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce)); + if (sshkey_cert_is_legacy(k)) { + if ((ret = sshbuf_put_string(cert, nonce, sizeof(nonce))) != 0) + goto out; + } - buffer_put_string(&k->cert->certblob, NULL, 0); /* reserved */ - buffer_put_string(&k->cert->certblob, ca_blob, ca_len); - xfree(ca_blob); + if ((ret = sshbuf_put_string(cert, NULL, 0)) != 0 || /* Reserved */ + (ret = sshbuf_put_string(cert, ca_blob, ca_len)) != 0) + goto out; /* Sign the whole mess */ - if (key_sign(ca, &sig_blob, &sig_len, buffer_ptr(&k->cert->certblob), - buffer_len(&k->cert->certblob)) != 0) { - error("%s: signature operation failed", __func__); - buffer_clear(&k->cert->certblob); - return -1; - } - /* Append signature and we are done */ - buffer_put_string(&k->cert->certblob, sig_blob, sig_len); - xfree(sig_blob); + if ((ret = sshkey_sign(ca, &sig_blob, &sig_len, sshbuf_ptr(cert), + sshbuf_len(cert), 0)) != 0) + goto out; - return 0; + /* Append signature and we are done */ + if ((ret = sshbuf_put_string(cert, sig_blob, sig_len)) != 0) + goto out; + ret = 0; + out: + if (ret != 0) + sshbuf_reset(cert); + if (sig_blob != NULL) + free(sig_blob); + if (ca_blob != NULL) + free(ca_blob); + if (principals != NULL) + sshbuf_free(principals); + return ret; } int -key_cert_check_authority(const Key *k, int want_host, int require_principal, +sshkey_cert_check_authority(const struct sshkey *k, + int want_host, int require_principal, const char *name, const char **reason) { u_int i, principal_matches; @@ -1910,31 +2070,31 @@ key_cert_check_authority(const Key *k, int want_host, int require_principal, if (want_host) { if (k->cert->type != SSH2_CERT_TYPE_HOST) { *reason = "Certificate invalid: not a host certificate"; - return -1; + return SSH_ERR_KEY_CERT_INVALID; } } else { if (k->cert->type != SSH2_CERT_TYPE_USER) { *reason = "Certificate invalid: not a user certificate"; - return -1; + return SSH_ERR_KEY_CERT_INVALID; } } if (now < 0) { - error("%s: system clock lies before epoch", __func__); + /* yikes - system clock before epoch! */ *reason = "Certificate invalid: not yet valid"; - return -1; + return SSH_ERR_KEY_CERT_INVALID; } if ((u_int64_t)now < k->cert->valid_after) { *reason = "Certificate invalid: not yet valid"; - return -1; + return SSH_ERR_KEY_CERT_INVALID; } if ((u_int64_t)now >= k->cert->valid_before) { *reason = "Certificate invalid: expired"; - return -1; + return SSH_ERR_KEY_CERT_INVALID; } if (k->cert->nprincipals == 0) { if (require_principal) { *reason = "Certificate lacks principal list"; - return -1; + return SSH_ERR_KEY_CERT_INVALID; } } else if (name != NULL) { principal_matches = 0; @@ -1947,95 +2107,22 @@ key_cert_check_authority(const Key *k, int want_host, int require_principal, if (!principal_matches) { *reason = "Certificate invalid: name is not a listed " "principal"; - return -1; + return SSH_ERR_KEY_CERT_INVALID; } } return 0; } int -key_cert_is_legacy(Key *k) -{ - switch (k->type) { - case KEY_DSA_CERT_V00: - case KEY_RSA_CERT_V00: - return 1; - default: - return 0; - } -} - -/* XXX: these are really begging for a table-driven approach */ -int -key_curve_name_to_nid(const char *name) -{ - if (strcmp(name, "nistp256") == 0) - return NID_X9_62_prime256v1; - else if (strcmp(name, "nistp384") == 0) - return NID_secp384r1; - else if (strcmp(name, "nistp521") == 0) - return NID_secp521r1; - - debug("%s: unsupported EC curve name \"%.100s\"", __func__, name); - return -1; -} - -u_int -key_curve_nid_to_bits(int nid) -{ - switch (nid) { - case NID_X9_62_prime256v1: - return 256; - case NID_secp384r1: - return 384; - case NID_secp521r1: - return 521; - default: - error("%s: unsupported EC curve nid %d", __func__, nid); - return 0; - } -} - -const char * -key_curve_nid_to_name(int nid) -{ - if (nid == NID_X9_62_prime256v1) - return "nistp256"; - else if (nid == NID_secp384r1) - return "nistp384"; - else if (nid == NID_secp521r1) - return "nistp521"; - - error("%s: unsupported EC curve nid %d", __func__, nid); - return NULL; -} - -const EVP_MD * -key_ec_nid_to_evpmd(int nid) -{ - int kbits = key_curve_nid_to_bits(nid); - - if (kbits == 0) - fatal("%s: invalid nid %d", __func__, nid); - /* RFC5656 section 6.2.1 */ - if (kbits <= 256) - return EVP_sha256(); - else if (kbits <= 384) - return EVP_sha384(); - else - return EVP_sha512(); -} - -int -key_ec_validate_public(const EC_GROUP *group, const EC_POINT *public) +sshkey_ec_validate_public(const EC_GROUP *group, const EC_POINT *public) { BN_CTX *bnctx; EC_POINT *nq = NULL; BIGNUM *order, *x, *y, *tmp; - int ret = -1; + int ret = SSH_ERR_KEY_INVALID_EC_VALUE; if ((bnctx = BN_CTX_new()) == NULL) - fatal("%s: BN_CTX_new failed", __func__); + return SSH_ERR_ALLOC_FAIL; BN_CTX_start(bnctx); /* @@ -2043,117 +2130,100 @@ key_ec_validate_public(const EC_GROUP *group, const EC_POINT *public) * refuses to load GF2m points. */ if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) != - NID_X9_62_prime_field) { - error("%s: group is not a prime field", __func__); + NID_X9_62_prime_field) goto out; - } /* Q != infinity */ - if (EC_POINT_is_at_infinity(group, public)) { - error("%s: received degenerate public key (infinity)", - __func__); + if (EC_POINT_is_at_infinity(group, public)) goto out; - } if ((x = BN_CTX_get(bnctx)) == NULL || (y = BN_CTX_get(bnctx)) == NULL || (order = BN_CTX_get(bnctx)) == NULL || - (tmp = BN_CTX_get(bnctx)) == NULL) - fatal("%s: BN_CTX_get failed", __func__); + (tmp = BN_CTX_get(bnctx)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } /* log2(x) > log2(order)/2, log2(y) > log2(order)/2 */ - if (EC_GROUP_get_order(group, order, bnctx) != 1) - fatal("%s: EC_GROUP_get_order failed", __func__); - if (EC_POINT_get_affine_coordinates_GFp(group, public, - x, y, bnctx) != 1) - fatal("%s: EC_POINT_get_affine_coordinates_GFp", __func__); - if (BN_num_bits(x) <= BN_num_bits(order) / 2) { - error("%s: public key x coordinate too small: " - "bits(x) = %d, bits(order)/2 = %d", __func__, - BN_num_bits(x), BN_num_bits(order) / 2); + if (EC_GROUP_get_order(group, order, bnctx) != 1 || + EC_POINT_get_affine_coordinates_GFp(group, public, + x, y, bnctx) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } - if (BN_num_bits(y) <= BN_num_bits(order) / 2) { - error("%s: public key y coordinate too small: " - "bits(y) = %d, bits(order)/2 = %d", __func__, - BN_num_bits(x), BN_num_bits(order) / 2); + if (BN_num_bits(x) <= BN_num_bits(order) / 2 || + BN_num_bits(y) <= BN_num_bits(order) / 2) goto out; - } /* nQ == infinity (n == order of subgroup) */ - if ((nq = EC_POINT_new(group)) == NULL) - fatal("%s: BN_CTX_tmp failed", __func__); - if (EC_POINT_mul(group, nq, NULL, public, order, bnctx) != 1) - fatal("%s: EC_GROUP_mul failed", __func__); - if (EC_POINT_is_at_infinity(group, nq) != 1) { - error("%s: received degenerate public key (nQ != infinity)", - __func__); + if ((nq = EC_POINT_new(group)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; goto out; } + if (EC_POINT_mul(group, nq, NULL, public, order, bnctx) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (EC_POINT_is_at_infinity(group, nq) != 1) + goto out; /* x < order - 1, y < order - 1 */ - if (!BN_sub(tmp, order, BN_value_one())) - fatal("%s: BN_sub failed", __func__); - if (BN_cmp(x, tmp) >= 0) { - error("%s: public key x coordinate >= group order - 1", - __func__); + if (!BN_sub(tmp, order, BN_value_one())) { + ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } - if (BN_cmp(y, tmp) >= 0) { - error("%s: public key y coordinate >= group order - 1", - __func__); + if (BN_cmp(x, tmp) >= 0 || BN_cmp(y, tmp) >= 0) goto out; - } ret = 0; out: BN_CTX_free(bnctx); - EC_POINT_free(nq); + if (nq != NULL) + EC_POINT_free(nq); return ret; } int -key_ec_validate_private(const EC_KEY *key) +sshkey_ec_validate_private(const EC_KEY *key) { BN_CTX *bnctx; BIGNUM *order, *tmp; - int ret = -1; + int ret = SSH_ERR_KEY_INVALID_EC_VALUE; if ((bnctx = BN_CTX_new()) == NULL) - fatal("%s: BN_CTX_new failed", __func__); + return SSH_ERR_ALLOC_FAIL; BN_CTX_start(bnctx); if ((order = BN_CTX_get(bnctx)) == NULL || - (tmp = BN_CTX_get(bnctx)) == NULL) - fatal("%s: BN_CTX_get failed", __func__); + (tmp = BN_CTX_get(bnctx)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } /* log2(private) > log2(order)/2 */ - if (EC_GROUP_get_order(EC_KEY_get0_group(key), order, bnctx) != 1) - fatal("%s: EC_GROUP_get_order failed", __func__); - if (BN_num_bits(EC_KEY_get0_private_key(key)) <= - BN_num_bits(order) / 2) { - error("%s: private key too small: " - "bits(y) = %d, bits(order)/2 = %d", __func__, - BN_num_bits(EC_KEY_get0_private_key(key)), - BN_num_bits(order) / 2); + if (EC_GROUP_get_order(EC_KEY_get0_group(key), order, bnctx) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } + if (BN_num_bits(EC_KEY_get0_private_key(key)) <= + BN_num_bits(order) / 2) + goto out; /* private < order - 1 */ - if (!BN_sub(tmp, order, BN_value_one())) - fatal("%s: BN_sub failed", __func__); - if (BN_cmp(EC_KEY_get0_private_key(key), tmp) >= 0) { - error("%s: private key >= group order - 1", __func__); + if (!BN_sub(tmp, order, BN_value_one())) { + ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } + if (BN_cmp(EC_KEY_get0_private_key(key), tmp) >= 0) + goto out; ret = 0; out: BN_CTX_free(bnctx); return ret; } -#if defined(DEBUG_KEXECDH) || defined(DEBUG_PK) void -key_dump_ec_point(const EC_GROUP *group, const EC_POINT *point) +sshkey_dump_ec_point(const EC_GROUP *group, const EC_POINT *point) { BIGNUM *x, *y; BN_CTX *bnctx; @@ -2162,16 +2232,27 @@ key_dump_ec_point(const EC_GROUP *group, const EC_POINT *point) fputs("point=(NULL)\n", stderr); return; } - if ((bnctx = BN_CTX_new()) == NULL) - fatal("%s: BN_CTX_new failed", __func__); + if ((bnctx = BN_CTX_new()) == NULL) { + fprintf(stderr, "%s: BN_CTX_new failed\n", __func__); + return; + } BN_CTX_start(bnctx); - if ((x = BN_CTX_get(bnctx)) == NULL || (y = BN_CTX_get(bnctx)) == NULL) - fatal("%s: BN_CTX_get failed", __func__); + if ((x = BN_CTX_get(bnctx)) == NULL || + (y = BN_CTX_get(bnctx)) == NULL) { + fprintf(stderr, "%s: BN_CTX_get failed\n", __func__); + return; + } if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) != - NID_X9_62_prime_field) - fatal("%s: group is not a prime field", __func__); - if (EC_POINT_get_affine_coordinates_GFp(group, point, x, y, bnctx) != 1) - fatal("%s: EC_POINT_get_affine_coordinates_GFp", __func__); + NID_X9_62_prime_field) { + fprintf(stderr, "%s: group is not a prime field\n", __func__); + return; + } + if (EC_POINT_get_affine_coordinates_GFp(group, point, x, y, + bnctx) != 1) { + fprintf(stderr, "%s: EC_POINT_get_affine_coordinates_GFp\n", + __func__); + return; + } fputs("x=", stderr); BN_print_fp(stderr, x); fputs("\ny=", stderr); @@ -2181,11 +2262,12 @@ key_dump_ec_point(const EC_GROUP *group, const EC_POINT *point) } void -key_dump_ec_key(const EC_KEY *key) +sshkey_dump_ec_key(const EC_KEY *key) { const BIGNUM *exponent; - key_dump_ec_point(EC_KEY_get0_group(key), EC_KEY_get0_public_key(key)); + sshkey_dump_ec_point(EC_KEY_get0_group(key), + EC_KEY_get0_public_key(key)); fputs("exponent=", stderr); if ((exponent = EC_KEY_get0_private_key(key)) == NULL) fputs("(NULL)", stderr); @@ -2193,5 +2275,4 @@ key_dump_ec_key(const EC_KEY *key) BN_print_fp(stderr, EC_KEY_get0_private_key(key)); fputs("\n", stderr); } -#endif /* defined(DEBUG_KEXECDH) || defined(DEBUG_PK) */ diff --git a/ssh/key.h b/ssh/key.h index 1b9b21c..0d2b8fa 100644 --- a/ssh/key.h +++ b/ssh/key.h @@ -26,13 +26,23 @@ #ifndef KEY_H #define KEY_H -#include "buffer.h" +#include + #include #include #include -typedef struct Key Key; -enum types { +#define SSH_RSA_MINIMUM_MODULUS_SIZE 768 + +/* XXX compat, remove when we can */ +#define types sshkey_types +#define fp_type sshkey_fp_type +#define fp_rep sshkey_fp_rep + +struct sshbuf; + +/* Key types */ +enum sshkey_types { KEY_RSA1, KEY_RSA, KEY_DSA, @@ -44,100 +54,125 @@ enum types { KEY_DSA_CERT_V00, KEY_UNSPEC }; -enum fp_type { + +/* Fingerprint hash algorithms */ +/* XXX add SHA256 */ +enum sshkey_fp_type { SSH_FP_SHA1, SSH_FP_MD5 }; -enum fp_rep { + +/* Fingerprint representation formats */ +enum sshkey_fp_rep { SSH_FP_HEX, SSH_FP_BUBBLEBABBLE, SSH_FP_RANDOMART }; /* key is stored in external hardware */ -#define KEY_FLAG_EXT 0x0001 +#define SSHKEY_FLAG_EXT 0x0001 -#define CERT_MAX_PRINCIPALS 256 -struct KeyCert { - Buffer certblob; /* Kept around for use on wire */ +#define SSHKEY_CERT_MAX_PRINCIPALS 256 +/* XXX opaquify? */ +struct sshkey_cert { + struct sshbuf *certblob; /* Kept around for use on wire */ u_int type; /* SSH2_CERT_TYPE_USER or SSH2_CERT_TYPE_HOST */ u_int64_t serial; char *key_id; u_int nprincipals; char **principals; u_int64_t valid_after, valid_before; - Buffer critical; - Buffer extensions; - Key *signature_key; + struct sshbuf *critical; + struct sshbuf *extensions; + struct sshkey *signature_key; }; -struct Key { +/* XXX opaquify? */ +struct sshkey { int type; int flags; RSA *rsa; DSA *dsa; int ecdsa_nid; /* NID of curve */ EC_KEY *ecdsa; - struct KeyCert *cert; + struct sshkey_cert *cert; }; -Key *key_new(int); -void key_add_private(Key *); -Key *key_new_private(int); -void key_free(Key *); -Key *key_demote(const Key *); -int key_equal_public(const Key *, const Key *); -int key_equal(const Key *, const Key *); -char *key_fingerprint(Key *, enum fp_type, enum fp_rep); -u_char *key_fingerprint_raw(Key *, enum fp_type, u_int *); -const char *key_type(const Key *); -const char *key_cert_type(const Key *); -int key_write(const Key *, FILE *); -int key_read(Key *, char **); -u_int key_size(const Key *); +struct sshkey *sshkey_new(int); +int sshkey_add_private(struct sshkey *); +struct sshkey *sshkey_new_private(int); +void sshkey_free(struct sshkey *); +int sshkey_demote(const struct sshkey *, struct sshkey **); +int sshkey_equal_public(const struct sshkey *, + const struct sshkey *); +int sshkey_equal(const struct sshkey *, const struct sshkey *); +char *sshkey_fingerprint(struct sshkey *, + enum sshkey_fp_type, enum sshkey_fp_rep); +u_char *sshkey_fingerprint_raw(struct sshkey *, + enum sshkey_fp_type, u_int *); +const char *sshkey_type(const struct sshkey *); +const char *sshkey_cert_type(const struct sshkey *); +int sshkey_write(const struct sshkey *, FILE *); +int sshkey_read(struct sshkey *, char **); +u_int sshkey_size(const struct sshkey *); -Key *key_generate(int, u_int); -Key *key_from_private(const Key *); -int key_type_from_name(char *); -int key_is_cert(const Key *); -int key_type_plain(int); -int key_to_certified(Key *, int); -int key_drop_cert(Key *); -int key_certify(Key *, Key *); -void key_cert_copy(const Key *, struct Key *); -int key_cert_check_authority(const Key *, int, int, const char *, +int sshkey_generate(int type, u_int bits, struct sshkey **keyp); +int sshkey_from_private(const struct sshkey *, struct sshkey **); +int sshkey_type_from_name(char *); +int sshkey_is_cert(const struct sshkey *); +int sshkey_type_plain(int); +int sshkey_to_certified(struct sshkey *, int); +int sshkey_drop_cert(struct sshkey *); +int sshkey_certify(struct sshkey *, struct sshkey *); +int sshkey_cert_copy(const struct sshkey *, struct sshkey *); +int sshkey_cert_check_authority(const struct sshkey *, int, int, + const char *, const char **); -int key_cert_is_legacy(Key *); +int sshkey_cert_is_legacy(struct sshkey *); -int key_ecdsa_nid_from_name(const char *); -int key_curve_name_to_nid(const char *); -const char * key_curve_nid_to_name(int); -u_int key_curve_nid_to_bits(int); -int key_ecdsa_bits_to_nid(int); -int key_ecdsa_key_to_nid(EC_KEY *); -const EVP_MD * key_ec_nid_to_evpmd(int nid); -int key_ec_validate_public(const EC_GROUP *, const EC_POINT *); -int key_ec_validate_private(const EC_KEY *); +int sshkey_ecdsa_nid_from_name(const char *); +int sshkey_curve_name_to_nid(const char *); +const char * sshkey_curve_nid_to_name(int); +u_int sshkey_curve_nid_to_bits(int); +int sshkey_ecdsa_bits_to_nid(int); +int sshkey_ecdsa_key_to_nid(EC_KEY *); +const EVP_MD * sshkey_ec_nid_to_evpmd(int nid); +int sshkey_ec_validate_public(const EC_GROUP *, const EC_POINT *); +int sshkey_ec_validate_private(const EC_KEY *); -Key *key_from_blob(const u_char *, u_int); -int key_to_blob(const Key *, u_char **, u_int *); -const char *key_ssh_name(const Key *); -const char *key_ssh_name_plain(const Key *); -int key_names_valid2(const char *); +int sshkey_from_blob(const u_char *, u_int, struct sshkey **); +int sshkey_to_blob_buf(const struct sshkey *, struct sshbuf *); +int sshkey_to_blob(const struct sshkey *, u_char **, u_int *); +const char *sshkey_ssh_name(const struct sshkey *); +const char *sshkey_ssh_name_plain(const struct sshkey *); +int sshkey_names_valid2(const char *); -int key_sign(const Key *, u_char **, u_int *, const u_char *, u_int); -int key_verify(const Key *, const u_char *, u_int, const u_char *, u_int); +int sshkey_sign(const struct sshkey *, u_char **, u_int *, + const u_char *, u_int, u_int); +int sshkey_verify(const struct sshkey *, const u_char *, u_int, + const u_char *, u_int, u_int); -int ssh_dss_sign(const Key *, u_char **, u_int *, const u_char *, u_int); -int ssh_dss_verify(const Key *, const u_char *, u_int, const u_char *, u_int); -int ssh_ecdsa_sign(const Key *, u_char **, u_int *, const u_char *, u_int); -int ssh_ecdsa_verify(const Key *, const u_char *, u_int, const u_char *, u_int); -int ssh_rsa_sign(const Key *, u_char **, u_int *, const u_char *, u_int); -int ssh_rsa_verify(const Key *, const u_char *, u_int, const u_char *, u_int); +/* for debug */ +void sshkey_dump_ec_point(const EC_GROUP *, const EC_POINT *); +void sshkey_dump_ec_key(const EC_KEY *); -#if defined(DEBUG_KEXECDH) || defined(DEBUG_PK) -void key_dump_ec_point(const EC_GROUP *, const EC_POINT *); -void key_dump_ec_key(const EC_KEY *); +#ifdef SSHKEY_INTERNAL +int ssh_rsa_sign(const struct sshkey *key, u_char **sigp, u_int *lenp, + const u_char *data, u_int datalen, u_int compat); +int ssh_rsa_verify(const struct sshkey *key, + const u_char *signature, u_int signaturelen, + const u_char *data, u_int datalen, u_int compat); +int ssh_dss_sign(const struct sshkey *key, u_char **sigp, u_int *lenp, + const u_char *data, u_int datalen, u_int compat); +int ssh_dss_verify(const struct sshkey *key, + const u_char *signature, u_int signaturelen, + const u_char *data, u_int datalen, u_int compat); +int ssh_ecdsa_sign(const struct sshkey *key, u_char **sigp, u_int *lenp, + const u_char *data, u_int datalen, u_int compat); +int ssh_ecdsa_verify(const struct sshkey *key, + const u_char *signature, u_int signaturelen, + const u_char *data, u_int datalen, u_int compat); #endif + #endif diff --git a/ssh/lib/Makefile b/ssh/lib/Makefile index a07a5d3..0628864 100644 --- a/ssh/lib/Makefile +++ b/ssh/lib/Makefile @@ -13,7 +13,13 @@ SRCS= authfd.c authfile.c bufaux.c bufec.c bufbn.c buffer.c canohost.c \ key.c dispatch.c kex.c mac.c uidswap.c uuencode.c misc.c \ ssh-dss.c ssh-rsa.c ssh-ecdsa.c dh.c kexdh.c kexgex.c kexecdh.c \ kexdhc.c kexgexc.c kexecdhc.c msg.c progressmeter.c dns.c \ - monitor_fdpass.c umac.c addrmatch.c schnorr.c jpake.c ssh-pkcs11.c + monitor_fdpass.c umac.c addrmatch.c schnorr.c jpake.c ssh-pkcs11.c \ + \ + sshbuf-getput-basic.c \ + sshbuf-getput-crypto.c \ + sshbuf-misc.c \ + sshbuf.c \ + err.c SRCS+= opacket.c ssh_api.c diff --git a/ssh/monitor.c b/ssh/monitor.c index c3017fc..05d5c53 100644 --- a/ssh/monitor.c +++ b/ssh/monitor.c @@ -75,6 +75,7 @@ #include "ssh2.h" #include "jpake.h" #include "roaming.h" +#include "err.h" #ifdef GSSAPI static Gssctxt *gsscontext = NULL; @@ -584,11 +585,11 @@ mm_answer_moduli(int sock, Buffer *m) int mm_answer_sign(int sock, Buffer *m) { - Key *key; + struct sshkey *key; u_char *p; u_char *signature; u_int siglen, datlen; - int keyid; + int keyid, r; debug3("%s", __func__); @@ -611,8 +612,9 @@ mm_answer_sign(int sock, Buffer *m) if ((key = get_hostkey_by_index(keyid)) == NULL) fatal("%s: no hostkey from index %d", __func__, keyid); - if (key_sign(key, &signature, &siglen, p, datlen) < 0) - fatal("%s: key_sign failed", __func__); + if ((r = sshkey_sign(key, &signature, &siglen, p, datlen, + datafellows)) != 0) + fatal("%s: sshkey_sign failed: %s", __func__, ssh_err(r)); debug3("%s: signature %p(%u)", __func__, signature, siglen); @@ -829,12 +831,12 @@ mm_answer_bsdauthrespond(int sock, Buffer *m) int mm_answer_keyallowed(int sock, Buffer *m) { - Key *key; + struct sshkey *key; char *cuser, *chost; u_char *blob; u_int bloblen; enum mm_keytype type = 0; - int allowed = 0; + int r, allowed = 0; debug3("%s entering", __func__); @@ -843,14 +845,13 @@ mm_answer_keyallowed(int sock, Buffer *m) chost = buffer_get_string(m, NULL); blob = buffer_get_string(m, &bloblen); - key = key_from_blob(blob, bloblen); + if ((r = sshkey_from_blob(blob, bloblen, &key)) != 0) + fatal("%s: cannot parse key: %s", __func__, ssh_err(r)); if ((compat20 && type == MM_RSAHOSTKEY) || (!compat20 && type != MM_RSAHOSTKEY)) fatal("%s: key type and protocol mismatch", __func__); - debug3("%s: key_from_blob: %p", __func__, key); - if (key != NULL && authctxt->valid) { switch (type) { case MM_USERKEY: @@ -881,7 +882,7 @@ mm_answer_keyallowed(int sock, Buffer *m) } } if (key != NULL) - key_free(key); + sshkey_free(key); /* clear temporarily storage (used by verify) */ monitor_reset_key_state(); @@ -1031,11 +1032,10 @@ monitor_valid_hostbasedblob(u_char *data, u_int datalen, char *cuser, int mm_answer_keyverify(int sock, Buffer *m) { - Key *key; + struct sshkey *key; u_char *signature, *data, *blob; u_int signaturelen, datalen, bloblen; - int verified = 0; - int valid_data = 0; + int r, verified = 0, valid_data = 0; blob = buffer_get_string(m, &bloblen); signature = buffer_get_string(m, &signaturelen); @@ -1045,9 +1045,8 @@ mm_answer_keyverify(int sock, Buffer *m) !monitor_allowed_key(blob, bloblen)) fatal("%s: bad key, not previously allowed", __func__); - key = key_from_blob(blob, bloblen); - if (key == NULL) - fatal("%s: bad public key blob", __func__); + if ((r = sshkey_from_blob(blob, bloblen, &key)) != 0) + fatal("%s: bad public key blob: %s", __func__, ssh_err(r)); switch (key_blobtype) { case MM_USERKEY: @@ -1064,11 +1063,12 @@ mm_answer_keyverify(int sock, Buffer *m) if (!valid_data) fatal("%s: bad signature data blob", __func__); - verified = key_verify(key, signature, signaturelen, data, datalen); + verified = sshkey_verify(key, signature, signaturelen, data, datalen, + datafellows); debug3("%s: key %p signature %s", __func__, key, (verified == 1) ? "verified" : "unverified"); - key_free(key); + sshkey_free(key); xfree(blob); xfree(signature); xfree(data); @@ -1255,10 +1255,10 @@ int mm_answer_rsa_keyallowed(int sock, Buffer *m) { BIGNUM *client_n; - Key *key = NULL; + struct sshkey *key = NULL; u_char *blob = NULL; u_int blen = 0; - int allowed = 0; + int r, allowed = 0; debug3("%s entering", __func__); @@ -1279,8 +1279,9 @@ mm_answer_rsa_keyallowed(int sock, Buffer *m) if (allowed && key != NULL) { key->type = KEY_RSA; /* cheat for key_to_blob */ - if (key_to_blob(key, &blob, &blen) == 0) - fatal("%s: key_to_blob failed", __func__); + if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) + fatal("%s: key_to_blob failed: %s", + __func__, ssh_err(r)); buffer_put_string(m, blob, blen); /* Save temporarily for comparison in verify */ @@ -1289,7 +1290,7 @@ mm_answer_rsa_keyallowed(int sock, Buffer *m) key_blobtype = MM_RSAUSERKEY; } if (key != NULL) - key_free(key); + sshkey_free(key); mm_request_send(sock, MONITOR_ANS_RSAKEYALLOWED, m); @@ -1301,9 +1302,10 @@ mm_answer_rsa_keyallowed(int sock, Buffer *m) int mm_answer_rsa_challenge(int sock, Buffer *m) { - Key *key = NULL; + struct sshkey *key = NULL; u_char *blob; u_int blen; + int r; debug3("%s entering", __func__); @@ -1314,8 +1316,8 @@ mm_answer_rsa_challenge(int sock, Buffer *m) fatal("%s: bad key, not previously allowed", __func__); if (key_blobtype != MM_RSAUSERKEY && key_blobtype != MM_RSAHOSTKEY) fatal("%s: key type mismatch", __func__); - if ((key = key_from_blob(blob, blen)) == NULL) - fatal("%s: received bad key", __func__); + if ((r = sshkey_from_blob(blob, blen, &key)) != 0) + fatal("%s: received bad key: %s", __func__, ssh_err(r)); if (key->type != KEY_RSA) fatal("%s: received bad key type %d", __func__, key->type); key->type = KEY_RSA1; @@ -1332,17 +1334,17 @@ mm_answer_rsa_challenge(int sock, Buffer *m) monitor_permit(mon_dispatch, MONITOR_REQ_RSARESPONSE, 1); xfree(blob); - key_free(key); + sshkey_free(key); return (0); } int mm_answer_rsa_response(int sock, Buffer *m) { - Key *key = NULL; + struct sshkey *key = NULL; u_char *blob, *response; u_int blen, len; - int success; + int r, success; debug3("%s entering", __func__); @@ -1356,15 +1358,15 @@ mm_answer_rsa_response(int sock, Buffer *m) fatal("%s: bad key, not previously allowed", __func__); if (key_blobtype != MM_RSAUSERKEY && key_blobtype != MM_RSAHOSTKEY) fatal("%s: key type mismatch: %d", __func__, key_blobtype); - if ((key = key_from_blob(blob, blen)) == NULL) - fatal("%s: received bad key", __func__); + if ((r = sshkey_from_blob(blob, blen, &key)) != 0) + fatal("%s: received bad key: %s", __func__, ssh_err(r)); response = buffer_get_string(m, &len); if (len != 16) fatal("%s: received bad response to challenge", __func__); success = auth_rsa_verify_response(key, ssh1_challenge, response); xfree(blob); - key_free(key); + sshkey_free(key); xfree(response); auth_method = key_blobtype == MM_RSAUSERKEY ? "rsa" : "rhosts-rsa"; diff --git a/ssh/monitor_wrap.c b/ssh/monitor_wrap.c index 836c511..3cc5d97 100644 --- a/ssh/monitor_wrap.c +++ b/ssh/monitor_wrap.c @@ -70,6 +70,7 @@ #include "session.h" #include "servconf.h" #include "roaming.h" +#include "err.h" /* Imports */ extern int compat20; @@ -202,7 +203,8 @@ mm_choose_dh(int min, int nbits, int max) } int -mm_key_sign(Key *key, u_char **sigp, u_int *lenp, u_char *data, u_int datalen) +mm_sshkey_sign(struct sshkey *key, u_char **sigp, u_int *lenp, + u_char *data, u_int datalen, u_int compat) { Kex *kex = *pmonitor->m_pkex; Buffer m; @@ -351,21 +353,21 @@ mm_auth_password(Authctxt *authctxt, char *password) } int -mm_user_key_allowed(struct passwd *pw, Key *key) +mm_user_key_allowed(struct passwd *pw, struct sshkey *key) { return (mm_key_allowed(MM_USERKEY, NULL, NULL, key)); } int mm_hostbased_key_allowed(struct passwd *pw, char *user, char *host, - Key *key) + struct sshkey *key) { return (mm_key_allowed(MM_HOSTKEY, user, host, key)); } int mm_auth_rhosts_rsa_key_allowed(struct passwd *pw, char *user, - char *host, Key *key) + char *host, struct sshkey *key) { int ret; @@ -376,18 +378,21 @@ mm_auth_rhosts_rsa_key_allowed(struct passwd *pw, char *user, } int -mm_key_allowed(enum mm_keytype type, char *user, char *host, Key *key) +mm_key_allowed(enum mm_keytype type, char *user, char *host, + struct sshkey *key) { Buffer m; u_char *blob; u_int len; - int allowed = 0, have_forced = 0; + int r, allowed = 0, have_forced = 0; debug3("%s entering", __func__); /* Convert the key to a blob and the pass it over */ - if (!key_to_blob(key, &blob, &len)) + if ((r = sshkey_to_blob(key, &blob, &len)) != 0) { + error("%s: key_to_blob: %s", __func__, ssh_err(r)); return (0); + } buffer_init(&m); buffer_put_int(&m, type); @@ -420,18 +425,21 @@ mm_key_allowed(enum mm_keytype type, char *user, char *host, Key *key) */ int -mm_key_verify(Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen) +mm_sshkey_verify(struct sshkey *key, u_char *sig, u_int siglen, + u_char *data, u_int datalen, u_int compat) { Buffer m; u_char *blob; u_int len; - int verified = 0; + int r, verified = 0; debug3("%s entering", __func__); /* Convert the key to a blob and the pass it over */ - if (!key_to_blob(key, &blob, &len)) + if ((r = sshkey_to_blob(key, &blob, &len)) != 0) { + error("%s: sshkey_to_blob failed: %s", __func__, ssh_err(r)); return (0); + } buffer_init(&m); buffer_put_string(&m, blob, len); @@ -875,13 +883,14 @@ mm_ssh1_session_id(u_char session_id[16]) } int -mm_auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) +mm_auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, + struct sshkey **rkey) { Buffer m; - Key *key; + struct sshkey *key; u_char *blob; u_int blen; - int allowed = 0, have_forced = 0; + int r, allowed = 0, have_forced = 0; debug3("%s entering", __func__); @@ -900,8 +909,9 @@ mm_auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) if (allowed && rkey != NULL) { blob = buffer_get_string(&m, &blen); - if ((key = key_from_blob(blob, blen)) == NULL) - fatal("%s: key_from_blob failed", __func__); + if ((r = sshkey_from_blob(blob, blen, &key)) != 0) + fatal("%s: key_from_blob failed: %s", + __func__, ssh_err(r)); *rkey = key; xfree(blob); } @@ -911,12 +921,13 @@ mm_auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) } BIGNUM * -mm_auth_rsa_generate_challenge(Key *key) +mm_auth_rsa_generate_challenge(struct sshkey *key) { Buffer m; BIGNUM *challenge; u_char *blob; u_int blen; + int r; debug3("%s entering", __func__); @@ -924,8 +935,8 @@ mm_auth_rsa_generate_challenge(Key *key) fatal("%s: BN_new failed", __func__); key->type = KEY_RSA; /* XXX cheat for key_to_blob */ - if (key_to_blob(key, &blob, &blen) == 0) - fatal("%s: key_to_blob failed", __func__); + if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) + fatal("%s: key_to_blob failed: %s", __func__, ssh_err(r)); key->type = KEY_RSA1; buffer_init(&m); @@ -942,18 +953,18 @@ mm_auth_rsa_generate_challenge(Key *key) } int -mm_auth_rsa_verify_response(Key *key, BIGNUM *p, u_char response[16]) +mm_auth_rsa_verify_response(struct sshkey *key, BIGNUM *p, u_char response[16]) { Buffer m; u_char *blob; u_int blen; - int success = 0; + int r, success = 0; debug3("%s entering", __func__); key->type = KEY_RSA; /* XXX cheat for key_to_blob */ - if (key_to_blob(key, &blob, &blen) == 0) - fatal("%s: key_to_blob failed", __func__); + if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) + fatal("%s: key_to_blob failed: %s", __func__, ssh_err(r)); key->type = KEY_RSA1; buffer_init(&m); diff --git a/ssh/monitor_wrap.h b/ssh/monitor_wrap.h index 004f65c..7d8091f 100644 --- a/ssh/monitor_wrap.h +++ b/ssh/monitor_wrap.h @@ -40,19 +40,20 @@ struct Authctxt; void mm_log_handler(LogLevel, const char *, void *); int mm_is_monitor(void); DH *mm_choose_dh(int, int, int); -int mm_key_sign(Key *, u_char **, u_int *, u_char *, u_int); +int mm_sshkey_sign(struct sshkey *, u_char **, u_int *, u_char *, u_int, u_int); void mm_inform_authserv(char *, char *); struct passwd *mm_getpwnamallow(const char *); char *mm_auth2_read_banner(void); int mm_auth_password(struct Authctxt *, char *); -int mm_key_allowed(enum mm_keytype, char *, char *, Key *); -int mm_user_key_allowed(struct passwd *, Key *); -int mm_hostbased_key_allowed(struct passwd *, char *, char *, Key *); -int mm_auth_rhosts_rsa_key_allowed(struct passwd *, char *, char *, Key *); -int mm_key_verify(Key *, u_char *, u_int, u_char *, u_int); -int mm_auth_rsa_key_allowed(struct passwd *, BIGNUM *, Key **); -int mm_auth_rsa_verify_response(Key *, BIGNUM *, u_char *); -BIGNUM *mm_auth_rsa_generate_challenge(Key *); +int mm_key_allowed(enum mm_keytype, char *, char *, struct sshkey *); +int mm_user_key_allowed(struct passwd *, struct sshkey *); +int mm_hostbased_key_allowed(struct passwd *, char *, char *, struct sshkey *); +int mm_auth_rhosts_rsa_key_allowed(struct passwd *, char *, char *, + struct sshkey *); +int mm_sshkey_verify(struct sshkey *, u_char *, u_int, u_char *, u_int, u_int); +int mm_auth_rsa_key_allowed(struct passwd *, BIGNUM *, struct sshkey **); +int mm_auth_rsa_verify_response(struct sshkey *, BIGNUM *, u_char *); +BIGNUM *mm_auth_rsa_generate_challenge(struct sshkey *); #ifdef GSSAPI OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID); diff --git a/ssh/mux.c b/ssh/mux.c index 0e78800..ed68742 100644 --- a/ssh/mux.c +++ b/ssh/mux.c @@ -1000,7 +1000,7 @@ mux_master_read_cb(Channel *c) { struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; Buffer in, out; - void *ptr; + const void *ptr; u_int type, rid, have, i; int ret = -1; @@ -1412,7 +1412,7 @@ mux_client_read_packet(int fd, Buffer *m) { Buffer queue; u_int need, have; - void *ptr; + const void *ptr; int oerrno; buffer_init(&queue); diff --git a/ssh/packet.c b/ssh/packet.c index cb32a5f..3902ded 100644 --- a/ssh/packet.c +++ b/ssh/packet.c @@ -1576,7 +1576,7 @@ ssh_packet_get_string(struct ssh *ssh, u_int *length_ptr) return buffer_get_string(&ssh->state->incoming_packet, length_ptr); } -void * +const void * ssh_packet_get_string_ptr(struct ssh *ssh, u_int *length_ptr) { return buffer_get_string_ptr(&ssh->state->incoming_packet, length_ptr); diff --git a/ssh/packet.h b/ssh/packet.h index 890330c..69f89fa 100644 --- a/ssh/packet.h +++ b/ssh/packet.h @@ -33,7 +33,7 @@ struct key_entry { TAILQ_ENTRY(key_entry) next; - Key *key; + struct sshkey *key; }; struct session_state; /* private session data */ @@ -120,7 +120,7 @@ void ssh_packet_get_ecpoint(struct ssh *, const EC_GROUP *, EC_POINT *); void *ssh_packet_get_raw(struct ssh *, u_int *length_ptr); void *ssh_packet_get_string(struct ssh *, u_int *length_ptr); char *ssh_packet_get_cstring(struct ssh *, u_int *length_ptr); -void *ssh_packet_get_string_ptr(struct ssh *, u_int *length_ptr); +const void *ssh_packet_get_string_ptr(struct ssh *, u_int *length_ptr); void ssh_packet_disconnect(struct ssh *, const char *fmt, ...) __attribute__((format(printf, 2, 3))); void ssh_packet_send_debug(struct ssh *, const char *fmt, ...) __attribute__((format(printf, 2, 3))); diff --git a/ssh/readconf.c b/ssh/readconf.c index 8f40dc6..690dc71 100644 --- a/ssh/readconf.c +++ b/ssh/readconf.c @@ -727,7 +727,7 @@ parse_int: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); - if (!key_names_valid2(arg)) + if (!sshkey_names_valid2(arg)) fatal("%.200s line %d: Bad protocol 2 host key algorithms '%s'.", filename, linenum, arg ? arg : ""); if (*activep && options->hostkeyalgorithms == NULL) diff --git a/ssh/readconf.h b/ssh/readconf.h index be30ee0..fd5d7f3 100644 --- a/ssh/readconf.h +++ b/ssh/readconf.h @@ -96,7 +96,7 @@ typedef struct { int num_identity_files; /* Number of files for RSA/DSA identities. */ char *identity_files[SSH_MAX_IDENTITY_FILES]; - Key *identity_keys[SSH_MAX_IDENTITY_FILES]; + struct sshkey *identity_keys[SSH_MAX_IDENTITY_FILES]; /* Local TCP/IP forward requests. */ int num_local_forwards; diff --git a/ssh/ssh-add.c b/ssh/ssh-add.c index ab6ed51..1bde4ea 100644 --- a/ssh/ssh-add.c +++ b/ssh/ssh-add.c @@ -58,6 +58,7 @@ #include "authfile.h" #include "pathnames.h" #include "misc.h" +#include "err.h" /* argv0 */ extern char *__progname; @@ -92,7 +93,7 @@ clear_pass(void) static int delete_file(AuthenticationConnection *ac, const char *filename) { - Key *public; + struct sshkey *public; char *comment = NULL; int ret = -1; @@ -107,7 +108,7 @@ delete_file(AuthenticationConnection *ac, const char *filename) } else fprintf(stderr, "Could not remove identity: %s\n", filename); - key_free(public); + sshkey_free(public); xfree(comment); return ret; @@ -135,10 +136,10 @@ delete_all(AuthenticationConnection *ac) static int add_file(AuthenticationConnection *ac, const char *filename, int key_only) { - Key *private, *cert; + struct sshkey *private, *cert; char *comment = NULL; char msg[1024], *certpath = NULL; - int fd, perms_ok, ret = -1; + int r, fd, perms_ok, ret = -1; Buffer keyblob; if (strcmp(filename, "-") == 0) { @@ -222,21 +223,26 @@ add_file(AuthenticationConnection *ac, const char *filename, int key_only) if ((cert = key_load_public(certpath, NULL)) == NULL) goto out; - if (!key_equal_public(cert, private)) { + if (!sshkey_equal_public(cert, private)) { error("Certificate %s does not match private key %s", certpath, filename); - key_free(cert); + sshkey_free(cert); goto out; } /* Graft with private bits */ - if (key_to_certified(private, key_cert_is_legacy(cert)) != 0) { - error("%s: key_to_certified failed", __func__); - key_free(cert); + if ((r = sshkey_to_certified(private, + sshkey_cert_is_legacy(cert))) != 0) { + error("%s: sshkey_to_certified: %s", __func__, ssh_err(r)); + sshkey_free(cert); goto out; } - key_cert_copy(cert, private); - key_free(cert); + if ((r = sshkey_cert_copy(cert, private)) != 0) { + error("%s: key_cert_copy: %s", __func__, ssh_err(r)); + sshkey_free(cert); + goto out; + } + sshkey_free(cert); if (!ssh_add_identity_constrained(ac, private, comment, lifetime, confirm)) { @@ -253,7 +259,7 @@ add_file(AuthenticationConnection *ac, const char *filename, int key_only) if (certpath != NULL) xfree(certpath); xfree(comment); - key_free(private); + sshkey_free(private); return ret; } @@ -284,10 +290,9 @@ update_card(AuthenticationConnection *ac, int add, const char *id) static int list_identities(AuthenticationConnection *ac, int do_fp) { - Key *key; + struct sshkey *key; char *comment, *fp; - int had_identities = 0; - int version; + int version, r, had_identities = 0; for (version = 1; version <= 2; version++) { for (key = ssh_get_first_identity(ac, &comment, version); @@ -295,17 +300,19 @@ list_identities(AuthenticationConnection *ac, int do_fp) key = ssh_get_next_identity(ac, &comment, version)) { had_identities = 1; if (do_fp) { - fp = key_fingerprint(key, SSH_FP_MD5, + fp = sshkey_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); printf("%d %s %s (%s)\n", - key_size(key), fp, comment, key_type(key)); + sshkey_size(key), fp, comment, + sshkey_type(key)); xfree(fp); } else { - if (!key_write(key, stdout)) - fprintf(stderr, "key_write failed"); + if ((r = sshkey_write(key, stdout)) != 0) + fprintf(stderr, "sshkey_write: %s", + ssh_err(r)); fprintf(stdout, " %s\n", comment); } - key_free(key); + sshkey_free(key); xfree(comment); } } diff --git a/ssh/ssh-agent.c b/ssh/ssh-agent.c index ca962f1..4df2312 100644 --- a/ssh/ssh-agent.c +++ b/ssh/ssh-agent.c @@ -65,6 +65,7 @@ #include "compat.h" #include "log.h" #include "misc.h" +#include "err.h" #ifdef ENABLE_PKCS11 #include "ssh-pkcs11.h" @@ -89,7 +90,7 @@ SocketEntry *sockets = NULL; typedef struct identity { TAILQ_ENTRY(identity) next; - Key *key; + struct sshkey *key; char *comment; char *provider; u_int death; @@ -157,7 +158,7 @@ idtab_lookup(int version) static void free_identity(Identity *id) { - key_free(id->key); + sshkey_free(id->key); if (id->provider != NULL) xfree(id->provider); xfree(id->comment); @@ -166,13 +167,13 @@ free_identity(Identity *id) /* return matching private key for given public key */ static Identity * -lookup_identity(Key *key, int version) +lookup_identity(struct sshkey *key, int version) { Identity *id; Idtab *tab = idtab_lookup(version); TAILQ_FOREACH(id, &tab->idlist, next) { - if (key_equal(key, id->key)) + if (sshkey_equal(key, id->key)) return (id); } return (NULL); @@ -185,7 +186,7 @@ confirm_key(Identity *id) char *p; int ret = -1; - p = key_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX); + p = sshkey_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX); if (ask_permission("Allow use of key %s?\nKey fingerprint %s.", id->comment, p)) ret = 0; @@ -201,6 +202,7 @@ process_request_identities(SocketEntry *e, int version) Idtab *tab = idtab_lookup(version); Identity *id; Buffer msg; + int r; buffer_init(&msg); buffer_put_char(&msg, (version == 1) ? @@ -214,7 +216,11 @@ process_request_identities(SocketEntry *e, int version) } else { u_char *blob; u_int blen; - key_to_blob(id->key, &blob, &blen); + if ((r = sshkey_to_blob(id->key, &blob, &blen)) != 0) { + error("%s: sshkey_to_blob: %s", __func__, + ssh_err(r)); + continue; + } buffer_put_string(&msg, blob, blen); xfree(blob); } @@ -236,12 +242,13 @@ process_authentication_challenge1(SocketEntry *e) int i, len; Buffer msg; MD5_CTX md; - Key *key; + struct sshkey *key; buffer_init(&msg); - key = key_new(KEY_RSA1); + if ((key = sshkey_new(KEY_RSA1)) == NULL) + fatal("%s: sshkey_new failed", __func__); if ((challenge = BN_new()) == NULL) - fatal("process_authentication_challenge1: BN_new failed"); + fatal("%s: BN_new failed", __func__); (void) buffer_get_int(&e->request); /* ignored */ buffer_get_bignum(&e->request, key->rsa->e); @@ -258,7 +265,7 @@ process_authentication_challenge1(SocketEntry *e) id = lookup_identity(key, 1); if (id != NULL && (!id->confirm || confirm_key(id) == 0)) { - Key *private = id->key; + struct sshkey *private = id->key; /* Decrypt the challenge using the private key. */ if (rsa_private_decrypt(challenge, challenge, private->rsa) <= 0) goto failure; @@ -266,7 +273,7 @@ process_authentication_challenge1(SocketEntry *e) /* The response is MD5 of decrypted challenge plus session id. */ len = BN_num_bytes(challenge); if (len <= 0 || len > 32) { - logit("process_authentication_challenge: bad challenge length %d", len); + logit("%s: bad challenge length %d", __func__, len); goto failure; } memset(buf, 0, 32); @@ -289,7 +296,7 @@ failure: send: buffer_put_int(&e->output, buffer_len(&msg)); buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); - key_free(key); + sshkey_free(key); BN_clear_free(challenge); buffer_free(&msg); } @@ -304,7 +311,7 @@ process_sign_request2(SocketEntry *e) int odatafellows; int ok = -1, flags; Buffer msg; - Key *key; + struct sshkey *key; datafellows = 0; @@ -316,12 +323,17 @@ process_sign_request2(SocketEntry *e) if (flags & SSH_AGENT_OLD_SIGNATURE) datafellows = SSH_BUG_SIGBLOB; - key = key_from_blob(blob, blen); - if (key != NULL) { + if ((ok = sshkey_from_blob(blob, blen, &key)) != 0) + error("%s: cannot parse key blob: %s", __func__, ssh_err(ok)); + else { Identity *id = lookup_identity(key, 2); - if (id != NULL && (!id->confirm || confirm_key(id) == 0)) - ok = key_sign(id->key, &signature, &slen, data, dlen); - key_free(key); + if (id != NULL && (!id->confirm || confirm_key(id) == 0)) { + if ((ok = sshkey_sign(id->key, &signature, &slen, + data, dlen, 0)) != 0) + error("%s: sshkey_sign: %s", + __func__, ssh_err(ok)); + } + sshkey_free(key); } buffer_init(&msg); if (ok == 0) { @@ -346,24 +358,30 @@ static void process_remove_identity(SocketEntry *e, int version) { u_int blen, bits; - int success = 0; - Key *key = NULL; + int r, success = 0; + struct sshkey *key = NULL; u_char *blob; switch (version) { case 1: - key = key_new(KEY_RSA1); + if ((key = sshkey_new(KEY_RSA1)) == NULL) { + error("%s: sshkey_new failed", __func__); + return; + } bits = buffer_get_int(&e->request); buffer_get_bignum(&e->request, key->rsa->e); buffer_get_bignum(&e->request, key->rsa->n); - if (bits != key_size(key)) - logit("Warning: identity keysize mismatch: actual %u, announced %u", - key_size(key), bits); + if (bits != sshkey_size(key)) + logit("Warning: identity keysize mismatch: " + "actual %u, announced %u", + sshkey_size(key), bits); break; case 2: blob = buffer_get_string(&e->request, &blen); - key = key_from_blob(blob, blen); + if ((r = sshkey_from_blob(blob, blen, &key)) != 0) + error("%s: sshkey_from_blob failed: %s", + __func__, ssh_err(r)); xfree(blob); break; } @@ -387,7 +405,7 @@ process_remove_identity(SocketEntry *e, int version) tab->nentries--; success = 1; } - key_free(key); + sshkey_free(key); } buffer_put_int(&e->output, 1); buffer_put_char(&e->output, @@ -451,9 +469,9 @@ process_add_identity(SocketEntry *e, int version) { Idtab *tab = idtab_lookup(version); Identity *id; - int type, success = 0, death = 0, confirm = 0; + int r, type, success = 0, death = 0, confirm = 0; char *type_name, *comment, *curve; - Key *k = NULL; + struct sshkey *k = NULL; BIGNUM *exponent; EC_POINT *q; u_char *cert; @@ -461,7 +479,8 @@ process_add_identity(SocketEntry *e, int version) switch (version) { case 1: - k = key_new_private(KEY_RSA1); + if ((k = sshkey_new_private(KEY_RSA1)) == NULL) + fatal("%s: sshkey_new_private failed", __func__); (void) buffer_get_int(&e->request); /* ignored */ buffer_get_bignum(&e->request, k->rsa->n); buffer_get_bignum(&e->request, k->rsa->e); @@ -477,10 +496,12 @@ process_add_identity(SocketEntry *e, int version) break; case 2: type_name = buffer_get_string(&e->request, NULL); - type = key_type_from_name(type_name); + type = sshkey_type_from_name(type_name); switch (type) { case KEY_DSA: - k = key_new_private(type); + if ((k = sshkey_new_private(type)) == NULL) + fatal("%s: sshkey_new_private failed", + __func__); buffer_get_bignum2(&e->request, k->dsa->p); buffer_get_bignum2(&e->request, k->dsa->q); buffer_get_bignum2(&e->request, k->dsa->g); @@ -490,17 +511,22 @@ process_add_identity(SocketEntry *e, int version) case KEY_DSA_CERT_V00: case KEY_DSA_CERT: cert = buffer_get_string(&e->request, &len); - if ((k = key_from_blob(cert, len)) == NULL) - fatal("Certificate parse failed"); + if ((r = sshkey_from_blob(cert, len, &k)) != 0) + fatal("Certificate parse failed: %s", + ssh_err(r)); xfree(cert); - key_add_private(k); + if ((r = sshkey_add_private(k)) != 0) + fatal("%s: sshkey_add_private: %s", + __func__, ssh_err(r)); buffer_get_bignum2(&e->request, k->dsa->priv_key); break; case KEY_ECDSA: - k = key_new_private(type); - k->ecdsa_nid = key_ecdsa_nid_from_name(type_name); + if ((k = sshkey_new_private(type)) == NULL) + fatal("%s: sshkey_new_private failed", + __func__); + k->ecdsa_nid = sshkey_ecdsa_nid_from_name(type_name); curve = buffer_get_string(&e->request, NULL); - if (k->ecdsa_nid != key_curve_name_to_nid(curve)) + if (k->ecdsa_nid != sshkey_curve_name_to_nid(curve)) fatal("%s: curve names mismatch", __func__); xfree(curve); k->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid); @@ -521,34 +547,41 @@ process_add_identity(SocketEntry *e, int version) if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) fatal("%s: EC_KEY_set_private_key failed", __func__); - if (key_ec_validate_public(EC_KEY_get0_group(k->ecdsa), + if (sshkey_ec_validate_public( + EC_KEY_get0_group(k->ecdsa), EC_KEY_get0_public_key(k->ecdsa)) != 0) fatal("%s: bad ECDSA public key", __func__); - if (key_ec_validate_private(k->ecdsa) != 0) + if (sshkey_ec_validate_private(k->ecdsa) != 0) fatal("%s: bad ECDSA private key", __func__); BN_clear_free(exponent); EC_POINT_free(q); break; case KEY_ECDSA_CERT: cert = buffer_get_string(&e->request, &len); - if ((k = key_from_blob(cert, len)) == NULL) - fatal("Certificate parse failed"); + if ((r = sshkey_from_blob(cert, len, &k)) != 0) + fatal("Certificate parse failed: %s", + ssh_err(r)); xfree(cert); - key_add_private(k); + if ((r = sshkey_add_private(k)) != 0) + fatal("%s: key_add_private: %s", + __func__, ssh_err(r)); if ((exponent = BN_new()) == NULL) fatal("%s: BN_new failed", __func__); buffer_get_bignum2(&e->request, exponent); if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) fatal("%s: EC_KEY_set_private_key failed", __func__); - if (key_ec_validate_public(EC_KEY_get0_group(k->ecdsa), + if (sshkey_ec_validate_public( + EC_KEY_get0_group(k->ecdsa), EC_KEY_get0_public_key(k->ecdsa)) != 0 || - key_ec_validate_private(k->ecdsa) != 0) + sshkey_ec_validate_private(k->ecdsa) != 0) fatal("%s: bad ECDSA key", __func__); BN_clear_free(exponent); break; case KEY_RSA: - k = key_new_private(type); + if ((k = sshkey_new_private(type)) == NULL) + fatal("%s: sshkey_new_private failed", + __func__); buffer_get_bignum2(&e->request, k->rsa->n); buffer_get_bignum2(&e->request, k->rsa->e); buffer_get_bignum2(&e->request, k->rsa->d); @@ -562,10 +595,13 @@ process_add_identity(SocketEntry *e, int version) case KEY_RSA_CERT_V00: case KEY_RSA_CERT: cert = buffer_get_string(&e->request, &len); - if ((k = key_from_blob(cert, len)) == NULL) - fatal("Certificate parse failed"); + if ((r = sshkey_from_blob(cert, len, &k)) != 0) + fatal("Certificate parse failed: %s", + ssh_err(r)); xfree(cert); - key_add_private(k); + if ((r = sshkey_add_private(k)) != 0) + fatal("%s: key_add_private: %s", + __func__, ssh_err(r)); buffer_get_bignum2(&e->request, k->rsa->d); buffer_get_bignum2(&e->request, k->rsa->iqmp); buffer_get_bignum2(&e->request, k->rsa->p); @@ -579,6 +615,11 @@ process_add_identity(SocketEntry *e, int version) xfree(type_name); break; } + comment = buffer_get_string(&e->request, NULL); + if (k == NULL) { + xfree(comment); + goto send; + } /* enable blinding */ switch (k->type) { case KEY_RSA: @@ -587,16 +628,11 @@ process_add_identity(SocketEntry *e, int version) case KEY_RSA1: if (RSA_blinding_on(k->rsa, NULL) != 1) { error("process_add_identity: RSA_blinding_on failed"); - key_free(k); + sshkey_free(k); goto send; } break; } - comment = buffer_get_string(&e->request, NULL); - if (k == NULL) { - xfree(comment); - goto send; - } while (buffer_len(&e->request)) { switch ((type = buffer_get_char(&e->request))) { case SSH_AGENT_CONSTRAIN_LIFETIME: @@ -609,7 +645,7 @@ process_add_identity(SocketEntry *e, int version) error("process_add_identity: " "Unknown constraint type %d", type); xfree(comment); - key_free(k); + sshkey_free(k); goto send; } } @@ -623,7 +659,7 @@ process_add_identity(SocketEntry *e, int version) /* Increment the number of identities. */ tab->nentries++; } else { - key_free(k); + sshkey_free(k); xfree(id->comment); } id->comment = comment; @@ -683,7 +719,7 @@ process_add_smartcard_key(SocketEntry *e) { char *provider = NULL, *pin; int i, type, version, count = 0, success = 0, death = 0, confirm = 0; - Key **keys = NULL, *k; + struct sshkey **keys = NULL, *k; Identity *id; Idtab *tab; @@ -723,7 +759,7 @@ process_add_smartcard_key(SocketEntry *e) tab->nentries++; success = 1; } else { - key_free(k); + sshkey_free(k); } keys[i] = NULL; } diff --git a/ssh/ssh-dss.c b/ssh/ssh-dss.c index 9ffd87f..d877942 100644 --- a/ssh/ssh-dss.c +++ b/ssh/ssh-dss.c @@ -30,155 +30,190 @@ #include -#include "xmalloc.h" -#include "buffer.h" +#include "sshbuf.h" #include "compat.h" -#include "log.h" +#include "err.h" +#define SSHKEY_INTERNAL #include "key.h" #define INTBLOB_LEN 20 #define SIGBLOB_LEN (2*INTBLOB_LEN) int -ssh_dss_sign(const Key *key, u_char **sigp, u_int *lenp, - const u_char *data, u_int datalen) +ssh_dss_sign(const struct sshkey *key, u_char **sigp, u_int *lenp, + const u_char *data, u_int datalen, u_int compat) { - DSA_SIG *sig; + DSA_SIG *sig = NULL; const EVP_MD *evp_md = EVP_sha1(); EVP_MD_CTX md; u_char digest[EVP_MAX_MD_SIZE], sigblob[SIGBLOB_LEN]; u_int rlen, slen, len, dlen; - Buffer b; + struct sshbuf *b = NULL; + int ret = SSH_ERR_INVALID_ARGUMENT; if (key == NULL || key->dsa == NULL || (key->type != KEY_DSA && - key->type != KEY_DSA_CERT && key->type != KEY_DSA_CERT_V00)) { - error("ssh_dss_sign: no DSA key"); - return -1; + key->type != KEY_DSA_CERT && key->type != KEY_DSA_CERT_V00)) + return SSH_ERR_INVALID_ARGUMENT; + if (EVP_DigestInit(&md, evp_md) != 1 || + EVP_DigestUpdate(&md, data, datalen) != 1 || + EVP_DigestFinal(&md, digest, &dlen) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; } - EVP_DigestInit(&md, evp_md); - EVP_DigestUpdate(&md, data, datalen); - EVP_DigestFinal(&md, digest, &dlen); - sig = DSA_do_sign(digest, dlen, key->dsa); - memset(digest, 'd', sizeof(digest)); - - if (sig == NULL) { - error("ssh_dss_sign: sign failed"); - return -1; + if ((sig = DSA_do_sign(digest, dlen, key->dsa)) == NULL) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; } rlen = BN_num_bytes(sig->r); slen = BN_num_bytes(sig->s); if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN) { - error("bad sig size %u %u", rlen, slen); - DSA_SIG_free(sig); - return -1; + ret = SSH_ERR_INTERNAL_ERROR; + goto out; } - memset(sigblob, 0, SIGBLOB_LEN); - BN_bn2bin(sig->r, sigblob+ SIGBLOB_LEN - INTBLOB_LEN - rlen); - BN_bn2bin(sig->s, sigblob+ SIGBLOB_LEN - slen); - DSA_SIG_free(sig); + bzero(sigblob, SIGBLOB_LEN); + BN_bn2bin(sig->r, sigblob + SIGBLOB_LEN - INTBLOB_LEN - rlen); + BN_bn2bin(sig->s, sigblob + SIGBLOB_LEN - slen); - if (datafellows & SSH_BUG_SIGBLOB) { + if (compat & SSH_BUG_SIGBLOB) { if (lenp != NULL) *lenp = SIGBLOB_LEN; if (sigp != NULL) { - *sigp = xmalloc(SIGBLOB_LEN); + if ((*sigp = malloc(SIGBLOB_LEN)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } memcpy(*sigp, sigblob, SIGBLOB_LEN); } + ret = 0; } else { /* ietf-drafts */ - buffer_init(&b); - buffer_put_cstring(&b, "ssh-dss"); - buffer_put_string(&b, sigblob, SIGBLOB_LEN); - len = buffer_len(&b); + if ((b = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((ret = sshbuf_put_cstring(b, "ssh-dss")) != 0 || + (ret = sshbuf_put_string(b, sigblob, SIGBLOB_LEN)) != 0) + goto out; + len = sshbuf_len(b); if (lenp != NULL) *lenp = len; if (sigp != NULL) { - *sigp = xmalloc(len); - memcpy(*sigp, buffer_ptr(&b), len); + if ((*sigp = malloc(len)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(*sigp, sshbuf_ptr(b), len); } - buffer_free(&b); + ret = 0; } - return 0; + out: + bzero(&md, sizeof(md)); + bzero(digest, sizeof(digest)); + if (sig != NULL) + DSA_SIG_free(sig); + if (b != NULL) + sshbuf_free(b); + return ret; } + int -ssh_dss_verify(const Key *key, const u_char *signature, u_int signaturelen, - const u_char *data, u_int datalen) +ssh_dss_verify(const struct sshkey *key, + const u_char *signature, u_int signaturelen, + const u_char *data, u_int datalen, u_int compat) { - DSA_SIG *sig; + DSA_SIG *sig = NULL; const EVP_MD *evp_md = EVP_sha1(); EVP_MD_CTX md; - u_char digest[EVP_MAX_MD_SIZE], *sigblob; - u_int len, dlen; - int rlen, ret; - Buffer b; + u_char digest[EVP_MAX_MD_SIZE], *sigblob = NULL; + size_t len; + u_int dlen; + int ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL; + char *ktype = NULL; if (key == NULL || key->dsa == NULL || (key->type != KEY_DSA && - key->type != KEY_DSA_CERT && key->type != KEY_DSA_CERT_V00)) { - error("ssh_dss_verify: no DSA key"); - return -1; - } + key->type != KEY_DSA_CERT && key->type != KEY_DSA_CERT_V00)) + return SSH_ERR_INVALID_ARGUMENT; /* fetch signature */ - if (datafellows & SSH_BUG_SIGBLOB) { - sigblob = xmalloc(signaturelen); + if (compat & SSH_BUG_SIGBLOB) { + if ((sigblob = malloc(signaturelen)) == NULL) + return SSH_ERR_ALLOC_FAIL; memcpy(sigblob, signature, signaturelen); len = signaturelen; } else { /* ietf-drafts */ - char *ktype; - buffer_init(&b); - buffer_append(&b, signature, signaturelen); - ktype = buffer_get_cstring(&b, NULL); - if (strcmp("ssh-dss", ktype) != 0) { - error("ssh_dss_verify: cannot handle type %s", ktype); - buffer_free(&b); - xfree(ktype); - return -1; + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((ret = sshbuf_put(b, signature, signaturelen)) != 0) + goto out; + if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || + sshbuf_get_string(b, &sigblob, &len) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; } - xfree(ktype); - sigblob = buffer_get_string(&b, &len); - rlen = buffer_len(&b); - buffer_free(&b); - if (rlen != 0) { - error("ssh_dss_verify: " - "remaining bytes in signature %d", rlen); - xfree(sigblob); - return -1; + if (strcmp("ssh-dss", ktype) != 0) { + ret = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } + if (sshbuf_len(b) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; } } if (len != SIGBLOB_LEN) { - fatal("bad sigbloblen %u != SIGBLOB_LEN", len); + ret = SSH_ERR_INVALID_FORMAT; + goto out; } /* parse signature */ - if ((sig = DSA_SIG_new()) == NULL) - fatal("ssh_dss_verify: DSA_SIG_new failed"); - if ((sig->r = BN_new()) == NULL) - fatal("ssh_dss_verify: BN_new failed"); - if ((sig->s = BN_new()) == NULL) - fatal("ssh_dss_verify: BN_new failed"); + if ((sig = DSA_SIG_new()) == NULL || + (sig->r = BN_new()) == NULL || + (sig->s = BN_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } if ((BN_bin2bn(sigblob, INTBLOB_LEN, sig->r) == NULL) || - (BN_bin2bn(sigblob+ INTBLOB_LEN, INTBLOB_LEN, sig->s) == NULL)) - fatal("ssh_dss_verify: BN_bin2bn failed"); - - /* clean up */ - memset(sigblob, 0, len); - xfree(sigblob); + (BN_bin2bn(sigblob+ INTBLOB_LEN, INTBLOB_LEN, sig->s) == NULL)) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } /* sha1 the data */ - EVP_DigestInit(&md, evp_md); - EVP_DigestUpdate(&md, data, datalen); - EVP_DigestFinal(&md, digest, &dlen); + if (EVP_DigestInit(&md, evp_md) != 1 || + EVP_DigestUpdate(&md, data, datalen) != 1 || + EVP_DigestFinal(&md, digest, &dlen) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } - ret = DSA_do_verify(digest, dlen, sig, key->dsa); - memset(digest, 'd', sizeof(digest)); + switch (DSA_do_verify(digest, dlen, sig, key->dsa)) { + case 1: + ret = 0; + break; + case 0: + ret = SSH_ERR_SIGNATURE_INVALID; + goto out; + default: + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } - DSA_SIG_free(sig); - - debug("ssh_dss_verify: signature %s", - ret == 1 ? "correct" : ret == 0 ? "incorrect" : "error"); + out: + bzero(digest, sizeof(digest)); + bzero(&md, sizeof(md)); + if (sig != NULL) + DSA_SIG_free(sig); + if (b != NULL) + sshbuf_free(b); + if (ktype != NULL) + free(ktype); + if (sigblob != NULL) { + memset(sigblob, 0, len); + free(sigblob); + } return ret; } diff --git a/ssh/ssh-ecdsa.c b/ssh/ssh-ecdsa.c index 42e81c7..2ef3e1c 100644 --- a/ssh/ssh-ecdsa.c +++ b/ssh/ssh-ecdsa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-ecdsa.c,v 1.5 2012/01/08 13:17:11 miod Exp $ */ +/* $OpenBSD: ssh-ecdsa.c,v 1.4 2010/09/10 01:04:10 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -33,131 +33,171 @@ #include -#include "xmalloc.h" -#include "buffer.h" -#include "compat.h" -#include "log.h" +#include "sshbuf.h" +#include "err.h" +#define SSHKEY_INTERNAL #include "key.h" +/* ARGSUSED */ int -ssh_ecdsa_sign(const Key *key, u_char **sigp, u_int *lenp, - const u_char *data, u_int datalen) +ssh_ecdsa_sign(const struct sshkey *key, u_char **sigp, u_int *lenp, + const u_char *data, u_int datalen, u_int compat) { - ECDSA_SIG *sig; + ECDSA_SIG *sig = NULL; const EVP_MD *evp_md; EVP_MD_CTX md; u_char digest[EVP_MAX_MD_SIZE]; u_int len, dlen; - Buffer b, bb; + struct sshbuf *b = NULL, *bb = NULL; + int ret = SSH_ERR_INTERNAL_ERROR; if (key == NULL || key->ecdsa == NULL || - (key->type != KEY_ECDSA && key->type != KEY_ECDSA_CERT)) { - error("%s: no ECDSA key", __func__); - return -1; + (key->type != KEY_ECDSA && key->type != KEY_ECDSA_CERT)) + return SSH_ERR_INVALID_ARGUMENT; + + if ((evp_md = sshkey_ec_nid_to_evpmd(key->ecdsa_nid)) == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (EVP_DigestInit(&md, evp_md) != 1 || + EVP_DigestUpdate(&md, data, datalen) != 1 || + EVP_DigestFinal(&md, digest, &dlen) != 1) { + bzero(&md, sizeof(md)); + return SSH_ERR_LIBCRYPTO_ERROR; } - evp_md = key_ec_nid_to_evpmd(key->ecdsa_nid); - EVP_DigestInit(&md, evp_md); - EVP_DigestUpdate(&md, data, datalen); - EVP_DigestFinal(&md, digest, &dlen); + bzero(&md, sizeof(md)); - sig = ECDSA_do_sign(digest, dlen, key->ecdsa); - memset(digest, 'd', sizeof(digest)); - - if (sig == NULL) { - error("%s: sign failed", __func__); - return -1; + if ((sig = ECDSA_do_sign(digest, dlen, key->ecdsa)) == NULL) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; } - buffer_init(&bb); - buffer_put_bignum2(&bb, sig->r); - buffer_put_bignum2(&bb, sig->s); - ECDSA_SIG_free(sig); - - buffer_init(&b); - buffer_put_cstring(&b, key_ssh_name_plain(key)); - buffer_put_string(&b, buffer_ptr(&bb), buffer_len(&bb)); - buffer_free(&bb); - len = buffer_len(&b); + if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((ret = sshbuf_put_bignum2(bb, sig->r)) != 0 || + (ret = sshbuf_put_bignum2(bb, sig->s)) != 0) + goto out; + if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 || + (ret = sshbuf_putb(b, bb)) != 0) + goto out; + len = sshbuf_len(b); if (lenp != NULL) *lenp = len; if (sigp != NULL) { - *sigp = xmalloc(len); - memcpy(*sigp, buffer_ptr(&b), len); + if ((*sigp = malloc(len)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(*sigp, sshbuf_ptr(b), len); } - buffer_free(&b); - - return 0; + ret = 0; + out: + memset(digest, 'd', sizeof(digest)); + if (b != NULL) + sshbuf_free(b); + if (bb != NULL) + sshbuf_free(bb); + if (sig != NULL) + ECDSA_SIG_free(sig); + return ret; } + +/* ARGSUSED */ int -ssh_ecdsa_verify(const Key *key, const u_char *signature, u_int signaturelen, - const u_char *data, u_int datalen) +ssh_ecdsa_verify(const struct sshkey *key, + const u_char *signature, u_int signaturelen, + const u_char *data, u_int datalen, u_int compat) { - ECDSA_SIG *sig; + ECDSA_SIG *sig = NULL; const EVP_MD *evp_md; EVP_MD_CTX md; - u_char digest[EVP_MAX_MD_SIZE], *sigblob; - u_int len, dlen; - int rlen, ret; - Buffer b, bb; + u_char digest[EVP_MAX_MD_SIZE], *sigblob = NULL; + size_t len; + u_int dlen; + int ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL, *bb = NULL; char *ktype; if (key == NULL || key->ecdsa == NULL || - (key->type != KEY_ECDSA && key->type != KEY_ECDSA_CERT)) { - error("%s: no ECDSA key", __func__); - return -1; - } - evp_md = key_ec_nid_to_evpmd(key->ecdsa_nid); + (key->type != KEY_ECDSA && key->type != KEY_ECDSA_CERT)) + return SSH_ERR_INTERNAL_ERROR; + if ((evp_md = sshkey_ec_nid_to_evpmd(key->ecdsa_nid)) == NULL) + return SSH_ERR_INTERNAL_ERROR; /* fetch signature */ - buffer_init(&b); - buffer_append(&b, signature, signaturelen); - ktype = buffer_get_string(&b, NULL); - if (strcmp(key_ssh_name_plain(key), ktype) != 0) { - error("%s: cannot handle type %s", __func__, ktype); - buffer_free(&b); - xfree(ktype); - return -1; + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((ret = sshbuf_put(b, signature, signaturelen)) != 0) + goto out; + if (sshbuf_get_cstring(b, &ktype, NULL) !=0 || + sshbuf_get_string(b, &sigblob, &len) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; } - xfree(ktype); - sigblob = buffer_get_string(&b, &len); - rlen = buffer_len(&b); - buffer_free(&b); - if (rlen != 0) { - error("%s: remaining bytes in signature %d", __func__, rlen); - xfree(sigblob); - return -1; + if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) { + ret = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } + if (sshbuf_len(b) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; } /* parse signature */ - if ((sig = ECDSA_SIG_new()) == NULL) - fatal("%s: ECDSA_SIG_new failed", __func__); - if ((sig->r = BN_new()) == NULL || - (sig->s = BN_new()) == NULL) - fatal("%s: BN_new failed", __func__); - - buffer_init(&bb); - buffer_append(&bb, sigblob, len); - buffer_get_bignum2(&bb, sig->r); - buffer_get_bignum2(&bb, sig->s); - if (buffer_len(&bb) != 0) - fatal("%s: remaining bytes in inner sigblob", __func__); - buffer_free(&bb); - - /* clean up */ - memset(sigblob, 0, len); - xfree(sigblob); - + if ((sig = ECDSA_SIG_new()) == NULL || + (sig->r = BN_new()) == NULL || + (sig->s = BN_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((bb = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((ret = sshbuf_put(bb, sigblob, len)) != 0) + goto out; + if (sshbuf_get_bignum2(bb, sig->r) != 0 || + sshbuf_get_bignum2(bb, sig->s) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (sshbuf_len(bb) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; + } /* hash the data */ - EVP_DigestInit(&md, evp_md); - EVP_DigestUpdate(&md, data, datalen); - EVP_DigestFinal(&md, digest, &dlen); + if (EVP_DigestInit(&md, evp_md) != 1 || + EVP_DigestUpdate(&md, data, datalen) != 1 || + EVP_DigestFinal(&md, digest, &dlen) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } - ret = ECDSA_do_verify(digest, dlen, sig, key->ecdsa); + switch (ECDSA_do_verify(digest, dlen, sig, key->ecdsa)) { + case 1: + ret = 0; + break; + case 0: + ret = SSH_ERR_SIGNATURE_INVALID; + goto out; + default: + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + + out: memset(digest, 'd', sizeof(digest)); - - ECDSA_SIG_free(sig); - - debug("%s: signature %s", __func__, - ret == 1 ? "correct" : ret == 0 ? "incorrect" : "error"); + if (bb != NULL) + sshbuf_free(bb); + if (b != NULL) + sshbuf_free(b); + if (sig != NULL) + ECDSA_SIG_free(sig); + if (ktype != NULL) + free(ktype); + if (sigblob != NULL) { + memset(sigblob, 0, len); + free(sigblob); + } return ret; } diff --git a/ssh/ssh-keygen.c b/ssh/ssh-keygen.c index ad0c4af..f1fa08e 100644 --- a/ssh/ssh-keygen.c +++ b/ssh/ssh-keygen.c @@ -41,6 +41,7 @@ #include "hostfile.h" #include "dns.h" #include "ssh2.h" +#include "err.h" #ifdef ENABLE_PKCS11 #include "ssh-pkcs11.h" @@ -178,7 +179,7 @@ type_bits_valid(int type, u_int32_t *bitsp) fatal("DSA keys must be 1024 bits"); else if (type != KEY_ECDSA && *bitsp < 768) fatal("Key must at least be 768 bits"); - else if (type == KEY_ECDSA && key_ecdsa_bits_to_nid(*bitsp) == -1) + else if (type == KEY_ECDSA && sshkey_ecdsa_bits_to_nid(*bitsp) == -1) fatal("Invalid ECDSA key length - valid lengths are " "256, 384 or 521 bits"); } @@ -192,7 +193,7 @@ ask_filename(struct passwd *pw, const char *prompt) if (key_type_name == NULL) name = _PATH_SSH_CLIENT_ID_RSA; else { - switch (key_type_from_name(key_type_name)) { + switch (sshkey_type_from_name(key_type_name)) { case KEY_RSA1: name = _PATH_SSH_CLIENT_IDENTITY; break; @@ -226,11 +227,11 @@ ask_filename(struct passwd *pw, const char *prompt) have_identity = 1; } -static Key * +static struct sshkey * load_identity(char *filename) { char *pass; - Key *prv; + struct sshkey *prv; prv = key_load_private(filename, "", NULL); if (prv == NULL) { @@ -252,35 +253,36 @@ load_identity(char *filename) #define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb static void -do_convert_to_ssh2(struct passwd *pw, Key *k) +do_convert_to_ssh2(struct passwd *pw, struct sshkey *k) { u_int len; u_char *blob; char comment[61]; + int r; - if (key_to_blob(k, &blob, &len) <= 0) { - fprintf(stderr, "key_to_blob failed\n"); + if ((r = sshkey_to_blob(k, &blob, &len)) != 0) { + fprintf(stderr, "key_to_blob failed: %s\n", ssh_err(r)); exit(1); } /* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */ snprintf(comment, sizeof(comment), "%u-bit %s, converted by %s@%s from OpenSSH", - key_size(k), key_type(k), + sshkey_size(k), sshkey_type(k), pw->pw_name, hostname); fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); fprintf(stdout, "Comment: \"%s\"\n", comment); dump_base64(stdout, blob, len); fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END); - key_free(k); + sshkey_free(k); xfree(blob); exit(0); } static void -do_convert_to_pkcs8(Key *k) +do_convert_to_pkcs8(struct sshkey *k) { - switch (key_type_plain(k->type)) { + switch (sshkey_type_plain(k->type)) { case KEY_RSA: if (!PEM_write_RSA_PUBKEY(stdout, k->rsa)) fatal("PEM_write_RSA_PUBKEY failed"); @@ -294,15 +296,15 @@ do_convert_to_pkcs8(Key *k) fatal("PEM_write_EC_PUBKEY failed"); break; default: - fatal("%s: unsupported key type %s", __func__, key_type(k)); + fatal("%s: unsupported key type %s", __func__, sshkey_type(k)); } exit(0); } static void -do_convert_to_pem(Key *k) +do_convert_to_pem(struct sshkey *k) { - switch (key_type_plain(k->type)) { + switch (sshkey_type_plain(k->type)) { case KEY_RSA: if (!PEM_write_RSAPublicKey(stdout, k->rsa)) fatal("PEM_write_RSAPublicKey failed"); @@ -315,7 +317,7 @@ do_convert_to_pem(Key *k) #endif /* XXX ECDSA? */ default: - fatal("%s: unsupported key type %s", __func__, key_type(k)); + fatal("%s: unsupported key type %s", __func__, sshkey_type(k)); } exit(0); } @@ -323,7 +325,7 @@ do_convert_to_pem(Key *k) static void do_convert_to(struct passwd *pw) { - Key *k; + struct sshkey *k; struct stat st; if (!have_identity) @@ -371,13 +373,13 @@ buffer_get_bignum_bits(Buffer *b, BIGNUM *value) buffer_consume(b, bytes); } -static Key * +static struct sshkey * do_convert_private_ssh2_from_blob(u_char *blob, u_int blen) { Buffer b; - Key *key = NULL; + struct sshkey *key = NULL; char *type, *cipher; - u_char *sig, data[] = "abcde12345"; + u_char *sig = NULL, data[] = "abcde12345"; int magic, rlen, ktype, i1, i2, i3, i4; u_int slen; u_long e; @@ -416,7 +418,8 @@ do_convert_private_ssh2_from_blob(u_char *blob, u_int blen) xfree(type); return NULL; } - key = key_new_private(ktype); + if ((key = sshkey_new_private(ktype)) == NULL) + fatal("key_new_private failed"); xfree(type); switch (key->type) { @@ -440,7 +443,7 @@ do_convert_private_ssh2_from_blob(u_char *blob, u_int blen) } if (!BN_set_word(key->rsa->e, e)) { buffer_free(&b); - key_free(key); + sshkey_free(key); return NULL; } buffer_get_bignum_bits(&b, key->rsa->d); @@ -458,8 +461,13 @@ do_convert_private_ssh2_from_blob(u_char *blob, u_int blen) buffer_free(&b); /* try the key */ - key_sign(key, &sig, &slen, data, sizeof(data)); - key_verify(key, sig, slen, data, sizeof(data)); + if (sshkey_sign(key, &sig, &slen, data, sizeof(data), 0) != 0 || + sshkey_verify(key, sig, slen, data, sizeof(data), 0) != 0) { + sshkey_free(key); + if (sig != NULL) + xfree(sig); + return NULL; + } xfree(sig); return key; } @@ -495,14 +503,13 @@ get_line(FILE *fp, char *line, size_t len) } static void -do_convert_from_ssh2(struct passwd *pw, Key **k, int *private) +do_convert_from_ssh2(struct passwd *pw, struct sshkey **k, int *private) { - int blen; + int r, blen, escaped = 0; u_int len; char line[1024]; u_char blob[8096]; char encoded[8096]; - int escaped = 0; FILE *fp; if ((fp = fopen(identity_file, "r")) == NULL) @@ -539,18 +546,17 @@ do_convert_from_ssh2(struct passwd *pw, Key **k, int *private) fprintf(stderr, "uudecode failed.\n"); exit(1); } - *k = *private ? - do_convert_private_ssh2_from_blob(blob, blen) : - key_from_blob(blob, blen); - if (*k == NULL) { - fprintf(stderr, "decode blob failed.\n"); + if (*private) + *k = do_convert_private_ssh2_from_blob(blob, blen); + else if ((r = sshkey_from_blob(blob, blen, k)) != 0) { + fprintf(stderr, "decode blob failed: %s\n", ssh_err(r)); exit(1); } fclose(fp); } static void -do_convert_from_pkcs8(Key **k, int *private) +do_convert_from_pkcs8(struct sshkey **k, int *private) { EVP_PKEY *pubkey; FILE *fp; @@ -564,20 +570,23 @@ do_convert_from_pkcs8(Key **k, int *private) fclose(fp); switch (EVP_PKEY_type(pubkey->type)) { case EVP_PKEY_RSA: - *k = key_new(KEY_UNSPEC); + if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); (*k)->type = KEY_RSA; (*k)->rsa = EVP_PKEY_get1_RSA(pubkey); break; case EVP_PKEY_DSA: - *k = key_new(KEY_UNSPEC); + if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); (*k)->type = KEY_DSA; (*k)->dsa = EVP_PKEY_get1_DSA(pubkey); break; case EVP_PKEY_EC: - *k = key_new(KEY_UNSPEC); + if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); (*k)->type = KEY_ECDSA; (*k)->ecdsa = EVP_PKEY_get1_EC_KEY(pubkey); - (*k)->ecdsa_nid = key_ecdsa_key_to_nid((*k)->ecdsa); + (*k)->ecdsa_nid = sshkey_ecdsa_key_to_nid((*k)->ecdsa); break; default: fatal("%s: unsupported pubkey type %d", __func__, @@ -588,7 +597,7 @@ do_convert_from_pkcs8(Key **k, int *private) } static void -do_convert_from_pem(Key **k, int *private) +do_convert_from_pem(struct sshkey **k, int *private) { FILE *fp; RSA *rsa; @@ -599,7 +608,8 @@ do_convert_from_pem(Key **k, int *private) if ((fp = fopen(identity_file, "r")) == NULL) fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); if ((rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL)) != NULL) { - *k = key_new(KEY_UNSPEC); + if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); (*k)->type = KEY_RSA; (*k)->rsa = rsa; fclose(fp); @@ -608,7 +618,8 @@ do_convert_from_pem(Key **k, int *private) #if notyet /* OpenSSH 0.9.8 lacks this function */ rewind(fp); if ((dsa = PEM_read_DSAPublicKey(fp, NULL, NULL, NULL)) != NULL) { - *k = key_new(KEY_UNSPEC); + if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); (*k)->type = KEY_DSA; (*k)->dsa = dsa; fclose(fp); @@ -622,8 +633,8 @@ do_convert_from_pem(Key **k, int *private) static void do_convert_from(struct passwd *pw) { - Key *k = NULL; - int private = 0, ok = 0; + struct sshkey *k = NULL; + int r, private = 0, ok = 0; struct stat st; if (!have_identity) @@ -646,7 +657,8 @@ do_convert_from(struct passwd *pw) } if (!private) - ok = key_write(k, stdout); + if ((r = sshkey_write(k, stdout)) == 0) + ok = 1; if (ok) fprintf(stdout, "\n"); else { @@ -665,7 +677,7 @@ do_convert_from(struct passwd *pw) break; default: fatal("%s: unsupported key type %s", __func__, - key_type(k)); + sshkey_type(k)); } } @@ -673,15 +685,16 @@ do_convert_from(struct passwd *pw) fprintf(stderr, "key write failed\n"); exit(1); } - key_free(k); + sshkey_free(k); exit(0); } static void do_print_public(struct passwd *pw) { - Key *prv; + struct sshkey *prv; struct stat st; + int r; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); @@ -694,9 +707,9 @@ do_print_public(struct passwd *pw) fprintf(stderr, "load failed\n"); exit(1); } - if (!key_write(prv, stdout)) - fprintf(stderr, "key_write failed"); - key_free(prv); + if ((r = sshkey_write(prv, stdout)) != 0) + fprintf(stderr, "key_write failed: %s", ssh_err(r)); + sshkey_free(prv); fprintf(stdout, "\n"); exit(0); } @@ -705,7 +718,7 @@ static void do_download(struct passwd *pw) { #ifdef ENABLE_PKCS11 - Key **keys = NULL; + struct sshkey **keys = NULL; int i, nkeys; pkcs11_init(0); @@ -713,8 +726,8 @@ do_download(struct passwd *pw) if (nkeys <= 0) fatal("cannot read public key from pkcs11"); for (i = 0; i < nkeys; i++) { - key_write(keys[i], stdout); - key_free(keys[i]); + (void) sshkey_write(keys[i], stdout); /* XXX check */ + sshkey_free(keys[i]); fprintf(stdout, "\n"); } xfree(keys); @@ -729,11 +742,11 @@ static void do_fingerprint(struct passwd *pw) { FILE *f; - Key *public; + struct sshkey *public; char *comment = NULL, *cp, *ep, line[16*1024], *fp, *ra; - int i, skip = 0, num = 0, invalid = 1; - enum fp_rep rep; - enum fp_type fptype; + int r, i, skip = 0, num = 0, invalid = 1; + enum sshkey_fp_rep rep; + enum sshkey_fp_type fptype; struct stat st; fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5; @@ -747,13 +760,13 @@ do_fingerprint(struct passwd *pw) } public = key_load_public(identity_file, &comment); if (public != NULL) { - fp = key_fingerprint(public, fptype, rep); - ra = key_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART); - printf("%u %s %s (%s)\n", key_size(public), fp, comment, - key_type(public)); + fp = sshkey_fingerprint(public, fptype, rep); + ra = sshkey_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART); + printf("%u %s %s (%s)\n", sshkey_size(public), fp, comment, + sshkey_type(public)); if (log_level >= SYSLOG_LEVEL_VERBOSE) printf("%s\n", ra); - key_free(public); + sshkey_free(public); xfree(comment); xfree(ra); xfree(fp); @@ -802,26 +815,28 @@ do_fingerprint(struct passwd *pw) *cp++ = '\0'; } ep = cp; - public = key_new(KEY_RSA1); - if (key_read(public, &cp) != 1) { + if ((public = sshkey_new(KEY_RSA1)) == NULL) + fatal("sshkey_new failed"); + if ((r = sshkey_read(public, &cp)) != 0) { cp = ep; - key_free(public); - public = key_new(KEY_UNSPEC); - if (key_read(public, &cp) != 1) { - key_free(public); + sshkey_free(public); + if ((public = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); + if ((r = sshkey_read(public, &cp)) != 0) { + sshkey_free(public); continue; } } comment = *cp ? cp : comment; - fp = key_fingerprint(public, fptype, rep); - ra = key_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART); - printf("%u %s %s (%s)\n", key_size(public), fp, - comment ? comment : "no comment", key_type(public)); + fp = sshkey_fingerprint(public, fptype, rep); + ra = sshkey_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART); + printf("%u %s %s (%s)\n", sshkey_size(public), fp, + comment ? comment : "no comment", sshkey_type(public)); if (log_level >= SYSLOG_LEVEL_VERBOSE) printf("%s\n", ra); xfree(ra); xfree(fp); - key_free(public); + sshkey_free(public); invalid = 0; } fclose(f); @@ -850,9 +865,9 @@ do_gen_all_hostkeys(struct passwd *pw) int first = 0; struct stat st; - Key *private, *public; + struct sshkey *private, *public; char comment[1024]; - int i, type, fd; + int i, type, fd, r; FILE *f; for (i = 0; key_types[i].key_type; i++) { @@ -872,53 +887,54 @@ do_gen_all_hostkeys(struct passwd *pw) printf("%s ", key_types[i].key_type_display); fflush(stdout); arc4random_stir(); - type = key_type_from_name(key_types[i].key_type); + type = sshkey_type_from_name(key_types[i].key_type); strlcpy(identity_file, key_types[i].path, sizeof(identity_file)); bits = 0; type_bits_valid(type, &bits); - private = key_generate(type, bits); - if (private == NULL) { - fprintf(stderr, "key_generate failed\n"); + if ((r = sshkey_generate(type, bits, &private)) != 0) { + fprintf(stderr, "key_generate failed: %s\n", + ssh_err(r)); first = 0; continue; } - public = key_from_private(private); + if ((r = sshkey_from_private(private, &public)) != 0) + fatal("sshkey_from_private failed: %s", ssh_err(r)); snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); if (!key_save_private(private, identity_file, "", comment)) { printf("Saving the key failed: %s.\n", identity_file); - key_free(private); - key_free(public); + sshkey_free(private); + sshkey_free(public); first = 0; continue; } - key_free(private); + sshkey_free(private); arc4random_stir(); strlcat(identity_file, ".pub", sizeof(identity_file)); fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd == -1) { printf("Could not save your public key in %s\n", identity_file); - key_free(public); + sshkey_free(public); first = 0; continue; } f = fdopen(fd, "w"); if (f == NULL) { printf("fdopen %s failed\n", identity_file); - key_free(public); + sshkey_free(public); first = 0; continue; } - if (!key_write(public, f)) { + if (!sshkey_write(public, f)) { fprintf(stderr, "write key failed\n"); - key_free(public); + sshkey_free(public); first = 0; continue; } fprintf(f, " %s\n", comment); fclose(f); - key_free(public); + sshkey_free(public); } if (first != 0) @@ -926,29 +942,31 @@ do_gen_all_hostkeys(struct passwd *pw) } static void -printhost(FILE *f, const char *name, Key *public, int ca, int hash) +printhost(FILE *f, const char *name, struct sshkey *public, int ca, int hash) { if (print_fingerprint) { - enum fp_rep rep; - enum fp_type fptype; + enum sshkey_fp_rep rep; + enum sshkey_fp_type fptype; char *fp, *ra; fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5; rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX; - fp = key_fingerprint(public, fptype, rep); - ra = key_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART); - printf("%u %s %s (%s)\n", key_size(public), fp, name, - key_type(public)); + fp = sshkey_fingerprint(public, fptype, rep); + ra = sshkey_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART); + printf("%u %s %s (%s)\n", sshkey_size(public), fp, name, + sshkey_type(public)); if (log_level >= SYSLOG_LEVEL_VERBOSE) printf("%s\n", ra); xfree(ra); xfree(fp); } else { + int r; + if (hash && (name = host_hash(name, NULL, 0)) == NULL) fatal("hash_host failed"); fprintf(f, "%s%s%s ", ca ? CA_MARKER : "", ca ? " " : "", name); - if (!key_write(public, f)) - fatal("key_write failed"); + if ((r = sshkey_write(public, f)) != 0) + fatal("key_write failed: %s", ssh_err(r)); fprintf(f, "\n"); } } @@ -957,11 +975,11 @@ static void do_known_hosts(struct passwd *pw, const char *name) { FILE *in, *out = stdout; - Key *pub; + struct sshkey *pub; char *cp, *cp2, *kp, *kp2; char line[16*1024], tmp[MAXPATHLEN], old[MAXPATHLEN]; int c, skip = 0, inplace = 0, num = 0, invalid = 0, has_unhashed = 0; - int ca; + int ca, r; if (!have_identity) { cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); @@ -1039,15 +1057,17 @@ do_known_hosts(struct passwd *pw, const char *name) *kp++ = '\0'; kp2 = kp; - pub = key_new(KEY_RSA1); - if (key_read(pub, &kp) != 1) { + if ((pub = sshkey_new(KEY_RSA1)) == NULL) + fatal("sshkey_new failed"); + if ((r = sshkey_read(pub, &kp)) != 0) { kp = kp2; - key_free(pub); - pub = key_new(KEY_UNSPEC); - if (key_read(pub, &kp) != 1) { + sshkey_free(pub); + if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); + if ((r = sshkey_read(pub, &kp)) != 0) { error("line %d invalid key: %.40s...", num, line); - key_free(pub); + sshkey_free(pub); invalid = 1; continue; } @@ -1066,7 +1086,7 @@ do_known_hosts(struct passwd *pw, const char *name) if (find_host && c) { printf("# Host %s found: " "line %d type %s%s\n", name, - num, key_type(pub), + num, sshkey_type(pub), ca ? " (CA key)" : ""); printhost(out, cp, pub, ca, 0); } @@ -1081,7 +1101,7 @@ do_known_hosts(struct passwd *pw, const char *name) if (find_host && c) { printf("# Host %s found: " "line %d type %s%s\n", name, - num, key_type(pub), + num, sshkey_type(pub), ca ? " (CA key)" : ""); printhost(out, name, pub, ca, hash_hosts && !ca); @@ -1110,7 +1130,7 @@ do_known_hosts(struct passwd *pw, const char *name) has_unhashed = 1; } } - key_free(pub); + sshkey_free(pub); } fclose(in); @@ -1167,7 +1187,7 @@ do_change_passphrase(struct passwd *pw) char *comment; char *old_passphrase, *passphrase1, *passphrase2; struct stat st; - Key *private; + struct sshkey *private; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); @@ -1225,14 +1245,14 @@ do_change_passphrase(struct passwd *pw) printf("Saving the key failed: %s.\n", identity_file); memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); - key_free(private); + sshkey_free(private); xfree(comment); exit(1); } /* Destroy the passphrase and the copy of the key in memory. */ memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); - key_free(private); /* Destroys contents */ + sshkey_free(private); /* Destroys contents */ xfree(comment); printf("Your identification has been saved with the new passphrase.\n"); @@ -1245,7 +1265,7 @@ do_change_passphrase(struct passwd *pw) static int do_print_resource_record(struct passwd *pw, char *fname, char *hname) { - Key *public; + struct sshkey *public; char *comment = NULL; struct stat st; @@ -1260,7 +1280,7 @@ do_print_resource_record(struct passwd *pw, char *fname, char *hname) public = key_load_public(fname, &comment); if (public != NULL) { export_dns_rr(hname, public, stdout, print_generic); - key_free(public); + sshkey_free(public); xfree(comment); return 1; } @@ -1278,11 +1298,11 @@ static void do_change_comment(struct passwd *pw) { char new_comment[1024], *comment, *passphrase; - Key *private; - Key *public; + struct sshkey *private; + struct sshkey *public; struct stat st; FILE *f; - int fd; + int r, fd; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); @@ -1312,7 +1332,7 @@ do_change_comment(struct passwd *pw) } if (private->type != KEY_RSA1) { fprintf(stderr, "Comments are only supported for RSA1 keys.\n"); - key_free(private); + sshkey_free(private); exit(1); } printf("Key now has comment '%s'\n", comment); @@ -1324,25 +1344,27 @@ do_change_comment(struct passwd *pw) fflush(stdout); if (!fgets(new_comment, sizeof(new_comment), stdin)) { memset(passphrase, 0, strlen(passphrase)); - key_free(private); + sshkey_free(private); exit(1); } new_comment[strcspn(new_comment, "\n")] = '\0'; } /* Save the file using the new passphrase. */ - if (!key_save_private(private, identity_file, passphrase, new_comment)) { + if (!key_save_private(private, identity_file, passphrase, + new_comment)) { printf("Saving the key failed: %s.\n", identity_file); memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); - key_free(private); + sshkey_free(private); xfree(comment); exit(1); } memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); - public = key_from_private(private); - key_free(private); + if ((r = sshkey_from_private(private, &public)) != 0) + fatal("key_from_private failed: %s", ssh_err(r)); + sshkey_free(private); strlcat(identity_file, ".pub", sizeof(identity_file)); fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); @@ -1355,9 +1377,9 @@ do_change_comment(struct passwd *pw) printf("fdopen %s failed\n", identity_file); exit(1); } - if (!key_write(public, f)) - fprintf(stderr, "write key failed\n"); - key_free(public); + if ((r = sshkey_write(public, f)) != 0) + fprintf(stderr, "write key failed: %s\n", ssh_err(r)); + sshkey_free(public); fprintf(f, " %s\n", new_comment); fclose(f); @@ -1457,11 +1479,11 @@ prepare_options_buf(Buffer *c, int which) add_string_option(c, "source-address", certflags_src_addr); } -static Key * +static struct sshkey * load_pkcs11_key(char *path) { #ifdef ENABLE_PKCS11 - Key **keys = NULL, *public, *private = NULL; + struct sshkey **keys = NULL, *public, *private = NULL; int i, nkeys; if ((public = key_load_public(path, NULL)) == NULL) @@ -1472,14 +1494,14 @@ load_pkcs11_key(char *path) if (nkeys <= 0) fatal("cannot read public key from pkcs11"); for (i = 0; i < nkeys; i++) { - if (key_equal_public(public, keys[i])) { + if (sshkey_equal_public(public, keys[i])) { private = keys[i]; continue; } - key_free(keys[i]); + sshkey_free(keys[i]); } xfree(keys); - key_free(public); + sshkey_free(public); return private; #else fatal("no pkcs11 support"); @@ -1489,15 +1511,15 @@ load_pkcs11_key(char *path) static void do_ca_sign(struct passwd *pw, int argc, char **argv) { - int i, fd; + int r, i, fd; u_int n; - Key *ca, *public; + struct sshkey *ca, *public; char *otmp, *tmp, *cp, *out, *comment, **plist = NULL; FILE *f; int v00 = 0; /* legacy keys */ if (key_type_name != NULL) { - switch (key_type_from_name(key_type_name)) { + switch (sshkey_type_from_name(key_type_name)) { case KEY_RSA_CERT_V00: case KEY_DSA_CERT_V00: v00 = 1; @@ -1544,11 +1566,12 @@ do_ca_sign(struct passwd *pw, int argc, char **argv) if (public->type != KEY_RSA && public->type != KEY_DSA && public->type != KEY_ECDSA) fatal("%s: key \"%s\" type %s cannot be certified", - __func__, tmp, key_type(public)); + __func__, tmp, sshkey_type(public)); /* Prepare certificate to sign */ - if (key_to_certified(public, v00) != 0) - fatal("Could not upgrade key %s to certificate", tmp); + if ((r = sshkey_to_certified(public, v00)) != 0) + fatal("Could not upgrade key %s to certificate: %s", + tmp, ssh_err(r)); public->cert->type = cert_key_type; public->cert->serial = (u_int64_t)cert_serial; public->cert->key_id = xstrdup(cert_key_id); @@ -1557,17 +1580,19 @@ do_ca_sign(struct passwd *pw, int argc, char **argv) public->cert->valid_after = cert_valid_from; public->cert->valid_before = cert_valid_to; if (v00) { - prepare_options_buf(&public->cert->critical, + prepare_options_buf(public->cert->critical, OPTIONS_CRITICAL|OPTIONS_EXTENSIONS); } else { - prepare_options_buf(&public->cert->critical, + prepare_options_buf(public->cert->critical, OPTIONS_CRITICAL); - prepare_options_buf(&public->cert->extensions, + prepare_options_buf(public->cert->extensions, OPTIONS_EXTENSIONS); } - public->cert->signature_key = key_from_private(ca); + if ((r = sshkey_from_private(ca, + &public->cert->signature_key)) != 0) + fatal("key_from_private (ca key): %s", ssh_err(r)); - if (key_certify(public, ca) != 0) + if (sshkey_certify(public, ca) != 0) fatal("Couldn't not certify key %s", tmp); if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0) @@ -1580,14 +1605,15 @@ do_ca_sign(struct passwd *pw, int argc, char **argv) strerror(errno)); if ((f = fdopen(fd, "w")) == NULL) fatal("%s: fdopen: %s", __func__, strerror(errno)); - if (!key_write(public, f)) - fatal("Could not write certified key to %s", out); + if ((r = sshkey_write(public, f)) != 0) + fatal("Could not write certified key to %s: %s", + out, ssh_err(r)); fprintf(f, " %s\n", comment); fclose(f); if (!quiet) { logit("Signed %s key %s: id \"%s\" serial %llu%s%s " - "valid %s", key_cert_type(public), + "valid %s", sshkey_cert_type(public), out, public->cert->key_id, (unsigned long long)public->cert->serial, cert_principals != NULL ? " for " : "", @@ -1595,7 +1621,7 @@ do_ca_sign(struct passwd *pw, int argc, char **argv) fmt_validity(cert_valid_from, cert_valid_to)); } - key_free(public); + sshkey_free(public); xfree(out); } pkcs11_terminate(); @@ -1747,6 +1773,7 @@ static void show_options(const Buffer *optbuf, int v00, int in_critical) { u_char *name, *data; + const u_char *odata; u_int dlen; Buffer options, option; @@ -1756,8 +1783,8 @@ show_options(const Buffer *optbuf, int v00, int in_critical) buffer_init(&option); while (buffer_len(&options) != 0) { name = buffer_get_string(&options, NULL); - data = buffer_get_string_ptr(&options, &dlen); - buffer_append(&option, data, dlen); + odata = buffer_get_string_ptr(&options, &dlen); + buffer_append(&option, odata, dlen); printf(" %s", name); if ((v00 || !in_critical) && (strcmp(name, "permit-X11-forwarding") == 0 || @@ -1788,7 +1815,7 @@ show_options(const Buffer *optbuf, int v00, int in_critical) static void do_show_cert(struct passwd *pw) { - Key *key; + struct sshkey *key; struct stat st; char *key_fp, *ca_fp; u_int i, v00; @@ -1799,20 +1826,20 @@ do_show_cert(struct passwd *pw) fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); if ((key = key_load_public(identity_file, NULL)) == NULL) fatal("%s is not a public key", identity_file); - if (!key_is_cert(key)) + if (!sshkey_is_cert(key)) fatal("%s is not a certificate", identity_file); v00 = key->type == KEY_RSA_CERT_V00 || key->type == KEY_DSA_CERT_V00; - key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); - ca_fp = key_fingerprint(key->cert->signature_key, + key_fp = sshkey_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + ca_fp = sshkey_fingerprint(key->cert->signature_key, SSH_FP_MD5, SSH_FP_HEX); printf("%s:\n", identity_file); - printf(" Type: %s %s certificate\n", key_ssh_name(key), - key_cert_type(key)); - printf(" Public key: %s %s\n", key_type(key), key_fp); + printf(" Type: %s %s certificate\n", sshkey_ssh_name(key), + sshkey_cert_type(key)); + printf(" Public key: %s %s\n", sshkey_type(key), key_fp); printf(" Signing CA: %s %s\n", - key_type(key->cert->signature_key), ca_fp); + sshkey_type(key->cert->signature_key), ca_fp); printf(" Key ID: \"%s\"\n", key->cert->key_id); if (!v00) { printf(" Serial: %llu\n", @@ -1830,19 +1857,19 @@ do_show_cert(struct passwd *pw) printf("\n"); } printf(" Critical Options: "); - if (buffer_len(&key->cert->critical) == 0) + if (buffer_len(key->cert->critical) == 0) printf("(none)\n"); else { printf("\n"); - show_options(&key->cert->critical, v00, 1); + show_options(key->cert->critical, v00, 1); } if (!v00) { printf(" Extensions: "); - if (buffer_len(&key->cert->extensions) == 0) + if (buffer_len(key->cert->extensions) == 0) printf("(none)\n"); else { printf("\n"); - show_options(&key->cert->extensions, v00, 0); + show_options(key->cert->extensions, v00, 0); } } exit(0); @@ -1906,10 +1933,10 @@ main(int argc, char **argv) char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2; char *checkpoint = NULL; char out_file[MAXPATHLEN], *rr_hostname = NULL; - Key *private, *public; + struct sshkey *private, *public; struct passwd *pw; struct stat st; - int opt, type, fd; + int r, opt, type, fd; u_int32_t memory = 0, generator_wanted = 0, trials = 100; int do_gen_candidates = 0, do_screen_candidates = 0; int gen_all_hostkeys = 0; @@ -2231,17 +2258,20 @@ main(int argc, char **argv) if (key_type_name == NULL) key_type_name = "rsa"; - type = key_type_from_name(key_type_name); + type = sshkey_type_from_name(key_type_name); type_bits_valid(type, &bits); if (!quiet) - printf("Generating public/private %s key pair.\n", key_type_name); - private = key_generate(type, bits); - if (private == NULL) { + printf("Generating public/private %s key pair.\n", + key_type_name); + if ((r = sshkey_generate(type, bits, &private)) != 0) { fprintf(stderr, "key_generate failed\n"); exit(1); } - public = key_from_private(private); + if ((r = sshkey_from_private(private, &public)) != 0) { + fprintf(stderr, "key_from_private failed: %s\n", ssh_err(r)); + exit(1); + } if (!have_identity) ask_filename(pw, "Enter file in which to save the key"); @@ -2320,7 +2350,7 @@ passphrase_again: xfree(passphrase1); /* Clear the private key and the random number generator. */ - key_free(private); + sshkey_free(private); arc4random_stir(); if (!quiet) @@ -2337,14 +2367,14 @@ passphrase_again: printf("fdopen %s failed\n", identity_file); exit(1); } - if (!key_write(public, f)) - fprintf(stderr, "write key failed\n"); + if ((r = sshkey_write(public, f)) != 0) + fprintf(stderr, "write key failed: %s\n", ssh_err(r)); fprintf(f, " %s\n", comment); fclose(f); if (!quiet) { - char *fp = key_fingerprint(public, SSH_FP_MD5, SSH_FP_HEX); - char *ra = key_fingerprint(public, SSH_FP_MD5, + char *fp = sshkey_fingerprint(public, SSH_FP_MD5, SSH_FP_HEX); + char *ra = sshkey_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART); printf("Your public key has been saved in %s.\n", identity_file); @@ -2356,6 +2386,6 @@ passphrase_again: xfree(fp); } - key_free(public); + sshkey_free(public); exit(0); } diff --git a/ssh/ssh-keyscan.c b/ssh/ssh-keyscan.c index 9f1a872..5cf7c61 100644 --- a/ssh/ssh-keyscan.c +++ b/ssh/ssh-keyscan.c @@ -40,6 +40,7 @@ #include "atomicio.h" #include "misc.h" #include "hostfile.h" +#include "err.h" /* Flag indicating whether IPv4 or IPv6. This can be set on the command line. Default value is AF_UNSPEC means both IPv4 and IPv6. */ @@ -70,7 +71,7 @@ size_t read_wait_nfdset; int ncon; int nonfatal_fatal = 0; jmp_buf kexjmp; -Key *kexjmp_key; +struct sshkey *kexjmp_key; /* * Keep a connection structure for each file descriptor. The state @@ -166,15 +167,18 @@ strnnsep(char **stringp, char *delim) return (tok); } -static Key * +static struct sshkey * keygrab_ssh1(con *c) { - static Key *rsa; + static struct sshkey *rsa; static Buffer msg; if (rsa == NULL) { + if ((rsa = sshkey_new(KEY_RSA1)) == NULL) { + error("%s: sshkey_new failed", __func__); + return NULL; + } buffer_init(&msg); - rsa = key_new(KEY_RSA1); } buffer_append(&msg, c->c_data, c->c_plen); buffer_consume(&msg, 8 - (c->c_plen & 7)); /* padding */ @@ -201,7 +205,7 @@ keygrab_ssh1(con *c) } static int -hostjump(Key *hostkey, struct ssh *ssh) +hostjump(struct sshkey *hostkey, struct ssh *ssh) { kexjmp_key = hostkey; longjmp(kexjmp, 1); @@ -223,7 +227,7 @@ ssh2_capable(int remote_major, int remote_minor) return 0; } -static Key * +static struct sshkey * keygrab_ssh2(con *c) { int j; @@ -257,7 +261,7 @@ keygrab_ssh2(con *c) } static void -keyprint(con *c, Key *key) +keyprint(con *c, struct sshkey *key) { char *host = c->c_output_name ? c->c_output_name : c->c_name; @@ -267,7 +271,7 @@ keyprint(con *c, Key *key) fatal("host_hash failed"); fprintf(stdout, "%s ", host); - key_write(key, stdout); + sshkey_write(key, stdout); fputs("\n", stdout); } @@ -651,7 +655,7 @@ main(int argc, char **argv) get_keytypes = 0; tname = strtok(optarg, ","); while (tname) { - int type = key_type_from_name(tname); + int type = sshkey_type_from_name(tname); switch (type) { case KEY_RSA1: get_keytypes |= KT_RSA1; diff --git a/ssh/ssh-keysign.c b/ssh/ssh-keysign.c index 2443f92..a3b1804 100644 --- a/ssh/ssh-keysign.c +++ b/ssh/ssh-keysign.c @@ -49,20 +49,21 @@ #include "pathnames.h" #include "readconf.h" #include "uidswap.h" +#include "err.h" /* XXX readconf.c needs these */ uid_t original_real_uid; static int -valid_request(struct passwd *pw, char *host, Key **ret, u_char *data, +valid_request(struct passwd *pw, char *host, struct sshkey **ret, u_char *data, u_int datalen) { Buffer b; - Key *key = NULL; + struct sshkey *key = NULL; u_char *pkblob; u_int blen, len; char *pkalg, *p; - int pktype, fail; + int r, pktype, fail; fail = 0; @@ -97,12 +98,13 @@ valid_request(struct passwd *pw, char *host, Key **ret, u_char *data, pkalg = buffer_get_string(&b, NULL); pkblob = buffer_get_string(&b, &blen); - pktype = key_type_from_name(pkalg); + pktype = sshkey_type_from_name(pkalg); if (pktype == KEY_UNSPEC) fail++; - else if ((key = key_from_blob(pkblob, blen)) == NULL) + else if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) { + error("%s: bad key blob: %s", __func__, ssh_err(r)); fail++; - else if (key->type != pktype) + } else if (key->type != pktype) fail++; xfree(pkalg); xfree(pkblob); @@ -133,7 +135,7 @@ valid_request(struct passwd *pw, char *host, Key **ret, u_char *data, debug3("valid_request: fail %d", fail); if (fail && key != NULL) - key_free(key); + sshkey_free(key); else *ret = key; @@ -146,9 +148,9 @@ main(int argc, char **argv) Buffer b; Options options; #define NUM_KEYTYPES 3 - Key *keys[NUM_KEYTYPES], *key = NULL; + struct sshkey *keys[NUM_KEYTYPES], *key = NULL; struct passwd *pw; - int key_fd[NUM_KEYTYPES], i, found, version = 2, fd; + int r, key_fd[NUM_KEYTYPES], i, found, version = 2, fd; u_char *signature, *data; char *host; u_int slen, dlen; @@ -230,7 +232,7 @@ main(int argc, char **argv) found = 0; for (i = 0; i < NUM_KEYTYPES; i++) { if (keys[i] != NULL && - key_equal_public(key, keys[i])) { + sshkey_equal_public(key, keys[i])) { found = 1; break; } @@ -238,8 +240,8 @@ main(int argc, char **argv) if (!found) fatal("no matching hostkey found"); - if (key_sign(keys[i], &signature, &slen, data, dlen) != 0) - fatal("key_sign failed"); + if ((r = sshkey_sign(keys[i], &signature, &slen, data, dlen, 0)) != 0) + fatal("sshkey_sign failed: %s", ssh_err(r)); xfree(data); /* send reply */ diff --git a/ssh/ssh-pkcs11-client.c b/ssh/ssh-pkcs11-client.c index 72ebbf3..c025849 100644 --- a/ssh/ssh-pkcs11-client.c +++ b/ssh/ssh-pkcs11-client.c @@ -33,6 +33,7 @@ #include "authfd.h" #include "atomicio.h" #include "ssh-pkcs11.h" +#include "err.h" /* borrows code from sftp-server and ssh-agent */ @@ -98,18 +99,20 @@ static int pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, int padding) { - Key key; + struct sshkey key; u_char *blob, *signature = NULL; u_int blen, slen = 0; - int ret = -1; + int r, ret = -1; Buffer msg; if (padding != RSA_PKCS1_PADDING) return (-1); key.type = KEY_RSA; key.rsa = rsa; - if (key_to_blob(&key, &blob, &blen) == 0) + if ((r = sshkey_to_blob(&key, &blob, &blen)) != 0) { + error("%s: sshkey_to_blob: %s", __func__, ssh_err(r)); return -1; + } buffer_init(&msg); buffer_put_char(&msg, SSH2_AGENTC_SIGN_REQUEST); buffer_put_string(&msg, blob, blen); @@ -174,10 +177,10 @@ pkcs11_start_helper(void) } int -pkcs11_add_provider(char *name, char *pin, Key ***keysp) +pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp) { - Key *k; - int i, nkeys; + struct sshkey *k; + int i, nkeys, r; u_char *blob; u_int blen; Buffer msg; @@ -194,11 +197,13 @@ pkcs11_add_provider(char *name, char *pin, Key ***keysp) if (recv_msg(&msg) == SSH2_AGENT_IDENTITIES_ANSWER) { nkeys = buffer_get_int(&msg); - *keysp = xcalloc(nkeys, sizeof(Key *)); + *keysp = xcalloc(nkeys, sizeof(struct sshkey *)); for (i = 0; i < nkeys; i++) { blob = buffer_get_string(&msg, &blen); xfree(buffer_get_string(&msg, NULL)); - k = key_from_blob(blob, blen); + /* XXX clean up properly instead of fatal() */ + if ((r = sshkey_from_blob(blob, blen, &k)) != 0) + fatal("%s: bad key: %s", __func__, ssh_err(r)); wrap_key(k->rsa); (*keysp)[i] = k; xfree(blob); diff --git a/ssh/ssh-pkcs11-helper.c b/ssh/ssh-pkcs11-helper.c index 5d61ea9..f2e9ca2 100644 --- a/ssh/ssh-pkcs11-helper.c +++ b/ssh/ssh-pkcs11-helper.c @@ -31,11 +31,12 @@ #include "key.h" #include "authfd.h" #include "ssh-pkcs11.h" +#include "err.h" /* borrows code from sftp-server and ssh-agent */ struct pkcs11_keyinfo { - Key *key; + struct sshkey *key; char *providername; TAILQ_ENTRY(pkcs11_keyinfo) next; }; @@ -53,7 +54,7 @@ Buffer iqueue; Buffer oqueue; static void -add_key(Key *k, char *name) +add_key(struct sshkey *k, char *name) { struct pkcs11_keyinfo *ki; @@ -73,21 +74,21 @@ del_keys_by_name(char *name) if (!strcmp(ki->providername, name)) { TAILQ_REMOVE(&pkcs11_keylist, ki, next); xfree(ki->providername); - key_free(ki->key); + sshkey_free(ki->key); free(ki); } } } /* lookup matching 'private' key */ -static Key * -lookup_key(Key *k) +static struct sshkey * +lookup_key(struct sshkey *k) { struct pkcs11_keyinfo *ki; TAILQ_FOREACH(ki, &pkcs11_keylist, next) { debug("check %p %s", ki, ki->providername); - if (key_equal(k, ki->key)) + if (sshkey_equal(k, ki->key)) return (ki->key); } return (NULL); @@ -107,8 +108,8 @@ static void process_add(void) { char *name, *pin; - Key **keys; - int i, nkeys; + struct sshkey **keys; + int r, i, nkeys; u_char *blob; u_int blen; Buffer msg; @@ -120,7 +121,9 @@ process_add(void) buffer_put_char(&msg, SSH2_AGENT_IDENTITIES_ANSWER); buffer_put_int(&msg, nkeys); for (i = 0; i < nkeys; i++) { - key_to_blob(keys[i], &blob, &blen); + if ((r = sshkey_to_blob(keys[i], &blob, &blen)) != 0) + fatal("%s: sshkey_to_blob: %s", + __func__, ssh_err(r)); buffer_put_string(&msg, blob, blen); buffer_put_cstring(&msg, name); xfree(blob); @@ -161,15 +164,17 @@ process_sign(void) { u_char *blob, *data, *signature = NULL; u_int blen, dlen, slen = 0; - int ok = -1, flags, ret; - Key *key, *found; + int r, ok = -1, flags, ret; + struct sshkey *key, *found; Buffer msg; blob = get_string(&blen); data = get_string(&dlen); flags = get_int(); /* XXX ignore */ - if ((key = key_from_blob(blob, blen)) != NULL) { + if ((r = sshkey_from_blob(blob, blen, &key)) != 0) + error("%s: sshkey_from_blob: %s", __func__, ssh_err(r)); + else { if ((found = lookup_key(key)) != NULL) { slen = RSA_size(key->rsa); signature = xmalloc(slen); @@ -179,7 +184,7 @@ process_sign(void) ok = 0; } } - key_free(key); + sshkey_free(key); } buffer_init(&msg); if (ok == 0) { diff --git a/ssh/ssh-pkcs11.c b/ssh/ssh-pkcs11.c index dbcefff..2df99bc 100644 --- a/ssh/ssh-pkcs11.c +++ b/ssh/ssh-pkcs11.c @@ -375,10 +375,10 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin) * keysp points to an (possibly empty) array with *nkeys keys. */ static int -pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, Key ***keysp, - int *nkeys) +pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, + struct sshkey ***keysp, int *nkeys) { - Key *key; + struct sshkey *key; RSA *rsa; int i; CK_RV rv; @@ -439,14 +439,15 @@ pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, Key ***keysp, rsa->e = BN_bin2bn(attribs[2].pValue, attribs[2].ulValueLen, NULL); if (rsa->n && rsa->e && - pkcs11_rsa_wrap(p, slotidx, &attribs[0], rsa) == 0) { - key = key_new(KEY_UNSPEC); + pkcs11_rsa_wrap(p, slotidx, &attribs[0], + rsa) == 0 && + (key = sshkey_new(KEY_UNSPEC)) != NULL) { key->rsa = rsa; key->type = KEY_RSA; - key->flags |= KEY_FLAG_EXT; + key->flags |= SSHKEY_FLAG_EXT; /* expand key array and add key */ *keysp = xrealloc(*keysp, *nkeys + 1, - sizeof(Key *)); + sizeof(struct sshkey *)); (*keysp)[*nkeys] = key; *nkeys = *nkeys + 1; debug("have %d keys", *nkeys); @@ -465,7 +466,7 @@ pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, Key ***keysp, #ifdef HAVE_DLOPEN /* register a new provider, fails if provider already exists */ int -pkcs11_add_provider(char *provider_id, char *pin, Key ***keyp) +pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) { int nkeys, need_finalize = 0; struct pkcs11_provider *p = NULL; @@ -577,7 +578,7 @@ fail: } #else int -pkcs11_add_provider(char *provider_id, char *pin, Key ***keyp) +pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) { error("dlopen() not supported"); return (-1); diff --git a/ssh/ssh-pkcs11.h b/ssh/ssh-pkcs11.h index 59f456a..1288651 100644 --- a/ssh/ssh-pkcs11.h +++ b/ssh/ssh-pkcs11.h @@ -16,5 +16,5 @@ */ int pkcs11_init(int); void pkcs11_terminate(void); -int pkcs11_add_provider(char *, char *, Key ***); +int pkcs11_add_provider(char *, char *, struct sshkey ***); int pkcs11_del_provider(char *); diff --git a/ssh/ssh-rsa.c b/ssh/ssh-rsa.c index 5766582..1c30ee9 100644 --- a/ssh/ssh-rsa.c +++ b/ssh/ssh-rsa.c @@ -22,154 +22,170 @@ #include -#include "xmalloc.h" -#include "log.h" -#include "buffer.h" -#include "key.h" +#include "sshbuf.h" #include "compat.h" -#include "misc.h" -#include "ssh.h" +#include "err.h" +#define SSHKEY_INTERNAL +#include "key.h" static int openssh_RSA_verify(int, u_char *, u_int, u_char *, u_int, RSA *); /* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */ int -ssh_rsa_sign(const Key *key, u_char **sigp, u_int *lenp, - const u_char *data, u_int datalen) +ssh_rsa_sign(const struct sshkey *key, u_char **sigp, u_int *lenp, + const u_char *data, u_int datalen, u_int compat) { const EVP_MD *evp_md; EVP_MD_CTX md; - u_char digest[EVP_MAX_MD_SIZE], *sig; + u_char digest[EVP_MAX_MD_SIZE], *sig = NULL; u_int slen, dlen, len; - int ok, nid; - Buffer b; + int nid, ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL; if (key == NULL || key->rsa == NULL || (key->type != KEY_RSA && - key->type != KEY_RSA_CERT && key->type != KEY_RSA_CERT_V00)) { - error("ssh_rsa_sign: no RSA key"); - return -1; - } - nid = (datafellows & SSH_BUG_RSASIGMD5) ? NID_md5 : NID_sha1; - if ((evp_md = EVP_get_digestbynid(nid)) == NULL) { - error("ssh_rsa_sign: EVP_get_digestbynid %d failed", nid); - return -1; - } - EVP_DigestInit(&md, evp_md); - EVP_DigestUpdate(&md, data, datalen); - EVP_DigestFinal(&md, digest, &dlen); - + key->type != KEY_RSA_CERT && key->type != KEY_RSA_CERT_V00)) + return SSH_ERR_INVALID_ARGUMENT; slen = RSA_size(key->rsa); - sig = xmalloc(slen); + if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM) + return SSH_ERR_INVALID_ARGUMENT; - ok = RSA_sign(nid, digest, dlen, sig, &len, key->rsa); - memset(digest, 'd', sizeof(digest)); + nid = (compat & SSH_BUG_RSASIGMD5) ? NID_md5 : NID_sha1; + if ((evp_md = EVP_get_digestbynid(nid)) == NULL) + return SSH_ERR_LIBCRYPTO_ERROR; + if (EVP_DigestInit(&md, evp_md) != 1 || + EVP_DigestUpdate(&md, data, datalen) != 1 || + EVP_DigestFinal(&md, digest, &dlen) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if ((sig = malloc(slen)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } - if (ok != 1) { - int ecode = ERR_get_error(); - - error("ssh_rsa_sign: RSA_sign failed: %s", - ERR_error_string(ecode, NULL)); - xfree(sig); - return -1; + if (RSA_sign(nid, digest, dlen, sig, &len, key->rsa) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; } if (len < slen) { u_int diff = slen - len; - debug("slen %u > len %u", slen, len); memmove(sig + diff, sig, len); memset(sig, 0, diff); } else if (len > slen) { - error("ssh_rsa_sign: slen %u slen2 %u", slen, len); - xfree(sig); - return -1; + ret = SSH_ERR_INTERNAL_ERROR; + goto out; } /* encode signature */ - buffer_init(&b); - buffer_put_cstring(&b, "ssh-rsa"); - buffer_put_string(&b, sig, slen); - len = buffer_len(&b); + if ((b = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((ret = sshbuf_put_cstring(b, "ssh-rsa")) != 0 || + (ret = sshbuf_put_string(b, sig, slen)) != 0) + goto out; + len = sshbuf_len(b); if (lenp != NULL) *lenp = len; if (sigp != NULL) { - *sigp = xmalloc(len); - memcpy(*sigp, buffer_ptr(&b), len); + if ((*sigp = malloc(len)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(*sigp, sshbuf_ptr(b), len); } - buffer_free(&b); - memset(sig, 's', slen); - xfree(sig); - + ret = 0; + out: + bzero(digest, sizeof(digest)); + bzero(&md, sizeof(md)); + if (sig != NULL) { + memset(sig, 's', slen); + free(sig); + } + if (b != NULL) + sshbuf_free(b); return 0; } int -ssh_rsa_verify(const Key *key, const u_char *signature, u_int signaturelen, - const u_char *data, u_int datalen) +ssh_rsa_verify(const struct sshkey *key, + const u_char *signature, u_int signaturelen, + const u_char *data, u_int datalen, u_int compat) { - Buffer b; + struct sshbuf *b = NULL; const EVP_MD *evp_md; EVP_MD_CTX md; char *ktype; - u_char digest[EVP_MAX_MD_SIZE], *sigblob; - u_int len, dlen, modlen; - int rlen, ret, nid; + u_char digest[EVP_MAX_MD_SIZE], *osigblob, *sigblob = NULL; + size_t len; + u_int diff, dlen, modlen; + int nid, ret = SSH_ERR_INTERNAL_ERROR; if (key == NULL || key->rsa == NULL || (key->type != KEY_RSA && - key->type != KEY_RSA_CERT && key->type != KEY_RSA_CERT_V00)) { - error("ssh_rsa_verify: no RSA key"); - return -1; + key->type != KEY_RSA_CERT && key->type != KEY_RSA_CERT_V00) || + BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) + return SSH_ERR_INVALID_ARGUMENT; + + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((ret = sshbuf_put(b, signature, signaturelen)) != 0) + goto out; + if (sshbuf_get_cstring(b, &ktype, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; } - if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { - error("ssh_rsa_verify: RSA modulus too small: %d < minimum %d bits", - BN_num_bits(key->rsa->n), SSH_RSA_MINIMUM_MODULUS_SIZE); - return -1; - } - buffer_init(&b); - buffer_append(&b, signature, signaturelen); - ktype = buffer_get_cstring(&b, NULL); if (strcmp("ssh-rsa", ktype) != 0) { - error("ssh_rsa_verify: cannot handle type %s", ktype); - buffer_free(&b); - xfree(ktype); - return -1; + ret = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; } - xfree(ktype); - sigblob = buffer_get_string(&b, &len); - rlen = buffer_len(&b); - buffer_free(&b); - if (rlen != 0) { - error("ssh_rsa_verify: remaining bytes in signature %d", rlen); - xfree(sigblob); - return -1; + if (sshbuf_get_string(b, &sigblob, &len) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (sshbuf_len(b) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; } /* RSA_verify expects a signature of RSA_size */ modlen = RSA_size(key->rsa); if (len > modlen) { - error("ssh_rsa_verify: len %u > modlen %u", len, modlen); - xfree(sigblob); - return -1; + ret = SSH_ERR_KEY_BITS_MISMATCH; + goto out; } else if (len < modlen) { - u_int diff = modlen - len; - debug("ssh_rsa_verify: add padding: modlen %u > len %u", - modlen, len); - sigblob = xrealloc(sigblob, 1, modlen); + diff = modlen - len; + osigblob = sigblob; + if ((sigblob = realloc(sigblob, modlen)) == NULL) { + free(osigblob); + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } memmove(sigblob + diff, sigblob, len); memset(sigblob, 0, diff); len = modlen; } - nid = (datafellows & SSH_BUG_RSASIGMD5) ? NID_md5 : NID_sha1; + nid = (compat & SSH_BUG_RSASIGMD5) ? NID_md5 : NID_sha1; if ((evp_md = EVP_get_digestbynid(nid)) == NULL) { - error("ssh_rsa_verify: EVP_get_digestbynid %d failed", nid); - xfree(sigblob); - return -1; + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (EVP_DigestInit(&md, evp_md) != 1 || + EVP_DigestUpdate(&md, data, datalen) != 1 || + EVP_DigestFinal(&md, digest, &dlen) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; } - EVP_DigestInit(&md, evp_md); - EVP_DigestUpdate(&md, data, datalen); - EVP_DigestFinal(&md, digest, &dlen); ret = openssh_RSA_verify(nid, digest, dlen, sigblob, len, key->rsa); - memset(digest, 'd', sizeof(digest)); - memset(sigblob, 's', len); - xfree(sigblob); - debug("ssh_rsa_verify: signature %scorrect", (ret==0) ? "in" : ""); + out: + if (sigblob != NULL) { + memset(sigblob, 's', len); + free(sigblob); + } + if (ktype != NULL) + free(ktype); + if (b != NULL) + sshbuf_free(b); + bzero(digest, sizeof(digest)); + bzero(&md, sizeof(md)); return ret; } @@ -207,12 +223,12 @@ static int openssh_RSA_verify(int type, u_char *hash, u_int hashlen, u_char *sigbuf, u_int siglen, RSA *rsa) { - u_int ret, rsasize, oidlen = 0, hlen = 0; + u_int ret, rsasize = 0, oidlen = 0, hlen = 0; int len, oidmatch, hashmatch; const u_char *oid = NULL; u_char *decrypted = NULL; - ret = 0; + ret = SSH_ERR_INTERNAL_ERROR; switch (type) { case NID_sha1: oid = id_sha1; @@ -228,38 +244,39 @@ openssh_RSA_verify(int type, u_char *hash, u_int hashlen, goto done; } if (hashlen != hlen) { - error("bad hashlen"); + ret = SSH_ERR_INVALID_ARGUMENT; goto done; } rsasize = RSA_size(rsa); - if (siglen == 0 || siglen > rsasize) { - error("bad siglen"); + if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM || + siglen == 0 || siglen > rsasize) { + ret = SSH_ERR_INVALID_ARGUMENT; + goto done; + } + if ((decrypted = malloc(rsasize)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; goto done; } - decrypted = xmalloc(rsasize); if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa, RSA_PKCS1_PADDING)) < 0) { - error("RSA_public_decrypt failed: %s", - ERR_error_string(ERR_get_error(), NULL)); + ret = SSH_ERR_LIBCRYPTO_ERROR; goto done; } if (len < 0 || (u_int)len != hlen + oidlen) { - error("bad decrypted len: %d != %d + %d", len, hlen, oidlen); + ret = SSH_ERR_INVALID_FORMAT; goto done; } oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0; hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0; - if (!oidmatch) { - error("oid mismatch"); + if (!oidmatch || !hashmatch) { + ret = SSH_ERR_SIGNATURE_INVALID; goto done; } - if (!hashmatch) { - error("hash mismatch"); - goto done; - } - ret = 1; + ret = 0; done: - if (decrypted) - xfree(decrypted); + if (decrypted) { + bzero(decrypted, rsasize); + free(decrypted); + } return ret; } diff --git a/ssh/ssh.c b/ssh/ssh.c index 83c2afe..9526de4 100644 --- a/ssh/ssh.c +++ b/ssh/ssh.c @@ -786,7 +786,7 @@ main(int ac, char **av) options.hostbased_authentication) { sensitive_data.nkeys = 7; sensitive_data.keys = xcalloc(sensitive_data.nkeys, - sizeof(Key)); + sizeof(struct sshkey)); PRIV_START; sensitive_data.keys[0] = key_load_private_type(KEY_RSA1, @@ -878,7 +878,7 @@ main(int ac, char **av) if (sensitive_data.keys[i] != NULL) { /* Destroys contents safely */ debug3("clear hostkey %d", i); - key_free(sensitive_data.keys[i]); + sshkey_free(sensitive_data.keys[i]); sensitive_data.keys[i] = NULL; } } @@ -890,7 +890,7 @@ main(int ac, char **av) options.identity_files[i] = NULL; } if (options.identity_keys[i]) { - key_free(options.identity_keys[i]); + sshkey_free(options.identity_keys[i]); options.identity_keys[i] = NULL; } } @@ -1444,13 +1444,13 @@ load_public_identity_files(void) char *filename, *cp, thishost[NI_MAXHOST]; char *pwdir = NULL, *pwname = NULL; int i = 0; - Key *public; + struct sshkey *public; struct passwd *pw; u_int n_ids; char *identity_files[SSH_MAX_IDENTITY_FILES]; - Key *identity_keys[SSH_MAX_IDENTITY_FILES]; + struct sshkey *identity_keys[SSH_MAX_IDENTITY_FILES]; #ifdef ENABLE_PKCS11 - Key **keys; + struct sshkey **keys; int nkeys; #endif /* PKCS11 */ @@ -1466,7 +1466,7 @@ load_public_identity_files(void) &keys)) > 0) { for (i = 0; i < nkeys; i++) { if (n_ids >= SSH_MAX_IDENTITY_FILES) { - key_free(keys[i]); + sshkey_free(keys[i]); continue; } identity_keys[n_ids] = keys[i]; @@ -1514,10 +1514,10 @@ load_public_identity_files(void) xfree(cp); continue; } - if (!key_is_cert(public)) { + if (!sshkey_is_cert(public)) { debug("%s: key %s type %s is not a certificate", - __func__, cp, key_type(public)); - key_free(public); + __func__, cp, sshkey_type(public)); + sshkey_free(public); xfree(cp); continue; } diff --git a/ssh/ssh_api.c b/ssh/ssh_api.c index 9905aae..0349d0f 100644 --- a/ssh/ssh_api.c +++ b/ssh/ssh_api.c @@ -15,20 +15,22 @@ void _ssh_exchange_banner(struct ssh *); char *_ssh_send_banner(struct ssh *); char *_ssh_read_banner(struct ssh *); -Key *_ssh_host_public_key(int, struct ssh *); -Key *_ssh_host_private_key(int, struct ssh *); -int _ssh_verify_host_key(Key *, struct ssh *); +int _ssh_verify_host_key(struct sshkey *, struct ssh *); +struct sshkey *_ssh_host_public_key(int, struct ssh *); +struct sshkey *_ssh_host_private_key(int, struct ssh *); /* * stubs for the server side implementation of kex. * disable privsep so our stubs will never be called. */ int use_privsep = 0; -int mm_key_sign(Key *, u_char **, u_int *, u_char *, u_int); +int mm_sshkey_sign(struct sshkey *, u_char **, u_int *, + u_char *, u_int, u_int); DH *mm_choose_dh(int, int, int); int -mm_key_sign(Key *key, u_char **sigp, u_int *lenp, u_char *data, u_int datalen) +mm_sshkey_sign(struct sshkey *key, u_char **sigp, u_int *lenp, + u_char *data, u_int datalen, u_int compat) { return (-1); } @@ -91,7 +93,7 @@ ssh_free(struct ssh *ssh) int ssh_add_hostkey(struct ssh* ssh, char *key) { - Key *parsed_key; + struct sshkey *parsed_key, *pubkey; struct key_entry *k; Buffer key_buf; @@ -103,28 +105,33 @@ ssh_add_hostkey(struct ssh* ssh, char *key) buffer_free(&key_buf); if (parsed_key != NULL) { + if (sshkey_from_private(parsed_key, &pubkey) != 0) { + sshkey_free(parsed_key); + return (-1); + } k = xmalloc(sizeof(*k)); k->key = parsed_key; TAILQ_INSERT_TAIL(&ssh->private_keys, k, next); /* add the public key, too */ k = xmalloc(sizeof(*k)); - k->key = key_from_private(parsed_key); + k->key = pubkey; TAILQ_INSERT_TAIL(&ssh->public_keys, k, next); return (0); } } else { /* Parse public key */ - parsed_key = key_new(KEY_UNSPEC); - if (key_read(parsed_key, &key) == 1) { + if ((parsed_key = sshkey_new(KEY_UNSPEC)) == NULL) + return -1; + if (sshkey_read(parsed_key, &key) != 0) { k = xmalloc(sizeof(*k)); k->key = parsed_key; TAILQ_INSERT_TAIL(&ssh->public_keys, k, next); return (0); } else { - key_free(parsed_key); + sshkey_free(parsed_key); } } @@ -313,21 +320,21 @@ _ssh_exchange_banner(struct ssh *ssh) kex_send_kexinit(ssh); } -Key * +struct sshkey * _ssh_host_public_key(int type, struct ssh *ssh) { struct key_entry *k; debug3("%s: need %d", __func__, type); TAILQ_FOREACH(k, &ssh->public_keys, next) { - debug3("%s: check %s", __func__, key_type(k->key)); + debug3("%s: check %s", __func__, sshkey_type(k->key)); if (k->key->type == type) return (k->key); } return (NULL); } -Key * +struct sshkey * _ssh_host_private_key(int type, struct ssh *ssh) { struct key_entry *k; @@ -335,7 +342,7 @@ _ssh_host_private_key(int type, struct ssh *ssh) datafellows = ssh->datafellows; /* XXX */ debug3("%s: need %d", __func__, type); TAILQ_FOREACH(k, &ssh->private_keys, next) { - debug3("%s: check %s", __func__, key_type(k->key)); + debug3("%s: check %s", __func__, sshkey_type(k->key)); if (k->key->type == type) return (k->key); } @@ -343,14 +350,14 @@ _ssh_host_private_key(int type, struct ssh *ssh) } int -_ssh_verify_host_key(Key *hostkey, struct ssh *ssh) +_ssh_verify_host_key(struct sshkey *hostkey, struct ssh *ssh) { struct key_entry *k; - debug3("%s: need %s", __func__, key_type(hostkey)); + debug3("%s: need %s", __func__, sshkey_type(hostkey)); TAILQ_FOREACH(k, &ssh->public_keys, next) { - debug3("%s: check %s", __func__, key_type(k->key)); - if (key_equal_public(hostkey, k->key)) + debug3("%s: check %s", __func__, sshkey_type(k->key)); + if (sshkey_equal_public(hostkey, k->key)) return (0); /* ok */ } return (-1); /* failed */ diff --git a/ssh/sshbuf-getput-basic.c b/ssh/sshbuf-getput-basic.c new file mode 100644 index 0000000..1750d02 --- /dev/null +++ b/ssh/sshbuf-getput-basic.c @@ -0,0 +1,334 @@ +/* $OpenBSD$ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include "err.h" +#define SSHBUF_INTERNAL +#include "sshbuf.h" + +int +sshbuf_get(struct sshbuf *buf, void *v, size_t len) +{ + u_char *p = sshbuf_ptr(buf); + int r; + + if ((r = sshbuf_consume(buf, len)) < 0) + return r; + if (v != NULL) + memcpy(v, p, len); + return 0; +} + +int +sshbuf_get_u64(struct sshbuf *buf, u_int64_t *valp) +{ + u_char *p = sshbuf_ptr(buf); + int r; + + if ((r = sshbuf_consume(buf, 8)) < 0) + return r; + if (valp != NULL) + *valp = PEEK_U64(p); + return 0; +} + +int +sshbuf_get_u32(struct sshbuf *buf, u_int32_t *valp) +{ + u_char *p = sshbuf_ptr(buf); + int r; + + if ((r = sshbuf_consume(buf, 4)) < 0) + return r; + if (valp != NULL) + *valp = PEEK_U32(p); + return 0; +} + +int +sshbuf_get_u16(struct sshbuf *buf, u_int16_t *valp) +{ + u_char *p = sshbuf_ptr(buf); + int r; + + if ((r = sshbuf_consume(buf, 2)) < 0) + return r; + if (valp != NULL) + *valp = PEEK_U16(p); + return 0; +} + +int +sshbuf_get_u8(struct sshbuf *buf, u_char *valp) +{ + u_char *p = sshbuf_ptr(buf); + int r; + + if ((r = sshbuf_consume(buf, 1)) < 0) + return r; + if (valp != NULL) + *valp = (u_int8_t)*p; + return 0; +} + +int +sshbuf_get_string(struct sshbuf *buf, u_char **valp, size_t *lenp) +{ + const u_char *val; + size_t len; + int r; + + if ((r = sshbuf_get_string_direct(buf, &val, &len)) < 0) + return r; + if (valp != NULL) { + if ((*valp = malloc(len + 1)) == NULL) { + SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL")); + return SSH_ERR_ALLOC_FAIL; + } + memcpy(*valp, val, len); + (*valp)[len] = '\0'; + } + if (lenp != NULL) + *lenp = len; + return 0; +} + +int +sshbuf_get_string_direct(struct sshbuf *buf, const u_char **valp, size_t *lenp) +{ + size_t len; + const u_char *p; + int r; + + if ((r = sshbuf_peek_string_direct(buf, &p, &len)) < 0) + return r; + if (valp != 0) + *valp = p; + if (lenp != NULL) + *lenp = len; + if (sshbuf_consume(buf, len + 4) != 0) { + /* Shouldn't happen */ + SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); + SSHBUF_ABORT(); + return SSH_ERR_INTERNAL_ERROR; + } + return 0; +} + +int +sshbuf_peek_string_direct(const struct sshbuf *buf, const u_char **valp, + size_t *lenp) +{ + u_int32_t len; + u_char *p = sshbuf_ptr(buf); + + if (sshbuf_len(buf) < 4) { + SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE")); + return SSH_ERR_MESSAGE_INCOMPLETE; + } + len = PEEK_U32(p); + if (sshbuf_len(buf) - 4 < len) { + SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE")); + return SSH_ERR_MESSAGE_INCOMPLETE; + } + if (valp != 0) + *valp = p + 4; + if (lenp != NULL) + *lenp = len; + return 0; +} + +int +sshbuf_get_cstring(struct sshbuf *buf, char **valp, size_t *lenp) +{ + u_int32_t len; + u_char *p = sshbuf_ptr(buf), *z; + int r; + + if (sshbuf_len(buf) < 4) { + SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE")); + return SSH_ERR_MESSAGE_INCOMPLETE; + } + len = PEEK_U32(p); + if (sshbuf_len(buf) < (size_t)len + 4) { + SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE")); + return SSH_ERR_MESSAGE_INCOMPLETE; + } + /* Allow a \0 only at the end of the string */ + if ((z = memchr(p + 4, '\0', len)) != NULL && z < p + 4 + len - 1) { + SSHBUF_DBG(("SSH_ERR_INVALID_FORMAT")); + return SSH_ERR_INVALID_FORMAT; + } + if ((r = sshbuf_consume(buf, 4 + (size_t)len)) < 0) + return -1; + if (valp != NULL) { + if ((*valp = malloc(len + 1)) == NULL) { + SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL")); + return SSH_ERR_ALLOC_FAIL; + } + memcpy(*valp, p + 4, len); + (*valp)[len] = '\0'; + } + if (lenp != NULL) + *lenp = (size_t)len; + return 0; +} + +int +sshbuf_put(struct sshbuf *buf, const void *v, size_t len) +{ + u_char *p; + int r; + + if ((r = sshbuf_reserve(buf, len, &p)) < 0) + return r; + memcpy(p, v, len); + return 0; +} + +int +sshbuf_putb(struct sshbuf *buf, const struct sshbuf *v) +{ + return sshbuf_put(buf, sshbuf_ptr(v), sshbuf_len(v)); +} + +int +sshbuf_putf(struct sshbuf *buf, const char *fmt, ...) +{ + va_list ap; + int r; + + va_start(ap, fmt); + r = sshbuf_putfv(buf, fmt, ap); + va_end(ap); + return r; +} + +int +sshbuf_putfv(struct sshbuf *buf, const char *fmt, va_list ap) +{ + va_list ap2; + int r, len; + u_char *p; + + va_copy(ap2, ap); + if ((len = vsnprintf(NULL, 0, fmt, ap2)) < 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if (len == 0) { + r = 0; + goto out; /* Nothing to do */ + } + va_end(ap2); + va_copy(ap2, ap); + if ((r = sshbuf_reserve(buf, (size_t)len + 1, &p)) < 0) + goto out; + if ((r = vsnprintf(p, len + 1, fmt, ap2)) != len) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; /* Shouldn't happen */ + } + /* Consume terminating \0 */ + if ((r = sshbuf_consume_end(buf, 1)) != 0) + goto out; + r = 0; + out: + va_end(ap2); + return r; +} + +int +sshbuf_put_u64(struct sshbuf *buf, u_int64_t val) +{ + u_char *p; + int r; + + if ((r = sshbuf_reserve(buf, 8, &p)) < 0) + return r; + POKE_U64(p, val); + return 0; +} + +int +sshbuf_put_u32(struct sshbuf *buf, u_int32_t val) +{ + u_char *p; + int r; + + if ((r = sshbuf_reserve(buf, 4, &p)) < 0) + return r; + POKE_U32(p, val); + return 0; +} + +int +sshbuf_put_u16(struct sshbuf *buf, u_int16_t val) +{ + u_char *p; + int r; + + if ((r = sshbuf_reserve(buf, 2, &p)) < 0) + return r; + POKE_U16(p, val); + return 0; +} + +int +sshbuf_put_u8(struct sshbuf *buf, u_char val) +{ + u_char *p; + int r; + + if ((r = sshbuf_reserve(buf, 1, &p)) < 0) + return r; + p[0] = val; + return 0; +} + +int +sshbuf_put_string(struct sshbuf *buf, const void *v, size_t len) +{ + u_char *d; + int r; + + if (len > 0xFFFFFFFF - 4) { + SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE")); + return SSH_ERR_NO_BUFFER_SPACE; + } + if ((r = sshbuf_reserve(buf, len + 4, &d)) < 0) + return r; + POKE_U32(d, len); + memcpy(d + 4, v, len); + return 0; +} + +int +sshbuf_put_cstring(struct sshbuf *buf, const char *v) +{ + return sshbuf_put_string(buf, (u_char *)v, strlen(v)); +} + +int +sshbuf_put_stringb(struct sshbuf *buf, const struct sshbuf *v) +{ + return sshbuf_put_string(buf, sshbuf_ptr(v), sshbuf_len(v)); +} + diff --git a/ssh/sshbuf-getput-crypto.c b/ssh/sshbuf-getput-crypto.c new file mode 100644 index 0000000..e1bccb6 --- /dev/null +++ b/ssh/sshbuf-getput-crypto.c @@ -0,0 +1,205 @@ +/* $OpenBSD$ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include + +#include "err.h" +#define SSHBUF_INTERNAL +#include "sshbuf.h" + +int +sshbuf_get_bignum2(struct sshbuf *buf, BIGNUM *v) +{ + const u_char *d; + size_t len; + int r; + + if ((r = sshbuf_peek_string_direct(buf, &d, &len)) < 0) + return r; + /* Refuse negative (MSB set) and overlong bignums */ + if ((len != 0 && (*d & 0x80) != 0)) + return SSH_ERR_BIGNUM_IS_NEGATIVE; + if (len > SSHBUF_MAX_BIGNUM) + return SSH_ERR_BIGNUM_TOO_LARGE; + if (BN_bin2bn(d, len, v) == NULL) + return SSH_ERR_ALLOC_FAIL; + /* Consume the string */ + if (sshbuf_get_string_direct(buf, NULL, NULL) != 0) { + /* Shouldn't happen */ + SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); + SSHBUF_ABORT(); + return SSH_ERR_INTERNAL_ERROR; + } + return 0; +} + +int +sshbuf_get_bignum1(struct sshbuf *buf, BIGNUM *v) +{ + u_char *d = sshbuf_ptr(buf); + u_int16_t len_bits; + size_t len_bytes; + + /* Length in bits */ + if (sshbuf_len(buf) < 2) + return SSH_ERR_MESSAGE_INCOMPLETE; + len_bits = PEEK_U16(d); + len_bytes = (len_bits + 7) >> 3; + if (len_bytes > SSHBUF_MAX_BIGNUM + 1) + return SSH_ERR_BIGNUM_TOO_LARGE; + if (sshbuf_len(buf) < 2 + len_bytes) + return SSH_ERR_MESSAGE_INCOMPLETE; + if (BN_bin2bn(d + 2, len_bytes, v) == NULL) + return SSH_ERR_ALLOC_FAIL; + if (sshbuf_consume(buf, 2 + len_bytes) != 0) { + SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); + SSHBUF_ABORT(); + return SSH_ERR_INTERNAL_ERROR; + } + return 0; +} + +int +sshbuf_get_ec(struct sshbuf *buf, EC_POINT *v, const EC_GROUP *g) +{ + const u_char *d; + size_t len; + int r; + + if ((r = sshbuf_peek_string_direct(buf, &d, &len)) < 0) + return r; + /* Refuse overlong bignums */ + if (len == 0 || len > SSHBUF_MAX_ECPOINT) + return SSH_ERR_ECPOINT_TOO_LARGE; + /* Only handle uncompressed points */ + if (*d != POINT_CONVERSION_UNCOMPRESSED || + EC_POINT_oct2point(g, v, d, len, NULL) != 1) + return SSH_ERR_INVALID_FORMAT; + /* Skip string */ + if (sshbuf_get_string_direct(buf, NULL, NULL) != 0) { + /* Shouldn't happen */ + SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); + SSHBUF_ABORT(); + return SSH_ERR_INTERNAL_ERROR; + } + return 0; +} + +int +sshbuf_get_eckey(struct sshbuf *buf, EC_KEY *v) +{ + EC_POINT *pt = EC_POINT_new(EC_KEY_get0_group(v)); + int r; + size_t ooff = buf->off; /* NB. uses buffer internals to rewind on err */ + + if (pt == NULL) { + SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL")); + return SSH_ERR_ALLOC_FAIL; + } + if ((r = sshbuf_get_ec(buf, pt, EC_KEY_get0_group(v))) < 0) + return r; + if (EC_KEY_set_public_key(v, pt) != 1) { + buf->off = ooff; + EC_POINT_free(pt); + return SSH_ERR_ALLOC_FAIL; /* XXX assumption */ + } + EC_POINT_free(pt); + return 0; +} + +int +sshbuf_put_bignum2(struct sshbuf *buf, const BIGNUM *v) +{ + u_char d[SSHBUF_MAX_BIGNUM + 1]; + int len = BN_num_bytes(v), prepend = 0, r; + + if (len < 0 || len > SSHBUF_MAX_BIGNUM) + return SSH_ERR_INVALID_ARGUMENT; + *d = '\0'; + if (BN_bn2bin(v, d + 1) != len) + return SSH_ERR_INTERNAL_ERROR; /* Shouldn't happen */ + /* If MSB is set, prepend a \0 */ + if (len > 0 && (d[1] & 0x80) != 0) + prepend = 1; + if ((r = sshbuf_put_string(buf, d + 1 - prepend, len + prepend)) < 0) { + bzero(d, sizeof(d)); + return r; + } + bzero(d, sizeof(d)); + return 0; +} + +int +sshbuf_put_bignum1(struct sshbuf *buf, const BIGNUM *v) +{ + int r, len_bits = BN_num_bits(v); + size_t len_bytes = (len_bits + 7) / 8; + u_char d[SSHBUF_MAX_BIGNUM], *dp; + + if (len_bits < 0 || len_bytes > SSHBUF_MAX_BIGNUM) + return SSH_ERR_INVALID_ARGUMENT; + if (BN_bn2bin(v, d) != (int)len_bytes) + return SSH_ERR_INTERNAL_ERROR; /* Shouldn't happen */ + if ((r = sshbuf_reserve(buf, len_bytes + 2, &dp)) < 0) { + bzero(d, sizeof(d)); + return r; + } + POKE_U16(dp, len_bits); + memcpy(dp + 2, d, len_bytes); + bzero(d, sizeof(d)); + return 0; +} + +int +sshbuf_put_ec(struct sshbuf *buf, const EC_POINT *v, const EC_GROUP *g) +{ + u_char d[SSHBUF_MAX_ECPOINT]; + BN_CTX *bn_ctx; + size_t len; + int ret; + + if ((bn_ctx = BN_CTX_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((len = EC_POINT_point2oct(g, v, POINT_CONVERSION_UNCOMPRESSED, + NULL, 0, bn_ctx)) > SSHBUF_MAX_ECPOINT) { + BN_CTX_free(bn_ctx); + return SSH_ERR_INVALID_ARGUMENT; + } + if (EC_POINT_point2oct(g, v, POINT_CONVERSION_UNCOMPRESSED, + d, len, bn_ctx) != len) { + BN_CTX_free(bn_ctx); + return SSH_ERR_INTERNAL_ERROR; /* Shouldn't happen */ + } + BN_CTX_free(bn_ctx); + ret = sshbuf_put_string(buf, d, len); + bzero(d, len); + return ret; +} + +int +sshbuf_put_eckey(struct sshbuf *buf, const EC_KEY *v) +{ + return sshbuf_put_ec(buf, EC_KEY_get0_public_key(v), + EC_KEY_get0_group(v)); +} + diff --git a/ssh/sshbuf-misc.c b/ssh/sshbuf-misc.c new file mode 100644 index 0000000..da9c51d --- /dev/null +++ b/ssh/sshbuf-misc.c @@ -0,0 +1,114 @@ +/* $OpenBSD$ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "err.h" +#define SSHBUF_INTERNAL +#include "sshbuf.h" + +void +sshbuf_dump(struct sshbuf *buf, FILE *f) +{ + u_char *p = sshbuf_ptr(buf); + size_t i, len = sshbuf_len(buf); + + /* XXX replace with pretty hexdump + ASCII */ + fprintf(f, "buffer %p len = %zu\n", buf, len); + for (i = 0; i < len; i++) { + if (i > 0 && (i % 32) == 0) + fprintf(f, "\n"); + fprintf(f, "%02x", p[i]); + } + fprintf(f, "\n"); +} + +char * +sshbuf_dtob16(struct sshbuf *buf) +{ + size_t i, j, len = sshbuf_len(buf); + u_char *p = sshbuf_ptr(buf); + char *ret; + const char hex[] = "0123456789abcdef"; + + if (len == 0) + return strdup(""); + if (SIZE_MAX / 2 <= len || (ret = malloc(len * 2 + 1)) == NULL) + return NULL; + for (i = j = 0; i < len; i++) { + ret[j++] = hex[(p[i] >> 4) & 0xf]; + ret[j++] = hex[p[i] & 0xf]; + } + ret[j] = '\0'; + return ret; +} + +char * +sshbuf_dtob64(struct sshbuf *buf) +{ + size_t len = sshbuf_len(buf), plen; + u_char *p = sshbuf_ptr(buf); + char *ret; + int r; + + if (len == 0) + return strdup(""); + plen = ((len + 2) / 3) * 4 + 1; + if (SIZE_MAX / 2 <= len || (ret = malloc(plen)) == NULL) + return NULL; + if ((r = b64_ntop(p, len, ret, plen)) == -1) { + bzero(ret, plen); + free(ret); + return NULL; + } + return ret; +} + +int +sshbuf_b64tod(struct sshbuf *buf, const char *b64) +{ + size_t plen = strlen(b64); + int nlen, r; + u_char *p; + + if (plen == 0) + return 0; + if ((p = malloc(plen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((nlen = b64_pton(b64, p, plen)) < 0) { + bzero(p, plen); + free(p); + return SSH_ERR_INVALID_FORMAT; + } + if ((r = sshbuf_put(buf, p, nlen)) < 0) { + bzero(p, plen); + free(p); + return r; + } + bzero(p, plen); + free(p); + return 0; +} + diff --git a/ssh/sshbuf.c b/ssh/sshbuf.c new file mode 100644 index 0000000..85de811 --- /dev/null +++ b/ssh/sshbuf.c @@ -0,0 +1,273 @@ +/* $OpenBSD$ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "err.h" +#define SSHBUF_INTERNAL +#include "sshbuf.h" + +static inline int +sshbuf_check_sanity(const struct sshbuf *buf) +{ + SSHBUF_TELL("sanity"); + if (__predict_false(buf == NULL || buf->d == NULL || + buf->max_size > SSHBUF_SIZE_MAX || + buf->alloc > buf->max_size || + buf->size > buf->alloc || + buf->off > buf->size)) { + SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); + SSHBUF_ABORT(); + return SSH_ERR_INTERNAL_ERROR; + } + return 0; +} + +static void +sshbuf_maybe_pack(struct sshbuf *buf, int force) +{ + SSHBUF_DBG(("force %d", force)); + SSHBUF_TELL("pre-pack"); + if (force || (buf->off >= SSHBUF_PACK_MIN && buf->off >= buf->size / 2)) { + memmove(buf->d, buf->d + buf->off, buf->size - buf->off); + buf->size -= buf->off; + buf->off = 0; + SSHBUF_TELL("packed"); + } +} + +struct sshbuf * +sshbuf_new(void) +{ + struct sshbuf *ret; + + if ((ret = calloc(sizeof(*ret), 1)) == NULL) + return NULL; + ret->alloc = SSHBUF_SIZE_INIT; + ret->max_size = SSHBUF_SIZE_MAX; + if ((ret->d = calloc(1, ret->alloc)) == NULL) { + free(ret); + return NULL; + } + return ret; +} + +void +sshbuf_init(struct sshbuf *ret) +{ + bzero(ret, sizeof(*ret)); + ret->alloc = SSHBUF_SIZE_INIT; + ret->max_size = SSHBUF_SIZE_MAX; + if ((ret->d = calloc(1, ret->alloc)) == NULL) + ret->alloc = 0; +} + +void +sshbuf_free(struct sshbuf *buf) +{ + int freeme; + + if (sshbuf_check_sanity(buf) == 0) + bzero(buf->d, buf->alloc); + free(buf->d); + freeme = buf->freeme; + bzero(buf, sizeof(buf)); + if (freeme) + free(buf); +} + +void +sshbuf_reset(struct sshbuf *buf) +{ + u_char *d; + + if (sshbuf_check_sanity(buf) == 0) + bzero(buf->d, buf->alloc); + buf->off = buf->size = 0; + if (buf->alloc != SSHBUF_SIZE_INIT) { + if ((d = realloc(buf->d, SSHBUF_SIZE_INIT)) != NULL) { + buf->d = d; + buf->alloc = SSHBUF_SIZE_INIT; + } + } +} + +size_t +sshbuf_max_size(const struct sshbuf *buf) +{ + return buf->max_size; +} + +int +sshbuf_set_max_size(struct sshbuf *buf, size_t max_size) +{ + size_t rlen; + u_char *dp; + int r; + + SSHBUF_DBG(("set max buf = %p len = %zu", buf, max_size)); + if ((r = sshbuf_check_sanity(buf)) < 0) + return r; + if (max_size > SSHBUF_SIZE_MAX) + return SSH_ERR_NO_BUFFER_SPACE; + /* pack and realloc if necessary */ + sshbuf_maybe_pack(buf, max_size < buf->size); + if (max_size < buf->alloc && max_size > buf->size) { + rlen = roundup(buf->size, SSHBUF_SIZE_INC); + if (rlen > max_size) + rlen = max_size; + rlen = buf->size; + bzero(buf->d + buf->size, buf->alloc - buf->size); + SSHBUF_DBG(("new alloc = %zu", rlen)); + if ((dp = realloc(buf->d, rlen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + buf->d = dp; + buf->alloc = rlen; + } + SSHBUF_TELL("new-max"); + if (max_size < buf->alloc) + return SSH_ERR_NO_BUFFER_SPACE; + buf->max_size = max_size; + return 0; +} + +size_t +sshbuf_len(const struct sshbuf *buf) +{ + if (sshbuf_check_sanity(buf) != 0) + return 0; + return buf->size - buf->off; +} + +size_t +sshbuf_avail(const struct sshbuf *buf) +{ + if (sshbuf_check_sanity(buf) != 0) + return 0; + return buf->max_size - (buf->size - buf->off); +} + +u_char * +sshbuf_ptr(const struct sshbuf *buf) +{ + if (sshbuf_check_sanity(buf) != 0) + return NULL; + return buf->d + buf->off; +} + +int +sshbuf_check_reserve(const struct sshbuf *buf, size_t len) +{ + int r; + + if ((r = sshbuf_check_sanity(buf)) < 0) + return r; + SSHBUF_TELL("check"); + /* Slightly odd test construction is to prevent unsigned overflows */ + if (len > buf->max_size || buf->max_size - len < buf->size - buf->off) + return SSH_ERR_NO_BUFFER_SPACE; + return 0; +} + +int +sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp) +{ + size_t rlen, need; + u_char *dp; + int r; + + SSHBUF_DBG(("reserve buf = %p len = %zu", buf, len)); + if ((r = sshbuf_check_reserve(buf, len)) < 0) { /* does sanity check */ + if (dpp != NULL) + *dpp = NULL; + return r; + } + /* If we are running against max_size, we must pack */ + sshbuf_maybe_pack(buf, buf->size + len > buf->max_size); + SSHBUF_TELL("reserve"); + if (len + buf->size > buf->alloc) { + /* + * Prefer to alloc in SSHBUF_SIZE_INC units, but + * allocate less if doing so would overflow max_size. + */ + need = len + buf->size - buf->alloc; + rlen = roundup(buf->alloc + need, SSHBUF_SIZE_INC); + SSHBUF_DBG(("need %zu initial rlen %zu", need, rlen)); + if (rlen > buf->max_size) + rlen = buf->alloc + need; + SSHBUF_DBG(("adjusted rlen %zu", rlen)); + if ((dp = realloc(buf->d, rlen)) == NULL) { + SSHBUF_DBG(("realloc fail")); + if (dpp != NULL) + *dpp = NULL; + return SSH_ERR_ALLOC_FAIL; + } + buf->alloc = rlen; + buf->d = dp; + if ((r = sshbuf_check_reserve(buf, len)) < 0) { + /* shouldn't fail */ + if (dpp != NULL) + *dpp = NULL; + return r; + } + } + dp = buf->d + buf->size; + buf->size += len; + SSHBUF_TELL("done"); + if (dpp != NULL) + *dpp = dp; + return 0; +} + +int +sshbuf_consume(struct sshbuf *buf, size_t len) +{ + int r; + + SSHBUF_DBG(("len = %zu", len)); + if ((r = sshbuf_check_sanity(buf)) < 0) + return r; + if (len == 0) + return 0; + if (len > sshbuf_len(buf)) + return SSH_ERR_MESSAGE_INCOMPLETE; + buf->off += len; + SSHBUF_TELL("done"); + return 0; +} + +int +sshbuf_consume_end(struct sshbuf *buf, size_t len) +{ + int r; + + SSHBUF_DBG(("len = %zu", len)); + if ((r = sshbuf_check_sanity(buf)) < 0) + return r; + if (len == 0) + return 0; + if (len > sshbuf_len(buf)) + return SSH_ERR_MESSAGE_INCOMPLETE; + buf->size -= len; + SSHBUF_TELL("done"); + return 0; +} + diff --git a/ssh/sshbuf.h b/ssh/sshbuf.h new file mode 100644 index 0000000..2692d4f --- /dev/null +++ b/ssh/sshbuf.h @@ -0,0 +1,266 @@ +/* $OpenBSD$ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SSHBUF_H + +#include +#include +#include +#include +#include + +#define SSHBUF_SIZE_MAX 0x20000000 /* Hard maximum size */ +#define SSHBUF_MAX_BIGNUM (8192 / 8) /* Max bignum *bytes* */ +#define SSHBUF_MAX_ECPOINT ((528 * 2 / 8) + 1) /* Max EC point *bytes* */ + +/* + * NB. do not depend on the internals of this. It will be made opaque + * one day. + */ +struct sshbuf { + u_char *d; /* Data */ + size_t off; /* First available byte is buf->d + buf->off */ + size_t size; /* Last byte is buf->d + buf->size - 1 */ + size_t max_size; /* Maximum size of buffer */ + size_t alloc; /* Total bytes allocated to buf->d */ + int freeme; /* Kludge to support sshbuf_init */ +}; + +#ifndef SSHBUF_NO_DEPREACTED +/* + * NB. Please do not use sshbuf_init() in new code. Please use sshbuf_new() + * instead. sshbuf_init() is deprectated and will go away soon (it is + * only included to allow compat with buffer_* in OpenSSH) + */ +void sshbuf_init(struct sshbuf *buf); +#endif + +/* + * Create a new sshbuf buffer. + * Returns pointer to buffer on success, or NULL on allocation failure. + */ +struct sshbuf *sshbuf_new(void); + +/* + * Clear and free buf + */ +void sshbuf_free(struct sshbuf *buf); + +/* + * Reset buf, clearing its contents. NB. max_size is preserved. + */ +void sshbuf_reset(struct sshbuf *buf); + +/* + * Return the maximum size of buf + */ +size_t sshbuf_max_size(const struct sshbuf *buf); + +/* + * Set the maximum size of buf + * Returns 0 on success, or a negative SSH_ERR_* error code on failure. + */ +int sshbuf_set_max_size(struct sshbuf *buf, size_t max_size); + +/* + * Returns the length of data in buf + */ +size_t sshbuf_len(const struct sshbuf *buf); + +/* + * Returns number of bytes left in buffer before hitting max_size. + */ +size_t sshbuf_avail(const struct sshbuf *buf); + +/* + * Returns pointer to the start of the the data in buf + */ +u_char *sshbuf_ptr(const struct sshbuf *buf); + +/* + * Check whether a reservation of size len will succeed in buf + * Safer to use than direct comparisons again sshbuf_avail as it copes + * with unsigned overflows correctly. + * Returns 0 on success, or a negative SSH_ERR_* error code on failure. + */ + +int sshbuf_check_reserve(const struct sshbuf *buf, size_t len); + +/* + * Reserve len bytes in buf. + * Returns 0 on success and a pointer to the first reserved byte via the + * optional dpp parameter or a negative * SSH_ERR_* error code on failure. + */ +int sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp); + +/* + * Consume len bytes from the start of buf + * Returns 0 on success, or a negative SSH_ERR_* error code on failure. + */ +int sshbuf_consume(struct sshbuf *buf, size_t len); + +/* + * Consume len bytes from the end of buf + * Returns 0 on success, or a negative SSH_ERR_* error code on failure. + */ +int sshbuf_consume_end(struct sshbuf *buf, size_t len); + +/* Extract or deposit some bytes */ +int sshbuf_get(struct sshbuf *buf, void *v, size_t len); +int sshbuf_put(struct sshbuf *buf, const void *v, size_t len); +int sshbuf_putb(struct sshbuf *buf, const struct sshbuf *v); + +/* Append using a printf(3) format */ +int sshbuf_putf(struct sshbuf *buf, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); +int sshbuf_putfv(struct sshbuf *buf, const char *fmt, va_list ap); + +/* Functions to extract or store big-endian words of various sizes */ +int sshbuf_get_u64(struct sshbuf *buf, u_int64_t *valp); +int sshbuf_get_u32(struct sshbuf *buf, u_int32_t *valp); +int sshbuf_get_u16(struct sshbuf *buf, u_int16_t *valp); +int sshbuf_get_u8(struct sshbuf *buf, u_char *valp); +int sshbuf_put_u64(struct sshbuf *buf, u_int64_t val); +int sshbuf_put_u32(struct sshbuf *buf, u_int32_t val); +int sshbuf_put_u16(struct sshbuf *buf, u_int16_t val); +int sshbuf_put_u8(struct sshbuf *buf, u_char val); + +/* + * Functions to extract or store SSH wire encoded strings (u32 len || data) + * The "cstring" variants admit no \0 characters in the string contents. + * Caller must free *valp. + */ +int sshbuf_get_string(struct sshbuf *buf, u_char **valp, size_t *lenp); +int sshbuf_get_cstring(struct sshbuf *buf, char **valp, size_t *lenp); +int sshbuf_put_string(struct sshbuf *buf, const void *v, size_t len); +int sshbuf_put_cstring(struct sshbuf *buf, const char *v); +int sshbuf_put_stringb(struct sshbuf *buf, const struct sshbuf *v); + +/* + * "Direct" variant of sshbuf_get_string, returns pointer into the sshbuf to + * avoid an malloc+memcpy. The pointer is guaranteed to be valid until the + * next sshbuf-modifying function call. Caller does not free. + */ +int sshbuf_get_string_direct(struct sshbuf *buf, const u_char **valp, + size_t *lenp); + +/* Another variant: "peeks" into the buffer without modifying it */ +int sshbuf_peek_string_direct(const struct sshbuf *buf, const u_char **valp, + size_t *lenp); + +/* + * Functions to extract or store SSH wire encoded bignums and elliptic + * curve points. + */ +int sshbuf_get_bignum2(struct sshbuf *buf, BIGNUM *v); +int sshbuf_get_bignum1(struct sshbuf *buf, BIGNUM *v); +int sshbuf_get_ec(struct sshbuf *buf, EC_POINT *v, const EC_GROUP *g); +int sshbuf_get_eckey(struct sshbuf *buf, EC_KEY *v); +int sshbuf_put_bignum2(struct sshbuf *buf, const BIGNUM *v); +int sshbuf_put_bignum1(struct sshbuf *buf, const BIGNUM *v); +int sshbuf_put_ec(struct sshbuf *buf, const EC_POINT *v, const EC_GROUP *g); +int sshbuf_put_eckey(struct sshbuf *buf, const EC_KEY *v); + +/* Dump the contents of the buffer to stderr in a human-readable format */ +void sshbuf_dump(struct sshbuf *buf, FILE *f); + +/* Return the hexadecimal representation of the contents of the buffer */ +char *sshbuf_dtob16(struct sshbuf *buf); + +/* Encode the contents of the buffer as base64 */ +char *sshbuf_dtob64(struct sshbuf *buf); + +/* Decode base64 data and append it to the buffer */ +int sshbuf_b64tod(struct sshbuf *buf, const char *b64); + +/* Macros for decoding/encoding integers */ +#define PEEK_U64(p) \ + (((u_int64_t)(((u_char *)(p))[0]) << 56) | \ + ((u_int64_t)(((u_char *)(p))[1]) << 48) | \ + ((u_int64_t)(((u_char *)(p))[2]) << 40) | \ + ((u_int64_t)(((u_char *)(p))[3]) << 32) | \ + ((u_int64_t)(((u_char *)(p))[4]) << 24) | \ + ((u_int64_t)(((u_char *)(p))[5]) << 16) | \ + ((u_int64_t)(((u_char *)(p))[6]) << 8) | \ + (u_int64_t)(((u_char *)(p))[7])) +#define PEEK_U32(p) \ + (((u_int32_t)(((u_char *)(p))[0]) << 24) | \ + ((u_int32_t)(((u_char *)(p))[1]) << 16) | \ + ((u_int32_t)(((u_char *)(p))[2]) << 8) | \ + (u_int32_t)(((u_char *)(p))[3])) +#define PEEK_U16(p) \ + (((u_int16_t)(((u_char *)(p))[0]) << 8) | \ + (u_int16_t)(((u_char *)(p))[1])) + +#define POKE_U64(p, v) \ + do { \ + ((u_char *)(p))[0] = (((u_int64_t)(v)) >> 56) & 0xff; \ + ((u_char *)(p))[1] = (((u_int64_t)(v)) >> 48) & 0xff; \ + ((u_char *)(p))[2] = (((u_int64_t)(v)) >> 40) & 0xff; \ + ((u_char *)(p))[3] = (((u_int64_t)(v)) >> 32) & 0xff; \ + ((u_char *)(p))[4] = (((u_int64_t)(v)) >> 24) & 0xff; \ + ((u_char *)(p))[5] = (((u_int64_t)(v)) >> 16) & 0xff; \ + ((u_char *)(p))[6] = (((u_int64_t)(v)) >> 8) & 0xff; \ + ((u_char *)(p))[7] = ((u_int64_t)(v)) & 0xff; \ + } while (0) +#define POKE_U32(p, v) \ + do { \ + ((u_char *)(p))[0] = (((u_int64_t)(v)) >> 24) & 0xff; \ + ((u_char *)(p))[1] = (((u_int64_t)(v)) >> 16) & 0xff; \ + ((u_char *)(p))[2] = (((u_int64_t)(v)) >> 8) & 0xff; \ + ((u_char *)(p))[3] = ((u_int64_t)(v)) & 0xff; \ + } while (0) +#define POKE_U16(p, v) \ + do { \ + ((u_char *)(p))[0] = (((u_int64_t)(v)) >> 8) & 0xff; \ + ((u_char *)(p))[1] = ((u_int64_t)(v)) & 0xff; \ + } while (0) + +/* Internal definitions follow. Exposed for regress tests */ +#ifdef SSHBUF_INTERNAL + +# define SSHBUF_SIZE_INIT 256 /* Initial allocation */ +# define SSHBUF_SIZE_INC 256 /* Preferred increment length */ +# define SSHBUF_PACK_MIN 8192 /* Minimim packable offset */ + +# define SSHBUF_ABORT abort +/* # define SSHBUF_DEBUG */ + +# ifndef SSHBUF_ABORT +# define SSHBUF_ABORT +# endif + +# ifdef SSHBUF_DEBUG +# define SSHBUF_TELL(what) do { \ + printf("%s:%d %s: %s size %zu alloc %zu off %zu max %zu\n", \ + __FILE__, __LINE__, __func__, what, \ + buf->size, buf->alloc, buf->off, buf->max_size); \ + fflush(stdout); \ + } while (0) +# define SSHBUF_DBG(x) do { \ + printf("%s:%d %s: ", __FILE__, __LINE__, __func__); \ + printf x; \ + printf("\n"); \ + fflush(stdout); \ + } while (0) +# else +# define SSHBUF_TELL(what) +# define SSHBUF_DBG(x) +# endif +#endif /* SSHBUF_INTERNAL */ + +#endif /* _SSHBUF_H */ diff --git a/ssh/sshconnect.c b/ssh/sshconnect.c index 6f8646e..39784c8 100644 --- a/ssh/sshconnect.c +++ b/ssh/sshconnect.c @@ -51,6 +51,7 @@ #include "roaming.h" #include "ssh2.h" #include "version.h" +#include "err.h" char *client_version_string = NULL; char *server_version_string = NULL; @@ -65,8 +66,8 @@ extern char *__progname; extern uid_t original_real_uid; extern uid_t original_effective_uid; -static int show_other_keys(struct hostkeys *, Key *); -static void warn_changed_key(Key *); +static int show_other_keys(struct hostkeys *, struct sshkey *); +static void warn_changed_key(struct sshkey *); /* * Connect to the given ssh server using a proxy command. @@ -582,15 +583,15 @@ confirm(const char *prompt) } static int -check_host_cert(const char *host, const Key *host_key) +check_host_cert(const char *host, const struct sshkey *host_key) { const char *reason; - if (key_cert_check_authority(host_key, 1, 0, host, &reason) != 0) { + if (sshkey_cert_check_authority(host_key, 1, 0, host, &reason) != 0) { error("%s", reason); return 0; } - if (buffer_len(&host_key->cert->critical) != 0) { + if (buffer_len(host_key->cert->critical) != 0) { error("Certificate for %s contains unsupported " "critical options(s)", host); return 0; @@ -664,13 +665,13 @@ get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr, #define ROQUIET 2 static int check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, - Key *host_key, int readonly, + struct sshkey *host_key, int readonly, char **user_hostfiles, u_int num_user_hostfiles, char **system_hostfiles, u_int num_system_hostfiles) { HostStatus host_status; HostStatus ip_status; - Key *raw_key = NULL; + struct sshkey *raw_key = NULL; char *ip = NULL, *host = NULL; char hostline[1000], *hostp, *fp, *ra; char msg[1024]; @@ -678,7 +679,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, const struct hostkey_entry *host_found, *ip_found; int len, cancelled_forwarding = 0; int local = sockaddr_is_local(hostaddr); - int r, want_cert = key_is_cert(host_key), host_ip_differ = 0; + int r, want_cert = sshkey_is_cert(host_key), host_ip_differ = 0; struct hostkeys *host_hostkeys, *ip_hostkeys; u_int i; @@ -728,8 +729,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, retry: /* Reload these as they may have changed on cert->key downgrade */ - want_cert = key_is_cert(host_key); - type = key_type(host_key); + want_cert = sshkey_is_cert(host_key); + type = sshkey_type(host_key); /* * Check if the host key is present in the user's list of known @@ -749,7 +750,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, if (host_status == HOST_CHANGED && (ip_status != HOST_CHANGED || (ip_found != NULL && - !key_equal(ip_found->key, host_found->key)))) + !sshkey_equal(ip_found->key, host_found->key)))) host_ip_differ = 1; } else ip_status = host_status; @@ -779,8 +780,9 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, "key for IP address '%.128s' to the list " "of known hosts.", type, ip); } else if (options.visual_host_key) { - fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); - ra = key_fingerprint(host_key, SSH_FP_MD5, + fp = sshkey_fingerprint(host_key, SSH_FP_MD5, + SSH_FP_HEX); + ra = sshkey_fingerprint(host_key, SSH_FP_MD5, SSH_FP_RANDOMART); logit("Host key fingerprint is %s\n%s\n", fp, ra); xfree(ra); @@ -820,8 +822,9 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, else snprintf(msg1, sizeof(msg1), "."); /* The default */ - fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); - ra = key_fingerprint(host_key, SSH_FP_MD5, + fp = sshkey_fingerprint(host_key, SSH_FP_MD5, + SSH_FP_HEX); + ra = sshkey_fingerprint(host_key, SSH_FP_MD5, SSH_FP_RANDOMART); msg2[0] = '\0'; if (options.verify_host_key_dns) { @@ -938,7 +941,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, warn_changed_key(host_key); error("Add correct host key in %.100s to get rid of this message.", user_hostfiles[0]); - error("Offending %s key in %s:%lu", key_type(host_found->key), + error("Offending %s key in %s:%lu", + sshkey_type(host_found->key), host_found->file, host_found->line); /* @@ -1061,14 +1065,16 @@ fail: * search normally. */ debug("No matching CA found. Retry with plain key"); - raw_key = key_from_private(host_key); - if (key_drop_cert(raw_key) != 0) - fatal("Couldn't drop certificate"); + if ((r = sshkey_from_private(host_key, &raw_key)) != 0) + fatal("%s: sshkey_from_private: %s", + __func__, ssh_err(r)); + if ((r = sshkey_drop_cert(raw_key)) != 0) + fatal("Couldn't drop certificate: %s", ssh_err(r)); host_key = raw_key; goto retry; } if (raw_key != NULL) - key_free(raw_key); + sshkey_free(raw_key); xfree(ip); xfree(host); if (host_hostkeys != NULL) @@ -1080,17 +1086,17 @@ fail: /* returns 0 if key verifies or -1 if key does NOT verify */ int -verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) +verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key) { int flags = 0; char *fp; - fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); - debug("Server host key: %s %s", key_type(host_key), fp); + fp = sshkey_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); + debug("Server host key: %s %s", sshkey_type(host_key), fp); xfree(fp); /* XXX certs are not yet supported for DNS */ - if (!key_is_cert(host_key) && options.verify_host_key_dns && + if (!sshkey_is_cert(host_key) && options.verify_host_key_dns && verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) { if (flags & DNS_VERIFY_FOUND) { @@ -1175,7 +1181,7 @@ ssh_put_password(char *password) /* print all known host keys for a given host, but skip keys of given type */ static int -show_other_keys(struct hostkeys *hostkeys, Key *key) +show_other_keys(struct hostkeys *hostkeys, struct sshkey *key) { int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, KEY_ECDSA, -1}; int i, ret = 0; @@ -1187,14 +1193,15 @@ show_other_keys(struct hostkeys *hostkeys, Key *key) continue; if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found)) continue; - fp = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_HEX); - ra = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_RANDOMART); + fp = sshkey_fingerprint(found->key, SSH_FP_MD5, SSH_FP_HEX); + ra = sshkey_fingerprint(found->key, SSH_FP_MD5, + SSH_FP_RANDOMART); logit("WARNING: %s key found for host %s\n" "in %s:%lu\n" "%s key fingerprint %s.", - key_type(found->key), + sshkey_type(found->key), found->host, found->file, found->line, - key_type(found->key), fp); + sshkey_type(found->key), fp); if (options.visual_host_key) logit("%s", ra); xfree(ra); @@ -1205,11 +1212,11 @@ show_other_keys(struct hostkeys *hostkeys, Key *key) } static void -warn_changed_key(Key *host_key) +warn_changed_key(struct sshkey *host_key) { char *fp; - fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); + fp = sshkey_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); @@ -1218,7 +1225,7 @@ warn_changed_key(Key *host_key) error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); error("It is also possible that a host key has just been changed."); error("The fingerprint for the %s key sent by the remote host is\n%s.", - key_type(host_key), fp); + sshkey_type(host_key), fp); error("Please contact your system administrator."); xfree(fp); diff --git a/ssh/sshconnect.h b/ssh/sshconnect.h index fd7f7f7..f5077a7 100644 --- a/ssh/sshconnect.h +++ b/ssh/sshconnect.h @@ -26,7 +26,7 @@ typedef struct Sensitive Sensitive; struct Sensitive { - Key **keys; + struct sshkey **keys; int nkeys; int external_keysign; }; @@ -41,7 +41,7 @@ void ssh_login(Sensitive *, const char *, struct sockaddr *, u_short, void ssh_exchange_identification(int); -int verify_host_key(char *, struct sockaddr *, Key *); +int verify_host_key(char *, struct sockaddr *, struct sshkey *); void get_hostfile_hostname_ipaddr(char *, struct sockaddr *, u_short, char **, char **); diff --git a/ssh/sshconnect1.c b/ssh/sshconnect1.c index 57b2084..e779f3c 100644 --- a/ssh/sshconnect1.c +++ b/ssh/sshconnect1.c @@ -64,7 +64,7 @@ try_agent_authentication(void) AuthenticationConnection *auth; u_char response[16]; u_int i; - Key *key; + struct sshkey *key; BIGNUM *challenge; /* Get connection to the agent. */ @@ -96,13 +96,13 @@ try_agent_authentication(void) does not support RSA authentication. */ if (type == SSH_SMSG_FAILURE) { debug("Server refused our key."); - key_free(key); + sshkey_free(key); continue; } /* Otherwise it should have sent a challenge. */ if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) - packet_disconnect("Protocol error during RSA authentication: %d", - type); + packet_disconnect("Protocol error during RSA " + "authentication: %d", type); packet_get_bignum(challenge); packet_check_eom(); @@ -119,7 +119,7 @@ try_agent_authentication(void) logit("Authentication agent failed to decrypt challenge."); memset(response, 0, sizeof(response)); } - key_free(key); + sshkey_free(key); debug("Sending response to RSA challenge."); /* Send the decrypted challenge back to the server. */ @@ -203,7 +203,7 @@ static int try_rsa_authentication(int idx) { BIGNUM *challenge; - Key *public, *private; + struct sshkey *public, *private; char buf[300], *passphrase, *comment, *authfile; int i, perm_ok = 1, type, quit; @@ -248,7 +248,7 @@ try_rsa_authentication(int idx) * load the private key. Try first with empty passphrase; if it * fails, ask for a passphrase. */ - if (public->flags & KEY_FLAG_EXT) + if (public->flags & SSHKEY_FLAG_EXT) private = public; else private = key_load_private_type(KEY_RSA1, authfile, "", NULL, @@ -297,8 +297,8 @@ try_rsa_authentication(int idx) respond_to_rsa_challenge(challenge, private->rsa); /* Destroy the private key unless it in external hardware. */ - if (!(private->flags & KEY_FLAG_EXT)) - key_free(private); + if (!(private->flags & SSHKEY_FLAG_EXT)) + sshkey_free(private); /* We no longer need the challenge. */ BN_clear_free(challenge); @@ -320,7 +320,7 @@ try_rsa_authentication(int idx) * authentication and RSA host authentication. */ static int -try_rhosts_rsa_authentication(const char *local_user, Key * host_key) +try_rhosts_rsa_authentication(const char *local_user, struct sshkey * host_key) { int type; BIGNUM *challenge; @@ -478,7 +478,7 @@ ssh_kex(char *host, struct sockaddr *hostaddr) { int i; BIGNUM *key; - Key *host_key, *server_key; + struct sshkey *host_key, *server_key; int bits, rbits; int ssh_cipher_default = SSH_CIPHER_3DES; u_char session_key[SSH_SESSION_KEY_LENGTH]; @@ -497,7 +497,8 @@ ssh_kex(char *host, struct sockaddr *hostaddr) cookie[i] = packet_get_char(); /* Get the public key. */ - server_key = key_new(KEY_RSA1); + if ((server_key = sshkey_new(KEY_RSA1)) == NULL) + fatal("%s: sshkey_new failed", __func__); bits = packet_get_int(); packet_get_bignum(server_key->rsa->e); packet_get_bignum(server_key->rsa->n); @@ -509,7 +510,8 @@ ssh_kex(char *host, struct sockaddr *hostaddr) logit("Warning: This may be due to an old implementation of ssh."); } /* Get the host key. */ - host_key = key_new(KEY_RSA1); + if ((host_key = sshkey_new(KEY_RSA1)) == NULL) + fatal("%s: sshkey_new failed", __func__); bits = packet_get_int(); packet_get_bignum(host_key->rsa->e); packet_get_bignum(host_key->rsa->n); @@ -607,8 +609,8 @@ ssh_kex(char *host, struct sockaddr *hostaddr) } /* Destroy the public keys since we no longer need them. */ - key_free(server_key); - key_free(host_key); + sshkey_free(server_key); + sshkey_free(host_key); if (options.cipher == SSH_CIPHER_NOT_SET) { if (cipher_mask_ssh1(1) & supported_ciphers & (1 << ssh_cipher_default)) diff --git a/ssh/sshconnect2.c b/ssh/sshconnect2.c index 45fab5c..2b4caaa 100644 --- a/ssh/sshconnect2.c +++ b/ssh/sshconnect2.c @@ -68,6 +68,8 @@ #include "hostfile.h" #include "schnorr.h" #include "jpake.h" +#include "compat.h" +#include "err.h" #ifdef GSSAPI #include "ssh-gss.h" @@ -86,7 +88,7 @@ u_char *session_id2 = NULL; u_int session_id2_len = 0; static int -verify_host_key_callback(Key *hostkey, struct ssh *ssh) +verify_host_key_callback(struct sshkey *hostkey, struct ssh *ssh) { if (verify_host_key(ssh->host, ssh->hostaddr, hostkey) == -1) fatal("Host key verification failed."); @@ -124,10 +126,10 @@ order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) } while (0) while ((alg = strsep(&avail, ",")) && *alg != '\0') { - if ((ktype = key_type_from_name(alg)) == KEY_UNSPEC) + if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC) fatal("%s: unknown alg %s", __func__, alg); if (lookup_key_in_hostkeys_by_type(hostkeys, - key_type_plain(ktype), NULL)) + sshkey_type_plain(ktype), NULL)) ALG_APPEND(first, alg); else ALG_APPEND(last, alg); @@ -236,7 +238,7 @@ typedef struct idlist Idlist; struct identity { TAILQ_ENTRY(identity) next; AuthenticationConnection *ac; /* set if agent supports key */ - Key *key; /* public/private key */ + struct sshkey *key; /* public/private key */ char *filename; /* comment for agent-only keys */ int tried; int isprivate; /* key points to the private key */ @@ -310,7 +312,7 @@ void userauth(struct ssh *, char *); static int sign_and_send_pubkey(struct ssh *, Identity *); static void pubkey_prepare(struct ssh *); static void pubkey_cleanup(struct ssh *); -static Key *load_identity_file(char *); +static struct sshkey *load_identity_file(char *); static Authmethod *authmethod_get(char *authlist); static Authmethod *authmethod_lookup(const char *name); @@ -559,13 +561,14 @@ void input_userauth_pk_ok(int type, u_int32_t seq, struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; - Key *key = NULL; + struct sshkey *key = NULL; Identity *id = NULL; Buffer b; int pktype, sent = 0; u_int alen, blen; char *pkalg, *fp; u_char *pkblob; + int r; if (authctxt == NULL) fatal("input_userauth_pk_ok: no authentication context"); @@ -585,12 +588,12 @@ input_userauth_pk_ok(int type, u_int32_t seq, struct ssh *ssh) debug("Server accepts key: pkalg %s blen %u", pkalg, blen); - if ((pktype = key_type_from_name(pkalg)) == KEY_UNSPEC) { + if ((pktype = sshkey_type_from_name(pkalg)) == KEY_UNSPEC) { debug("unknown pkalg %s", pkalg); goto done; } - if ((key = key_from_blob(pkblob, blen)) == NULL) { - debug("no key from blob. pkalg %s", pkalg); + if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) { + debug("no key from blob. pkalg %s: %s", pkalg, ssh_err(r)); goto done; } if (key->type != pktype) { @@ -599,7 +602,7 @@ input_userauth_pk_ok(int type, u_int32_t seq, struct ssh *ssh) key->type, pktype); goto done; } - fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + fp = sshkey_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); debug2("input_userauth_pk_ok: fp %s", fp); xfree(fp); @@ -609,14 +612,14 @@ input_userauth_pk_ok(int type, u_int32_t seq, struct ssh *ssh) * duplicate keys */ TAILQ_FOREACH_REVERSE(id, &authctxt->keys, idlist, next) { - if (key_equal(key, id->key)) { + if (sshkey_equal(key, id->key)) { sent = sign_and_send_pubkey(ssh, id); break; } } done: if (key != NULL) - key_free(key); + sshkey_free(key); xfree(pkalg); xfree(pkblob); @@ -1180,7 +1183,7 @@ static int identity_sign(Identity *id, u_char **sigp, u_int *lenp, u_char *data, u_int datalen) { - Key *prv; + struct sshkey *prv; int ret; /* the agent supports this key */ @@ -1191,13 +1194,14 @@ identity_sign(Identity *id, u_char **sigp, u_int *lenp, * we have already loaded the private key or * the private key is stored in external hardware */ - if (id->isprivate || (id->key->flags & KEY_FLAG_EXT)) - return (key_sign(id->key, sigp, lenp, data, datalen)); + if (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT)) + return (sshkey_sign(id->key, sigp, lenp, data, datalen, + datafellows)); /* load the private key from the file */ if ((prv = load_identity_file(id->filename)) == NULL) return (-1); - ret = key_sign(prv, sigp, lenp, data, datalen); - key_free(prv); + ret = sshkey_sign(prv, sigp, lenp, data, datalen, datafellows); + sshkey_free(prv); return (ret); } @@ -1209,17 +1213,17 @@ sign_and_send_pubkey(struct ssh *ssh, Identity *id) u_char *blob, *signature; u_int bloblen, slen; u_int skip = 0; - int ret = -1; + int r, ret = -1; int have_sig = 1; char *fp; - fp = key_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX); - debug3("sign_and_send_pubkey: %s %s", key_type(id->key), fp); + fp = sshkey_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX); + debug3("%s: %s %s", __func__, sshkey_type(id->key), fp); xfree(fp); - if (key_to_blob(id->key, &blob, &bloblen) == 0) { + if ((r = sshkey_to_blob(id->key, &blob, &bloblen)) != 0) { /* we cannot handle this key */ - debug3("sign_and_send_pubkey: cannot handle key"); + debug3("%s: cannot handle key: %s", __func__, ssh_err(r)); return 0; } /* data to be signed */ @@ -1244,14 +1248,14 @@ sign_and_send_pubkey(struct ssh *ssh, Identity *id) } else { buffer_put_cstring(&b, authctxt->method->name); buffer_put_char(&b, have_sig); - buffer_put_cstring(&b, key_ssh_name(id->key)); + buffer_put_cstring(&b, sshkey_ssh_name(id->key)); } buffer_put_string(&b, blob, bloblen); /* generate signature */ - ret = identity_sign(id, &signature, &slen, - buffer_ptr(&b), buffer_len(&b)); - if (ret == -1) { + if ((ret = identity_sign(id, &signature, &slen, + buffer_ptr(&b), buffer_len(&b))) != 0) { + error("signature failed: %s", ssh_err(ret)); xfree(blob); buffer_free(&b); return 0; @@ -1270,7 +1274,7 @@ sign_and_send_pubkey(struct ssh *ssh, Identity *id) buffer_put_cstring(&b, authctxt->method->name); buffer_put_char(&b, have_sig); if (!(datafellows & SSH_BUG_PKAUTH)) - buffer_put_cstring(&b, key_ssh_name(id->key)); + buffer_put_cstring(&b, sshkey_ssh_name(id->key)); buffer_put_string(&b, blob, bloblen); } xfree(blob); @@ -1299,12 +1303,13 @@ send_pubkey_test(struct ssh *ssh, Identity *id) Authctxt *authctxt = ssh->authctxt; u_char *blob; u_int bloblen, have_sig = 0; + int r; debug3("send_pubkey_test"); - if (key_to_blob(id->key, &blob, &bloblen) == 0) { + if ((r = sshkey_to_blob(id->key, &blob, &bloblen)) != 0) { /* we cannot handle this key */ - debug3("send_pubkey_test: cannot handle key"); + debug3("%s: cannot handle key: %s", __func__, ssh_err(r)); return 0; } /* register callback for USERAUTH_PK_OK message */ @@ -1316,17 +1321,17 @@ send_pubkey_test(struct ssh *ssh, Identity *id) ssh_packet_put_cstring(ssh, authctxt->method->name); ssh_packet_put_char(ssh, have_sig); if (!(datafellows & SSH_BUG_PKAUTH)) - ssh_packet_put_cstring(ssh, key_ssh_name(id->key)); + ssh_packet_put_cstring(ssh, sshkey_ssh_name(id->key)); ssh_packet_put_string(ssh, blob, bloblen); xfree(blob); ssh_packet_send(ssh); return 1; } -static Key * +static struct sshkey * load_identity_file(char *filename) { - Key *private; + struct sshkey *private; char prompt[300], *passphrase; int perm_ok = 0, quit, i; struct stat st; @@ -1375,7 +1380,7 @@ pubkey_prepare(struct ssh *ssh) Authctxt *authctxt = ssh->authctxt; Identity *id; Idlist agent, files, *preferred; - Key *key; + struct sshkey *key; AuthenticationConnection *ac; char *comment; int i, found; @@ -1406,8 +1411,8 @@ pubkey_prepare(struct ssh *ssh) found = 0; TAILQ_FOREACH(id, &files, next) { /* agent keys from the config file are preferred */ - if (key_equal(key, id->key)) { - key_free(key); + if (sshkey_equal(key, id->key)) { + sshkey_free(key); xfree(comment); TAILQ_REMOVE(&files, id, next); TAILQ_INSERT_TAIL(preferred, id, next); @@ -1453,7 +1458,7 @@ pubkey_cleanup(struct ssh *ssh) id = TAILQ_FIRST(&authctxt->keys)) { TAILQ_REMOVE(&authctxt->keys, id, next); if (id->key) - key_free(id->key); + sshkey_free(id->key); if (id->filename) xfree(id->filename); xfree(id); @@ -1479,8 +1484,8 @@ userauth_pubkey(struct ssh *ssh) * private key instead */ if (id->key && id->key->type != KEY_RSA1) { - debug("Offering %s public key: %s", key_type(id->key), - id->filename); + debug("Offering %s public key: %s", + sshkey_type(id->key), id->filename); sent = send_pubkey_test(ssh, id); } else if (id->key == NULL) { debug("Trying private key: %s", id->filename); @@ -1488,7 +1493,7 @@ userauth_pubkey(struct ssh *ssh) if (id->key != NULL) { id->isprivate = 1; sent = sign_and_send_pubkey(ssh, id); - key_free(id->key); + sshkey_free(id->key); id->key = NULL; } } @@ -1587,7 +1592,7 @@ input_userauth_info_req(int type, u_int32_t seq, struct ssh *ssh) } static int -ssh_keysign(struct ssh *ssh, Key *key, u_char **sigp, u_int *lenp, +ssh_keysign(struct ssh *ssh, struct sshkey *key, u_char **sigp, u_int *lenp, u_char *data, u_int datalen) { Buffer b; @@ -1667,7 +1672,7 @@ int userauth_hostbased(struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; - Key *private = NULL; + struct sshkey *private = NULL; Sensitive *sensitive = authctxt->sensitive; Buffer b; u_char *signature, *blob; @@ -1690,15 +1695,16 @@ userauth_hostbased(struct ssh *ssh) debug("No more client hostkeys for hostbased authentication."); return 0; } - if (key_to_blob(private, &blob, &blen) == 0) { - key_free(private); + if ((ok = sshkey_to_blob(private, &blob, &blen)) != 0) { + error("%s: sshkey_to_blob failed: %s", __func__, ssh_err(ok)); + sshkey_free(private); return 0; } /* figure out a name for the client host */ p = get_local_name(ssh_packet_get_connection_in(ssh)); if (p == NULL) { error("userauth_hostbased: cannot get local ipaddr/name"); - key_free(private); + sshkey_free(private); xfree(blob); return 0; } @@ -1708,7 +1714,7 @@ userauth_hostbased(struct ssh *ssh) service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" : authctxt->service; - pkalg = xstrdup(key_ssh_name(private)); + pkalg = xstrdup(sshkey_ssh_name(private)); buffer_init(&b); /* construct data */ buffer_put_string(&b, ssh->kex->session_id, ssh->kex->session_id_len); @@ -1727,12 +1733,12 @@ userauth_hostbased(struct ssh *ssh) ok = ssh_keysign(ssh, private, &signature, &slen, buffer_ptr(&b), buffer_len(&b)); else - ok = key_sign(private, &signature, &slen, - buffer_ptr(&b), buffer_len(&b)); - key_free(private); + ok = sshkey_sign(private, &signature, &slen, + buffer_ptr(&b), buffer_len(&b), datafellows); + sshkey_free(private); buffer_free(&b); if (ok != 0) { - error("key_sign failed"); + error("sshkey_sign failed"); xfree(chost); xfree(pkalg); xfree(blob); diff --git a/ssh/sshd.c b/ssh/sshd.c index d9905be..3af28e7 100644 --- a/ssh/sshd.c +++ b/ssh/sshd.c @@ -104,6 +104,7 @@ #include "roaming.h" #include "ssh-sandbox.h" #include "version.h" +#include "err.h" #ifdef LIBWRAP #include @@ -183,10 +184,10 @@ char *server_version_string = NULL; * not very useful. Currently, memory locking is not implemented. */ struct { - Key *server_key; /* ephemeral server key */ - Key *ssh1_host_key; /* ssh1 host key */ - Key **host_keys; /* all private host keys */ - Key **host_certificates; /* all public host certificates */ + struct sshkey *server_key; /* ephemeral server key */ + struct sshkey *ssh1_host_key; /* ssh1 host key */ + struct sshkey **host_keys; /* all private host keys */ + struct sshkey **host_certificates; /* all public host certs */ int have_ssh1_key; int have_ssh2_key; u_char ssh1_cookie[SSH_SESSION_KEY_LENGTH]; @@ -350,12 +351,16 @@ grace_alarm_handler(int sig) static void generate_ephemeral_server_key(void) { + int r; + verbose("Generating %s%d bit RSA key.", sensitive_data.server_key ? "new " : "", options.server_key_bits); if (sensitive_data.server_key != NULL) - key_free(sensitive_data.server_key); - sensitive_data.server_key = key_generate(KEY_RSA1, - options.server_key_bits); + sshkey_free(sensitive_data.server_key); + if ((r = sshkey_generate(KEY_RSA1, options.server_key_bits, + &sensitive_data.server_key)) != 0) + fatal("RSA1 key ephemeral key generation failed: %s", + ssh_err(r)); verbose("RSA key generation complete."); arc4random_buf(sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH); @@ -517,16 +522,16 @@ destroy_sensitive_data(void) int i; if (sensitive_data.server_key) { - key_free(sensitive_data.server_key); + sshkey_free(sensitive_data.server_key); sensitive_data.server_key = NULL; } for (i = 0; i < options.num_host_key_files; i++) { if (sensitive_data.host_keys[i]) { - key_free(sensitive_data.host_keys[i]); + sshkey_free(sensitive_data.host_keys[i]); sensitive_data.host_keys[i] = NULL; } if (sensitive_data.host_certificates[i]) { - key_free(sensitive_data.host_certificates[i]); + sshkey_free(sensitive_data.host_certificates[i]); sensitive_data.host_certificates[i] = NULL; } } @@ -538,19 +543,24 @@ destroy_sensitive_data(void) void demote_sensitive_data(void) { - Key *tmp; - int i; + struct sshkey *tmp; + int i, r; if (sensitive_data.server_key) { - tmp = key_demote(sensitive_data.server_key); - key_free(sensitive_data.server_key); + if ((r = sshkey_demote(sensitive_data.server_key, &tmp)) != 0) + fatal("could not demote server key: %s", ssh_err(r)); + sshkey_free(sensitive_data.server_key); sensitive_data.server_key = tmp; } for (i = 0; i < options.num_host_key_files; i++) { if (sensitive_data.host_keys[i]) { - tmp = key_demote(sensitive_data.host_keys[i]); - key_free(sensitive_data.host_keys[i]); + if ((r = sshkey_demote(sensitive_data.host_keys[i], + &tmp)) != 0) + fatal("could not demote host %s key: %s", + sshkey_type(sensitive_data.host_keys[i]), + ssh_err(r)); + sshkey_free(sensitive_data.host_keys[i]); sensitive_data.host_keys[i] = tmp; if (tmp->type == KEY_RSA1) sensitive_data.ssh1_host_key = tmp; @@ -730,7 +740,7 @@ list_hostkey_types(void) const char *p; char *ret; int i; - Key *key; + struct sshkey *key; buffer_init(&b); for (i = 0; i < options.num_host_key_files; i++) { @@ -743,7 +753,7 @@ list_hostkey_types(void) case KEY_ECDSA: if (buffer_len(&b) > 0) buffer_append(&b, ",", 1); - p = key_ssh_name(key); + p = sshkey_ssh_name(key); buffer_append(&b, p, strlen(p)); break; } @@ -759,7 +769,7 @@ list_hostkey_types(void) case KEY_ECDSA_CERT: if (buffer_len(&b) > 0) buffer_append(&b, ",", 1); - p = key_ssh_name(key); + p = sshkey_ssh_name(key); buffer_append(&b, p, strlen(p)); break; } @@ -771,11 +781,11 @@ list_hostkey_types(void) return ret; } -static Key * +static struct sshkey * get_hostkey_by_type(int type, int need_private) { int i; - Key *key; + struct sshkey *key; for (i = 0; i < options.num_host_key_files; i++) { switch (type) { @@ -797,19 +807,19 @@ get_hostkey_by_type(int type, int need_private) return NULL; } -Key * +struct sshkey * get_hostkey_public_by_type(int type, struct ssh *ssh) { return get_hostkey_by_type(type, 0); } -Key * +struct sshkey * get_hostkey_private_by_type(int type, struct ssh *ssh) { return get_hostkey_by_type(type, 1); } -Key * +struct sshkey * get_hostkey_by_index(int ind) { if (ind < 0 || ind >= options.num_host_key_files) @@ -818,12 +828,12 @@ get_hostkey_by_index(int ind) } int -get_hostkey_index(Key *key) +get_hostkey_index(struct sshkey *key) { int i; for (i = 0; i < options.num_host_key_files; i++) { - if (key_is_cert(key)) { + if (sshkey_is_cert(key)) { if (key == sensitive_data.host_certificates[i]) return (i); } else { @@ -940,8 +950,10 @@ recv_rexec_state(int fd, Buffer *conf) if (buffer_get_int(&m)) { if (sensitive_data.server_key != NULL) - key_free(sensitive_data.server_key); - sensitive_data.server_key = key_new_private(KEY_RSA1); + sshkey_free(sensitive_data.server_key); + sensitive_data.server_key = sshkey_new_private(KEY_RSA1); + if (sensitive_data.server_key == NULL) + fatal("%s: sshkey_new_private failed", __func__); buffer_get_bignum(&m, sensitive_data.server_key->rsa->e); buffer_get_bignum(&m, sensitive_data.server_key->rsa->n); buffer_get_bignum(&m, sensitive_data.server_key->rsa->d); @@ -1288,7 +1300,7 @@ main(int ac, char **av) int config_s[2] = { -1 , -1 }; u_int64_t ibytes, obytes; mode_t new_umask; - Key *key; + struct sshkey *key; Authctxt *authctxt; /* Save argv. */ @@ -1500,7 +1512,7 @@ main(int ac, char **av) /* load private host keys */ sensitive_data.host_keys = xcalloc(options.num_host_key_files, - sizeof(Key *)); + sizeof(struct sshkey *)); for (i = 0; i < options.num_host_key_files; i++) sensitive_data.host_keys[i] = NULL; @@ -1525,7 +1537,7 @@ main(int ac, char **av) break; } debug("private host key: #%d type %d %s", i, key->type, - key_type(key)); + sshkey_type(key)); } if ((options.protocol & SSH_PROTO_1) && !sensitive_data.have_ssh1_key) { logit("Disabling protocol version 1. Could not load host key"); @@ -1545,7 +1557,7 @@ main(int ac, char **av) * indices to the public keys that they relate to. */ sensitive_data.host_certificates = xcalloc(options.num_host_key_files, - sizeof(Key *)); + sizeof(struct sshkey *)); for (i = 0; i < options.num_host_key_files; i++) sensitive_data.host_certificates[i] = NULL; @@ -1556,15 +1568,15 @@ main(int ac, char **av) options.host_cert_files[i]); continue; } - if (!key_is_cert(key)) { + if (!sshkey_is_cert(key)) { error("Certificate file is not a certificate: %s", options.host_cert_files[i]); - key_free(key); + sshkey_free(key); continue; } /* Find matching private key */ for (j = 0; j < options.num_host_key_files; j++) { - if (key_equal_public(key, + if (sshkey_equal_public(key, sensitive_data.host_keys[j])) { sensitive_data.host_certificates[j] = key; break; @@ -1573,12 +1585,12 @@ main(int ac, char **av) if (j >= options.num_host_key_files) { error("No matching private key for certificate: %s", options.host_cert_files[i]); - key_free(key); + sshkey_free(key); continue; } sensitive_data.host_certificates[j] = key; debug("host certificate: #%d type %d %s", j, key->type, - key_type(key)); + sshkey_type(key)); } /* Check certain values for sanity. */ if (options.protocol & SSH_PROTO_1) {