diff options
Diffstat (limited to 'monitor.c')
-rw-r--r-- | monitor.c | 1440 |
1 files changed, 1440 insertions, 0 deletions
diff --git a/monitor.c b/monitor.c new file mode 100644 index 000000000..921ad985a --- /dev/null +++ b/monitor.c | |||
@@ -0,0 +1,1440 @@ | |||
1 | /* | ||
2 | * Copyright 2002 Niels Provos <provos@citi.umich.edu> | ||
3 | * Copyright 2002 Markus Friedl <markus@openbsd.org> | ||
4 | * All rights reserved. | ||
5 | * | ||
6 | * Redistribution and use in source and binary forms, with or without | ||
7 | * modification, are permitted provided that the following conditions | ||
8 | * are met: | ||
9 | * 1. Redistributions of source code must retain the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer. | ||
11 | * 2. Redistributions in binary form must reproduce the above copyright | ||
12 | * notice, this list of conditions and the following disclaimer in the | ||
13 | * documentation and/or other materials provided with the distribution. | ||
14 | * | ||
15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||
16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
25 | */ | ||
26 | |||
27 | #include "includes.h" | ||
28 | RCSID("$OpenBSD: monitor.c,v 1.6 2002/03/21 18:38:33 stevesk Exp $"); | ||
29 | |||
30 | #include <openssl/dh.h> | ||
31 | |||
32 | #ifdef SKEY | ||
33 | #include <skey.h> | ||
34 | #endif | ||
35 | |||
36 | #include "ssh.h" | ||
37 | #include "auth.h" | ||
38 | #include "kex.h" | ||
39 | #include "dh.h" | ||
40 | #include "zlib.h" | ||
41 | #include "packet.h" | ||
42 | #include "auth-options.h" | ||
43 | #include "sshpty.h" | ||
44 | #include "channels.h" | ||
45 | #include "session.h" | ||
46 | #include "sshlogin.h" | ||
47 | #include "canohost.h" | ||
48 | #include "log.h" | ||
49 | #include "servconf.h" | ||
50 | #include "monitor.h" | ||
51 | #include "monitor_mm.h" | ||
52 | #include "monitor_wrap.h" | ||
53 | #include "monitor_fdpass.h" | ||
54 | #include "xmalloc.h" | ||
55 | #include "misc.h" | ||
56 | #include "buffer.h" | ||
57 | #include "bufaux.h" | ||
58 | #include "compat.h" | ||
59 | #include "ssh2.h" | ||
60 | #include "mpaux.h" | ||
61 | |||
62 | /* Imports */ | ||
63 | extern ServerOptions options; | ||
64 | extern u_int utmp_len; | ||
65 | extern Newkeys *current_keys[]; | ||
66 | extern z_stream incoming_stream; | ||
67 | extern z_stream outgoing_stream; | ||
68 | extern u_char session_id[]; | ||
69 | extern Buffer input, output; | ||
70 | extern Buffer auth_debug; | ||
71 | extern int auth_debug_init; | ||
72 | |||
73 | /* State exported from the child */ | ||
74 | |||
75 | struct { | ||
76 | z_stream incoming; | ||
77 | z_stream outgoing; | ||
78 | u_char *keyin; | ||
79 | u_int keyinlen; | ||
80 | u_char *keyout; | ||
81 | u_int keyoutlen; | ||
82 | u_char *ivin; | ||
83 | u_int ivinlen; | ||
84 | u_char *ivout; | ||
85 | u_int ivoutlen; | ||
86 | int ssh1cipher; | ||
87 | int ssh1protoflags; | ||
88 | u_char *input; | ||
89 | u_int ilen; | ||
90 | u_char *output; | ||
91 | u_int olen; | ||
92 | } child_state; | ||
93 | |||
94 | /* Functions on the montior that answer unprivileged requests */ | ||
95 | |||
96 | int mm_answer_moduli(int, Buffer *); | ||
97 | int mm_answer_sign(int, Buffer *); | ||
98 | int mm_answer_pwnamallow(int, Buffer *); | ||
99 | int mm_answer_authserv(int, Buffer *); | ||
100 | int mm_answer_authpassword(int, Buffer *); | ||
101 | int mm_answer_bsdauthquery(int, Buffer *); | ||
102 | int mm_answer_bsdauthrespond(int, Buffer *); | ||
103 | int mm_answer_skeyquery(int, Buffer *); | ||
104 | int mm_answer_skeyrespond(int, Buffer *); | ||
105 | int mm_answer_keyallowed(int, Buffer *); | ||
106 | int mm_answer_keyverify(int, Buffer *); | ||
107 | int mm_answer_pty(int, Buffer *); | ||
108 | int mm_answer_pty_cleanup(int, Buffer *); | ||
109 | int mm_answer_term(int, Buffer *); | ||
110 | int mm_answer_rsa_keyallowed(int, Buffer *); | ||
111 | int mm_answer_rsa_challenge(int, Buffer *); | ||
112 | int mm_answer_rsa_response(int, Buffer *); | ||
113 | int mm_answer_sesskey(int, Buffer *); | ||
114 | int mm_answer_sessid(int, Buffer *); | ||
115 | |||
116 | static Authctxt *authctxt; | ||
117 | static BIGNUM *ssh1_challenge = NULL; /* used for ssh1 rsa auth */ | ||
118 | |||
119 | /* local state for key verify */ | ||
120 | static u_char *key_blob = NULL; | ||
121 | static u_int key_bloblen = 0; | ||
122 | static int key_blobtype = MM_NOKEY; | ||
123 | static u_char *hostbased_cuser = NULL; | ||
124 | static u_char *hostbased_chost = NULL; | ||
125 | static char *auth_method = "unknown"; | ||
126 | |||
127 | struct mon_table { | ||
128 | enum monitor_reqtype type; | ||
129 | int flags; | ||
130 | int (*f)(int, Buffer *); | ||
131 | }; | ||
132 | |||
133 | #define MON_ISAUTH 0x0004 /* Required for Authentication */ | ||
134 | #define MON_AUTHDECIDE 0x0008 /* Decides Authentication */ | ||
135 | #define MON_ONCE 0x0010 /* Disable after calling */ | ||
136 | |||
137 | #define MON_AUTH (MON_ISAUTH|MON_AUTHDECIDE) | ||
138 | |||
139 | #define MON_PERMIT 0x1000 /* Request is permitted */ | ||
140 | |||
141 | struct mon_table mon_dispatch_proto20[] = { | ||
142 | {MONITOR_REQ_MODULI, MON_ONCE, mm_answer_moduli}, | ||
143 | {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign}, | ||
144 | {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow}, | ||
145 | {MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv}, | ||
146 | {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword}, | ||
147 | #ifdef BSD_AUTH | ||
148 | {MONITOR_REQ_BSDAUTHQUERY, MON_ISAUTH, mm_answer_bsdauthquery}, | ||
149 | {MONITOR_REQ_BSDAUTHRESPOND, MON_AUTH,mm_answer_bsdauthrespond}, | ||
150 | #endif | ||
151 | #ifdef SKEY | ||
152 | {MONITOR_REQ_SKEYQUERY, MON_ISAUTH, mm_answer_skeyquery}, | ||
153 | {MONITOR_REQ_SKEYRESPOND, MON_AUTH, mm_answer_skeyrespond}, | ||
154 | #endif | ||
155 | {MONITOR_REQ_KEYALLOWED, MON_ISAUTH, mm_answer_keyallowed}, | ||
156 | {MONITOR_REQ_KEYVERIFY, MON_AUTH, mm_answer_keyverify}, | ||
157 | {0, 0, NULL} | ||
158 | }; | ||
159 | |||
160 | struct mon_table mon_dispatch_postauth20[] = { | ||
161 | {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, | ||
162 | {MONITOR_REQ_SIGN, 0, mm_answer_sign}, | ||
163 | {MONITOR_REQ_PTY, 0, mm_answer_pty}, | ||
164 | {MONITOR_REQ_PTYCLEANUP, 0, mm_answer_pty_cleanup}, | ||
165 | {MONITOR_REQ_TERM, 0, mm_answer_term}, | ||
166 | {0, 0, NULL} | ||
167 | }; | ||
168 | |||
169 | struct mon_table mon_dispatch_proto15[] = { | ||
170 | {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow}, | ||
171 | {MONITOR_REQ_SESSKEY, MON_ONCE, mm_answer_sesskey}, | ||
172 | {MONITOR_REQ_SESSID, MON_ONCE, mm_answer_sessid}, | ||
173 | {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword}, | ||
174 | {MONITOR_REQ_RSAKEYALLOWED, MON_ISAUTH, mm_answer_rsa_keyallowed}, | ||
175 | {MONITOR_REQ_KEYALLOWED, MON_ISAUTH, mm_answer_keyallowed}, | ||
176 | {MONITOR_REQ_RSACHALLENGE, MON_ONCE, mm_answer_rsa_challenge}, | ||
177 | {MONITOR_REQ_RSARESPONSE, MON_ONCE|MON_AUTHDECIDE, mm_answer_rsa_response}, | ||
178 | #ifdef BSD_AUTH | ||
179 | {MONITOR_REQ_BSDAUTHQUERY, MON_ISAUTH, mm_answer_bsdauthquery}, | ||
180 | {MONITOR_REQ_BSDAUTHRESPOND, MON_AUTH,mm_answer_bsdauthrespond}, | ||
181 | #endif | ||
182 | #ifdef SKEY | ||
183 | {MONITOR_REQ_SKEYQUERY, MON_ISAUTH, mm_answer_skeyquery}, | ||
184 | {MONITOR_REQ_SKEYRESPOND, MON_AUTH, mm_answer_skeyrespond}, | ||
185 | #endif | ||
186 | {0, 0, NULL} | ||
187 | }; | ||
188 | |||
189 | struct mon_table mon_dispatch_postauth15[] = { | ||
190 | {MONITOR_REQ_PTY, MON_ONCE, mm_answer_pty}, | ||
191 | {MONITOR_REQ_PTYCLEANUP, MON_ONCE, mm_answer_pty_cleanup}, | ||
192 | {MONITOR_REQ_TERM, 0, mm_answer_term}, | ||
193 | {0, 0, NULL} | ||
194 | }; | ||
195 | |||
196 | struct mon_table *mon_dispatch; | ||
197 | |||
198 | /* Specifies if a certain message is allowed at the moment */ | ||
199 | |||
200 | static void | ||
201 | monitor_permit(struct mon_table *ent, enum monitor_reqtype type, int permit) | ||
202 | { | ||
203 | while (ent->f != NULL) { | ||
204 | if (ent->type == type) { | ||
205 | ent->flags &= ~MON_PERMIT; | ||
206 | ent->flags |= permit ? MON_PERMIT : 0; | ||
207 | return; | ||
208 | } | ||
209 | ent++; | ||
210 | } | ||
211 | } | ||
212 | |||
213 | static void | ||
214 | monitor_permit_authentications(int permit) | ||
215 | { | ||
216 | struct mon_table *ent = mon_dispatch; | ||
217 | |||
218 | while (ent->f != NULL) { | ||
219 | if (ent->flags & MON_AUTH) { | ||
220 | ent->flags &= ~MON_PERMIT; | ||
221 | ent->flags |= permit ? MON_PERMIT : 0; | ||
222 | } | ||
223 | ent++; | ||
224 | } | ||
225 | } | ||
226 | |||
227 | Authctxt * | ||
228 | monitor_child_preauth(struct monitor *monitor) | ||
229 | { | ||
230 | struct mon_table *ent; | ||
231 | int authenticated = 0; | ||
232 | |||
233 | debug3("preauth child monitor started"); | ||
234 | |||
235 | if (compat20) { | ||
236 | mon_dispatch = mon_dispatch_proto20; | ||
237 | |||
238 | /* Permit requests for moduli and signatures */ | ||
239 | monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); | ||
240 | monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); | ||
241 | } else { | ||
242 | mon_dispatch = mon_dispatch_proto15; | ||
243 | |||
244 | monitor_permit(mon_dispatch, MONITOR_REQ_SESSKEY, 1); | ||
245 | } | ||
246 | |||
247 | authctxt = authctxt_new(); | ||
248 | |||
249 | /* The first few requests do not require asynchronous access */ | ||
250 | while (!authenticated) { | ||
251 | authenticated = monitor_read(monitor, mon_dispatch, &ent); | ||
252 | if (authenticated) { | ||
253 | if (!(ent->flags & MON_AUTHDECIDE)) | ||
254 | fatal("%s: unexpected authentication from %d", | ||
255 | __FUNCTION__, ent->type); | ||
256 | if (authctxt->pw->pw_uid == 0 && | ||
257 | !auth_root_allowed(auth_method)) | ||
258 | authenticated = 0; | ||
259 | } | ||
260 | |||
261 | if (ent->flags & MON_AUTHDECIDE) { | ||
262 | auth_log(authctxt, authenticated, auth_method, | ||
263 | compat20 ? " ssh2" : ""); | ||
264 | if (!authenticated) | ||
265 | authctxt->failures++; | ||
266 | } | ||
267 | } | ||
268 | |||
269 | if (!authctxt->valid) | ||
270 | fatal("%s: authenticated invalid user", __FUNCTION__); | ||
271 | |||
272 | debug("%s: %s has been authenticated by privileged process", | ||
273 | __FUNCTION__, authctxt->user); | ||
274 | |||
275 | mm_get_keystate(monitor); | ||
276 | |||
277 | return (authctxt); | ||
278 | } | ||
279 | |||
280 | void | ||
281 | monitor_child_postauth(struct monitor *monitor) | ||
282 | { | ||
283 | if (compat20) { | ||
284 | mon_dispatch = mon_dispatch_postauth20; | ||
285 | |||
286 | /* Permit requests for moduli and signatures */ | ||
287 | monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); | ||
288 | monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); | ||
289 | monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); | ||
290 | |||
291 | } else { | ||
292 | mon_dispatch = mon_dispatch_postauth15; | ||
293 | monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); | ||
294 | } | ||
295 | if (!no_pty_flag) { | ||
296 | monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); | ||
297 | monitor_permit(mon_dispatch, MONITOR_REQ_PTYCLEANUP, 1); | ||
298 | } | ||
299 | |||
300 | for (;;) | ||
301 | monitor_read(monitor, mon_dispatch, NULL); | ||
302 | } | ||
303 | |||
304 | void | ||
305 | monitor_sync(struct monitor *monitor) | ||
306 | { | ||
307 | /* The member allocation is not visible, so sync it */ | ||
308 | mm_share_sync(&monitor->m_zlib, &monitor->m_zback); | ||
309 | } | ||
310 | |||
311 | int | ||
312 | monitor_read(struct monitor *monitor, struct mon_table *ent, | ||
313 | struct mon_table **pent) | ||
314 | { | ||
315 | Buffer m; | ||
316 | int ret; | ||
317 | u_char type; | ||
318 | |||
319 | buffer_init(&m); | ||
320 | |||
321 | mm_request_receive(monitor->m_sendfd, &m); | ||
322 | type = buffer_get_char(&m); | ||
323 | |||
324 | debug3("%s: checking request %d", __FUNCTION__, type); | ||
325 | |||
326 | while (ent->f != NULL) { | ||
327 | if (ent->type == type) | ||
328 | break; | ||
329 | ent++; | ||
330 | } | ||
331 | |||
332 | if (ent->f != NULL) { | ||
333 | if (!(ent->flags & MON_PERMIT)) | ||
334 | fatal("%s: unpermitted request %d", __FUNCTION__, | ||
335 | type); | ||
336 | ret = (*ent->f)(monitor->m_sendfd, &m); | ||
337 | buffer_free(&m); | ||
338 | |||
339 | /* The child may use this request only once, disable it */ | ||
340 | if (ent->flags & MON_ONCE) { | ||
341 | debug2("%s: %d used once, disabling now", __FUNCTION__, | ||
342 | type); | ||
343 | ent->flags &= ~MON_PERMIT; | ||
344 | } | ||
345 | |||
346 | if (pent != NULL) | ||
347 | *pent = ent; | ||
348 | |||
349 | return ret; | ||
350 | } | ||
351 | |||
352 | fatal("%s: unsupported request: %d\n", __FUNCTION__, type); | ||
353 | |||
354 | /* NOTREACHED */ | ||
355 | return (-1); | ||
356 | } | ||
357 | |||
358 | /* allowed key state */ | ||
359 | static int | ||
360 | monitor_allowed_key(u_char *blob, u_int bloblen) | ||
361 | { | ||
362 | /* make sure key is allowed */ | ||
363 | if (key_blob == NULL || key_bloblen != bloblen || | ||
364 | memcmp(key_blob, blob, key_bloblen)) | ||
365 | return (0); | ||
366 | return (1); | ||
367 | } | ||
368 | |||
369 | static void | ||
370 | monitor_reset_key_state(void) | ||
371 | { | ||
372 | /* reset state */ | ||
373 | if (key_blob != NULL) | ||
374 | xfree(key_blob); | ||
375 | if (hostbased_cuser != NULL) | ||
376 | xfree(hostbased_cuser); | ||
377 | if (hostbased_chost != NULL) | ||
378 | xfree(hostbased_chost); | ||
379 | key_blob = NULL; | ||
380 | key_bloblen = 0; | ||
381 | key_blobtype = MM_NOKEY; | ||
382 | hostbased_cuser = NULL; | ||
383 | hostbased_chost = NULL; | ||
384 | } | ||
385 | |||
386 | int | ||
387 | mm_answer_moduli(int socket, Buffer *m) | ||
388 | { | ||
389 | DH *dh; | ||
390 | int min, want, max; | ||
391 | |||
392 | min = buffer_get_int(m); | ||
393 | want = buffer_get_int(m); | ||
394 | max = buffer_get_int(m); | ||
395 | |||
396 | debug3("%s: got parameters: %d %d %d", | ||
397 | __FUNCTION__, min, want, max); | ||
398 | /* We need to check here, too, in case the child got corrupted */ | ||
399 | if (max < min || want < min || max < want) | ||
400 | fatal("%s: bad parameters: %d %d %d", | ||
401 | __FUNCTION__, min, want, max); | ||
402 | |||
403 | buffer_clear(m); | ||
404 | |||
405 | dh = choose_dh(min, want, max); | ||
406 | if (dh == NULL) { | ||
407 | buffer_put_char(m, 0); | ||
408 | return (0); | ||
409 | } else { | ||
410 | /* Send first bignum */ | ||
411 | buffer_put_char(m, 1); | ||
412 | buffer_put_bignum2(m, dh->p); | ||
413 | buffer_put_bignum2(m, dh->g); | ||
414 | |||
415 | DH_free(dh); | ||
416 | } | ||
417 | mm_request_send(socket, MONITOR_ANS_MODULI, m); | ||
418 | return (0); | ||
419 | } | ||
420 | |||
421 | int | ||
422 | mm_answer_sign(int socket, Buffer *m) | ||
423 | { | ||
424 | Key *key; | ||
425 | u_char *p; | ||
426 | u_char *signature; | ||
427 | u_int siglen, datlen; | ||
428 | int keyid; | ||
429 | |||
430 | debug3("%s", __FUNCTION__); | ||
431 | |||
432 | keyid = buffer_get_int(m); | ||
433 | p = buffer_get_string(m, &datlen); | ||
434 | |||
435 | if (datlen != 20) | ||
436 | fatal("%s: data length incorrect: %d", __FUNCTION__, datlen); | ||
437 | |||
438 | if ((key = get_hostkey_by_index(keyid)) == NULL) | ||
439 | fatal("%s: no hostkey from index %d", __FUNCTION__, keyid); | ||
440 | if (key_sign(key, &signature, &siglen, p, datlen) < 0) | ||
441 | fatal("%s: key_sign failed", __FUNCTION__); | ||
442 | |||
443 | debug3("%s: signature %p(%d)", __FUNCTION__, signature, siglen); | ||
444 | |||
445 | buffer_clear(m); | ||
446 | buffer_put_string(m, signature, siglen); | ||
447 | |||
448 | xfree(p); | ||
449 | xfree(signature); | ||
450 | |||
451 | mm_request_send(socket, MONITOR_ANS_SIGN, m); | ||
452 | |||
453 | /* Turn on permissions for getpwnam */ | ||
454 | monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); | ||
455 | |||
456 | return (0); | ||
457 | } | ||
458 | |||
459 | /* Retrieves the password entry and also checks if the user is permitted */ | ||
460 | |||
461 | int | ||
462 | mm_answer_pwnamallow(int socket, Buffer *m) | ||
463 | { | ||
464 | char *login; | ||
465 | struct passwd *pwent; | ||
466 | int allowed = 0; | ||
467 | |||
468 | debug3("%s", __FUNCTION__); | ||
469 | |||
470 | if (authctxt->attempt++ != 0) | ||
471 | fatal("%s: multiple attempts for getpwnam", __FUNCTION__); | ||
472 | |||
473 | login = buffer_get_string(m, NULL); | ||
474 | |||
475 | pwent = getpwnamallow(login); | ||
476 | |||
477 | authctxt->user = xstrdup(login); | ||
478 | setproctitle("%s [priv]", pwent ? login : "unknown"); | ||
479 | xfree(login); | ||
480 | |||
481 | buffer_clear(m); | ||
482 | |||
483 | if (pwent == NULL) { | ||
484 | buffer_put_char(m, 0); | ||
485 | goto out; | ||
486 | } | ||
487 | |||
488 | allowed = 1; | ||
489 | authctxt->pw = pwent; | ||
490 | authctxt->valid = 1; | ||
491 | |||
492 | buffer_put_char(m, 1); | ||
493 | buffer_put_string(m, pwent, sizeof(struct passwd)); | ||
494 | buffer_put_cstring(m, pwent->pw_name); | ||
495 | buffer_put_cstring(m, "*"); | ||
496 | buffer_put_cstring(m, pwent->pw_gecos); | ||
497 | buffer_put_cstring(m, pwent->pw_class); | ||
498 | buffer_put_cstring(m, pwent->pw_dir); | ||
499 | buffer_put_cstring(m, pwent->pw_shell); | ||
500 | |||
501 | out: | ||
502 | debug3("%s: sending MONITOR_ANS_PWNAM: %d", __FUNCTION__, allowed); | ||
503 | mm_request_send(socket, MONITOR_ANS_PWNAM, m); | ||
504 | |||
505 | /* For SSHv1 allow authentication now */ | ||
506 | if (!compat20) | ||
507 | monitor_permit_authentications(1); | ||
508 | else | ||
509 | /* Allow service/style information on the auth context */ | ||
510 | monitor_permit(mon_dispatch, MONITOR_REQ_AUTHSERV, 1); | ||
511 | |||
512 | |||
513 | return (0); | ||
514 | } | ||
515 | |||
516 | int | ||
517 | mm_answer_authserv(int socket, Buffer *m) | ||
518 | { | ||
519 | monitor_permit_authentications(1); | ||
520 | |||
521 | authctxt->service = buffer_get_string(m, NULL); | ||
522 | authctxt->style = buffer_get_string(m, NULL); | ||
523 | debug3("%s: service=%s, style=%s", | ||
524 | __FUNCTION__, authctxt->service, authctxt->style); | ||
525 | |||
526 | if (strlen(authctxt->style) == 0) { | ||
527 | xfree(authctxt->style); | ||
528 | authctxt->style = NULL; | ||
529 | } | ||
530 | |||
531 | return (0); | ||
532 | } | ||
533 | |||
534 | int | ||
535 | mm_answer_authpassword(int socket, Buffer *m) | ||
536 | { | ||
537 | static int call_count; | ||
538 | char *passwd; | ||
539 | int authenticated, plen; | ||
540 | |||
541 | passwd = buffer_get_string(m, &plen); | ||
542 | /* Only authenticate if the context is valid */ | ||
543 | authenticated = authctxt->valid && auth_password(authctxt, passwd); | ||
544 | memset(passwd, 0, strlen(passwd)); | ||
545 | xfree(passwd); | ||
546 | |||
547 | buffer_clear(m); | ||
548 | buffer_put_int(m, authenticated); | ||
549 | |||
550 | debug3("%s: sending result %d", __FUNCTION__, authenticated); | ||
551 | mm_request_send(socket, MONITOR_ANS_AUTHPASSWORD, m); | ||
552 | |||
553 | call_count++; | ||
554 | if (plen == 0 && call_count == 1) | ||
555 | auth_method = "none"; | ||
556 | else | ||
557 | auth_method = "password"; | ||
558 | |||
559 | /* Causes monitor loop to terminate if authenticated */ | ||
560 | return (authenticated); | ||
561 | } | ||
562 | |||
563 | #ifdef BSD_AUTH | ||
564 | int | ||
565 | mm_answer_bsdauthquery(int socket, Buffer *m) | ||
566 | { | ||
567 | char *name, *infotxt; | ||
568 | u_int numprompts; | ||
569 | u_int *echo_on; | ||
570 | char **prompts; | ||
571 | int res; | ||
572 | |||
573 | res = bsdauth_query(authctxt, &name, &infotxt, &numprompts, | ||
574 | &prompts, &echo_on); | ||
575 | |||
576 | buffer_clear(m); | ||
577 | buffer_put_int(m, res); | ||
578 | if (res != -1) | ||
579 | buffer_put_cstring(m, prompts[0]); | ||
580 | |||
581 | debug3("%s: sending challenge res: %d", __FUNCTION__, res); | ||
582 | mm_request_send(socket, MONITOR_ANS_BSDAUTHQUERY, m); | ||
583 | |||
584 | if (res != -1) { | ||
585 | xfree(name); | ||
586 | xfree(infotxt); | ||
587 | xfree(prompts); | ||
588 | xfree(echo_on); | ||
589 | } | ||
590 | |||
591 | return (0); | ||
592 | } | ||
593 | |||
594 | int | ||
595 | mm_answer_bsdauthrespond(int socket, Buffer *m) | ||
596 | { | ||
597 | char *response; | ||
598 | int authok; | ||
599 | |||
600 | if (authctxt->as == 0) | ||
601 | fatal("%s: no bsd auth session", __FUNCTION__); | ||
602 | |||
603 | response = buffer_get_string(m, NULL); | ||
604 | authok = auth_userresponse(authctxt->as, response, 0); | ||
605 | authctxt->as = NULL; | ||
606 | debug3("%s: <%s> = <%d>", __FUNCTION__, response, authok); | ||
607 | xfree(response); | ||
608 | |||
609 | buffer_clear(m); | ||
610 | buffer_put_int(m, authok); | ||
611 | |||
612 | debug3("%s: sending authenticated: %d", __FUNCTION__, authok); | ||
613 | mm_request_send(socket, MONITOR_ANS_BSDAUTHRESPOND, m); | ||
614 | |||
615 | auth_method = "bsdauth"; | ||
616 | |||
617 | return (authok != 0); | ||
618 | } | ||
619 | #endif | ||
620 | |||
621 | #ifdef SKEY | ||
622 | int | ||
623 | mm_answer_skeyquery(int socket, Buffer *m) | ||
624 | { | ||
625 | struct skey skey; | ||
626 | char challenge[1024]; | ||
627 | int res; | ||
628 | |||
629 | res = skeychallenge(&skey, authctxt->user, challenge); | ||
630 | |||
631 | buffer_clear(m); | ||
632 | buffer_put_int(m, res); | ||
633 | if (res != -1) | ||
634 | buffer_put_cstring(m, challenge); | ||
635 | |||
636 | debug3("%s: sending challenge res: %d", __FUNCTION__, res); | ||
637 | mm_request_send(socket, MONITOR_ANS_SKEYQUERY, m); | ||
638 | |||
639 | return (0); | ||
640 | } | ||
641 | |||
642 | int | ||
643 | mm_answer_skeyrespond(int socket, Buffer *m) | ||
644 | { | ||
645 | char *response; | ||
646 | int authok; | ||
647 | |||
648 | response = buffer_get_string(m, NULL); | ||
649 | |||
650 | authok = (authctxt->valid && | ||
651 | skey_haskey(authctxt->pw->pw_name) == 0 && | ||
652 | skey_passcheck(authctxt->pw->pw_name, response) != -1); | ||
653 | |||
654 | xfree(response); | ||
655 | |||
656 | buffer_clear(m); | ||
657 | buffer_put_int(m, authok); | ||
658 | |||
659 | debug3("%s: sending authenticated: %d", __FUNCTION__, authok); | ||
660 | mm_request_send(socket, MONITOR_ANS_SKEYRESPOND, m); | ||
661 | |||
662 | auth_method = "skey"; | ||
663 | |||
664 | return (authok != 0); | ||
665 | } | ||
666 | #endif | ||
667 | |||
668 | static void | ||
669 | mm_append_debug(Buffer *m) | ||
670 | { | ||
671 | if (auth_debug_init && buffer_len(&auth_debug)) { | ||
672 | debug3("%s: Appending debug messages for child", __FUNCTION__); | ||
673 | buffer_append(m, buffer_ptr(&auth_debug), | ||
674 | buffer_len(&auth_debug)); | ||
675 | buffer_clear(&auth_debug); | ||
676 | } | ||
677 | } | ||
678 | |||
679 | int | ||
680 | mm_answer_keyallowed(int socket, Buffer *m) | ||
681 | { | ||
682 | Key *key; | ||
683 | u_char *cuser, *chost, *blob; | ||
684 | u_int bloblen; | ||
685 | enum mm_keytype type = 0; | ||
686 | int allowed = 0; | ||
687 | |||
688 | debug3("%s entering", __FUNCTION__); | ||
689 | |||
690 | type = buffer_get_int(m); | ||
691 | cuser = buffer_get_string(m, NULL); | ||
692 | chost = buffer_get_string(m, NULL); | ||
693 | blob = buffer_get_string(m, &bloblen); | ||
694 | |||
695 | key = key_from_blob(blob, bloblen); | ||
696 | |||
697 | if ((compat20 && type == MM_RSAHOSTKEY) || | ||
698 | (!compat20 && type != MM_RSAHOSTKEY)) | ||
699 | fatal("%s: key type and protocol mismatch", __FUNCTION__); | ||
700 | |||
701 | debug3("%s: key_from_blob: %p", __FUNCTION__, key); | ||
702 | |||
703 | if (key != NULL && authctxt->pw != NULL) { | ||
704 | switch(type) { | ||
705 | case MM_USERKEY: | ||
706 | allowed = user_key_allowed(authctxt->pw, key); | ||
707 | break; | ||
708 | case MM_HOSTKEY: | ||
709 | allowed = hostbased_key_allowed(authctxt->pw, | ||
710 | cuser, chost, key); | ||
711 | break; | ||
712 | case MM_RSAHOSTKEY: | ||
713 | key->type = KEY_RSA1; /* XXX */ | ||
714 | allowed = auth_rhosts_rsa_key_allowed(authctxt->pw, | ||
715 | cuser, chost, key); | ||
716 | break; | ||
717 | default: | ||
718 | fatal("%s: unknown key type %d", __FUNCTION__, type); | ||
719 | break; | ||
720 | } | ||
721 | key_free(key); | ||
722 | } | ||
723 | |||
724 | /* clear temporarily storage (used by verify) */ | ||
725 | monitor_reset_key_state(); | ||
726 | |||
727 | if (allowed) { | ||
728 | /* Save temporarily for comparison in verify */ | ||
729 | key_blob = blob; | ||
730 | key_bloblen = bloblen; | ||
731 | key_blobtype = type; | ||
732 | hostbased_cuser = cuser; | ||
733 | hostbased_chost = chost; | ||
734 | } | ||
735 | |||
736 | debug3("%s: key %p is %s", | ||
737 | __FUNCTION__, key, allowed ? "allowed" : "disallowed"); | ||
738 | |||
739 | buffer_clear(m); | ||
740 | buffer_put_int(m, allowed); | ||
741 | |||
742 | mm_append_debug(m); | ||
743 | |||
744 | mm_request_send(socket, MONITOR_ANS_KEYALLOWED, m); | ||
745 | |||
746 | if (type == MM_RSAHOSTKEY) | ||
747 | monitor_permit(mon_dispatch, MONITOR_REQ_RSACHALLENGE, allowed); | ||
748 | |||
749 | return (0); | ||
750 | } | ||
751 | |||
752 | static int | ||
753 | monitor_valid_userblob(u_char *data, u_int datalen) | ||
754 | { | ||
755 | Buffer b; | ||
756 | u_char *p; | ||
757 | u_int len; | ||
758 | int fail = 0; | ||
759 | int session_id2_len = 20 /*XXX should get from [net] */; | ||
760 | |||
761 | buffer_init(&b); | ||
762 | buffer_append(&b, data, datalen); | ||
763 | |||
764 | if (datafellows & SSH_OLD_SESSIONID) { | ||
765 | buffer_consume(&b, session_id2_len); | ||
766 | } else { | ||
767 | xfree(buffer_get_string(&b, &len)); | ||
768 | if (len != session_id2_len) | ||
769 | fail++; | ||
770 | } | ||
771 | if (buffer_get_char(&b) != SSH2_MSG_USERAUTH_REQUEST) | ||
772 | fail++; | ||
773 | p = buffer_get_string(&b, NULL); | ||
774 | if (strcmp(authctxt->user, p) != 0) { | ||
775 | log("wrong user name passed to monitor: expected %s != %.100s", | ||
776 | authctxt->user, p); | ||
777 | fail++; | ||
778 | } | ||
779 | xfree(p); | ||
780 | buffer_skip_string(&b); | ||
781 | if (datafellows & SSH_BUG_PKAUTH) { | ||
782 | if (!buffer_get_char(&b)) | ||
783 | fail++; | ||
784 | } else { | ||
785 | p = buffer_get_string(&b, NULL); | ||
786 | if (strcmp("publickey", p) != 0) | ||
787 | fail++; | ||
788 | xfree(p); | ||
789 | if (!buffer_get_char(&b)) | ||
790 | fail++; | ||
791 | buffer_skip_string(&b); | ||
792 | } | ||
793 | buffer_skip_string(&b); | ||
794 | if (buffer_len(&b) != 0) | ||
795 | fail++; | ||
796 | buffer_free(&b); | ||
797 | return (fail == 0); | ||
798 | } | ||
799 | |||
800 | static int | ||
801 | monitor_valid_hostbasedblob(u_char *data, u_int datalen, u_char *cuser, | ||
802 | u_char *chost) | ||
803 | { | ||
804 | Buffer b; | ||
805 | u_char *p; | ||
806 | u_int len; | ||
807 | int fail = 0; | ||
808 | int session_id2_len = 20 /*XXX should get from [net] */; | ||
809 | |||
810 | buffer_init(&b); | ||
811 | buffer_append(&b, data, datalen); | ||
812 | |||
813 | xfree(buffer_get_string(&b, &len)); | ||
814 | if (len != session_id2_len) | ||
815 | fail++; | ||
816 | if (buffer_get_char(&b) != SSH2_MSG_USERAUTH_REQUEST) | ||
817 | fail++; | ||
818 | p = buffer_get_string(&b, NULL); | ||
819 | if (strcmp(authctxt->user, p) != 0) { | ||
820 | log("wrong user name passed to monitor: expected %s != %.100s", | ||
821 | authctxt->user, p); | ||
822 | fail++; | ||
823 | } | ||
824 | xfree(p); | ||
825 | buffer_skip_string(&b); /* service */ | ||
826 | p = buffer_get_string(&b, NULL); | ||
827 | if (strcmp(p, "hostbased") != 0) | ||
828 | fail++; | ||
829 | xfree(p); | ||
830 | buffer_skip_string(&b); /* pkalg */ | ||
831 | buffer_skip_string(&b); /* pkblob */ | ||
832 | |||
833 | /* verify client host, strip trailing dot if necessary */ | ||
834 | p = buffer_get_string(&b, NULL); | ||
835 | if (((len = strlen(p)) > 0) && p[len - 1] == '.') | ||
836 | p[len - 1] = '\0'; | ||
837 | if (strcmp(p, chost) != 0) | ||
838 | fail++; | ||
839 | xfree(p); | ||
840 | |||
841 | /* verify client user */ | ||
842 | p = buffer_get_string(&b, NULL); | ||
843 | if (strcmp(p, cuser) != 0) | ||
844 | fail++; | ||
845 | xfree(p); | ||
846 | |||
847 | if (buffer_len(&b) != 0) | ||
848 | fail++; | ||
849 | buffer_free(&b); | ||
850 | return (fail == 0); | ||
851 | } | ||
852 | |||
853 | int | ||
854 | mm_answer_keyverify(int socket, Buffer *m) | ||
855 | { | ||
856 | Key *key; | ||
857 | u_char *signature, *data, *blob; | ||
858 | u_int signaturelen, datalen, bloblen; | ||
859 | int verified = 0; | ||
860 | int valid_data = 0; | ||
861 | |||
862 | blob = buffer_get_string(m, &bloblen); | ||
863 | signature = buffer_get_string(m, &signaturelen); | ||
864 | data = buffer_get_string(m, &datalen); | ||
865 | |||
866 | if (hostbased_cuser == NULL || hostbased_chost == NULL || | ||
867 | monitor_allowed_key(blob, bloblen) == NULL) | ||
868 | fatal("%s: bad key, not previously allowed", __FUNCTION__); | ||
869 | |||
870 | key = key_from_blob(blob, bloblen); | ||
871 | if (key == NULL) | ||
872 | fatal("%s: bad public key blob", __FUNCTION__); | ||
873 | |||
874 | switch (key_blobtype) { | ||
875 | case MM_USERKEY: | ||
876 | valid_data = monitor_valid_userblob(data, datalen); | ||
877 | break; | ||
878 | case MM_HOSTKEY: | ||
879 | valid_data = monitor_valid_hostbasedblob(data, datalen, | ||
880 | hostbased_cuser, hostbased_chost); | ||
881 | break; | ||
882 | default: | ||
883 | valid_data = 0; | ||
884 | break; | ||
885 | } | ||
886 | if (!valid_data) | ||
887 | fatal("%s: bad signature data blob", __FUNCTION__); | ||
888 | |||
889 | verified = key_verify(key, signature, signaturelen, data, datalen); | ||
890 | debug3("%s: key %p signature %s", | ||
891 | __FUNCTION__, key, verified ? "verified" : "unverified"); | ||
892 | |||
893 | key_free(key); | ||
894 | xfree(blob); | ||
895 | xfree(signature); | ||
896 | xfree(data); | ||
897 | |||
898 | monitor_reset_key_state(); | ||
899 | |||
900 | buffer_clear(m); | ||
901 | buffer_put_int(m, verified); | ||
902 | mm_request_send(socket, MONITOR_ANS_KEYVERIFY, m); | ||
903 | |||
904 | auth_method = "publickey"; | ||
905 | |||
906 | return (verified); | ||
907 | } | ||
908 | |||
909 | static void | ||
910 | mm_record_login(Session *s, struct passwd *pw) | ||
911 | { | ||
912 | socklen_t fromlen; | ||
913 | struct sockaddr_storage from; | ||
914 | |||
915 | /* | ||
916 | * Get IP address of client. If the connection is not a socket, let | ||
917 | * the address be 0.0.0.0. | ||
918 | */ | ||
919 | memset(&from, 0, sizeof(from)); | ||
920 | if (packet_connection_is_on_socket()) { | ||
921 | fromlen = sizeof(from); | ||
922 | if (getpeername(packet_get_connection_in(), | ||
923 | (struct sockaddr *) & from, &fromlen) < 0) { | ||
924 | debug("getpeername: %.100s", strerror(errno)); | ||
925 | fatal_cleanup(); | ||
926 | } | ||
927 | } | ||
928 | /* Record that there was a login on that tty from the remote host. */ | ||
929 | record_login(s->pid, s->tty, pw->pw_name, pw->pw_uid, | ||
930 | get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping), | ||
931 | (struct sockaddr *)&from); | ||
932 | } | ||
933 | |||
934 | static void | ||
935 | mm_session_close(Session *s) | ||
936 | { | ||
937 | debug3("%s: session %d pid %d", __FUNCTION__, s->self, s->pid); | ||
938 | if (s->ttyfd != -1) { | ||
939 | debug3("%s: tty %s ptyfd %d", __FUNCTION__, s->tty, s->ptyfd); | ||
940 | fatal_remove_cleanup(session_pty_cleanup2, (void *)s); | ||
941 | session_pty_cleanup2(s); | ||
942 | } | ||
943 | s->used = 0; | ||
944 | } | ||
945 | |||
946 | int | ||
947 | mm_answer_pty(int socket, Buffer *m) | ||
948 | { | ||
949 | extern struct monitor *monitor; | ||
950 | Session *s; | ||
951 | int res, fd0; | ||
952 | |||
953 | debug3("%s entering", __FUNCTION__); | ||
954 | |||
955 | buffer_clear(m); | ||
956 | s = session_new(); | ||
957 | if (s == NULL) | ||
958 | goto error; | ||
959 | s->authctxt = authctxt; | ||
960 | s->pw = authctxt->pw; | ||
961 | s->pid = monitor->m_pid; | ||
962 | res = pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)); | ||
963 | if (res == 0) | ||
964 | goto error; | ||
965 | fatal_add_cleanup(session_pty_cleanup2, (void *)s); | ||
966 | pty_setowner(authctxt->pw, s->tty); | ||
967 | |||
968 | buffer_put_int(m, 1); | ||
969 | buffer_put_cstring(m, s->tty); | ||
970 | mm_request_send(socket, MONITOR_ANS_PTY, m); | ||
971 | |||
972 | mm_send_fd(socket, s->ptyfd); | ||
973 | mm_send_fd(socket, s->ttyfd); | ||
974 | |||
975 | /* We need to trick ttyslot */ | ||
976 | if (dup2(s->ttyfd, 0) == -1) | ||
977 | fatal("%s: dup2", __FUNCTION__); | ||
978 | |||
979 | mm_record_login(s, authctxt->pw); | ||
980 | |||
981 | /* Now we can close the file descriptor again */ | ||
982 | close(0); | ||
983 | |||
984 | /* make sure nothing uses fd 0 */ | ||
985 | if ((fd0 = open(_PATH_DEVNULL, O_RDONLY)) < 0) | ||
986 | fatal("%s: open(/dev/null): %s", __FUNCTION__, strerror(errno)); | ||
987 | if (fd0 != 0) | ||
988 | error("%s: fd0 %d != 0", __FUNCTION__, fd0); | ||
989 | |||
990 | /* slave is not needed */ | ||
991 | close(s->ttyfd); | ||
992 | s->ttyfd = s->ptyfd; | ||
993 | /* no need to dup() because nobody closes ptyfd */ | ||
994 | s->ptymaster = s->ptyfd; | ||
995 | |||
996 | debug3("%s: tty %s ptyfd %d", __FUNCTION__, s->tty, s->ttyfd); | ||
997 | |||
998 | return (0); | ||
999 | |||
1000 | error: | ||
1001 | if (s != NULL) | ||
1002 | mm_session_close(s); | ||
1003 | buffer_put_int(m, 0); | ||
1004 | mm_request_send(socket, MONITOR_ANS_PTY, m); | ||
1005 | return (0); | ||
1006 | } | ||
1007 | |||
1008 | int | ||
1009 | mm_answer_pty_cleanup(int socket, Buffer *m) | ||
1010 | { | ||
1011 | Session *s; | ||
1012 | char *tty; | ||
1013 | |||
1014 | debug3("%s entering", __FUNCTION__); | ||
1015 | |||
1016 | tty = buffer_get_string(m, NULL); | ||
1017 | if ((s = session_by_tty(tty)) != NULL) | ||
1018 | mm_session_close(s); | ||
1019 | buffer_clear(m); | ||
1020 | xfree(tty); | ||
1021 | return (0); | ||
1022 | } | ||
1023 | |||
1024 | int | ||
1025 | mm_answer_sesskey(int socket, Buffer *m) | ||
1026 | { | ||
1027 | BIGNUM *p; | ||
1028 | int rsafail; | ||
1029 | |||
1030 | /* Turn off permissions */ | ||
1031 | monitor_permit(mon_dispatch, MONITOR_REQ_SESSKEY, 1); | ||
1032 | |||
1033 | if ((p = BN_new()) == NULL) | ||
1034 | fatal("%s: BN_new", __FUNCTION__); | ||
1035 | |||
1036 | buffer_get_bignum2(m, p); | ||
1037 | |||
1038 | rsafail = ssh1_session_key(p); | ||
1039 | |||
1040 | buffer_clear(m); | ||
1041 | buffer_put_int(m, rsafail); | ||
1042 | buffer_put_bignum2(m, p); | ||
1043 | |||
1044 | BN_clear_free(p); | ||
1045 | |||
1046 | mm_request_send(socket, MONITOR_ANS_SESSKEY, m); | ||
1047 | |||
1048 | /* Turn on permissions for sessid passing */ | ||
1049 | monitor_permit(mon_dispatch, MONITOR_REQ_SESSID, 1); | ||
1050 | |||
1051 | return (0); | ||
1052 | } | ||
1053 | |||
1054 | int | ||
1055 | mm_answer_sessid(int socket, Buffer *m) | ||
1056 | { | ||
1057 | int i; | ||
1058 | |||
1059 | debug3("%s entering", __FUNCTION__); | ||
1060 | |||
1061 | if (buffer_len(m) != 16) | ||
1062 | fatal("%s: bad ssh1 session id", __FUNCTION__); | ||
1063 | for (i = 0; i < 16; i++) | ||
1064 | session_id[i] = buffer_get_char(m); | ||
1065 | |||
1066 | /* Turn on permissions for getpwnam */ | ||
1067 | monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); | ||
1068 | |||
1069 | return (0); | ||
1070 | } | ||
1071 | |||
1072 | int | ||
1073 | mm_answer_rsa_keyallowed(int socket, Buffer *m) | ||
1074 | { | ||
1075 | BIGNUM *client_n; | ||
1076 | Key *key = NULL; | ||
1077 | u_char *blob = NULL; | ||
1078 | u_int blen = 0; | ||
1079 | int allowed = 0; | ||
1080 | |||
1081 | debug3("%s entering", __FUNCTION__); | ||
1082 | |||
1083 | if (authctxt->valid) { | ||
1084 | if ((client_n = BN_new()) == NULL) | ||
1085 | fatal("%s: BN_new", __FUNCTION__); | ||
1086 | buffer_get_bignum2(m, client_n); | ||
1087 | allowed = auth_rsa_key_allowed(authctxt->pw, client_n, &key); | ||
1088 | BN_clear_free(client_n); | ||
1089 | } | ||
1090 | buffer_clear(m); | ||
1091 | buffer_put_int(m, allowed); | ||
1092 | |||
1093 | /* clear temporarily storage (used by generate challenge) */ | ||
1094 | monitor_reset_key_state(); | ||
1095 | |||
1096 | if (allowed && key != NULL) { | ||
1097 | key->type = KEY_RSA; /* cheat for key_to_blob */ | ||
1098 | if (key_to_blob(key, &blob, &blen) == 0) | ||
1099 | fatal("%s: key_to_blob failed", __FUNCTION__); | ||
1100 | buffer_put_string(m, blob, blen); | ||
1101 | |||
1102 | /* Save temporarily for comparison in verify */ | ||
1103 | key_blob = blob; | ||
1104 | key_bloblen = blen; | ||
1105 | key_blobtype = MM_RSAUSERKEY; | ||
1106 | key_free(key); | ||
1107 | } | ||
1108 | |||
1109 | mm_append_debug(m); | ||
1110 | |||
1111 | mm_request_send(socket, MONITOR_ANS_RSAKEYALLOWED, m); | ||
1112 | |||
1113 | monitor_permit(mon_dispatch, MONITOR_REQ_RSACHALLENGE, allowed); | ||
1114 | monitor_permit(mon_dispatch, MONITOR_REQ_RSARESPONSE, 0); | ||
1115 | return (0); | ||
1116 | } | ||
1117 | |||
1118 | int | ||
1119 | mm_answer_rsa_challenge(int socket, Buffer *m) | ||
1120 | { | ||
1121 | Key *key = NULL; | ||
1122 | u_char *blob; | ||
1123 | u_int blen; | ||
1124 | |||
1125 | debug3("%s entering", __FUNCTION__); | ||
1126 | |||
1127 | if (!authctxt->valid) | ||
1128 | fatal("%s: authctxt not valid", __FUNCTION__); | ||
1129 | blob = buffer_get_string(m, &blen); | ||
1130 | if (!monitor_allowed_key(blob, blen)) | ||
1131 | fatal("%s: bad key, not previously allowed", __FUNCTION__); | ||
1132 | if (key_blobtype != MM_RSAUSERKEY && key_blobtype != MM_RSAHOSTKEY) | ||
1133 | fatal("%s: key type mismatch", __FUNCTION__); | ||
1134 | if ((key = key_from_blob(blob, blen)) == NULL) | ||
1135 | fatal("%s: received bad key", __FUNCTION__); | ||
1136 | |||
1137 | if (ssh1_challenge) | ||
1138 | BN_clear_free(ssh1_challenge); | ||
1139 | ssh1_challenge = auth_rsa_generate_challenge(key); | ||
1140 | |||
1141 | buffer_clear(m); | ||
1142 | buffer_put_bignum2(m, ssh1_challenge); | ||
1143 | |||
1144 | debug3("%s sending reply", __FUNCTION__); | ||
1145 | mm_request_send(socket, MONITOR_ANS_RSACHALLENGE, m); | ||
1146 | |||
1147 | monitor_permit(mon_dispatch, MONITOR_REQ_RSARESPONSE, 1); | ||
1148 | return (0); | ||
1149 | } | ||
1150 | |||
1151 | int | ||
1152 | mm_answer_rsa_response(int socket, Buffer *m) | ||
1153 | { | ||
1154 | Key *key = NULL; | ||
1155 | u_char *blob, *response; | ||
1156 | u_int blen, len; | ||
1157 | int success; | ||
1158 | |||
1159 | debug3("%s entering", __FUNCTION__); | ||
1160 | |||
1161 | if (!authctxt->valid) | ||
1162 | fatal("%s: authctxt not valid", __FUNCTION__); | ||
1163 | if (ssh1_challenge == NULL) | ||
1164 | fatal("%s: no ssh1_challenge", __FUNCTION__); | ||
1165 | |||
1166 | blob = buffer_get_string(m, &blen); | ||
1167 | if (!monitor_allowed_key(blob, blen)) | ||
1168 | fatal("%s: bad key, not previously allowed", __FUNCTION__); | ||
1169 | if (key_blobtype != MM_RSAUSERKEY && key_blobtype != MM_RSAHOSTKEY) | ||
1170 | fatal("%s: key type mismatch: %d", __FUNCTION__, key_blobtype); | ||
1171 | if ((key = key_from_blob(blob, blen)) == NULL) | ||
1172 | fatal("%s: received bad key", __FUNCTION__); | ||
1173 | response = buffer_get_string(m, &len); | ||
1174 | if (len != 16) | ||
1175 | fatal("%s: received bad response to challenge", __FUNCTION__); | ||
1176 | success = auth_rsa_verify_response(key, ssh1_challenge, response); | ||
1177 | |||
1178 | key_free(key); | ||
1179 | xfree(response); | ||
1180 | |||
1181 | auth_method = key_blobtype == MM_RSAUSERKEY ? "rsa" : "rhosts-rsa"; | ||
1182 | |||
1183 | /* reset state */ | ||
1184 | BN_clear_free(ssh1_challenge); | ||
1185 | ssh1_challenge = NULL; | ||
1186 | monitor_reset_key_state(); | ||
1187 | |||
1188 | buffer_clear(m); | ||
1189 | buffer_put_int(m, success); | ||
1190 | mm_request_send(socket, MONITOR_ANS_RSARESPONSE, m); | ||
1191 | |||
1192 | return (success); | ||
1193 | } | ||
1194 | |||
1195 | int | ||
1196 | mm_answer_term(int socket, Buffer *req) | ||
1197 | { | ||
1198 | extern struct monitor *monitor; | ||
1199 | int res, status; | ||
1200 | |||
1201 | debug3("%s: tearing down sessions", __FUNCTION__); | ||
1202 | |||
1203 | /* The child is terminating */ | ||
1204 | session_destroy_all(&mm_session_close); | ||
1205 | |||
1206 | if (waitpid(monitor->m_pid, &status, 0) == -1) | ||
1207 | exit(1); | ||
1208 | |||
1209 | res = WIFEXITED(status) ? WEXITSTATUS(status) : 1; | ||
1210 | |||
1211 | /* Terminate process */ | ||
1212 | exit (res); | ||
1213 | } | ||
1214 | |||
1215 | void | ||
1216 | monitor_apply_keystate(struct monitor *monitor) | ||
1217 | { | ||
1218 | if (compat20) { | ||
1219 | set_newkeys(MODE_IN); | ||
1220 | set_newkeys(MODE_OUT); | ||
1221 | } else { | ||
1222 | u_char key[SSH_SESSION_KEY_LENGTH]; | ||
1223 | |||
1224 | memset(key, 'a', sizeof(key)); | ||
1225 | packet_set_protocol_flags(child_state.ssh1protoflags); | ||
1226 | packet_set_encryption_key(key, SSH_SESSION_KEY_LENGTH, | ||
1227 | child_state.ssh1cipher); | ||
1228 | } | ||
1229 | |||
1230 | packet_set_keycontext(MODE_OUT, child_state.keyout); | ||
1231 | xfree(child_state.keyout); | ||
1232 | packet_set_keycontext(MODE_IN, child_state.keyin); | ||
1233 | xfree(child_state.keyin); | ||
1234 | |||
1235 | if (!compat20) { | ||
1236 | packet_set_iv(MODE_OUT, child_state.ivout); | ||
1237 | xfree(child_state.ivout); | ||
1238 | packet_set_iv(MODE_IN, child_state.ivin); | ||
1239 | xfree(child_state.ivin); | ||
1240 | } | ||
1241 | |||
1242 | memcpy(&incoming_stream, &child_state.incoming, | ||
1243 | sizeof(incoming_stream)); | ||
1244 | memcpy(&outgoing_stream, &child_state.outgoing, | ||
1245 | sizeof(outgoing_stream)); | ||
1246 | |||
1247 | /* Update with new address */ | ||
1248 | mm_init_compression(monitor->m_zlib); | ||
1249 | |||
1250 | /* Network I/O buffers */ | ||
1251 | /* XXX inefficient for large buffers, need: buffer_init_from_string */ | ||
1252 | buffer_clear(&input); | ||
1253 | buffer_append(&input, child_state.input, child_state.ilen); | ||
1254 | memset(child_state.input, 0, child_state.ilen); | ||
1255 | xfree(child_state.input); | ||
1256 | |||
1257 | buffer_clear(&output); | ||
1258 | buffer_append(&output, child_state.output, child_state.olen); | ||
1259 | memset(child_state.output, 0, child_state.olen); | ||
1260 | xfree(child_state.output); | ||
1261 | } | ||
1262 | |||
1263 | static Kex * | ||
1264 | mm_get_kex(Buffer *m) | ||
1265 | { | ||
1266 | Kex *kex; | ||
1267 | void *blob; | ||
1268 | u_int bloblen; | ||
1269 | |||
1270 | kex = xmalloc(sizeof(*kex)); | ||
1271 | memset(kex, 0, sizeof(*kex)); | ||
1272 | kex->session_id = buffer_get_string(m, &kex->session_id_len); | ||
1273 | kex->we_need = buffer_get_int(m); | ||
1274 | kex->server = 1; | ||
1275 | kex->hostkey_type = buffer_get_int(m); | ||
1276 | kex->kex_type = buffer_get_int(m); | ||
1277 | blob = buffer_get_string(m, &bloblen); | ||
1278 | buffer_init(&kex->my); | ||
1279 | buffer_append(&kex->my, blob, bloblen); | ||
1280 | xfree(blob); | ||
1281 | blob = buffer_get_string(m, &bloblen); | ||
1282 | buffer_init(&kex->peer); | ||
1283 | buffer_append(&kex->peer, blob, bloblen); | ||
1284 | xfree(blob); | ||
1285 | kex->done = 1; | ||
1286 | kex->flags = buffer_get_int(m); | ||
1287 | kex->client_version_string = buffer_get_string(m, NULL); | ||
1288 | kex->server_version_string = buffer_get_string(m, NULL); | ||
1289 | kex->load_host_key=&get_hostkey_by_type; | ||
1290 | kex->host_key_index=&get_hostkey_index; | ||
1291 | |||
1292 | return (kex); | ||
1293 | } | ||
1294 | |||
1295 | /* This function requries careful sanity checking */ | ||
1296 | |||
1297 | void | ||
1298 | mm_get_keystate(struct monitor *monitor) | ||
1299 | { | ||
1300 | Buffer m; | ||
1301 | u_char *blob, *p; | ||
1302 | u_int bloblen, plen; | ||
1303 | |||
1304 | debug3("%s: Waiting for new keys", __FUNCTION__); | ||
1305 | |||
1306 | buffer_init(&m); | ||
1307 | mm_request_receive_expect(monitor->m_sendfd, MONITOR_REQ_KEYEXPORT, &m); | ||
1308 | if (!compat20) { | ||
1309 | child_state.ssh1protoflags = buffer_get_int(&m); | ||
1310 | child_state.ssh1cipher = buffer_get_int(&m); | ||
1311 | child_state.ivout = buffer_get_string(&m, | ||
1312 | &child_state.ivoutlen); | ||
1313 | child_state.ivin = buffer_get_string(&m, &child_state.ivinlen); | ||
1314 | goto skip; | ||
1315 | } else { | ||
1316 | /* Get the Kex for rekeying */ | ||
1317 | *monitor->m_pkex = mm_get_kex(&m); | ||
1318 | } | ||
1319 | |||
1320 | blob = buffer_get_string(&m, &bloblen); | ||
1321 | current_keys[MODE_OUT] = mm_newkeys_from_blob(blob, bloblen); | ||
1322 | xfree(blob); | ||
1323 | |||
1324 | debug3("%s: Waiting for second key", __FUNCTION__); | ||
1325 | blob = buffer_get_string(&m, &bloblen); | ||
1326 | current_keys[MODE_IN] = mm_newkeys_from_blob(blob, bloblen); | ||
1327 | xfree(blob); | ||
1328 | |||
1329 | /* Now get sequence numbers for the packets */ | ||
1330 | packet_set_seqnr(MODE_OUT, buffer_get_int(&m)); | ||
1331 | packet_set_seqnr(MODE_IN, buffer_get_int(&m)); | ||
1332 | |||
1333 | skip: | ||
1334 | /* Get the key context */ | ||
1335 | child_state.keyout = buffer_get_string(&m, &child_state.keyoutlen); | ||
1336 | child_state.keyin = buffer_get_string(&m, &child_state.keyinlen); | ||
1337 | |||
1338 | debug3("%s: Getting compression state", __FUNCTION__); | ||
1339 | /* Get compression state */ | ||
1340 | p = buffer_get_string(&m, &plen); | ||
1341 | if (plen != sizeof(child_state.outgoing)) | ||
1342 | fatal("%s: bad request size", __FUNCTION__); | ||
1343 | memcpy(&child_state.outgoing, p, sizeof(child_state.outgoing)); | ||
1344 | xfree(p); | ||
1345 | |||
1346 | p = buffer_get_string(&m, &plen); | ||
1347 | if (plen != sizeof(child_state.incoming)) | ||
1348 | fatal("%s: bad request size", __FUNCTION__); | ||
1349 | memcpy(&child_state.incoming, p, sizeof(child_state.incoming)); | ||
1350 | xfree(p); | ||
1351 | |||
1352 | /* Network I/O buffers */ | ||
1353 | debug3("%s: Getting Network I/O buffers", __FUNCTION__); | ||
1354 | child_state.input = buffer_get_string(&m, &child_state.ilen); | ||
1355 | child_state.output = buffer_get_string(&m, &child_state.olen); | ||
1356 | |||
1357 | buffer_free(&m); | ||
1358 | } | ||
1359 | |||
1360 | |||
1361 | /* Allocation functions for zlib */ | ||
1362 | void * | ||
1363 | mm_zalloc(struct mm_master *mm, u_int ncount, u_int size) | ||
1364 | { | ||
1365 | void *address; | ||
1366 | |||
1367 | address = mm_malloc(mm, size * ncount); | ||
1368 | |||
1369 | return (address); | ||
1370 | } | ||
1371 | |||
1372 | void | ||
1373 | mm_zfree(struct mm_master *mm, void *address) | ||
1374 | { | ||
1375 | mm_free(mm, address); | ||
1376 | } | ||
1377 | |||
1378 | void | ||
1379 | mm_init_compression(struct mm_master *mm) | ||
1380 | { | ||
1381 | outgoing_stream.zalloc = (alloc_func)mm_zalloc; | ||
1382 | outgoing_stream.zfree = (free_func)mm_zfree; | ||
1383 | outgoing_stream.opaque = mm; | ||
1384 | |||
1385 | incoming_stream.zalloc = (alloc_func)mm_zalloc; | ||
1386 | incoming_stream.zfree = (free_func)mm_zfree; | ||
1387 | incoming_stream.opaque = mm; | ||
1388 | } | ||
1389 | |||
1390 | /* XXX */ | ||
1391 | |||
1392 | #define FD_CLOSEONEXEC(x) do { \ | ||
1393 | if (fcntl(x, F_SETFD, 1) == -1) \ | ||
1394 | fatal("fcntl(%d, F_SETFD)", x); \ | ||
1395 | } while (0) | ||
1396 | |||
1397 | static void | ||
1398 | monitor_socketpair(int *pair) | ||
1399 | { | ||
1400 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) | ||
1401 | fatal("%s: socketpair", __FUNCTION__); | ||
1402 | FD_CLOSEONEXEC(pair[0]); | ||
1403 | FD_CLOSEONEXEC(pair[1]); | ||
1404 | } | ||
1405 | |||
1406 | #define MM_MEMSIZE 65536 | ||
1407 | |||
1408 | struct monitor * | ||
1409 | monitor_init(void) | ||
1410 | { | ||
1411 | struct monitor *mon; | ||
1412 | int pair[2]; | ||
1413 | |||
1414 | mon = xmalloc(sizeof(*mon)); | ||
1415 | |||
1416 | monitor_socketpair(pair); | ||
1417 | |||
1418 | mon->m_recvfd = pair[0]; | ||
1419 | mon->m_sendfd = pair[1]; | ||
1420 | |||
1421 | /* Used to share zlib space across processes */ | ||
1422 | mon->m_zback = mm_create(NULL, MM_MEMSIZE); | ||
1423 | mon->m_zlib = mm_create(mon->m_zback, 20 * MM_MEMSIZE); | ||
1424 | |||
1425 | /* Compression needs to share state across borders */ | ||
1426 | mm_init_compression(mon->m_zlib); | ||
1427 | |||
1428 | return mon; | ||
1429 | } | ||
1430 | |||
1431 | void | ||
1432 | monitor_reinit(struct monitor *mon) | ||
1433 | { | ||
1434 | int pair[2]; | ||
1435 | |||
1436 | monitor_socketpair(pair); | ||
1437 | |||
1438 | mon->m_recvfd = pair[0]; | ||
1439 | mon->m_sendfd = pair[1]; | ||
1440 | } | ||