diff options
Diffstat (limited to 'ssh-agent.c')
-rw-r--r-- | ssh-agent.c | 744 |
1 files changed, 276 insertions, 468 deletions
diff --git a/ssh-agent.c b/ssh-agent.c index b987562b9..0c6c36592 100644 --- a/ssh-agent.c +++ b/ssh-agent.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-agent.c,v 1.218 2017/03/15 03:52:30 deraadt Exp $ */ | 1 | /* $OpenBSD: ssh-agent.c,v 1.224 2017/07/24 04:34:28 djm 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 |
@@ -60,6 +60,9 @@ | |||
60 | #ifdef HAVE_PATHS_H | 60 | #ifdef HAVE_PATHS_H |
61 | # include <paths.h> | 61 | # include <paths.h> |
62 | #endif | 62 | #endif |
63 | #ifdef HAVE_POLL_H | ||
64 | # include <poll.h> | ||
65 | #endif | ||
63 | #include <signal.h> | 66 | #include <signal.h> |
64 | #include <stdarg.h> | 67 | #include <stdarg.h> |
65 | #include <stdio.h> | 68 | #include <stdio.h> |
@@ -73,7 +76,6 @@ | |||
73 | 76 | ||
74 | #include "xmalloc.h" | 77 | #include "xmalloc.h" |
75 | #include "ssh.h" | 78 | #include "ssh.h" |
76 | #include "rsa.h" | ||
77 | #include "sshbuf.h" | 79 | #include "sshbuf.h" |
78 | #include "sshkey.h" | 80 | #include "sshkey.h" |
79 | #include "authfd.h" | 81 | #include "authfd.h" |
@@ -92,6 +94,9 @@ | |||
92 | # define DEFAULT_PKCS11_WHITELIST "/usr/lib*/*,/usr/local/lib*/*" | 94 | # define DEFAULT_PKCS11_WHITELIST "/usr/lib*/*,/usr/local/lib*/*" |
93 | #endif | 95 | #endif |
94 | 96 | ||
97 | /* Maximum accepted message length */ | ||
98 | #define AGENT_MAX_LEN (256*1024) | ||
99 | |||
95 | typedef enum { | 100 | typedef enum { |
96 | AUTH_UNUSED, | 101 | AUTH_UNUSED, |
97 | AUTH_SOCKET, | 102 | AUTH_SOCKET, |
@@ -118,13 +123,13 @@ typedef struct identity { | |||
118 | u_int confirm; | 123 | u_int confirm; |
119 | } Identity; | 124 | } Identity; |
120 | 125 | ||
121 | typedef struct { | 126 | struct idtable { |
122 | int nentries; | 127 | int nentries; |
123 | TAILQ_HEAD(idqueue, identity) idlist; | 128 | TAILQ_HEAD(idqueue, identity) idlist; |
124 | } Idtab; | 129 | }; |
125 | 130 | ||
126 | /* private key table, one per protocol version */ | 131 | /* private key table */ |
127 | Idtab idtable[3]; | 132 | struct idtable *idtab; |
128 | 133 | ||
129 | int max_fd = 0; | 134 | int max_fd = 0; |
130 | 135 | ||
@@ -171,21 +176,9 @@ close_socket(SocketEntry *e) | |||
171 | static void | 176 | static void |
172 | idtab_init(void) | 177 | idtab_init(void) |
173 | { | 178 | { |
174 | int i; | 179 | idtab = xcalloc(1, sizeof(*idtab)); |
175 | 180 | TAILQ_INIT(&idtab->idlist); | |
176 | for (i = 0; i <=2; i++) { | 181 | idtab->nentries = 0; |
177 | TAILQ_INIT(&idtable[i].idlist); | ||
178 | idtable[i].nentries = 0; | ||
179 | } | ||
180 | } | ||
181 | |||
182 | /* return private key table for requested protocol version */ | ||
183 | static Idtab * | ||
184 | idtab_lookup(int version) | ||
185 | { | ||
186 | if (version < 1 || version > 2) | ||
187 | fatal("internal error, bad protocol version %d", version); | ||
188 | return &idtable[version]; | ||
189 | } | 182 | } |
190 | 183 | ||
191 | static void | 184 | static void |
@@ -199,12 +192,11 @@ free_identity(Identity *id) | |||
199 | 192 | ||
200 | /* return matching private key for given public key */ | 193 | /* return matching private key for given public key */ |
201 | static Identity * | 194 | static Identity * |
202 | lookup_identity(struct sshkey *key, int version) | 195 | lookup_identity(struct sshkey *key) |
203 | { | 196 | { |
204 | Identity *id; | 197 | Identity *id; |
205 | 198 | ||
206 | Idtab *tab = idtab_lookup(version); | 199 | TAILQ_FOREACH(id, &idtab->idlist, next) { |
207 | TAILQ_FOREACH(id, &tab->idlist, next) { | ||
208 | if (sshkey_equal(key, id->key)) | 200 | if (sshkey_equal(key, id->key)) |
209 | return (id); | 201 | return (id); |
210 | } | 202 | } |
@@ -241,135 +233,30 @@ send_status(SocketEntry *e, int success) | |||
241 | 233 | ||
242 | /* send list of supported public keys to 'client' */ | 234 | /* send list of supported public keys to 'client' */ |
243 | static void | 235 | static void |
244 | process_request_identities(SocketEntry *e, int version) | 236 | process_request_identities(SocketEntry *e) |
245 | { | 237 | { |
246 | Idtab *tab = idtab_lookup(version); | ||
247 | Identity *id; | 238 | Identity *id; |
248 | struct sshbuf *msg; | 239 | struct sshbuf *msg; |
249 | int r; | 240 | int r; |
250 | 241 | ||
251 | if ((msg = sshbuf_new()) == NULL) | 242 | if ((msg = sshbuf_new()) == NULL) |
252 | fatal("%s: sshbuf_new failed", __func__); | 243 | fatal("%s: sshbuf_new failed", __func__); |
253 | if ((r = sshbuf_put_u8(msg, (version == 1) ? | 244 | if ((r = sshbuf_put_u8(msg, SSH2_AGENT_IDENTITIES_ANSWER)) != 0 || |
254 | SSH_AGENT_RSA_IDENTITIES_ANSWER : | 245 | (r = sshbuf_put_u32(msg, idtab->nentries)) != 0) |
255 | SSH2_AGENT_IDENTITIES_ANSWER)) != 0 || | ||
256 | (r = sshbuf_put_u32(msg, tab->nentries)) != 0) | ||
257 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | ||
258 | TAILQ_FOREACH(id, &tab->idlist, next) { | ||
259 | if (id->key->type == KEY_RSA1) { | ||
260 | #ifdef WITH_SSH1 | ||
261 | if ((r = sshbuf_put_u32(msg, | ||
262 | BN_num_bits(id->key->rsa->n))) != 0 || | ||
263 | (r = sshbuf_put_bignum1(msg, | ||
264 | id->key->rsa->e)) != 0 || | ||
265 | (r = sshbuf_put_bignum1(msg, | ||
266 | id->key->rsa->n)) != 0) | ||
267 | fatal("%s: buffer error: %s", | ||
268 | __func__, ssh_err(r)); | ||
269 | #endif | ||
270 | } else { | ||
271 | u_char *blob; | ||
272 | size_t blen; | ||
273 | |||
274 | if ((r = sshkey_to_blob(id->key, &blob, &blen)) != 0) { | ||
275 | error("%s: sshkey_to_blob: %s", __func__, | ||
276 | ssh_err(r)); | ||
277 | continue; | ||
278 | } | ||
279 | if ((r = sshbuf_put_string(msg, blob, blen)) != 0) | ||
280 | fatal("%s: buffer error: %s", | ||
281 | __func__, ssh_err(r)); | ||
282 | free(blob); | ||
283 | } | ||
284 | if ((r = sshbuf_put_cstring(msg, id->comment)) != 0) | ||
285 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | ||
286 | } | ||
287 | if ((r = sshbuf_put_stringb(e->output, msg)) != 0) | ||
288 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | ||
289 | sshbuf_free(msg); | ||
290 | } | ||
291 | |||
292 | #ifdef WITH_SSH1 | ||
293 | /* ssh1 only */ | ||
294 | static void | ||
295 | process_authentication_challenge1(SocketEntry *e) | ||
296 | { | ||
297 | u_char buf[32], mdbuf[16], session_id[16]; | ||
298 | u_int response_type; | ||
299 | BIGNUM *challenge; | ||
300 | Identity *id; | ||
301 | int r, len; | ||
302 | struct sshbuf *msg; | ||
303 | struct ssh_digest_ctx *md; | ||
304 | struct sshkey *key; | ||
305 | |||
306 | if ((msg = sshbuf_new()) == NULL) | ||
307 | fatal("%s: sshbuf_new failed", __func__); | ||
308 | if ((key = sshkey_new(KEY_RSA1)) == NULL) | ||
309 | fatal("%s: sshkey_new failed", __func__); | ||
310 | if ((challenge = BN_new()) == NULL) | ||
311 | fatal("%s: BN_new failed", __func__); | ||
312 | |||
313 | if ((r = sshbuf_get_u32(e->request, NULL)) != 0 || /* ignored */ | ||
314 | (r = sshbuf_get_bignum1(e->request, key->rsa->e)) != 0 || | ||
315 | (r = sshbuf_get_bignum1(e->request, key->rsa->n)) != 0 || | ||
316 | (r = sshbuf_get_bignum1(e->request, challenge))) | ||
317 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | ||
318 | |||
319 | /* Only protocol 1.1 is supported */ | ||
320 | if (sshbuf_len(e->request) == 0) | ||
321 | goto failure; | ||
322 | if ((r = sshbuf_get(e->request, session_id, sizeof(session_id))) != 0 || | ||
323 | (r = sshbuf_get_u32(e->request, &response_type)) != 0) | ||
324 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | 246 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); |
325 | if (response_type != 1) | 247 | TAILQ_FOREACH(id, &idtab->idlist, next) { |
326 | goto failure; | 248 | if ((r = sshkey_puts(id->key, msg)) != 0 || |
327 | 249 | (r = sshbuf_put_cstring(msg, id->comment)) != 0) { | |
328 | id = lookup_identity(key, 1); | 250 | error("%s: put key/comment: %s", __func__, |
329 | if (id != NULL && (!id->confirm || confirm_key(id) == 0)) { | ||
330 | struct sshkey *private = id->key; | ||
331 | /* Decrypt the challenge using the private key. */ | ||
332 | if ((r = rsa_private_decrypt(challenge, challenge, | ||
333 | private->rsa) != 0)) { | ||
334 | fatal("%s: rsa_public_encrypt: %s", __func__, | ||
335 | ssh_err(r)); | 251 | ssh_err(r)); |
336 | goto failure; /* XXX ? */ | 252 | continue; |
337 | } | 253 | } |
338 | |||
339 | /* The response is MD5 of decrypted challenge plus session id */ | ||
340 | len = BN_num_bytes(challenge); | ||
341 | if (len <= 0 || len > 32) { | ||
342 | logit("%s: bad challenge length %d", __func__, len); | ||
343 | goto failure; | ||
344 | } | ||
345 | memset(buf, 0, 32); | ||
346 | BN_bn2bin(challenge, buf + 32 - len); | ||
347 | if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL || | ||
348 | ssh_digest_update(md, buf, 32) < 0 || | ||
349 | ssh_digest_update(md, session_id, 16) < 0 || | ||
350 | ssh_digest_final(md, mdbuf, sizeof(mdbuf)) < 0) | ||
351 | fatal("%s: md5 failed", __func__); | ||
352 | ssh_digest_free(md); | ||
353 | |||
354 | /* Send the response. */ | ||
355 | if ((r = sshbuf_put_u8(msg, SSH_AGENT_RSA_RESPONSE)) != 0 || | ||
356 | (r = sshbuf_put(msg, mdbuf, sizeof(mdbuf))) != 0) | ||
357 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | ||
358 | goto send; | ||
359 | } | 254 | } |
360 | |||
361 | failure: | ||
362 | /* Unknown identity or protocol error. Send failure. */ | ||
363 | if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE)) != 0) | ||
364 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | ||
365 | send: | ||
366 | if ((r = sshbuf_put_stringb(e->output, msg)) != 0) | 255 | if ((r = sshbuf_put_stringb(e->output, msg)) != 0) |
367 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | 256 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); |
368 | sshkey_free(key); | ||
369 | BN_clear_free(challenge); | ||
370 | sshbuf_free(msg); | 257 | sshbuf_free(msg); |
371 | } | 258 | } |
372 | #endif | 259 | |
373 | 260 | ||
374 | static char * | 261 | static char * |
375 | agent_decode_alg(struct sshkey *key, u_int flags) | 262 | agent_decode_alg(struct sshkey *key, u_int flags) |
@@ -387,27 +274,24 @@ agent_decode_alg(struct sshkey *key, u_int flags) | |||
387 | static void | 274 | static void |
388 | process_sign_request2(SocketEntry *e) | 275 | process_sign_request2(SocketEntry *e) |
389 | { | 276 | { |
390 | u_char *blob, *data, *signature = NULL; | 277 | const u_char *data; |
391 | size_t blen, dlen, slen = 0; | 278 | u_char *signature = NULL; |
279 | size_t dlen, slen = 0; | ||
392 | u_int compat = 0, flags; | 280 | u_int compat = 0, flags; |
393 | int r, ok = -1; | 281 | int r, ok = -1; |
394 | struct sshbuf *msg; | 282 | struct sshbuf *msg; |
395 | struct sshkey *key; | 283 | struct sshkey *key = NULL; |
396 | struct identity *id; | 284 | struct identity *id; |
397 | 285 | ||
398 | if ((msg = sshbuf_new()) == NULL) | 286 | if ((msg = sshbuf_new()) == NULL) |
399 | fatal("%s: sshbuf_new failed", __func__); | 287 | fatal("%s: sshbuf_new failed", __func__); |
400 | if ((r = sshbuf_get_string(e->request, &blob, &blen)) != 0 || | 288 | if ((r = sshkey_froms(e->request, &key)) != 0 || |
401 | (r = sshbuf_get_string(e->request, &data, &dlen)) != 0 || | 289 | (r = sshbuf_get_string_direct(e->request, &data, &dlen)) != 0 || |
402 | (r = sshbuf_get_u32(e->request, &flags)) != 0) | 290 | (r = sshbuf_get_u32(e->request, &flags)) != 0) |
403 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | 291 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); |
404 | if (flags & SSH_AGENT_OLD_SIGNATURE) | 292 | if (flags & SSH_AGENT_OLD_SIGNATURE) |
405 | compat = SSH_BUG_SIGBLOB; | 293 | compat = SSH_BUG_SIGBLOB; |
406 | if ((r = sshkey_from_blob(blob, blen, &key)) != 0) { | 294 | if ((id = lookup_identity(key)) == NULL) { |
407 | error("%s: cannot parse key blob: %s", __func__, ssh_err(r)); | ||
408 | goto send; | ||
409 | } | ||
410 | if ((id = lookup_identity(key, 2)) == NULL) { | ||
411 | verbose("%s: %s key not found", __func__, sshkey_type(key)); | 295 | verbose("%s: %s key not found", __func__, sshkey_type(key)); |
412 | goto send; | 296 | goto send; |
413 | } | 297 | } |
@@ -435,90 +319,52 @@ process_sign_request2(SocketEntry *e) | |||
435 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | 319 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); |
436 | 320 | ||
437 | sshbuf_free(msg); | 321 | sshbuf_free(msg); |
438 | free(data); | ||
439 | free(blob); | ||
440 | free(signature); | 322 | free(signature); |
441 | } | 323 | } |
442 | 324 | ||
443 | /* shared */ | 325 | /* shared */ |
444 | static void | 326 | static void |
445 | process_remove_identity(SocketEntry *e, int version) | 327 | process_remove_identity(SocketEntry *e) |
446 | { | 328 | { |
447 | size_t blen; | ||
448 | int r, success = 0; | 329 | int r, success = 0; |
449 | struct sshkey *key = NULL; | 330 | struct sshkey *key = NULL; |
450 | u_char *blob; | 331 | Identity *id; |
451 | #ifdef WITH_SSH1 | ||
452 | u_int bits; | ||
453 | #endif /* WITH_SSH1 */ | ||
454 | |||
455 | switch (version) { | ||
456 | #ifdef WITH_SSH1 | ||
457 | case 1: | ||
458 | if ((key = sshkey_new(KEY_RSA1)) == NULL) { | ||
459 | error("%s: sshkey_new failed", __func__); | ||
460 | return; | ||
461 | } | ||
462 | if ((r = sshbuf_get_u32(e->request, &bits)) != 0 || | ||
463 | (r = sshbuf_get_bignum1(e->request, key->rsa->e)) != 0 || | ||
464 | (r = sshbuf_get_bignum1(e->request, key->rsa->n)) != 0) | ||
465 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | ||
466 | 332 | ||
467 | if (bits != sshkey_size(key)) | 333 | if ((r = sshkey_froms(e->request, &key)) != 0) { |
468 | logit("Warning: identity keysize mismatch: " | 334 | error("%s: get key: %s", __func__, ssh_err(r)); |
469 | "actual %u, announced %u", | 335 | goto done; |
470 | sshkey_size(key), bits); | ||
471 | break; | ||
472 | #endif /* WITH_SSH1 */ | ||
473 | case 2: | ||
474 | if ((r = sshbuf_get_string(e->request, &blob, &blen)) != 0) | ||
475 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | ||
476 | if ((r = sshkey_from_blob(blob, blen, &key)) != 0) | ||
477 | error("%s: sshkey_from_blob failed: %s", | ||
478 | __func__, ssh_err(r)); | ||
479 | free(blob); | ||
480 | break; | ||
481 | } | 336 | } |
482 | if (key != NULL) { | 337 | if ((id = lookup_identity(key)) == NULL) { |
483 | Identity *id = lookup_identity(key, version); | 338 | debug("%s: key not found", __func__); |
484 | if (id != NULL) { | 339 | goto done; |
485 | /* | ||
486 | * We have this key. Free the old key. Since we | ||
487 | * don't want to leave empty slots in the middle of | ||
488 | * the array, we actually free the key there and move | ||
489 | * all the entries between the empty slot and the end | ||
490 | * of the array. | ||
491 | */ | ||
492 | Idtab *tab = idtab_lookup(version); | ||
493 | if (tab->nentries < 1) | ||
494 | fatal("process_remove_identity: " | ||
495 | "internal error: tab->nentries %d", | ||
496 | tab->nentries); | ||
497 | TAILQ_REMOVE(&tab->idlist, id, next); | ||
498 | free_identity(id); | ||
499 | tab->nentries--; | ||
500 | success = 1; | ||
501 | } | ||
502 | sshkey_free(key); | ||
503 | } | 340 | } |
341 | /* We have this key, free it. */ | ||
342 | if (idtab->nentries < 1) | ||
343 | fatal("%s: internal error: nentries %d", | ||
344 | __func__, idtab->nentries); | ||
345 | TAILQ_REMOVE(&idtab->idlist, id, next); | ||
346 | free_identity(id); | ||
347 | idtab->nentries--; | ||
348 | sshkey_free(key); | ||
349 | success = 1; | ||
350 | done: | ||
504 | send_status(e, success); | 351 | send_status(e, success); |
505 | } | 352 | } |
506 | 353 | ||
507 | static void | 354 | static void |
508 | process_remove_all_identities(SocketEntry *e, int version) | 355 | process_remove_all_identities(SocketEntry *e) |
509 | { | 356 | { |
510 | Idtab *tab = idtab_lookup(version); | ||
511 | Identity *id; | 357 | Identity *id; |
512 | 358 | ||
513 | /* Loop over all identities and clear the keys. */ | 359 | /* Loop over all identities and clear the keys. */ |
514 | for (id = TAILQ_FIRST(&tab->idlist); id; | 360 | for (id = TAILQ_FIRST(&idtab->idlist); id; |
515 | id = TAILQ_FIRST(&tab->idlist)) { | 361 | id = TAILQ_FIRST(&idtab->idlist)) { |
516 | TAILQ_REMOVE(&tab->idlist, id, next); | 362 | TAILQ_REMOVE(&idtab->idlist, id, next); |
517 | free_identity(id); | 363 | free_identity(id); |
518 | } | 364 | } |
519 | 365 | ||
520 | /* Mark that there are no identities. */ | 366 | /* Mark that there are no identities. */ |
521 | tab->nentries = 0; | 367 | idtab->nentries = 0; |
522 | 368 | ||
523 | /* Send success. */ | 369 | /* Send success. */ |
524 | send_status(e, 1); | 370 | send_status(e, 1); |
@@ -530,24 +376,19 @@ reaper(void) | |||
530 | { | 376 | { |
531 | time_t deadline = 0, now = monotime(); | 377 | time_t deadline = 0, now = monotime(); |
532 | Identity *id, *nxt; | 378 | Identity *id, *nxt; |
533 | int version; | 379 | |
534 | Idtab *tab; | 380 | for (id = TAILQ_FIRST(&idtab->idlist); id; id = nxt) { |
535 | 381 | nxt = TAILQ_NEXT(id, next); | |
536 | for (version = 1; version < 3; version++) { | 382 | if (id->death == 0) |
537 | tab = idtab_lookup(version); | 383 | continue; |
538 | for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) { | 384 | if (now >= id->death) { |
539 | nxt = TAILQ_NEXT(id, next); | 385 | debug("expiring key '%s'", id->comment); |
540 | if (id->death == 0) | 386 | TAILQ_REMOVE(&idtab->idlist, id, next); |
541 | continue; | 387 | free_identity(id); |
542 | if (now >= id->death) { | 388 | idtab->nentries--; |
543 | debug("expiring key '%s'", id->comment); | 389 | } else |
544 | TAILQ_REMOVE(&tab->idlist, id, next); | 390 | deadline = (deadline == 0) ? id->death : |
545 | free_identity(id); | 391 | MINIMUM(deadline, id->death); |
546 | tab->nentries--; | ||
547 | } else | ||
548 | deadline = (deadline == 0) ? id->death : | ||
549 | MINIMUM(deadline, id->death); | ||
550 | } | ||
551 | } | 392 | } |
552 | if (deadline == 0 || deadline <= now) | 393 | if (deadline == 0 || deadline <= now) |
553 | return 0; | 394 | return 0; |
@@ -555,54 +396,9 @@ reaper(void) | |||
555 | return (deadline - now); | 396 | return (deadline - now); |
556 | } | 397 | } |
557 | 398 | ||
558 | /* | ||
559 | * XXX this and the corresponding serialisation function probably belongs | ||
560 | * in key.c | ||
561 | */ | ||
562 | #ifdef WITH_SSH1 | ||
563 | static int | ||
564 | agent_decode_rsa1(struct sshbuf *m, struct sshkey **kp) | ||
565 | { | ||
566 | struct sshkey *k = NULL; | ||
567 | int r = SSH_ERR_INTERNAL_ERROR; | ||
568 | |||
569 | *kp = NULL; | ||
570 | if ((k = sshkey_new_private(KEY_RSA1)) == NULL) | ||
571 | return SSH_ERR_ALLOC_FAIL; | ||
572 | |||
573 | if ((r = sshbuf_get_u32(m, NULL)) != 0 || /* ignored */ | ||
574 | (r = sshbuf_get_bignum1(m, k->rsa->n)) != 0 || | ||
575 | (r = sshbuf_get_bignum1(m, k->rsa->e)) != 0 || | ||
576 | (r = sshbuf_get_bignum1(m, k->rsa->d)) != 0 || | ||
577 | (r = sshbuf_get_bignum1(m, k->rsa->iqmp)) != 0 || | ||
578 | /* SSH1 and SSL have p and q swapped */ | ||
579 | (r = sshbuf_get_bignum1(m, k->rsa->q)) != 0 || /* p */ | ||
580 | (r = sshbuf_get_bignum1(m, k->rsa->p)) != 0) /* q */ | ||
581 | goto out; | ||
582 | |||
583 | /* Generate additional parameters */ | ||
584 | if ((r = rsa_generate_additional_parameters(k->rsa)) != 0) | ||
585 | goto out; | ||
586 | /* enable blinding */ | ||
587 | if (RSA_blinding_on(k->rsa, NULL) != 1) { | ||
588 | r = SSH_ERR_LIBCRYPTO_ERROR; | ||
589 | goto out; | ||
590 | } | ||
591 | |||
592 | r = 0; /* success */ | ||
593 | out: | ||
594 | if (r == 0) | ||
595 | *kp = k; | ||
596 | else | ||
597 | sshkey_free(k); | ||
598 | return r; | ||
599 | } | ||
600 | #endif /* WITH_SSH1 */ | ||
601 | |||
602 | static void | 399 | static void |
603 | process_add_identity(SocketEntry *e, int version) | 400 | process_add_identity(SocketEntry *e) |
604 | { | 401 | { |
605 | Idtab *tab = idtab_lookup(version); | ||
606 | Identity *id; | 402 | Identity *id; |
607 | int success = 0, confirm = 0; | 403 | int success = 0, confirm = 0; |
608 | u_int seconds; | 404 | u_int seconds; |
@@ -612,17 +408,8 @@ process_add_identity(SocketEntry *e, int version) | |||
612 | u_char ctype; | 408 | u_char ctype; |
613 | int r = SSH_ERR_INTERNAL_ERROR; | 409 | int r = SSH_ERR_INTERNAL_ERROR; |
614 | 410 | ||
615 | switch (version) { | 411 | if ((r = sshkey_private_deserialize(e->request, &k)) != 0 || |
616 | #ifdef WITH_SSH1 | 412 | k == NULL || |
617 | case 1: | ||
618 | r = agent_decode_rsa1(e->request, &k); | ||
619 | break; | ||
620 | #endif /* WITH_SSH1 */ | ||
621 | case 2: | ||
622 | r = sshkey_private_deserialize(e->request, &k); | ||
623 | break; | ||
624 | } | ||
625 | if (r != 0 || k == NULL || | ||
626 | (r = sshbuf_get_cstring(e->request, &comment, NULL)) != 0) { | 413 | (r = sshbuf_get_cstring(e->request, &comment, NULL)) != 0) { |
627 | error("%s: decode private key: %s", __func__, ssh_err(r)); | 414 | error("%s: decode private key: %s", __func__, ssh_err(r)); |
628 | goto err; | 415 | goto err; |
@@ -658,12 +445,12 @@ process_add_identity(SocketEntry *e, int version) | |||
658 | success = 1; | 445 | success = 1; |
659 | if (lifetime && !death) | 446 | if (lifetime && !death) |
660 | death = monotime() + lifetime; | 447 | death = monotime() + lifetime; |
661 | if ((id = lookup_identity(k, version)) == NULL) { | 448 | if ((id = lookup_identity(k)) == NULL) { |
662 | id = xcalloc(1, sizeof(Identity)); | 449 | id = xcalloc(1, sizeof(Identity)); |
663 | id->key = k; | 450 | id->key = k; |
664 | TAILQ_INSERT_TAIL(&tab->idlist, id, next); | 451 | TAILQ_INSERT_TAIL(&idtab->idlist, id, next); |
665 | /* Increment the number of identities. */ | 452 | /* Increment the number of identities. */ |
666 | tab->nentries++; | 453 | idtab->nentries++; |
667 | } else { | 454 | } else { |
668 | sshkey_free(k); | 455 | sshkey_free(k); |
669 | free(id->comment); | 456 | free(id->comment); |
@@ -724,17 +511,14 @@ process_lock_agent(SocketEntry *e, int lock) | |||
724 | } | 511 | } |
725 | 512 | ||
726 | static void | 513 | static void |
727 | no_identities(SocketEntry *e, u_int type) | 514 | no_identities(SocketEntry *e) |
728 | { | 515 | { |
729 | struct sshbuf *msg; | 516 | struct sshbuf *msg; |
730 | int r; | 517 | int r; |
731 | 518 | ||
732 | if ((msg = sshbuf_new()) == NULL) | 519 | if ((msg = sshbuf_new()) == NULL) |
733 | fatal("%s: sshbuf_new failed", __func__); | 520 | fatal("%s: sshbuf_new failed", __func__); |
734 | if ((r = sshbuf_put_u8(msg, | 521 | if ((r = sshbuf_put_u8(msg, SSH2_AGENT_IDENTITIES_ANSWER)) != 0 || |
735 | (type == SSH_AGENTC_REQUEST_RSA_IDENTITIES) ? | ||
736 | SSH_AGENT_RSA_IDENTITIES_ANSWER : | ||
737 | SSH2_AGENT_IDENTITIES_ANSWER)) != 0 || | ||
738 | (r = sshbuf_put_u32(msg, 0)) != 0 || | 522 | (r = sshbuf_put_u32(msg, 0)) != 0 || |
739 | (r = sshbuf_put_stringb(e->output, msg)) != 0) | 523 | (r = sshbuf_put_stringb(e->output, msg)) != 0) |
740 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | 524 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); |
@@ -746,13 +530,12 @@ static void | |||
746 | process_add_smartcard_key(SocketEntry *e) | 530 | process_add_smartcard_key(SocketEntry *e) |
747 | { | 531 | { |
748 | char *provider = NULL, *pin, canonical_provider[PATH_MAX]; | 532 | char *provider = NULL, *pin, canonical_provider[PATH_MAX]; |
749 | int r, i, version, count = 0, success = 0, confirm = 0; | 533 | int r, i, count = 0, success = 0, confirm = 0; |
750 | u_int seconds; | 534 | u_int seconds; |
751 | time_t death = 0; | 535 | time_t death = 0; |
752 | u_char type; | 536 | u_char type; |
753 | struct sshkey **keys = NULL, *k; | 537 | struct sshkey **keys = NULL, *k; |
754 | Identity *id; | 538 | Identity *id; |
755 | Idtab *tab; | ||
756 | 539 | ||
757 | if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 || | 540 | if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 || |
758 | (r = sshbuf_get_cstring(e->request, &pin, NULL)) != 0) | 541 | (r = sshbuf_get_cstring(e->request, &pin, NULL)) != 0) |
@@ -772,8 +555,7 @@ process_add_smartcard_key(SocketEntry *e) | |||
772 | confirm = 1; | 555 | confirm = 1; |
773 | break; | 556 | break; |
774 | default: | 557 | default: |
775 | error("process_add_smartcard_key: " | 558 | error("%s: Unknown constraint type %d", __func__, type); |
776 | "Unknown constraint type %d", type); | ||
777 | goto send; | 559 | goto send; |
778 | } | 560 | } |
779 | } | 561 | } |
@@ -794,17 +576,15 @@ process_add_smartcard_key(SocketEntry *e) | |||
794 | count = pkcs11_add_provider(canonical_provider, pin, &keys); | 576 | count = pkcs11_add_provider(canonical_provider, pin, &keys); |
795 | for (i = 0; i < count; i++) { | 577 | for (i = 0; i < count; i++) { |
796 | k = keys[i]; | 578 | k = keys[i]; |
797 | version = k->type == KEY_RSA1 ? 1 : 2; | 579 | if (lookup_identity(k) == NULL) { |
798 | tab = idtab_lookup(version); | ||
799 | if (lookup_identity(k, version) == NULL) { | ||
800 | id = xcalloc(1, sizeof(Identity)); | 580 | id = xcalloc(1, sizeof(Identity)); |
801 | id->key = k; | 581 | id->key = k; |
802 | id->provider = xstrdup(canonical_provider); | 582 | id->provider = xstrdup(canonical_provider); |
803 | id->comment = xstrdup(canonical_provider); /* XXX */ | 583 | id->comment = xstrdup(canonical_provider); /* XXX */ |
804 | id->death = death; | 584 | id->death = death; |
805 | id->confirm = confirm; | 585 | id->confirm = confirm; |
806 | TAILQ_INSERT_TAIL(&tab->idlist, id, next); | 586 | TAILQ_INSERT_TAIL(&idtab->idlist, id, next); |
807 | tab->nentries++; | 587 | idtab->nentries++; |
808 | success = 1; | 588 | success = 1; |
809 | } else { | 589 | } else { |
810 | sshkey_free(k); | 590 | sshkey_free(k); |
@@ -822,9 +602,8 @@ static void | |||
822 | process_remove_smartcard_key(SocketEntry *e) | 602 | process_remove_smartcard_key(SocketEntry *e) |
823 | { | 603 | { |
824 | char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX]; | 604 | char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX]; |
825 | int r, version, success = 0; | 605 | int r, success = 0; |
826 | Identity *id, *nxt; | 606 | Identity *id, *nxt; |
827 | Idtab *tab; | ||
828 | 607 | ||
829 | if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 || | 608 | if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 || |
830 | (r = sshbuf_get_cstring(e->request, &pin, NULL)) != 0) | 609 | (r = sshbuf_get_cstring(e->request, &pin, NULL)) != 0) |
@@ -838,25 +617,21 @@ process_remove_smartcard_key(SocketEntry *e) | |||
838 | } | 617 | } |
839 | 618 | ||
840 | debug("%s: remove %.100s", __func__, canonical_provider); | 619 | debug("%s: remove %.100s", __func__, canonical_provider); |
841 | for (version = 1; version < 3; version++) { | 620 | for (id = TAILQ_FIRST(&idtab->idlist); id; id = nxt) { |
842 | tab = idtab_lookup(version); | 621 | nxt = TAILQ_NEXT(id, next); |
843 | for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) { | 622 | /* Skip file--based keys */ |
844 | nxt = TAILQ_NEXT(id, next); | 623 | if (id->provider == NULL) |
845 | /* Skip file--based keys */ | 624 | continue; |
846 | if (id->provider == NULL) | 625 | if (!strcmp(canonical_provider, id->provider)) { |
847 | continue; | 626 | TAILQ_REMOVE(&idtab->idlist, id, next); |
848 | if (!strcmp(canonical_provider, id->provider)) { | 627 | free_identity(id); |
849 | TAILQ_REMOVE(&tab->idlist, id, next); | 628 | idtab->nentries--; |
850 | free_identity(id); | ||
851 | tab->nentries--; | ||
852 | } | ||
853 | } | 629 | } |
854 | } | 630 | } |
855 | if (pkcs11_del_provider(canonical_provider) == 0) | 631 | if (pkcs11_del_provider(canonical_provider) == 0) |
856 | success = 1; | 632 | success = 1; |
857 | else | 633 | else |
858 | error("process_remove_smartcard_key:" | 634 | error("%s: pkcs11_del_provider failed", __func__); |
859 | " pkcs11_del_provider failed"); | ||
860 | send: | 635 | send: |
861 | free(provider); | 636 | free(provider); |
862 | send_status(e, success); | 637 | send_status(e, success); |
@@ -865,88 +640,86 @@ send: | |||
865 | 640 | ||
866 | /* dispatch incoming messages */ | 641 | /* dispatch incoming messages */ |
867 | 642 | ||
868 | static void | 643 | static int |
869 | process_message(SocketEntry *e) | 644 | process_message(u_int socknum) |
870 | { | 645 | { |
871 | u_int msg_len; | 646 | u_int msg_len; |
872 | u_char type; | 647 | u_char type; |
873 | const u_char *cp; | 648 | const u_char *cp; |
874 | int r; | 649 | int r; |
650 | SocketEntry *e; | ||
651 | |||
652 | if (socknum >= sockets_alloc) { | ||
653 | fatal("%s: socket number %u >= allocated %u", | ||
654 | __func__, socknum, sockets_alloc); | ||
655 | } | ||
656 | e = &sockets[socknum]; | ||
875 | 657 | ||
876 | if (sshbuf_len(e->input) < 5) | 658 | if (sshbuf_len(e->input) < 5) |
877 | return; /* Incomplete message. */ | 659 | return 0; /* Incomplete message header. */ |
878 | cp = sshbuf_ptr(e->input); | 660 | cp = sshbuf_ptr(e->input); |
879 | msg_len = PEEK_U32(cp); | 661 | msg_len = PEEK_U32(cp); |
880 | if (msg_len > 256 * 1024) { | 662 | if (msg_len > AGENT_MAX_LEN) { |
881 | close_socket(e); | 663 | debug("%s: socket %u (fd=%d) message too long %u > %u", |
882 | return; | 664 | __func__, socknum, e->fd, msg_len, AGENT_MAX_LEN); |
665 | return -1; | ||
883 | } | 666 | } |
884 | if (sshbuf_len(e->input) < msg_len + 4) | 667 | if (sshbuf_len(e->input) < msg_len + 4) |
885 | return; | 668 | return 0; /* Incomplete message body. */ |
886 | 669 | ||
887 | /* move the current input to e->request */ | 670 | /* move the current input to e->request */ |
888 | sshbuf_reset(e->request); | 671 | sshbuf_reset(e->request); |
889 | if ((r = sshbuf_get_stringb(e->input, e->request)) != 0 || | 672 | if ((r = sshbuf_get_stringb(e->input, e->request)) != 0 || |
890 | (r = sshbuf_get_u8(e->request, &type)) != 0) | 673 | (r = sshbuf_get_u8(e->request, &type)) != 0) { |
674 | if (r == SSH_ERR_MESSAGE_INCOMPLETE || | ||
675 | r == SSH_ERR_STRING_TOO_LARGE) { | ||
676 | debug("%s: buffer error: %s", __func__, ssh_err(r)); | ||
677 | return -1; | ||
678 | } | ||
891 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | 679 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); |
680 | } | ||
681 | |||
682 | debug("%s: socket %u (fd=%d) type %d", __func__, socknum, e->fd, type); | ||
892 | 683 | ||
893 | /* check wheter agent is locked */ | 684 | /* check wheter agent is locked */ |
894 | if (locked && type != SSH_AGENTC_UNLOCK) { | 685 | if (locked && type != SSH_AGENTC_UNLOCK) { |
895 | sshbuf_reset(e->request); | 686 | sshbuf_reset(e->request); |
896 | switch (type) { | 687 | switch (type) { |
897 | case SSH_AGENTC_REQUEST_RSA_IDENTITIES: | ||
898 | case SSH2_AGENTC_REQUEST_IDENTITIES: | 688 | case SSH2_AGENTC_REQUEST_IDENTITIES: |
899 | /* send empty lists */ | 689 | /* send empty lists */ |
900 | no_identities(e, type); | 690 | no_identities(e); |
901 | break; | 691 | break; |
902 | default: | 692 | default: |
903 | /* send a fail message for all other request types */ | 693 | /* send a fail message for all other request types */ |
904 | send_status(e, 0); | 694 | send_status(e, 0); |
905 | } | 695 | } |
906 | return; | 696 | return 0; |
907 | } | 697 | } |
908 | 698 | ||
909 | debug("type %d", type); | ||
910 | switch (type) { | 699 | switch (type) { |
911 | case SSH_AGENTC_LOCK: | 700 | case SSH_AGENTC_LOCK: |
912 | case SSH_AGENTC_UNLOCK: | 701 | case SSH_AGENTC_UNLOCK: |
913 | process_lock_agent(e, type == SSH_AGENTC_LOCK); | 702 | process_lock_agent(e, type == SSH_AGENTC_LOCK); |
914 | break; | 703 | break; |
915 | #ifdef WITH_SSH1 | ||
916 | /* ssh1 */ | ||
917 | case SSH_AGENTC_RSA_CHALLENGE: | ||
918 | process_authentication_challenge1(e); | ||
919 | break; | ||
920 | case SSH_AGENTC_REQUEST_RSA_IDENTITIES: | ||
921 | process_request_identities(e, 1); | ||
922 | break; | ||
923 | case SSH_AGENTC_ADD_RSA_IDENTITY: | ||
924 | case SSH_AGENTC_ADD_RSA_ID_CONSTRAINED: | ||
925 | process_add_identity(e, 1); | ||
926 | break; | ||
927 | case SSH_AGENTC_REMOVE_RSA_IDENTITY: | ||
928 | process_remove_identity(e, 1); | ||
929 | break; | ||
930 | #endif | ||
931 | case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES: | 704 | case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES: |
932 | process_remove_all_identities(e, 1); /* safe for !WITH_SSH1 */ | 705 | process_remove_all_identities(e); /* safe for !WITH_SSH1 */ |
933 | break; | 706 | break; |
934 | /* ssh2 */ | 707 | /* ssh2 */ |
935 | case SSH2_AGENTC_SIGN_REQUEST: | 708 | case SSH2_AGENTC_SIGN_REQUEST: |
936 | process_sign_request2(e); | 709 | process_sign_request2(e); |
937 | break; | 710 | break; |
938 | case SSH2_AGENTC_REQUEST_IDENTITIES: | 711 | case SSH2_AGENTC_REQUEST_IDENTITIES: |
939 | process_request_identities(e, 2); | 712 | process_request_identities(e); |
940 | break; | 713 | break; |
941 | case SSH2_AGENTC_ADD_IDENTITY: | 714 | case SSH2_AGENTC_ADD_IDENTITY: |
942 | case SSH2_AGENTC_ADD_ID_CONSTRAINED: | 715 | case SSH2_AGENTC_ADD_ID_CONSTRAINED: |
943 | process_add_identity(e, 2); | 716 | process_add_identity(e); |
944 | break; | 717 | break; |
945 | case SSH2_AGENTC_REMOVE_IDENTITY: | 718 | case SSH2_AGENTC_REMOVE_IDENTITY: |
946 | process_remove_identity(e, 2); | 719 | process_remove_identity(e); |
947 | break; | 720 | break; |
948 | case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: | 721 | case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: |
949 | process_remove_all_identities(e, 2); | 722 | process_remove_all_identities(e); |
950 | break; | 723 | break; |
951 | #ifdef ENABLE_PKCS11 | 724 | #ifdef ENABLE_PKCS11 |
952 | case SSH_AGENTC_ADD_SMARTCARD_KEY: | 725 | case SSH_AGENTC_ADD_SMARTCARD_KEY: |
@@ -964,6 +737,7 @@ process_message(SocketEntry *e) | |||
964 | send_status(e, 0); | 737 | send_status(e, 0); |
965 | break; | 738 | break; |
966 | } | 739 | } |
740 | return 0; | ||
967 | } | 741 | } |
968 | 742 | ||
969 | static void | 743 | static void |
@@ -1005,19 +779,141 @@ new_socket(sock_type type, int fd) | |||
1005 | } | 779 | } |
1006 | 780 | ||
1007 | static int | 781 | static int |
1008 | prepare_select(fd_set **fdrp, fd_set **fdwp, int *fdl, u_int *nallocp, | 782 | handle_socket_read(u_int socknum) |
1009 | struct timeval **tvpp) | 783 | { |
784 | struct sockaddr_un sunaddr; | ||
785 | socklen_t slen; | ||
786 | uid_t euid; | ||
787 | gid_t egid; | ||
788 | int fd; | ||
789 | |||
790 | slen = sizeof(sunaddr); | ||
791 | fd = accept(sockets[socknum].fd, (struct sockaddr *)&sunaddr, &slen); | ||
792 | if (fd < 0) { | ||
793 | error("accept from AUTH_SOCKET: %s", strerror(errno)); | ||
794 | return -1; | ||
795 | } | ||
796 | if (getpeereid(fd, &euid, &egid) < 0) { | ||
797 | error("getpeereid %d failed: %s", fd, strerror(errno)); | ||
798 | close(fd); | ||
799 | return -1; | ||
800 | } | ||
801 | if ((euid != 0) && (getuid() != euid)) { | ||
802 | error("uid mismatch: peer euid %u != uid %u", | ||
803 | (u_int) euid, (u_int) getuid()); | ||
804 | close(fd); | ||
805 | return -1; | ||
806 | } | ||
807 | new_socket(AUTH_CONNECTION, fd); | ||
808 | return 0; | ||
809 | } | ||
810 | |||
811 | static int | ||
812 | handle_conn_read(u_int socknum) | ||
813 | { | ||
814 | char buf[1024]; | ||
815 | ssize_t len; | ||
816 | int r; | ||
817 | |||
818 | if ((len = read(sockets[socknum].fd, buf, sizeof(buf))) <= 0) { | ||
819 | if (len == -1) { | ||
820 | if (errno == EAGAIN || errno == EINTR) | ||
821 | return 0; | ||
822 | error("%s: read error on socket %u (fd %d): %s", | ||
823 | __func__, socknum, sockets[socknum].fd, | ||
824 | strerror(errno)); | ||
825 | } | ||
826 | return -1; | ||
827 | } | ||
828 | if ((r = sshbuf_put(sockets[socknum].input, buf, len)) != 0) | ||
829 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | ||
830 | explicit_bzero(buf, sizeof(buf)); | ||
831 | process_message(socknum); | ||
832 | return 0; | ||
833 | } | ||
834 | |||
835 | static int | ||
836 | handle_conn_write(u_int socknum) | ||
1010 | { | 837 | { |
1011 | u_int i, sz; | 838 | ssize_t len; |
1012 | int n = 0; | 839 | int r; |
1013 | static struct timeval tv; | 840 | |
841 | if (sshbuf_len(sockets[socknum].output) == 0) | ||
842 | return 0; /* shouldn't happen */ | ||
843 | if ((len = write(sockets[socknum].fd, | ||
844 | sshbuf_ptr(sockets[socknum].output), | ||
845 | sshbuf_len(sockets[socknum].output))) <= 0) { | ||
846 | if (len == -1) { | ||
847 | if (errno == EAGAIN || errno == EINTR) | ||
848 | return 0; | ||
849 | error("%s: read error on socket %u (fd %d): %s", | ||
850 | __func__, socknum, sockets[socknum].fd, | ||
851 | strerror(errno)); | ||
852 | } | ||
853 | return -1; | ||
854 | } | ||
855 | if ((r = sshbuf_consume(sockets[socknum].output, len)) != 0) | ||
856 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | ||
857 | return 0; | ||
858 | } | ||
859 | |||
860 | static void | ||
861 | after_poll(struct pollfd *pfd, size_t npfd) | ||
862 | { | ||
863 | size_t i; | ||
864 | u_int socknum; | ||
865 | |||
866 | for (i = 0; i < npfd; i++) { | ||
867 | if (pfd[i].revents == 0) | ||
868 | continue; | ||
869 | /* Find sockets entry */ | ||
870 | for (socknum = 0; socknum < sockets_alloc; socknum++) { | ||
871 | if (sockets[socknum].type != AUTH_SOCKET && | ||
872 | sockets[socknum].type != AUTH_CONNECTION) | ||
873 | continue; | ||
874 | if (pfd[i].fd == sockets[socknum].fd) | ||
875 | break; | ||
876 | } | ||
877 | if (socknum >= sockets_alloc) { | ||
878 | error("%s: no socket for fd %d", __func__, pfd[i].fd); | ||
879 | continue; | ||
880 | } | ||
881 | /* Process events */ | ||
882 | switch (sockets[socknum].type) { | ||
883 | case AUTH_SOCKET: | ||
884 | if ((pfd[i].revents & (POLLIN|POLLERR)) != 0 && | ||
885 | handle_socket_read(socknum) != 0) | ||
886 | close_socket(&sockets[socknum]); | ||
887 | break; | ||
888 | case AUTH_CONNECTION: | ||
889 | if ((pfd[i].revents & (POLLIN|POLLERR)) != 0 && | ||
890 | handle_conn_read(socknum) != 0) { | ||
891 | close_socket(&sockets[socknum]); | ||
892 | break; | ||
893 | } | ||
894 | if ((pfd[i].revents & (POLLOUT|POLLHUP)) != 0 && | ||
895 | handle_conn_write(socknum) != 0) | ||
896 | close_socket(&sockets[socknum]); | ||
897 | break; | ||
898 | default: | ||
899 | break; | ||
900 | } | ||
901 | } | ||
902 | } | ||
903 | |||
904 | static int | ||
905 | prepare_poll(struct pollfd **pfdp, size_t *npfdp, int *timeoutp) | ||
906 | { | ||
907 | struct pollfd *pfd = *pfdp; | ||
908 | size_t i, j, npfd = 0; | ||
1014 | time_t deadline; | 909 | time_t deadline; |
1015 | 910 | ||
911 | /* Count active sockets */ | ||
1016 | for (i = 0; i < sockets_alloc; i++) { | 912 | for (i = 0; i < sockets_alloc; i++) { |
1017 | switch (sockets[i].type) { | 913 | switch (sockets[i].type) { |
1018 | case AUTH_SOCKET: | 914 | case AUTH_SOCKET: |
1019 | case AUTH_CONNECTION: | 915 | case AUTH_CONNECTION: |
1020 | n = MAXIMUM(n, sockets[i].fd); | 916 | npfd++; |
1021 | break; | 917 | break; |
1022 | case AUTH_UNUSED: | 918 | case AUTH_UNUSED: |
1023 | break; | 919 | break; |
@@ -1026,28 +922,23 @@ prepare_select(fd_set **fdrp, fd_set **fdwp, int *fdl, u_int *nallocp, | |||
1026 | break; | 922 | break; |
1027 | } | 923 | } |
1028 | } | 924 | } |
925 | if (npfd != *npfdp && | ||
926 | (pfd = recallocarray(pfd, *npfdp, npfd, sizeof(*pfd))) == NULL) | ||
927 | fatal("%s: recallocarray failed", __func__); | ||
928 | *pfdp = pfd; | ||
929 | *npfdp = npfd; | ||
1029 | 930 | ||
1030 | sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); | 931 | for (i = j = 0; i < sockets_alloc; i++) { |
1031 | if (*fdrp == NULL || sz > *nallocp) { | ||
1032 | free(*fdrp); | ||
1033 | free(*fdwp); | ||
1034 | *fdrp = xmalloc(sz); | ||
1035 | *fdwp = xmalloc(sz); | ||
1036 | *nallocp = sz; | ||
1037 | } | ||
1038 | if (n < *fdl) | ||
1039 | debug("XXX shrink: %d < %d", n, *fdl); | ||
1040 | *fdl = n; | ||
1041 | memset(*fdrp, 0, sz); | ||
1042 | memset(*fdwp, 0, sz); | ||
1043 | |||
1044 | for (i = 0; i < sockets_alloc; i++) { | ||
1045 | switch (sockets[i].type) { | 932 | switch (sockets[i].type) { |
1046 | case AUTH_SOCKET: | 933 | case AUTH_SOCKET: |
1047 | case AUTH_CONNECTION: | 934 | case AUTH_CONNECTION: |
1048 | FD_SET(sockets[i].fd, *fdrp); | 935 | pfd[j].fd = sockets[i].fd; |
936 | pfd[j].revents = 0; | ||
937 | /* XXX backoff when input buffer full */ | ||
938 | pfd[j].events = POLLIN; | ||
1049 | if (sshbuf_len(sockets[i].output) > 0) | 939 | if (sshbuf_len(sockets[i].output) > 0) |
1050 | FD_SET(sockets[i].fd, *fdwp); | 940 | pfd[j].events |= POLLOUT; |
941 | j++; | ||
1051 | break; | 942 | break; |
1052 | default: | 943 | default: |
1053 | break; | 944 | break; |
@@ -1058,99 +949,17 @@ prepare_select(fd_set **fdrp, fd_set **fdwp, int *fdl, u_int *nallocp, | |||
1058 | deadline = (deadline == 0) ? parent_alive_interval : | 949 | deadline = (deadline == 0) ? parent_alive_interval : |
1059 | MINIMUM(deadline, parent_alive_interval); | 950 | MINIMUM(deadline, parent_alive_interval); |
1060 | if (deadline == 0) { | 951 | if (deadline == 0) { |
1061 | *tvpp = NULL; | 952 | *timeoutp = -1; /* INFTIM */ |
1062 | } else { | 953 | } else { |
1063 | tv.tv_sec = deadline; | 954 | if (deadline > INT_MAX / 1000) |
1064 | tv.tv_usec = 0; | 955 | *timeoutp = INT_MAX / 1000; |
1065 | *tvpp = &tv; | 956 | else |
957 | *timeoutp = deadline * 1000; | ||
1066 | } | 958 | } |
1067 | return (1); | 959 | return (1); |
1068 | } | 960 | } |
1069 | 961 | ||
1070 | static void | 962 | static void |
1071 | after_select(fd_set *readset, fd_set *writeset) | ||
1072 | { | ||
1073 | struct sockaddr_un sunaddr; | ||
1074 | socklen_t slen; | ||
1075 | char buf[1024]; | ||
1076 | int len, sock, r; | ||
1077 | u_int i, orig_alloc; | ||
1078 | uid_t euid; | ||
1079 | gid_t egid; | ||
1080 | |||
1081 | for (i = 0, orig_alloc = sockets_alloc; i < orig_alloc; i++) | ||
1082 | switch (sockets[i].type) { | ||
1083 | case AUTH_UNUSED: | ||
1084 | break; | ||
1085 | case AUTH_SOCKET: | ||
1086 | if (FD_ISSET(sockets[i].fd, readset)) { | ||
1087 | slen = sizeof(sunaddr); | ||
1088 | sock = accept(sockets[i].fd, | ||
1089 | (struct sockaddr *)&sunaddr, &slen); | ||
1090 | if (sock < 0) { | ||
1091 | error("accept from AUTH_SOCKET: %s", | ||
1092 | strerror(errno)); | ||
1093 | break; | ||
1094 | } | ||
1095 | if (getpeereid(sock, &euid, &egid) < 0) { | ||
1096 | error("getpeereid %d failed: %s", | ||
1097 | sock, strerror(errno)); | ||
1098 | close(sock); | ||
1099 | break; | ||
1100 | } | ||
1101 | if ((euid != 0) && (getuid() != euid)) { | ||
1102 | error("uid mismatch: " | ||
1103 | "peer euid %u != uid %u", | ||
1104 | (u_int) euid, (u_int) getuid()); | ||
1105 | close(sock); | ||
1106 | break; | ||
1107 | } | ||
1108 | new_socket(AUTH_CONNECTION, sock); | ||
1109 | } | ||
1110 | break; | ||
1111 | case AUTH_CONNECTION: | ||
1112 | if (sshbuf_len(sockets[i].output) > 0 && | ||
1113 | FD_ISSET(sockets[i].fd, writeset)) { | ||
1114 | len = write(sockets[i].fd, | ||
1115 | sshbuf_ptr(sockets[i].output), | ||
1116 | sshbuf_len(sockets[i].output)); | ||
1117 | if (len == -1 && (errno == EAGAIN || | ||
1118 | errno == EWOULDBLOCK || | ||
1119 | errno == EINTR)) | ||
1120 | continue; | ||
1121 | if (len <= 0) { | ||
1122 | close_socket(&sockets[i]); | ||
1123 | break; | ||
1124 | } | ||
1125 | if ((r = sshbuf_consume(sockets[i].output, | ||
1126 | len)) != 0) | ||
1127 | fatal("%s: buffer error: %s", | ||
1128 | __func__, ssh_err(r)); | ||
1129 | } | ||
1130 | if (FD_ISSET(sockets[i].fd, readset)) { | ||
1131 | len = read(sockets[i].fd, buf, sizeof(buf)); | ||
1132 | if (len == -1 && (errno == EAGAIN || | ||
1133 | errno == EWOULDBLOCK || | ||
1134 | errno == EINTR)) | ||
1135 | continue; | ||
1136 | if (len <= 0) { | ||
1137 | close_socket(&sockets[i]); | ||
1138 | break; | ||
1139 | } | ||
1140 | if ((r = sshbuf_put(sockets[i].input, | ||
1141 | buf, len)) != 0) | ||
1142 | fatal("%s: buffer error: %s", | ||
1143 | __func__, ssh_err(r)); | ||
1144 | explicit_bzero(buf, sizeof(buf)); | ||
1145 | process_message(&sockets[i]); | ||
1146 | } | ||
1147 | break; | ||
1148 | default: | ||
1149 | fatal("Unknown type %d", sockets[i].type); | ||
1150 | } | ||
1151 | } | ||
1152 | |||
1153 | static void | ||
1154 | cleanup_socket(void) | 963 | cleanup_socket(void) |
1155 | { | 964 | { |
1156 | if (cleanup_pid != 0 && getpid() != cleanup_pid) | 965 | if (cleanup_pid != 0 && getpid() != cleanup_pid) |
@@ -1209,9 +1018,7 @@ main(int ac, char **av) | |||
1209 | { | 1018 | { |
1210 | int c_flag = 0, d_flag = 0, D_flag = 0, k_flag = 0, s_flag = 0; | 1019 | int c_flag = 0, d_flag = 0, D_flag = 0, k_flag = 0, s_flag = 0; |
1211 | int sock, fd, ch, result, saved_errno; | 1020 | int sock, fd, ch, result, saved_errno; |
1212 | u_int nalloc; | ||
1213 | char *shell, *format, *pidstr, *agentsocket = NULL; | 1021 | char *shell, *format, *pidstr, *agentsocket = NULL; |
1214 | fd_set *readsetp = NULL, *writesetp = NULL; | ||
1215 | #ifdef HAVE_SETRLIMIT | 1022 | #ifdef HAVE_SETRLIMIT |
1216 | struct rlimit rlim; | 1023 | struct rlimit rlim; |
1217 | #endif | 1024 | #endif |
@@ -1219,9 +1026,11 @@ main(int ac, char **av) | |||
1219 | extern char *optarg; | 1026 | extern char *optarg; |
1220 | pid_t pid; | 1027 | pid_t pid; |
1221 | char pidstrbuf[1 + 3 * sizeof pid]; | 1028 | char pidstrbuf[1 + 3 * sizeof pid]; |
1222 | struct timeval *tvp = NULL; | ||
1223 | size_t len; | 1029 | size_t len; |
1224 | mode_t prev_mask; | 1030 | mode_t prev_mask; |
1031 | int timeout = -1; /* INFTIM */ | ||
1032 | struct pollfd *pfd = NULL; | ||
1033 | size_t npfd = 0; | ||
1225 | 1034 | ||
1226 | ssh_malloc_init(); /* must be called before any mallocs */ | 1035 | ssh_malloc_init(); /* must be called before any mallocs */ |
1227 | /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ | 1036 | /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ |
@@ -1442,15 +1251,14 @@ skip: | |||
1442 | signal(SIGINT, (d_flag | D_flag) ? cleanup_handler : SIG_IGN); | 1251 | signal(SIGINT, (d_flag | D_flag) ? cleanup_handler : SIG_IGN); |
1443 | signal(SIGHUP, cleanup_handler); | 1252 | signal(SIGHUP, cleanup_handler); |
1444 | signal(SIGTERM, cleanup_handler); | 1253 | signal(SIGTERM, cleanup_handler); |
1445 | nalloc = 0; | ||
1446 | 1254 | ||
1447 | if (pledge("stdio rpath cpath unix id proc exec", NULL) == -1) | 1255 | if (pledge("stdio rpath cpath unix id proc exec", NULL) == -1) |
1448 | fatal("%s: pledge: %s", __progname, strerror(errno)); | 1256 | fatal("%s: pledge: %s", __progname, strerror(errno)); |
1449 | platform_pledge_agent(); | 1257 | platform_pledge_agent(); |
1450 | 1258 | ||
1451 | while (1) { | 1259 | while (1) { |
1452 | prepare_select(&readsetp, &writesetp, &max_fd, &nalloc, &tvp); | 1260 | prepare_poll(&pfd, &npfd, &timeout); |
1453 | result = select(max_fd + 1, readsetp, writesetp, NULL, tvp); | 1261 | result = poll(pfd, npfd, timeout); |
1454 | saved_errno = errno; | 1262 | saved_errno = errno; |
1455 | if (parent_alive_interval != 0) | 1263 | if (parent_alive_interval != 0) |
1456 | check_parent_exists(); | 1264 | check_parent_exists(); |
@@ -1458,9 +1266,9 @@ skip: | |||
1458 | if (result < 0) { | 1266 | if (result < 0) { |
1459 | if (saved_errno == EINTR) | 1267 | if (saved_errno == EINTR) |
1460 | continue; | 1268 | continue; |
1461 | fatal("select: %s", strerror(saved_errno)); | 1269 | fatal("poll: %s", strerror(saved_errno)); |
1462 | } else if (result > 0) | 1270 | } else if (result > 0) |
1463 | after_select(readsetp, writesetp); | 1271 | after_poll(pfd, npfd); |
1464 | } | 1272 | } |
1465 | /* NOTREACHED */ | 1273 | /* NOTREACHED */ |
1466 | } | 1274 | } |