| @@ -1,569 +1,569 @@ | | | @@ -1,569 +1,569 @@ |
1 | /* $NetBSD: ssh_api.c,v 1.14 2023/07/26 17:58:16 christos Exp $ */ | | 1 | /* $NetBSD: ssh_api.c,v 1.15 2023/07/28 04:58:27 rin Exp $ */ |
2 | /* $OpenBSD: ssh_api.c,v 1.27 2021/04/03 06:18:41 djm Exp $ */ | | 2 | /* $OpenBSD: ssh_api.c,v 1.27 2021/04/03 06:18:41 djm Exp $ */ |
3 | | | 3 | |
4 | /* | | 4 | /* |
5 | * Copyright (c) 2012 Markus Friedl. All rights reserved. | | 5 | * Copyright (c) 2012 Markus Friedl. All rights reserved. |
6 | * | | 6 | * |
7 | * Permission to use, copy, modify, and distribute this software for any | | 7 | * Permission to use, copy, modify, and distribute this software for any |
8 | * purpose with or without fee is hereby granted, provided that the above | | 8 | * purpose with or without fee is hereby granted, provided that the above |
9 | * copyright notice and this permission notice appear in all copies. | | 9 | * copyright notice and this permission notice appear in all copies. |
10 | * | | 10 | * |
11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | | 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | | 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | | 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | | 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | | 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | | 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | | 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
18 | */ | | 18 | */ |
19 | | | 19 | |
20 | #include "includes.h" | | 20 | #include "includes.h" |
21 | __RCSID("$NetBSD: ssh_api.c,v 1.14 2023/07/26 17:58:16 christos Exp $"); | | 21 | __RCSID("$NetBSD: ssh_api.c,v 1.15 2023/07/28 04:58:27 rin Exp $"); |
22 | | | 22 | |
23 | #include <sys/types.h> | | 23 | #include <sys/types.h> |
24 | | | 24 | |
25 | #include <stdio.h> | | 25 | #include <stdio.h> |
26 | #include <stdlib.h> | | 26 | #include <stdlib.h> |
27 | | | 27 | |
28 | #include "ssh_api.h" | | 28 | #include "ssh_api.h" |
29 | #include "compat.h" | | 29 | #include "compat.h" |
30 | #include "log.h" | | 30 | #include "log.h" |
31 | #include "authfile.h" | | 31 | #include "authfile.h" |
32 | #include "sshkey.h" | | 32 | #include "sshkey.h" |
33 | #include "misc.h" | | 33 | #include "misc.h" |
34 | #include "ssh2.h" | | 34 | #include "ssh2.h" |
35 | #include "version.h" | | 35 | #include "version.h" |
36 | #include "myproposal.h" | | 36 | #include "myproposal.h" |
37 | #include "ssherr.h" | | 37 | #include "ssherr.h" |
38 | #include "sshbuf.h" | | 38 | #include "sshbuf.h" |
39 | | | 39 | |
40 | #include <string.h> | | 40 | #include <string.h> |
41 | | | 41 | |
42 | int _ssh_exchange_banner(struct ssh *); | | 42 | int _ssh_exchange_banner(struct ssh *); |
43 | int _ssh_send_banner(struct ssh *, struct sshbuf *); | | 43 | int _ssh_send_banner(struct ssh *, struct sshbuf *); |
44 | int _ssh_read_banner(struct ssh *, struct sshbuf *); | | 44 | int _ssh_read_banner(struct ssh *, struct sshbuf *); |
45 | int _ssh_order_hostkeyalgs(struct ssh *); | | 45 | int _ssh_order_hostkeyalgs(struct ssh *); |
46 | int _ssh_verify_host_key(struct sshkey *, struct ssh *); | | 46 | int _ssh_verify_host_key(struct sshkey *, struct ssh *); |
47 | struct sshkey *_ssh_host_public_key(int, int, struct ssh *); | | 47 | struct sshkey *_ssh_host_public_key(int, int, struct ssh *); |
48 | struct sshkey *_ssh_host_private_key(int, int, struct ssh *); | | 48 | struct sshkey *_ssh_host_private_key(int, int, struct ssh *); |
49 | int _ssh_host_key_sign(struct ssh *, struct sshkey *, struct sshkey *, | | 49 | int _ssh_host_key_sign(struct ssh *, struct sshkey *, struct sshkey *, |
50 | u_char **, size_t *, const u_char *, size_t, const char *); | | 50 | u_char **, size_t *, const u_char *, size_t, const char *); |
51 | | | 51 | |
52 | /* | | 52 | /* |
53 | * stubs for the server side implementation of kex. | | 53 | * stubs for the server side implementation of kex. |
54 | * disable privsep so our stubs will never be called. | | 54 | * disable privsep so our stubs will never be called. |
55 | */ | | 55 | */ |
56 | int use_privsep = 0; | | 56 | int use_privsep = 0; |
57 | int mm_sshkey_sign(struct sshkey *, u_char **, u_int *, | | 57 | int mm_sshkey_sign(struct sshkey *, u_char **, u_int *, |
58 | const u_char *, u_int, const char *, const char *, const char *, u_int); | | 58 | const u_char *, u_int, const char *, const char *, const char *, u_int); |
59 | | | 59 | |
60 | #ifdef WITH_OPENSSL | | 60 | #ifdef WITH_OPENSSL |
61 | DH *mm_choose_dh(int, int, int); | | 61 | DH *mm_choose_dh(int, int, int); |
62 | #endif | | 62 | #endif |
63 | | | 63 | |
64 | int | | 64 | int |
65 | mm_sshkey_sign(struct sshkey *key, u_char **sigp, u_int *lenp, | | 65 | mm_sshkey_sign(struct sshkey *key, u_char **sigp, u_int *lenp, |
66 | const u_char *data, u_int datalen, const char *alg, | | 66 | const u_char *data, u_int datalen, const char *alg, |
67 | const char *sk_provider, const char *sk_pin, u_int compat) | | 67 | const char *sk_provider, const char *sk_pin, u_int compat) |
68 | { | | 68 | { |
69 | return (-1); | | 69 | return (-1); |
70 | } | | 70 | } |
71 | | | 71 | |
72 | #ifdef WITH_OPENSSL | | 72 | #ifdef WITH_OPENSSL |
73 | DH * | | 73 | DH * |
74 | mm_choose_dh(int min, int nbits, int max) | | 74 | mm_choose_dh(int min, int nbits, int max) |
75 | { | | 75 | { |
76 | return (NULL); | | 76 | return (NULL); |
77 | } | | 77 | } |
78 | #endif | | 78 | #endif |
79 | | | 79 | |
80 | /* API */ | | 80 | /* API */ |
81 | | | 81 | |
82 | int | | 82 | int |
83 | ssh_init(struct ssh **sshp, int is_server, struct kex_params *kex_params) | | 83 | ssh_init(struct ssh **sshp, int is_server, struct kex_params *kex_params) |
84 | { | | 84 | { |
85 | const char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; | | 85 | const char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; |
86 | struct ssh *ssh; | | 86 | struct ssh *ssh; |
87 | const char **proposal; | | 87 | const char **proposal; |
88 | static int called; | | 88 | static int called; |
89 | int r; | | 89 | int r; |
90 | | | 90 | |
91 | if (!called) { | | 91 | if (!called) { |
92 | #ifdef WITH_OPENSSL | | 92 | #ifdef WITH_OPENSSL |
93 | OpenSSL_add_all_algorithms(); | | 93 | OpenSSL_add_all_algorithms(); |
94 | #endif | | 94 | #endif |
95 | called = 1; | | 95 | called = 1; |
96 | } | | 96 | } |
97 | | | 97 | |
98 | if ((ssh = ssh_packet_set_connection(NULL, -1, -1)) == NULL) | | 98 | if ((ssh = ssh_packet_set_connection(NULL, -1, -1)) == NULL) |
99 | return SSH_ERR_ALLOC_FAIL; | | 99 | return SSH_ERR_ALLOC_FAIL; |
100 | if (is_server) | | 100 | if (is_server) |
101 | ssh_packet_set_server(ssh); | | 101 | ssh_packet_set_server(ssh); |
102 | | | 102 | |
103 | /* Initialize key exchange */ | | 103 | /* Initialize key exchange */ |
104 | proposal = kex_params ? __UNCONST(kex_params->proposal) : myproposal; | | 104 | proposal = kex_params ? __UNCONST(kex_params->proposal) : myproposal; |
105 | if ((r = kex_ready(ssh, __UNCONST(proposal))) != 0) { | | 105 | if ((r = kex_ready(ssh, __UNCONST(proposal))) != 0) { |
106 | ssh_free(ssh); | | 106 | ssh_free(ssh); |
107 | return r; | | 107 | return r; |
108 | } | | 108 | } |
109 | ssh->kex->server = is_server; | | 109 | ssh->kex->server = is_server; |
110 | if (is_server) { | | 110 | if (is_server) { |
111 | #ifdef WITH_OPENSSL | | 111 | #ifdef WITH_OPENSSL |
112 | ssh->kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_server; | | 112 | ssh->kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_server; |
113 | ssh->kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_server; | | 113 | ssh->kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_server; |
114 | ssh->kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_server; | | 114 | ssh->kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_server; |
115 | ssh->kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_server; | | 115 | ssh->kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_server; |
116 | ssh->kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_server; | | 116 | ssh->kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_server; |
117 | ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; | | 117 | ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; |
118 | ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; | | 118 | ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; |
119 | ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_server; | | 119 | ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_server; |
120 | #endif /* WITH_OPENSSL */ | | 120 | #endif /* WITH_OPENSSL */ |
121 | ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_server; | | 121 | ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_server; |
122 | ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server; | | 122 | ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server; |
123 | ssh->kex->load_host_public_key=&_ssh_host_public_key; | | 123 | ssh->kex->load_host_public_key=&_ssh_host_public_key; |
124 | ssh->kex->load_host_private_key=&_ssh_host_private_key; | | 124 | ssh->kex->load_host_private_key=&_ssh_host_private_key; |
125 | ssh->kex->sign=&_ssh_host_key_sign; | | 125 | ssh->kex->sign=&_ssh_host_key_sign; |
126 | } else { | | 126 | } else { |
127 | #ifdef WITH_OPENSSL | | 127 | #ifdef WITH_OPENSSL |
128 | ssh->kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_client; | | 128 | ssh->kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_client; |
129 | ssh->kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_client; | | 129 | ssh->kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_client; |
130 | ssh->kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_client; | | 130 | ssh->kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_client; |
131 | ssh->kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_client; | | 131 | ssh->kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_client; |
132 | ssh->kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_client; | | 132 | ssh->kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_client; |
133 | ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; | | 133 | ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; |
134 | ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; | | 134 | ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; |
135 | ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client; | | 135 | ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client; |
136 | #endif /* WITH_OPENSSL */ | | 136 | #endif /* WITH_OPENSSL */ |
137 | ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; | | 137 | ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; |
138 | ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client; | | 138 | ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client; |
139 | ssh->kex->verify_host_key =&_ssh_verify_host_key; | | 139 | ssh->kex->verify_host_key =&_ssh_verify_host_key; |
140 | } | | 140 | } |
141 | *sshp = ssh; | | 141 | *sshp = ssh; |
142 | return 0; | | 142 | return 0; |
143 | } | | 143 | } |
144 | | | 144 | |
145 | void | | 145 | void |
146 | ssh_free(struct ssh *ssh) | | 146 | ssh_free(struct ssh *ssh) |
147 | { | | 147 | { |
148 | struct key_entry *k; | | 148 | struct key_entry *k; |
149 | | | 149 | |
150 | if (ssh == NULL) | | 150 | if (ssh == NULL) |
151 | return; | | 151 | return; |
152 | | | 152 | |
153 | /* | | 153 | /* |
154 | * we've only created the public keys variants in case we | | 154 | * we've only created the public keys variants in case we |
155 | * are a acting as a server. | | 155 | * are a acting as a server. |
156 | */ | | 156 | */ |
157 | while ((k = TAILQ_FIRST(&ssh->public_keys)) != NULL) { | | 157 | while ((k = TAILQ_FIRST(&ssh->public_keys)) != NULL) { |
158 | TAILQ_REMOVE(&ssh->public_keys, k, next); | | 158 | TAILQ_REMOVE(&ssh->public_keys, k, next); |
159 | if (ssh->kex && ssh->kex->server) | | 159 | if (ssh->kex && ssh->kex->server) |
160 | sshkey_free(k->key); | | 160 | sshkey_free(k->key); |
161 | free(k); | | 161 | free(k); |
162 | } | | 162 | } |
163 | while ((k = TAILQ_FIRST(&ssh->private_keys)) != NULL) { | | 163 | while ((k = TAILQ_FIRST(&ssh->private_keys)) != NULL) { |
164 | TAILQ_REMOVE(&ssh->private_keys, k, next); | | 164 | TAILQ_REMOVE(&ssh->private_keys, k, next); |
165 | free(k); | | 165 | free(k); |
166 | } | | 166 | } |
167 | ssh_packet_close(ssh); | | 167 | ssh_packet_close(ssh); |
168 | free(ssh); | | 168 | free(ssh); |
169 | } | | 169 | } |
170 | | | 170 | |
171 | void | | 171 | void |
172 | ssh_set_app_data(struct ssh *ssh, void *app_data) | | 172 | ssh_set_app_data(struct ssh *ssh, void *app_data) |
173 | { | | 173 | { |
174 | ssh->app_data = app_data; | | 174 | ssh->app_data = app_data; |
175 | } | | 175 | } |
176 | | | 176 | |
177 | void * | | 177 | void * |
178 | ssh_get_app_data(struct ssh *ssh) | | 178 | ssh_get_app_data(struct ssh *ssh) |
179 | { | | 179 | { |
180 | return ssh->app_data; | | 180 | return ssh->app_data; |
181 | } | | 181 | } |
182 | | | 182 | |
183 | /* Returns < 0 on error, 0 otherwise */ | | 183 | /* Returns < 0 on error, 0 otherwise */ |
184 | int | | 184 | int |
185 | ssh_add_hostkey(struct ssh *ssh, struct sshkey *key) | | 185 | ssh_add_hostkey(struct ssh *ssh, struct sshkey *key) |
186 | { | | 186 | { |
187 | struct sshkey *pubkey = NULL; | | 187 | struct sshkey *pubkey = NULL; |
188 | struct key_entry *k = NULL, *k_prv = NULL; | | 188 | struct key_entry *k = NULL, *k_prv = NULL; |
189 | int r; | | 189 | int r; |
190 | | | 190 | |
191 | if (ssh->kex->server) { | | 191 | if (ssh->kex->server) { |
192 | if ((r = sshkey_from_private(key, &pubkey)) != 0) | | 192 | if ((r = sshkey_from_private(key, &pubkey)) != 0) |
193 | return r; | | 193 | return r; |
194 | if ((k = malloc(sizeof(*k))) == NULL || | | 194 | if ((k = malloc(sizeof(*k))) == NULL || |
195 | (k_prv = malloc(sizeof(*k_prv))) == NULL) { | | 195 | (k_prv = malloc(sizeof(*k_prv))) == NULL) { |
196 | free(k); | | 196 | free(k); |
197 | sshkey_free(pubkey); | | 197 | sshkey_free(pubkey); |
198 | return SSH_ERR_ALLOC_FAIL; | | 198 | return SSH_ERR_ALLOC_FAIL; |
199 | } | | 199 | } |
200 | k_prv->key = key; | | 200 | k_prv->key = key; |
201 | TAILQ_INSERT_TAIL(&ssh->private_keys, k_prv, next); | | 201 | TAILQ_INSERT_TAIL(&ssh->private_keys, k_prv, next); |
202 | | | 202 | |
203 | /* add the public key, too */ | | 203 | /* add the public key, too */ |
204 | k->key = pubkey; | | 204 | k->key = pubkey; |
205 | TAILQ_INSERT_TAIL(&ssh->public_keys, k, next); | | 205 | TAILQ_INSERT_TAIL(&ssh->public_keys, k, next); |
206 | r = 0; | | 206 | r = 0; |
207 | } else { | | 207 | } else { |
208 | if ((k = malloc(sizeof(*k))) == NULL) | | 208 | if ((k = malloc(sizeof(*k))) == NULL) |
209 | return SSH_ERR_ALLOC_FAIL; | | 209 | return SSH_ERR_ALLOC_FAIL; |
210 | k->key = key; | | 210 | k->key = key; |
211 | TAILQ_INSERT_TAIL(&ssh->public_keys, k, next); | | 211 | TAILQ_INSERT_TAIL(&ssh->public_keys, k, next); |
212 | r = 0; | | 212 | r = 0; |
213 | } | | 213 | } |
214 | | | 214 | |
215 | return r; | | 215 | return r; |
216 | } | | 216 | } |
217 | | | 217 | |
218 | int | | 218 | int |
219 | ssh_set_verify_host_key_callback(struct ssh *ssh, | | 219 | ssh_set_verify_host_key_callback(struct ssh *ssh, |
220 | int (*cb)(struct sshkey *, struct ssh *)) | | 220 | int (*cb)(struct sshkey *, struct ssh *)) |
221 | { | | 221 | { |
222 | if (cb == NULL || ssh->kex == NULL) | | 222 | if (cb == NULL || ssh->kex == NULL) |
223 | return SSH_ERR_INVALID_ARGUMENT; | | 223 | return SSH_ERR_INVALID_ARGUMENT; |
224 | | | 224 | |
225 | ssh->kex->verify_host_key = cb; | | 225 | ssh->kex->verify_host_key = cb; |
226 | | | 226 | |
227 | return 0; | | 227 | return 0; |
228 | } | | 228 | } |
229 | | | 229 | |
230 | int | | 230 | int |
231 | ssh_input_append(struct ssh *ssh, const u_char *data, size_t len) | | 231 | ssh_input_append(struct ssh *ssh, const u_char *data, size_t len) |
232 | { | | 232 | { |
233 | return sshbuf_put(ssh_packet_get_input(ssh), data, len); | | 233 | return sshbuf_put(ssh_packet_get_input(ssh), data, len); |
234 | } | | 234 | } |
235 | | | 235 | |
236 | int | | 236 | int |
237 | ssh_packet_next(struct ssh *ssh, u_char *typep) | | 237 | ssh_packet_next(struct ssh *ssh, u_char *typep) |
238 | { | | 238 | { |
239 | int r; | | 239 | int r; |
240 | u_int32_t seqnr; | | 240 | u_int32_t seqnr; |
241 | u_char type; | | 241 | u_char type; |
242 | | | 242 | |
243 | /* | | 243 | /* |
244 | * Try to read a packet. Return SSH_MSG_NONE if no packet or not | | 244 | * Try to read a packet. Return SSH_MSG_NONE if no packet or not |
245 | * enough data. | | 245 | * enough data. |
246 | */ | | 246 | */ |
247 | *typep = SSH_MSG_NONE; | | 247 | *typep = SSH_MSG_NONE; |
248 | if (sshbuf_len(ssh->kex->client_version) == 0 || | | 248 | if (sshbuf_len(ssh->kex->client_version) == 0 || |
249 | sshbuf_len(ssh->kex->server_version) == 0) | | 249 | sshbuf_len(ssh->kex->server_version) == 0) |
250 | return _ssh_exchange_banner(ssh); | | 250 | return _ssh_exchange_banner(ssh); |
251 | /* | | 251 | /* |
252 | * If we enough data and a dispatch function then | | 252 | * If we enough data and a dispatch function then |
253 | * call the function and get the next packet. | | 253 | * call the function and get the next packet. |
254 | * Otherwise return the packet type to the caller so it | | 254 | * Otherwise return the packet type to the caller so it |
255 | * can decide how to go on. | | 255 | * can decide how to go on. |
256 | * | | 256 | * |
257 | * We will only call the dispatch function for: | | 257 | * We will only call the dispatch function for: |
258 | * 20-29 Algorithm negotiation | | 258 | * 20-29 Algorithm negotiation |
259 | * 30-49 Key exchange method specific (numbers can be reused for | | 259 | * 30-49 Key exchange method specific (numbers can be reused for |
260 | * different authentication methods) | | 260 | * different authentication methods) |
261 | */ | | 261 | */ |
262 | for (;;) { | | 262 | for (;;) { |
263 | if ((r = ssh_packet_read_poll2(ssh, &type, &seqnr)) != 0) | | 263 | if ((r = ssh_packet_read_poll2(ssh, &type, &seqnr)) != 0) |
264 | return r; | | 264 | return r; |
265 | if (type > 0 && type < DISPATCH_MAX && | | 265 | if (type > 0 && type < DISPATCH_MAX && |
266 | type >= SSH2_MSG_KEXINIT && type <= SSH2_MSG_TRANSPORT_MAX && | | 266 | type >= SSH2_MSG_KEXINIT && type <= SSH2_MSG_TRANSPORT_MAX && |
267 | ssh->dispatch[type] != NULL) { | | 267 | ssh->dispatch[type] != NULL) { |
268 | if ((r = (*ssh->dispatch[type])(type, seqnr, ssh)) != 0) | | 268 | if ((r = (*ssh->dispatch[type])(type, seqnr, ssh)) != 0) |
269 | return r; | | 269 | return r; |
270 | } else { | | 270 | } else { |
271 | *typep = type; | | 271 | *typep = type; |
272 | return 0; | | 272 | return 0; |
273 | } | | 273 | } |
274 | } | | 274 | } |
275 | } | | 275 | } |
276 | | | 276 | |
277 | const u_char * | | 277 | const u_char * |
278 | ssh_packet_payload(struct ssh *ssh, size_t *lenp) | | 278 | ssh_packet_payload(struct ssh *ssh, size_t *lenp) |
279 | { | | 279 | { |
280 | return sshpkt_ptr(ssh, lenp); | | 280 | return sshpkt_ptr(ssh, lenp); |
281 | } | | 281 | } |
282 | | | 282 | |
283 | int | | 283 | int |
284 | ssh_packet_put(struct ssh *ssh, int type, const u_char *data, size_t len) | | 284 | ssh_packet_put(struct ssh *ssh, int type, const u_char *data, size_t len) |
285 | { | | 285 | { |
286 | int r; | | 286 | int r; |
287 | | | 287 | |
288 | if ((r = sshpkt_start(ssh, type)) != 0 || | | 288 | if ((r = sshpkt_start(ssh, type)) != 0 || |
289 | (r = sshpkt_put(ssh, data, len)) != 0 || | | 289 | (r = sshpkt_put(ssh, data, len)) != 0 || |
290 | (r = sshpkt_send(ssh)) != 0) | | 290 | (r = sshpkt_send(ssh)) != 0) |
291 | return r; | | 291 | return r; |
292 | return 0; | | 292 | return 0; |
293 | } | | 293 | } |
294 | | | 294 | |
295 | const u_char * | | 295 | const u_char * |
296 | ssh_output_ptr(struct ssh *ssh, size_t *len) | | 296 | ssh_output_ptr(struct ssh *ssh, size_t *len) |
297 | { | | 297 | { |
298 | struct sshbuf *output = ssh_packet_get_output(ssh); | | 298 | struct sshbuf *output = ssh_packet_get_output(ssh); |
299 | | | 299 | |
300 | *len = sshbuf_len(output); | | 300 | *len = sshbuf_len(output); |
301 | return sshbuf_ptr(output); | | 301 | return sshbuf_ptr(output); |
302 | } | | 302 | } |
303 | | | 303 | |
304 | int | | 304 | int |
305 | ssh_output_consume(struct ssh *ssh, size_t len) | | 305 | ssh_output_consume(struct ssh *ssh, size_t len) |
306 | { | | 306 | { |
307 | return sshbuf_consume(ssh_packet_get_output(ssh), len); | | 307 | return sshbuf_consume(ssh_packet_get_output(ssh), len); |
308 | } | | 308 | } |
309 | | | 309 | |
310 | int | | 310 | int |
311 | ssh_output_space(struct ssh *ssh, size_t len) | | 311 | ssh_output_space(struct ssh *ssh, size_t len) |
312 | { | | 312 | { |
313 | return (0 == sshbuf_check_reserve(ssh_packet_get_output(ssh), len)); | | 313 | return (0 == sshbuf_check_reserve(ssh_packet_get_output(ssh), len)); |
314 | } | | 314 | } |
315 | | | 315 | |
316 | int | | 316 | int |
317 | ssh_input_space(struct ssh *ssh, size_t len) | | 317 | ssh_input_space(struct ssh *ssh, size_t len) |
318 | { | | 318 | { |
319 | return (0 == sshbuf_check_reserve(ssh_packet_get_input(ssh), len)); | | 319 | return (0 == sshbuf_check_reserve(ssh_packet_get_input(ssh), len)); |
320 | } | | 320 | } |
321 | | | 321 | |
322 | /* Read other side's version identification. */ | | 322 | /* Read other side's version identification. */ |
323 | int | | 323 | int |
324 | _ssh_read_banner(struct ssh *ssh, struct sshbuf *banner) | | 324 | _ssh_read_banner(struct ssh *ssh, struct sshbuf *banner) |
325 | { | | 325 | { |
326 | struct sshbuf *input = ssh_packet_get_input(ssh); | | 326 | struct sshbuf *input = ssh_packet_get_input(ssh); |
327 | const char *mismatch = "Protocol mismatch.\r\n"; | | 327 | const char *mismatch = "Protocol mismatch.\r\n"; |
328 | const u_char *s = sshbuf_ptr(input); | | 328 | const u_char *s = sshbuf_ptr(input); |
329 | u_char c; | | 329 | u_char c; |
330 | char *cp = NULL, *remote_version = NULL; | | 330 | char *cp = NULL, *remote_version = NULL; |
331 | int r = 0, remote_major, remote_minor, expect_nl; | | 331 | int r = 0, remote_major, remote_minor, expect_nl; |
332 | size_t n, j; | | 332 | size_t n, j; |
333 | | | 333 | |
334 | for (j = n = 0;;) { | | 334 | for (j = n = 0;;) { |
335 | sshbuf_reset(banner); | | 335 | sshbuf_reset(banner); |
336 | expect_nl = 0; | | 336 | expect_nl = 0; |
337 | for (;;) { | | 337 | for (;;) { |
338 | if (j >= sshbuf_len(input)) | | 338 | if (j >= sshbuf_len(input)) |
339 | return 0; /* insufficient data in input buf */ | | 339 | return 0; /* insufficient data in input buf */ |
340 | c = s[j++]; | | 340 | c = s[j++]; |
341 | if (c == '\r') { | | 341 | if (c == '\r') { |
342 | expect_nl = 1; | | 342 | expect_nl = 1; |
343 | continue; | | 343 | continue; |
344 | } | | 344 | } |
345 | if (c == '\n') | | 345 | if (c == '\n') |
346 | break; | | 346 | break; |
347 | if (expect_nl) | | 347 | if (expect_nl) |
348 | goto bad; | | 348 | goto bad; |
349 | if ((r = sshbuf_put_u8(banner, c)) != 0) | | 349 | if ((r = sshbuf_put_u8(banner, c)) != 0) |
350 | return r; | | 350 | return r; |
351 | if (sshbuf_len(banner) > SSH_MAX_BANNER_LEN) | | 351 | if (sshbuf_len(banner) > SSH_MAX_BANNER_LEN) |
352 | goto bad; | | 352 | goto bad; |
353 | } | | 353 | } |
354 | if (sshbuf_len(banner) >= 4 && | | 354 | if (sshbuf_len(banner) >= 4 && |
355 | memcmp(sshbuf_ptr(banner), "SSH-", 4) == 0) | | 355 | memcmp(sshbuf_ptr(banner), "SSH-", 4) == 0) |
356 | break; | | 356 | break; |
357 | debug_f("%.*s", (int)sshbuf_len(banner), | | 357 | debug_f("%.*s", (int)sshbuf_len(banner), |
358 | sshbuf_ptr(banner)); | | 358 | sshbuf_ptr(banner)); |
359 | /* Accept lines before banner only on client */ | | 359 | /* Accept lines before banner only on client */ |
360 | if (ssh->kex->server || ++n > SSH_MAX_PRE_BANNER_LINES) { | | 360 | if (ssh->kex->server || ++n > SSH_MAX_PRE_BANNER_LINES) { |
361 | bad: | | 361 | bad: |
362 | if ((r = sshbuf_put(ssh_packet_get_output(ssh), | | 362 | if ((r = sshbuf_put(ssh_packet_get_output(ssh), |
363 | mismatch, strlen(mismatch))) != 0) | | 363 | mismatch, strlen(mismatch))) != 0) |
364 | return r; | | 364 | return r; |
365 | return SSH_ERR_NO_PROTOCOL_VERSION; | | 365 | return SSH_ERR_NO_PROTOCOL_VERSION; |
366 | } | | 366 | } |
367 | } | | 367 | } |
368 | if ((r = sshbuf_consume(input, j)) != 0) | | 368 | if ((r = sshbuf_consume(input, j)) != 0) |
369 | return r; | | 369 | return r; |
370 | | | 370 | |
371 | /* XXX remote version must be the same size as banner for sscanf */ | | 371 | /* XXX remote version must be the same size as banner for sscanf */ |
372 | if ((cp = sshbuf_dup_string(banner)) == NULL || | | 372 | if ((cp = sshbuf_dup_string(banner)) == NULL || |
373 | (remote_version = calloc(1, sshbuf_len(banner))) == NULL) { | | 373 | (remote_version = calloc(1, sshbuf_len(banner))) == NULL) { |
374 | r = SSH_ERR_ALLOC_FAIL; | | 374 | r = SSH_ERR_ALLOC_FAIL; |
375 | goto out; | | 375 | goto out; |
376 | } | | 376 | } |
377 | | | 377 | |
378 | /* | | 378 | /* |
379 | * Check that the versions match. In future this might accept | | 379 | * Check that the versions match. In future this might accept |
380 | * several versions and set appropriate flags to handle them. | | 380 | * several versions and set appropriate flags to handle them. |
381 | */ | | 381 | */ |
382 | if (sscanf(cp, "SSH-%d.%d-%[^\n]\n", | | 382 | if (sscanf(cp, "SSH-%d.%d-%[^\n]\n", |
383 | &remote_major, &remote_minor, remote_version) != 3) { | | 383 | &remote_major, &remote_minor, remote_version) != 3) { |
384 | r = SSH_ERR_INVALID_FORMAT; | | 384 | r = SSH_ERR_INVALID_FORMAT; |
385 | goto out; | | 385 | goto out; |
386 | } | | 386 | } |
387 | debug("Remote protocol version %d.%d, remote software version %.100s", | | 387 | debug("Remote protocol version %d.%d, remote software version %.100s", |
388 | remote_major, remote_minor, remote_version); | | 388 | remote_major, remote_minor, remote_version); |
389 | | | 389 | |
390 | compat_banner(ssh, remote_version); | | 390 | compat_banner(ssh, remote_version); |
391 | if (remote_major == 1 && remote_minor == 99) { | | 391 | if (remote_major == 1 && remote_minor == 99) { |
392 | remote_major = 2; | | 392 | remote_major = 2; |
393 | remote_minor = 0; | | 393 | remote_minor = 0; |
394 | } | | 394 | } |
395 | if (remote_major != 2) | | 395 | if (remote_major != 2) |
396 | r = SSH_ERR_PROTOCOL_MISMATCH; | | 396 | r = SSH_ERR_PROTOCOL_MISMATCH; |
397 | | | 397 | |
398 | debug("Remote version string %.100s", cp); | | 398 | debug("Remote version string %.100s", cp); |
399 | out: | | 399 | out: |
400 | free(cp); | | 400 | free(cp); |
401 | free(remote_version); | | 401 | free(remote_version); |
402 | return r; | | 402 | return r; |
403 | } | | 403 | } |
404 | | | 404 | |
405 | /* Send our own protocol version identification. */ | | 405 | /* Send our own protocol version identification. */ |
406 | int | | 406 | int |
407 | _ssh_send_banner(struct ssh *ssh, struct sshbuf *banner) | | 407 | _ssh_send_banner(struct ssh *ssh, struct sshbuf *banner) |
408 | { | | 408 | { |
409 | char *cp; | | 409 | char *cp; |
410 | int r; | | 410 | int r; |
411 | | | 411 | |
412 | if ((r = sshbuf_putf(banner, "SSH-2.0-%.100s\r\n", SSH_VERSION)) != 0) | | 412 | if ((r = sshbuf_putf(banner, "SSH-2.0-%.100s\r\n", SSH_VERSION)) != 0) |
413 | return r; | | 413 | return r; |
414 | if ((r = sshbuf_putb(ssh_packet_get_output(ssh), banner)) != 0) | | 414 | if ((r = sshbuf_putb(ssh_packet_get_output(ssh), banner)) != 0) |
415 | return r; | | 415 | return r; |
416 | /* Remove trailing \r\n */ | | 416 | /* Remove trailing \r\n */ |
417 | if ((r = sshbuf_consume_end(banner, 2)) != 0) | | 417 | if ((r = sshbuf_consume_end(banner, 2)) != 0) |
418 | return r; | | 418 | return r; |
419 | if ((cp = sshbuf_dup_string(banner)) == NULL) | | 419 | if ((cp = sshbuf_dup_string(banner)) == NULL) |
420 | return SSH_ERR_ALLOC_FAIL; | | 420 | return SSH_ERR_ALLOC_FAIL; |
421 | debug("Local version string %.100s", cp); | | 421 | debug("Local version string %.100s", cp); |
422 | free(cp); | | 422 | free(cp); |
423 | return 0; | | 423 | return 0; |
424 | } | | 424 | } |
425 | | | 425 | |
426 | int | | 426 | int |
427 | _ssh_exchange_banner(struct ssh *ssh) | | 427 | _ssh_exchange_banner(struct ssh *ssh) |
428 | { | | 428 | { |
429 | struct kex *kex = ssh->kex; | | 429 | struct kex *kex = ssh->kex; |
430 | int r; | | 430 | int r; |
431 | | | 431 | |
432 | /* | | 432 | /* |
433 | * if _ssh_read_banner() cannot parse a full version string | | 433 | * if _ssh_read_banner() cannot parse a full version string |
434 | * it will return NULL and we end up calling it again. | | 434 | * it will return NULL and we end up calling it again. |
435 | */ | | 435 | */ |
436 | | | 436 | |
437 | r = 0; | | 437 | r = 0; |
438 | if (kex->server) { | | 438 | if (kex->server) { |
439 | if (sshbuf_len(ssh->kex->server_version) == 0) | | 439 | if (sshbuf_len(ssh->kex->server_version) == 0) |
440 | r = _ssh_send_banner(ssh, ssh->kex->server_version); | | 440 | r = _ssh_send_banner(ssh, ssh->kex->server_version); |
441 | if (r == 0 && | | 441 | if (r == 0 && |
442 | sshbuf_len(ssh->kex->server_version) != 0 && | | 442 | sshbuf_len(ssh->kex->server_version) != 0 && |
443 | sshbuf_len(ssh->kex->client_version) == 0) | | 443 | sshbuf_len(ssh->kex->client_version) == 0) |
444 | r = _ssh_read_banner(ssh, ssh->kex->client_version); | | 444 | r = _ssh_read_banner(ssh, ssh->kex->client_version); |
445 | } else { | | 445 | } else { |
446 | if (sshbuf_len(ssh->kex->server_version) == 0) | | 446 | if (sshbuf_len(ssh->kex->server_version) == 0) |
447 | r = _ssh_read_banner(ssh, ssh->kex->server_version); | | 447 | r = _ssh_read_banner(ssh, ssh->kex->server_version); |
448 | if (r == 0 && | | 448 | if (r == 0 && |
449 | sshbuf_len(ssh->kex->server_version) != 0 && | | 449 | sshbuf_len(ssh->kex->server_version) != 0 && |
450 | sshbuf_len(ssh->kex->client_version) == 0) | | 450 | sshbuf_len(ssh->kex->client_version) == 0) |
451 | r = _ssh_send_banner(ssh, ssh->kex->client_version); | | 451 | r = _ssh_send_banner(ssh, ssh->kex->client_version); |
452 | } | | 452 | } |
453 | if (r != 0) | | 453 | if (r != 0) |
454 | return r; | | 454 | return r; |
455 | /* start initial kex as soon as we have exchanged the banners */ | | 455 | /* start initial kex as soon as we have exchanged the banners */ |
456 | if (sshbuf_len(ssh->kex->server_version) != 0 && | | 456 | if (sshbuf_len(ssh->kex->server_version) != 0 && |
457 | sshbuf_len(ssh->kex->client_version) != 0) { | | 457 | sshbuf_len(ssh->kex->client_version) != 0) { |
458 | if ((r = _ssh_order_hostkeyalgs(ssh)) != 0 || | | 458 | if ((r = _ssh_order_hostkeyalgs(ssh)) != 0 || |
459 | (r = kex_send_kexinit(ssh)) != 0) | | 459 | (r = kex_send_kexinit(ssh)) != 0) |
460 | return r; | | 460 | return r; |
461 | } | | 461 | } |
462 | return 0; | | 462 | return 0; |
463 | } | | 463 | } |
464 | | | 464 | |
465 | struct sshkey * | | 465 | struct sshkey * |
466 | _ssh_host_public_key(int type, int nid, struct ssh *ssh) | | 466 | _ssh_host_public_key(int type, int nid, struct ssh *ssh) |
467 | { | | 467 | { |
468 | struct key_entry *k; | | 468 | struct key_entry *k; |
469 | | | 469 | |
470 | debug3_f("need %d", type); | | 470 | debug3_f("need %d", type); |
471 | TAILQ_FOREACH(k, &ssh->public_keys, next) { | | 471 | TAILQ_FOREACH(k, &ssh->public_keys, next) { |
472 | debug3_f("check %s", sshkey_type(k->key)); | | 472 | debug3_f("check %s", sshkey_type(k->key)); |
473 | if (k->key->type == type && | | 473 | if (k->key->type == type && |
474 | (type != KEY_ECDSA || k->key->ecdsa_nid == nid)) | | 474 | (type != KEY_ECDSA || k->key->ecdsa_nid == nid)) |
475 | return (k->key); | | 475 | return (k->key); |
476 | } | | 476 | } |
477 | return (NULL); | | 477 | return (NULL); |
478 | } | | 478 | } |
479 | | | 479 | |
480 | struct sshkey * | | 480 | struct sshkey * |
481 | _ssh_host_private_key(int type, int nid, struct ssh *ssh) | | 481 | _ssh_host_private_key(int type, int nid, struct ssh *ssh) |
482 | { | | 482 | { |
483 | struct key_entry *k; | | 483 | struct key_entry *k; |
484 | | | 484 | |
485 | debug3_f("need %d", type); | | 485 | debug3_f("need %d", type); |
486 | TAILQ_FOREACH(k, &ssh->private_keys, next) { | | 486 | TAILQ_FOREACH(k, &ssh->private_keys, next) { |
487 | debug3_f("check %s", sshkey_type(k->key)); | | 487 | debug3_f("check %s", sshkey_type(k->key)); |
488 | if (k->key->type == type && | | 488 | if (k->key->type == type && |
489 | (type != KEY_ECDSA || k->key->ecdsa_nid == nid)) | | 489 | (type != KEY_ECDSA || k->key->ecdsa_nid == nid)) |
490 | return (k->key); | | 490 | return (k->key); |
491 | } | | 491 | } |
492 | return (NULL); | | 492 | return (NULL); |
493 | } | | 493 | } |
494 | | | 494 | |
495 | int | | 495 | int |
496 | _ssh_verify_host_key(struct sshkey *hostkey, struct ssh *ssh) | | 496 | _ssh_verify_host_key(struct sshkey *hostkey, struct ssh *ssh) |
497 | { | | 497 | { |
498 | struct key_entry *k; | | 498 | struct key_entry *k; |
499 | | | 499 | |
500 | debug3_f("need %s", sshkey_type(hostkey)); | | 500 | debug3_f("need %s", sshkey_type(hostkey)); |
501 | TAILQ_FOREACH(k, &ssh->public_keys, next) { | | 501 | TAILQ_FOREACH(k, &ssh->public_keys, next) { |
502 | debug3_f("check %s", sshkey_type(k->key)); | | 502 | debug3_f("check %s", sshkey_type(k->key)); |
503 | if (sshkey_equal_public(hostkey, k->key)) | | 503 | if (sshkey_equal_public(hostkey, k->key)) |
504 | return (0); /* ok */ | | 504 | return (0); /* ok */ |
505 | } | | 505 | } |
506 | return (-1); /* failed */ | | 506 | return (-1); /* failed */ |
507 | } | | 507 | } |
508 | | | 508 | |
509 | /* offer hostkey algorithms in kexinit depending on registered keys */ | | 509 | /* offer hostkey algorithms in kexinit depending on registered keys */ |
510 | int | | 510 | int |
511 | _ssh_order_hostkeyalgs(struct ssh *ssh) | | 511 | _ssh_order_hostkeyalgs(struct ssh *ssh) |
512 | { | | 512 | { |
513 | struct key_entry *k; | | 513 | struct key_entry *k; |
514 | char *orig, *avail, *oavail = NULL, *alg, *replace = NULL; | | 514 | char *orig, *avail, *oavail = NULL, *alg, *replace = NULL; |
515 | char **proposal; | | 515 | char **proposal; |
516 | size_t maxlen; | | 516 | size_t maxlen; |
517 | int ktype, r; | | 517 | int ktype, r; |
518 | | | 518 | |
519 | /* XXX we de-serialize ssh->kex->my, modify it, and change it */ | | 519 | /* XXX we de-serialize ssh->kex->my, modify it, and change it */ |
520 | if ((r = kex_buf2prop(ssh->kex->my, NULL, &proposal)) != 0) | | 520 | if ((r = kex_buf2prop(ssh->kex->my, NULL, &proposal)) != 0) |
521 | return r; | | 521 | return r; |
522 | orig = proposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; | | 522 | orig = proposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; |
523 | if ((oavail = avail = strdup(orig)) == NULL) { | | 523 | if ((oavail = avail = strdup(orig)) == NULL) { |
524 | r = SSH_ERR_ALLOC_FAIL; | | 524 | r = SSH_ERR_ALLOC_FAIL; |
525 | goto out; | | 525 | goto out; |
526 | } | | 526 | } |
527 | maxlen = strlen(avail) + 1; | | 527 | maxlen = strlen(avail) + 1; |
528 | if ((replace = calloc(1, maxlen)) == NULL) { | | 528 | if ((replace = calloc(1, maxlen)) == NULL) { |
529 | r = SSH_ERR_ALLOC_FAIL; | | 529 | r = SSH_ERR_ALLOC_FAIL; |
530 | goto out; | | 530 | goto out; |
531 | } | | 531 | } |
532 | *replace = '\0'; | | 532 | *replace = '\0'; |
533 | while ((alg = strsep(&avail, ",")) && *alg != '\0') { | | 533 | while ((alg = strsep(&avail, ",")) && *alg != '\0') { |
534 | if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC) | | 534 | if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC) |
535 | continue; | | 535 | continue; |
536 | TAILQ_FOREACH(k, &ssh->public_keys, next) { | | 536 | TAILQ_FOREACH(k, &ssh->public_keys, next) { |
537 | if (k->key->type == ktype || | | 537 | if (k->key->type == ktype || |
538 | (sshkey_is_cert(k->key) && k->key->type == | | 538 | (sshkey_is_cert(k->key) && k->key->type == |
539 | sshkey_type_plain(ktype))) { | | 539 | sshkey_type_plain(ktype))) { |
540 | if (*replace != '\0') | | 540 | if (*replace != '\0') |
541 | strlcat(replace, ",", maxlen); | | 541 | strlcat(replace, ",", maxlen); |
542 | strlcat(replace, alg, maxlen); | | 542 | strlcat(replace, alg, maxlen); |
543 | break; | | 543 | break; |
544 | } | | 544 | } |
545 | } | | 545 | } |
546 | } | | 546 | } |
547 | if (*replace != '\0') { | | 547 | if (*replace != '\0') { |
548 | debug2_f("orig/%d %s", ssh->kex->server, orig); | | 548 | debug2_f("orig/%d %s", ssh->kex->server, orig); |
549 | debug2_f("replace/%d %s", ssh->kex->server, replace); | | 549 | debug2_f("replace/%d %s", ssh->kex->server, replace); |
550 | free(orig); | | 550 | free(orig); |
551 | proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = replace; | | 551 | proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = replace; |
552 | replace = NULL; /* owned by proposal */ | | 552 | replace = NULL; /* owned by proposal */ |
553 | r = kex_prop2buf(ssh->kex->my, proposal); | | 553 | r = kex_prop2buf(ssh->kex->my, proposal); |
554 | } | | 554 | } |
555 | out: | | 555 | out: |
556 | free(oavail); | | 556 | free(oavail); |
557 | free(replace); | | 557 | free(replace); |
558 | kex_prop_free(proposal); | | 558 | kex_prop_free(proposal); |
559 | return r; | | 559 | return r; |
560 | } | | 560 | } |
561 | | | 561 | |
562 | int | | 562 | int |
563 | _ssh_host_key_sign(struct ssh *ssh, struct sshkey *privkey, | | 563 | _ssh_host_key_sign(struct ssh *ssh, struct sshkey *privkey, |
564 | struct sshkey *pubkey, u_char **signature, size_t *slen, | | 564 | struct sshkey *pubkey, u_char **signature, size_t *slen, |
565 | const u_char *data, size_t dlen, const char *alg) | | 565 | const u_char *data, size_t dlen, const char *alg) |
566 | { | | 566 | { |
567 | return sshkey_sign(privkey, signature, slen, data, dlen, | | 567 | return sshkey_sign(privkey, signature, slen, data, dlen, |
568 | alg, NULL, NULL, ssh->compat); | | 568 | alg, NULL, NULL, ssh->compat); |
569 | } | | 569 | } |