diff options
-rw-r--r-- | Makefile.in | 16 | ||||
-rw-r--r-- | packet.c | 4 | ||||
-rw-r--r-- | packet.h | 14 | ||||
-rw-r--r-- | ssh_api.c | 527 | ||||
-rw-r--r-- | ssh_api.h | 136 |
5 files changed, 688 insertions, 9 deletions
diff --git a/Makefile.in b/Makefile.in index 13256c2af..b494ac7ca 100644 --- a/Makefile.in +++ b/Makefile.in | |||
@@ -65,6 +65,7 @@ MANFMT=@MANFMT@ | |||
65 | TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) | 65 | TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) |
66 | 66 | ||
67 | LIBOPENSSH_OBJS=\ | 67 | LIBOPENSSH_OBJS=\ |
68 | ssh_api.o \ | ||
68 | ssherr.o \ | 69 | ssherr.o \ |
69 | sshbuf.o \ | 70 | sshbuf.o \ |
70 | sshkey.o \ | 71 | sshkey.o \ |
@@ -75,20 +76,22 @@ LIBOPENSSH_OBJS=\ | |||
75 | bitmap.o | 76 | bitmap.o |
76 | 77 | ||
77 | LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ | 78 | LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ |
78 | authfd.o authfile.o bufaux.o bufbn.o buffer.o \ | 79 | authfd.o authfile.o bufaux.o bufbn.o bufec.o buffer.o \ |
79 | canohost.o channels.o cipher.o cipher-aes.o cipher-aesctr.o \ | 80 | canohost.o channels.o cipher.o cipher-aes.o cipher-aesctr.o \ |
80 | cipher-bf1.o cipher-ctr.o cipher-3des1.o cleanup.o \ | 81 | cipher-bf1.o cipher-ctr.o cipher-3des1.o cleanup.o \ |
81 | compat.o crc32.o deattack.o fatal.o hostfile.o \ | 82 | compat.o crc32.o deattack.o fatal.o hostfile.o \ |
82 | log.o match.o md-sha256.o moduli.o nchan.o packet.o opacket.o \ | 83 | log.o match.o md-sha256.o moduli.o nchan.o packet.o opacket.o \ |
83 | readpass.o rsa.o ttymodes.o xmalloc.o addrmatch.o \ | 84 | readpass.o rsa.o ttymodes.o xmalloc.o addrmatch.o \ |
84 | atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \ | 85 | atomicio.o key.o dispatch.o mac.o uidswap.o uuencode.o misc.o \ |
85 | monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ | 86 | monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ |
86 | kexdh.o kexgex.o kexdhc.o kexgexc.o bufec.o kexecdh.o kexecdhc.o \ | ||
87 | msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ | 87 | msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ |
88 | ssh-pkcs11.o smult_curve25519_ref.o \ | 88 | ssh-pkcs11.o smult_curve25519_ref.o \ |
89 | kexc25519.o kexc25519c.o poly1305.o chacha.o cipher-chachapoly.o \ | 89 | poly1305.o chacha.o cipher-chachapoly.o \ |
90 | ssh-ed25519.o digest-openssl.o digest-libc.o hmac.o \ | 90 | ssh-ed25519.o digest-openssl.o digest-libc.o hmac.o \ |
91 | sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o blocks.o | 91 | sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o blocks.o \ |
92 | kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ | ||
93 | kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o \ | ||
94 | kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o | ||
92 | 95 | ||
93 | SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ | 96 | SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ |
94 | sshconnect.o sshconnect1.o sshconnect2.o mux.o \ | 97 | sshconnect.o sshconnect1.o sshconnect2.o mux.o \ |
@@ -101,8 +104,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \ | |||
101 | auth-chall.o auth2-chall.o groupaccess.o \ | 104 | auth-chall.o auth2-chall.o groupaccess.o \ |
102 | auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ | 105 | auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ |
103 | auth2-none.o auth2-passwd.o auth2-pubkey.o \ | 106 | auth2-none.o auth2-passwd.o auth2-pubkey.o \ |
104 | monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o kexecdhs.o \ | 107 | monitor_mm.o monitor.o monitor_wrap.o auth-krb5.o \ |
105 | kexc25519s.o auth-krb5.o \ | ||
106 | auth2-gss.o gss-serv.o gss-serv-krb5.o \ | 108 | auth2-gss.o gss-serv.o gss-serv-krb5.o \ |
107 | loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ | 109 | loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ |
108 | sftp-server.o sftp-common.o \ | 110 | sftp-server.o sftp-common.o \ |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: packet.c,v 1.201 2015/01/19 19:52:16 markus Exp $ */ | 1 | /* $OpenBSD: packet.c,v 1.202 2015/01/19 20:30:23 markus Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -233,6 +233,8 @@ ssh_alloc_session_state(void) | |||
233 | (state->incoming_packet = sshbuf_new()) == NULL) | 233 | (state->incoming_packet = sshbuf_new()) == NULL) |
234 | goto fail; | 234 | goto fail; |
235 | TAILQ_INIT(&state->outgoing); | 235 | TAILQ_INIT(&state->outgoing); |
236 | TAILQ_INIT(&ssh->private_keys); | ||
237 | TAILQ_INIT(&ssh->public_keys); | ||
236 | state->connection_in = -1; | 238 | state->connection_in = -1; |
237 | state->connection_out = -1; | 239 | state->connection_out = -1; |
238 | state->max_packet_size = 32768; | 240 | state->max_packet_size = 32768; |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: packet.h,v 1.63 2015/01/19 20:07:45 markus Exp $ */ | 1 | /* $OpenBSD: packet.h,v 1.64 2015/01/19 20:30:23 markus Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
@@ -34,6 +34,11 @@ struct session_state; /* private session data */ | |||
34 | 34 | ||
35 | #include "dispatch.h" /* typedef, DISPATCH_MAX */ | 35 | #include "dispatch.h" /* typedef, DISPATCH_MAX */ |
36 | 36 | ||
37 | struct key_entry { | ||
38 | TAILQ_ENTRY(key_entry) next; | ||
39 | struct sshkey *key; | ||
40 | }; | ||
41 | |||
37 | struct ssh { | 42 | struct ssh { |
38 | /* Session state */ | 43 | /* Session state */ |
39 | struct session_state *state; | 44 | struct session_state *state; |
@@ -52,6 +57,13 @@ struct ssh { | |||
52 | 57 | ||
53 | /* datafellows */ | 58 | /* datafellows */ |
54 | int compat; | 59 | int compat; |
60 | |||
61 | /* Lists for private and public keys */ | ||
62 | TAILQ_HEAD(, key_entry) private_keys; | ||
63 | TAILQ_HEAD(, key_entry) public_keys; | ||
64 | |||
65 | /* APP data */ | ||
66 | void *app_data; | ||
55 | }; | 67 | }; |
56 | 68 | ||
57 | struct ssh *ssh_alloc_session_state(void); | 69 | struct ssh *ssh_alloc_session_state(void); |
diff --git a/ssh_api.c b/ssh_api.c new file mode 100644 index 000000000..1df995c94 --- /dev/null +++ b/ssh_api.c | |||
@@ -0,0 +1,527 @@ | |||
1 | /* $OpenBSD: ssh_api.c,v 1.1 2015/01/19 20:30:23 markus Exp $ */ | ||
2 | /* | ||
3 | * Copyright (c) 2012 Markus Friedl. All rights reserved. | ||
4 | * | ||
5 | * Permission to use, copy, modify, and distribute this software for any | ||
6 | * purpose with or without fee is hereby granted, provided that the above | ||
7 | * copyright notice and this permission notice appear in all copies. | ||
8 | * | ||
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
16 | */ | ||
17 | |||
18 | #include "includes.h" | ||
19 | |||
20 | #include "ssh1.h" /* For SSH_MSG_NONE */ | ||
21 | #include "ssh_api.h" | ||
22 | #include "compat.h" | ||
23 | #include "log.h" | ||
24 | #include "authfile.h" | ||
25 | #include "sshkey.h" | ||
26 | #include "misc.h" | ||
27 | #include "ssh1.h" | ||
28 | #include "ssh2.h" | ||
29 | #include "version.h" | ||
30 | #include "myproposal.h" | ||
31 | #include "ssherr.h" | ||
32 | #include "sshbuf.h" | ||
33 | |||
34 | #include <string.h> | ||
35 | |||
36 | int _ssh_exchange_banner(struct ssh *); | ||
37 | int _ssh_send_banner(struct ssh *, char **); | ||
38 | int _ssh_read_banner(struct ssh *, char **); | ||
39 | int _ssh_order_hostkeyalgs(struct ssh *); | ||
40 | int _ssh_verify_host_key(struct sshkey *, struct ssh *); | ||
41 | struct sshkey *_ssh_host_public_key(int, struct ssh *); | ||
42 | struct sshkey *_ssh_host_private_key(int, struct ssh *); | ||
43 | int _ssh_host_key_sign(struct sshkey *, struct sshkey *, u_char **, | ||
44 | size_t *, u_char *, size_t, u_int); | ||
45 | |||
46 | /* | ||
47 | * stubs for the server side implementation of kex. | ||
48 | * disable privsep so our stubs will never be called. | ||
49 | */ | ||
50 | int use_privsep = 0; | ||
51 | int mm_sshkey_sign(struct sshkey *, u_char **, u_int *, | ||
52 | u_char *, u_int, u_int); | ||
53 | DH *mm_choose_dh(int, int, int); | ||
54 | |||
55 | /* Define these two variables here so that they are part of the library */ | ||
56 | u_char *session_id2 = NULL; | ||
57 | u_int session_id2_len = 0; | ||
58 | |||
59 | int | ||
60 | mm_sshkey_sign(struct sshkey *key, u_char **sigp, u_int *lenp, | ||
61 | u_char *data, u_int datalen, u_int compat) | ||
62 | { | ||
63 | return (-1); | ||
64 | } | ||
65 | |||
66 | DH * | ||
67 | mm_choose_dh(int min, int nbits, int max) | ||
68 | { | ||
69 | return (NULL); | ||
70 | } | ||
71 | |||
72 | /* API */ | ||
73 | |||
74 | int | ||
75 | ssh_init(struct ssh **sshp, int is_server, struct kex_params *kex_params) | ||
76 | { | ||
77 | char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; | ||
78 | struct ssh *ssh; | ||
79 | char **proposal; | ||
80 | static int called; | ||
81 | int r; | ||
82 | |||
83 | if (!called) { | ||
84 | OpenSSL_add_all_algorithms(); | ||
85 | called = 1; | ||
86 | } | ||
87 | |||
88 | ssh = ssh_packet_set_connection(NULL, -1, -1); | ||
89 | if (is_server) | ||
90 | ssh_packet_set_server(ssh); | ||
91 | |||
92 | /* Initialize key exchange */ | ||
93 | proposal = kex_params ? kex_params->proposal : myproposal; | ||
94 | if ((r = kex_new(ssh, proposal, &ssh->kex)) != 0) { | ||
95 | ssh_free(ssh); | ||
96 | return r; | ||
97 | } | ||
98 | ssh->kex->server = is_server; | ||
99 | if (is_server) { | ||
100 | #ifdef WITH_OPENSSL | ||
101 | ssh->kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; | ||
102 | ssh->kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; | ||
103 | ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; | ||
104 | ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; | ||
105 | ssh->kex->kex[KEX_ECDH_SHA2] = kexecdh_server; | ||
106 | #endif /* WITH_OPENSSL */ | ||
107 | ssh->kex->kex[KEX_C25519_SHA256] = kexc25519_server; | ||
108 | ssh->kex->load_host_public_key=&_ssh_host_public_key; | ||
109 | ssh->kex->load_host_private_key=&_ssh_host_private_key; | ||
110 | ssh->kex->sign=&_ssh_host_key_sign; | ||
111 | } else { | ||
112 | #ifdef WITH_OPENSSL | ||
113 | ssh->kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; | ||
114 | ssh->kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; | ||
115 | ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; | ||
116 | ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; | ||
117 | ssh->kex->kex[KEX_ECDH_SHA2] = kexecdh_client; | ||
118 | #endif /* WITH_OPENSSL */ | ||
119 | ssh->kex->kex[KEX_C25519_SHA256] = kexc25519_client; | ||
120 | ssh->kex->verify_host_key =&_ssh_verify_host_key; | ||
121 | } | ||
122 | *sshp = ssh; | ||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | void | ||
127 | ssh_free(struct ssh *ssh) | ||
128 | { | ||
129 | struct key_entry *k; | ||
130 | |||
131 | ssh_packet_close(ssh); | ||
132 | /* | ||
133 | * we've only created the public keys variants in case we | ||
134 | * are a acting as a server. | ||
135 | */ | ||
136 | while ((k = TAILQ_FIRST(&ssh->public_keys)) != NULL) { | ||
137 | TAILQ_REMOVE(&ssh->public_keys, k, next); | ||
138 | if (ssh->kex && ssh->kex->server) | ||
139 | sshkey_free(k->key); | ||
140 | free(k); | ||
141 | } | ||
142 | while ((k = TAILQ_FIRST(&ssh->private_keys)) != NULL) { | ||
143 | TAILQ_REMOVE(&ssh->private_keys, k, next); | ||
144 | free(k); | ||
145 | } | ||
146 | if (ssh->kex) | ||
147 | kex_free(ssh->kex); | ||
148 | free(ssh); | ||
149 | } | ||
150 | |||
151 | void | ||
152 | ssh_set_app_data(struct ssh *ssh, void *app_data) | ||
153 | { | ||
154 | ssh->app_data = app_data; | ||
155 | } | ||
156 | |||
157 | void * | ||
158 | ssh_get_app_data(struct ssh *ssh) | ||
159 | { | ||
160 | return ssh->app_data; | ||
161 | } | ||
162 | |||
163 | /* Returns < 0 on error, 0 otherwise */ | ||
164 | int | ||
165 | ssh_add_hostkey(struct ssh *ssh, struct sshkey *key) | ||
166 | { | ||
167 | struct sshkey *pubkey = NULL; | ||
168 | struct key_entry *k = NULL, *k_prv = NULL; | ||
169 | int r; | ||
170 | |||
171 | if (ssh->kex->server) { | ||
172 | if ((r = sshkey_from_private(key, &pubkey)) != 0) | ||
173 | return r; | ||
174 | if ((k = malloc(sizeof(*k))) == NULL || | ||
175 | (k_prv = malloc(sizeof(*k_prv))) == NULL) { | ||
176 | free(k); | ||
177 | sshkey_free(pubkey); | ||
178 | return SSH_ERR_ALLOC_FAIL; | ||
179 | } | ||
180 | k_prv->key = key; | ||
181 | TAILQ_INSERT_TAIL(&ssh->private_keys, k_prv, next); | ||
182 | |||
183 | /* add the public key, too */ | ||
184 | k->key = pubkey; | ||
185 | TAILQ_INSERT_TAIL(&ssh->public_keys, k, next); | ||
186 | r = 0; | ||
187 | } else { | ||
188 | if ((k = malloc(sizeof(*k))) == NULL) | ||
189 | return SSH_ERR_ALLOC_FAIL; | ||
190 | k->key = key; | ||
191 | TAILQ_INSERT_TAIL(&ssh->public_keys, k, next); | ||
192 | r = 0; | ||
193 | } | ||
194 | |||
195 | return r; | ||
196 | } | ||
197 | |||
198 | int | ||
199 | ssh_set_verify_host_key_callback(struct ssh *ssh, | ||
200 | int (*cb)(struct sshkey *, struct ssh *)) | ||
201 | { | ||
202 | if (cb == NULL || ssh->kex == NULL) | ||
203 | return SSH_ERR_INVALID_ARGUMENT; | ||
204 | |||
205 | ssh->kex->verify_host_key = cb; | ||
206 | |||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | int | ||
211 | ssh_input_append(struct ssh *ssh, const u_char *data, size_t len) | ||
212 | { | ||
213 | return sshbuf_put(ssh_packet_get_input(ssh), data, len); | ||
214 | } | ||
215 | |||
216 | int | ||
217 | ssh_packet_next(struct ssh *ssh, u_char *typep) | ||
218 | { | ||
219 | int r; | ||
220 | u_int32_t seqnr; | ||
221 | u_char type; | ||
222 | |||
223 | /* | ||
224 | * Try to read a packet. Return SSH_MSG_NONE if no packet or not | ||
225 | * enough data. | ||
226 | */ | ||
227 | *typep = SSH_MSG_NONE; | ||
228 | if (ssh->kex->client_version_string == NULL || | ||
229 | ssh->kex->server_version_string == NULL) | ||
230 | return _ssh_exchange_banner(ssh); | ||
231 | /* | ||
232 | * If we enough data and a dispatch function then | ||
233 | * call the function and get the next packet. | ||
234 | * Otherwise return the packet type to the caller so it | ||
235 | * can decide how to go on. | ||
236 | * | ||
237 | * We will only call the dispatch function for: | ||
238 | * 20-29 Algorithm negotiation | ||
239 | * 30-49 Key exchange method specific (numbers can be reused for | ||
240 | * different authentication methods) | ||
241 | */ | ||
242 | for (;;) { | ||
243 | if ((r = ssh_packet_read_poll2(ssh, &type, &seqnr)) != 0) | ||
244 | return r; | ||
245 | if (type > 0 && type < DISPATCH_MAX && | ||
246 | type >= SSH2_MSG_KEXINIT && type <= SSH2_MSG_TRANSPORT_MAX && | ||
247 | ssh->dispatch[type] != NULL) { | ||
248 | if ((r = (*ssh->dispatch[type])(type, seqnr, ssh)) != 0) | ||
249 | return r; | ||
250 | } else { | ||
251 | *typep = type; | ||
252 | return 0; | ||
253 | } | ||
254 | } | ||
255 | } | ||
256 | |||
257 | const u_char * | ||
258 | ssh_packet_payload(struct ssh *ssh, size_t *lenp) | ||
259 | { | ||
260 | return sshpkt_ptr(ssh, lenp); | ||
261 | } | ||
262 | |||
263 | int | ||
264 | ssh_packet_put(struct ssh *ssh, int type, const u_char *data, size_t len) | ||
265 | { | ||
266 | int r; | ||
267 | |||
268 | if ((r = sshpkt_start(ssh, type)) != 0 || | ||
269 | (r = sshpkt_put(ssh, data, len)) != 0 || | ||
270 | (r = sshpkt_send(ssh)) != 0) | ||
271 | return r; | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | const u_char * | ||
276 | ssh_output_ptr(struct ssh *ssh, size_t *len) | ||
277 | { | ||
278 | struct sshbuf *output = ssh_packet_get_output(ssh); | ||
279 | |||
280 | *len = sshbuf_len(output); | ||
281 | return sshbuf_ptr(output); | ||
282 | } | ||
283 | |||
284 | int | ||
285 | ssh_output_consume(struct ssh *ssh, size_t len) | ||
286 | { | ||
287 | return sshbuf_consume(ssh_packet_get_output(ssh), len); | ||
288 | } | ||
289 | |||
290 | int | ||
291 | ssh_output_space(struct ssh *ssh, size_t len) | ||
292 | { | ||
293 | return (0 == sshbuf_check_reserve(ssh_packet_get_output(ssh), len)); | ||
294 | } | ||
295 | |||
296 | int | ||
297 | ssh_input_space(struct ssh *ssh, size_t len) | ||
298 | { | ||
299 | return (0 == sshbuf_check_reserve(ssh_packet_get_input(ssh), len)); | ||
300 | } | ||
301 | |||
302 | /* Read other side's version identification. */ | ||
303 | int | ||
304 | _ssh_read_banner(struct ssh *ssh, char **bannerp) | ||
305 | { | ||
306 | struct sshbuf *input; | ||
307 | const char *s; | ||
308 | char buf[256], remote_version[256]; /* must be same size! */ | ||
309 | const char *mismatch = "Protocol mismatch.\r\n"; | ||
310 | int r, remote_major, remote_minor; | ||
311 | size_t i, n, j, len; | ||
312 | |||
313 | *bannerp = NULL; | ||
314 | input = ssh_packet_get_input(ssh); | ||
315 | len = sshbuf_len(input); | ||
316 | s = (const char *)sshbuf_ptr(input); | ||
317 | for (j = n = 0;;) { | ||
318 | for (i = 0; i < sizeof(buf) - 1; i++) { | ||
319 | if (j >= len) | ||
320 | return (0); | ||
321 | buf[i] = s[j++]; | ||
322 | if (buf[i] == '\r') { | ||
323 | buf[i] = '\n'; | ||
324 | buf[i + 1] = 0; | ||
325 | continue; /**XXX wait for \n */ | ||
326 | } | ||
327 | if (buf[i] == '\n') { | ||
328 | buf[i + 1] = 0; | ||
329 | break; | ||
330 | } | ||
331 | } | ||
332 | buf[sizeof(buf) - 1] = 0; | ||
333 | if (strncmp(buf, "SSH-", 4) == 0) | ||
334 | break; | ||
335 | debug("ssh_exchange_identification: %s", buf); | ||
336 | if (ssh->kex->server || ++n > 65536) { | ||
337 | if ((r = sshbuf_put(ssh_packet_get_output(ssh), | ||
338 | mismatch, strlen(mismatch))) != 0) | ||
339 | return r; | ||
340 | return SSH_ERR_NO_PROTOCOL_VERSION; | ||
341 | } | ||
342 | } | ||
343 | if ((r = sshbuf_consume(input, j)) != 0) | ||
344 | return r; | ||
345 | |||
346 | /* | ||
347 | * Check that the versions match. In future this might accept | ||
348 | * several versions and set appropriate flags to handle them. | ||
349 | */ | ||
350 | if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", | ||
351 | &remote_major, &remote_minor, remote_version) != 3) | ||
352 | return SSH_ERR_INVALID_FORMAT; | ||
353 | debug("Remote protocol version %d.%d, remote software version %.100s", | ||
354 | remote_major, remote_minor, remote_version); | ||
355 | |||
356 | ssh->compat = compat_datafellows(remote_version); | ||
357 | if (remote_major == 1 && remote_minor == 99) { | ||
358 | remote_major = 2; | ||
359 | remote_minor = 0; | ||
360 | } | ||
361 | if (remote_major != 2) | ||
362 | return SSH_ERR_PROTOCOL_MISMATCH; | ||
363 | enable_compat20(); | ||
364 | chop(buf); | ||
365 | debug("Remote version string %.100s", buf); | ||
366 | if ((*bannerp = strdup(buf)) == NULL) | ||
367 | return SSH_ERR_ALLOC_FAIL; | ||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | /* Send our own protocol version identification. */ | ||
372 | int | ||
373 | _ssh_send_banner(struct ssh *ssh, char **bannerp) | ||
374 | { | ||
375 | char buf[256]; | ||
376 | int r; | ||
377 | |||
378 | snprintf(buf, sizeof buf, "SSH-2.0-%.100s\r\n", SSH_VERSION); | ||
379 | if ((r = sshbuf_put(ssh_packet_get_output(ssh), buf, strlen(buf))) != 0) | ||
380 | return r; | ||
381 | chop(buf); | ||
382 | debug("Local version string %.100s", buf); | ||
383 | if ((*bannerp = strdup(buf)) == NULL) | ||
384 | return SSH_ERR_ALLOC_FAIL; | ||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | int | ||
389 | _ssh_exchange_banner(struct ssh *ssh) | ||
390 | { | ||
391 | struct kex *kex = ssh->kex; | ||
392 | int r; | ||
393 | |||
394 | /* | ||
395 | * if _ssh_read_banner() cannot parse a full version string | ||
396 | * it will return NULL and we end up calling it again. | ||
397 | */ | ||
398 | |||
399 | r = 0; | ||
400 | if (kex->server) { | ||
401 | if (kex->server_version_string == NULL) | ||
402 | r = _ssh_send_banner(ssh, &kex->server_version_string); | ||
403 | if (r == 0 && | ||
404 | kex->server_version_string != NULL && | ||
405 | kex->client_version_string == NULL) | ||
406 | r = _ssh_read_banner(ssh, &kex->client_version_string); | ||
407 | } else { | ||
408 | if (kex->server_version_string == NULL) | ||
409 | r = _ssh_read_banner(ssh, &kex->server_version_string); | ||
410 | if (r == 0 && | ||
411 | kex->server_version_string != NULL && | ||
412 | kex->client_version_string == NULL) | ||
413 | r = _ssh_send_banner(ssh, &kex->client_version_string); | ||
414 | } | ||
415 | if (r != 0) | ||
416 | return r; | ||
417 | /* start initial kex as soon as we have exchanged the banners */ | ||
418 | if (kex->server_version_string != NULL && | ||
419 | kex->client_version_string != NULL) { | ||
420 | if ((r = _ssh_order_hostkeyalgs(ssh)) != 0 || | ||
421 | (r = kex_send_kexinit(ssh)) != 0) | ||
422 | return r; | ||
423 | } | ||
424 | return 0; | ||
425 | } | ||
426 | |||
427 | struct sshkey * | ||
428 | _ssh_host_public_key(int type, struct ssh *ssh) | ||
429 | { | ||
430 | struct key_entry *k; | ||
431 | |||
432 | debug3("%s: need %d", __func__, type); | ||
433 | TAILQ_FOREACH(k, &ssh->public_keys, next) { | ||
434 | debug3("%s: check %s", __func__, sshkey_type(k->key)); | ||
435 | if (k->key->type == type) | ||
436 | return (k->key); | ||
437 | } | ||
438 | return (NULL); | ||
439 | } | ||
440 | |||
441 | struct sshkey * | ||
442 | _ssh_host_private_key(int type, struct ssh *ssh) | ||
443 | { | ||
444 | struct key_entry *k; | ||
445 | |||
446 | debug3("%s: need %d", __func__, type); | ||
447 | TAILQ_FOREACH(k, &ssh->private_keys, next) { | ||
448 | debug3("%s: check %s", __func__, sshkey_type(k->key)); | ||
449 | if (k->key->type == type) | ||
450 | return (k->key); | ||
451 | } | ||
452 | return (NULL); | ||
453 | } | ||
454 | |||
455 | int | ||
456 | _ssh_verify_host_key(struct sshkey *hostkey, struct ssh *ssh) | ||
457 | { | ||
458 | struct key_entry *k; | ||
459 | |||
460 | debug3("%s: need %s", __func__, sshkey_type(hostkey)); | ||
461 | TAILQ_FOREACH(k, &ssh->public_keys, next) { | ||
462 | debug3("%s: check %s", __func__, sshkey_type(k->key)); | ||
463 | if (sshkey_equal_public(hostkey, k->key)) | ||
464 | return (0); /* ok */ | ||
465 | } | ||
466 | return (-1); /* failed */ | ||
467 | } | ||
468 | |||
469 | /* offer hostkey algorithms in kexinit depending on registered keys */ | ||
470 | int | ||
471 | _ssh_order_hostkeyalgs(struct ssh *ssh) | ||
472 | { | ||
473 | struct key_entry *k; | ||
474 | char *orig, *avail, *oavail = NULL, *alg, *replace = NULL; | ||
475 | char **proposal; | ||
476 | size_t maxlen; | ||
477 | int ktype, r; | ||
478 | |||
479 | /* XXX we de-serialize ssh->kex->my, modify it, and change it */ | ||
480 | if ((r = kex_buf2prop(ssh->kex->my, NULL, &proposal)) != 0) | ||
481 | return r; | ||
482 | orig = proposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; | ||
483 | if ((oavail = avail = strdup(orig)) == NULL) { | ||
484 | r = SSH_ERR_ALLOC_FAIL; | ||
485 | goto out; | ||
486 | } | ||
487 | maxlen = strlen(avail) + 1; | ||
488 | if ((replace = calloc(1, maxlen)) == NULL) { | ||
489 | r = SSH_ERR_ALLOC_FAIL; | ||
490 | goto out; | ||
491 | } | ||
492 | *replace = '\0'; | ||
493 | while ((alg = strsep(&avail, ",")) && *alg != '\0') { | ||
494 | if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC) | ||
495 | continue; | ||
496 | TAILQ_FOREACH(k, &ssh->public_keys, next) { | ||
497 | if (k->key->type == ktype || | ||
498 | (sshkey_is_cert(k->key) && k->key->type == | ||
499 | sshkey_type_plain(ktype))) { | ||
500 | if (*replace != '\0') | ||
501 | strlcat(replace, ",", maxlen); | ||
502 | strlcat(replace, alg, maxlen); | ||
503 | break; | ||
504 | } | ||
505 | } | ||
506 | } | ||
507 | if (*replace != '\0') { | ||
508 | debug2("%s: orig/%d %s", __func__, ssh->kex->server, orig); | ||
509 | debug2("%s: replace/%d %s", __func__, ssh->kex->server, replace); | ||
510 | free(orig); | ||
511 | proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = replace; | ||
512 | replace = NULL; /* owned by proposal */ | ||
513 | r = kex_prop2buf(ssh->kex->my, proposal); | ||
514 | } | ||
515 | out: | ||
516 | free(oavail); | ||
517 | free(replace); | ||
518 | kex_prop_free(proposal); | ||
519 | return r; | ||
520 | } | ||
521 | |||
522 | int | ||
523 | _ssh_host_key_sign(struct sshkey *privkey, struct sshkey *pubkey, | ||
524 | u_char **signature, size_t *slen, u_char *data, size_t dlen, u_int compat) | ||
525 | { | ||
526 | return sshkey_sign(privkey, signature, slen, data, dlen, compat); | ||
527 | } | ||
diff --git a/ssh_api.h b/ssh_api.h new file mode 100644 index 000000000..a7e14e06e --- /dev/null +++ b/ssh_api.h | |||
@@ -0,0 +1,136 @@ | |||
1 | /* $OpenBSD: ssh_api.h,v 1.1 2015/01/19 20:30:23 markus Exp $ */ | ||
2 | /* | ||
3 | * Copyright (c) 2012 Markus Friedl. All rights reserved. | ||
4 | * | ||
5 | * Permission to use, copy, modify, and distribute this software for any | ||
6 | * purpose with or without fee is hereby granted, provided that the above | ||
7 | * copyright notice and this permission notice appear in all copies. | ||
8 | * | ||
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
16 | */ | ||
17 | |||
18 | #ifndef API_H | ||
19 | #define API_H | ||
20 | |||
21 | #include <sys/queue.h> | ||
22 | #include <sys/types.h> | ||
23 | #include <signal.h> | ||
24 | |||
25 | #include "cipher.h" | ||
26 | #include "sshkey.h" | ||
27 | #include "kex.h" | ||
28 | #include "ssh.h" | ||
29 | #include "ssh2.h" | ||
30 | #include "packet.h" | ||
31 | |||
32 | struct kex_params { | ||
33 | char *proposal[PROPOSAL_MAX]; | ||
34 | }; | ||
35 | |||
36 | /* public SSH API functions */ | ||
37 | |||
38 | /* | ||
39 | * ssh_init() create a ssh connection object with given (optional) | ||
40 | * key exchange parameters. | ||
41 | */ | ||
42 | int ssh_init(struct ssh **, int is_server, struct kex_params *kex_params); | ||
43 | |||
44 | /* | ||
45 | * release ssh connection state. | ||
46 | */ | ||
47 | void ssh_free(struct ssh *); | ||
48 | |||
49 | /* | ||
50 | * attach application specific data to the connection state | ||
51 | */ | ||
52 | void ssh_set_app_data(struct ssh *, void *); | ||
53 | void *ssh_get_app_data(struct ssh *); | ||
54 | |||
55 | /* | ||
56 | * ssh_add_hostkey() registers a private/public hostkey for an ssh | ||
57 | * connection. | ||
58 | * ssh_add_hostkey() needs to be called before a key exchange is | ||
59 | * initiated with ssh_packet_next(). | ||
60 | * private hostkeys are required if we need to act as a server. | ||
61 | * public hostkeys are used to verify the servers hostkey. | ||
62 | */ | ||
63 | int ssh_add_hostkey(struct ssh *ssh, struct sshkey *key); | ||
64 | |||
65 | /* | ||
66 | * ssh_set_verify_host_key_callback() registers a callback function | ||
67 | * which should be called instead of the default verification. The | ||
68 | * function given must return 0 if the hostkey is ok, -1 if the | ||
69 | * verification has failed. | ||
70 | */ | ||
71 | int ssh_set_verify_host_key_callback(struct ssh *ssh, | ||
72 | int (*cb)(struct sshkey *, struct ssh *)); | ||
73 | |||
74 | /* | ||
75 | * ssh_packet_next() advances to the next input packet and returns | ||
76 | * the packet type in typep. | ||
77 | * ssh_packet_next() works by processing an input byte-stream, | ||
78 | * decrypting the received data and hiding the key-exchange from | ||
79 | * the caller. | ||
80 | * ssh_packet_next() sets typep if there is no new packet available. | ||
81 | * in this case the caller must fill the input byte-stream by passing | ||
82 | * the data received over network to ssh_input_append(). | ||
83 | * additinally, the caller needs to send the resulting output | ||
84 | * byte-stream back over the network. otherwise the key exchange | ||
85 | * would not proceed. the output byte-stream is accessed through | ||
86 | * ssh_output_ptr(). | ||
87 | */ | ||
88 | int ssh_packet_next(struct ssh *ssh, u_char *typep); | ||
89 | |||
90 | /* | ||
91 | * ssh_packet_payload() returns a pointer to the raw payload data of | ||
92 | * the current input packet and the length of this payload. | ||
93 | * the payload is accessible until ssh_packet_next() is called again. | ||
94 | */ | ||
95 | const u_char *ssh_packet_payload(struct ssh *ssh, size_t *lenp); | ||
96 | |||
97 | /* | ||
98 | * ssh_packet_put() creates an encrypted packet with the given type | ||
99 | * and payload. | ||
100 | * the encrypted packet is appended to the output byte-stream. | ||
101 | */ | ||
102 | int ssh_packet_put(struct ssh *ssh, int type, const u_char *data, | ||
103 | size_t len); | ||
104 | |||
105 | /* | ||
106 | * ssh_input_space() checks if 'len' bytes can be appended to the | ||
107 | * input byte-stream. | ||
108 | */ | ||
109 | int ssh_input_space(struct ssh *ssh, size_t len); | ||
110 | |||
111 | /* | ||
112 | * ssh_input_append() appends data to the input byte-stream. | ||
113 | */ | ||
114 | int ssh_input_append(struct ssh *ssh, const u_char *data, size_t len); | ||
115 | |||
116 | /* | ||
117 | * ssh_output_space() checks if 'len' bytes can be appended to the | ||
118 | * output byte-stream. XXX | ||
119 | */ | ||
120 | int ssh_output_space(struct ssh *ssh, size_t len); | ||
121 | |||
122 | /* | ||
123 | * ssh_output_ptr() retrieves both a pointer and the length of the | ||
124 | * current output byte-stream. the bytes need to be sent over the | ||
125 | * network. the number of bytes that have been successfully sent can | ||
126 | * be removed from the output byte-stream with ssh_output_consume(). | ||
127 | */ | ||
128 | const u_char *ssh_output_ptr(struct ssh *ssh, size_t *len); | ||
129 | |||
130 | /* | ||
131 | * ssh_output_consume() removes the given number of bytes from | ||
132 | * the output byte-stream. | ||
133 | */ | ||
134 | int ssh_output_consume(struct ssh *ssh, size_t len); | ||
135 | |||
136 | #endif | ||