diff options
Diffstat (limited to 'sshd.c')
-rw-r--r-- | sshd.c | 209 |
1 files changed, 203 insertions, 6 deletions
@@ -15,8 +15,10 @@ | |||
15 | * called by a name other than "ssh" or "Secure Shell". | 15 | * called by a name other than "ssh" or "Secure Shell". |
16 | * | 16 | * |
17 | * SSH2 implementation: | 17 | * SSH2 implementation: |
18 | * Privilege Separation: | ||
18 | * | 19 | * |
19 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | 20 | * Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved. |
21 | * Copyright (c) 2002 Niels Provos. All rights reserved. | ||
20 | * | 22 | * |
21 | * Redistribution and use in source and binary forms, with or without | 23 | * Redistribution and use in source and binary forms, with or without |
22 | * modification, are permitted provided that the following conditions | 24 | * modification, are permitted provided that the following conditions |
@@ -40,11 +42,12 @@ | |||
40 | */ | 42 | */ |
41 | 43 | ||
42 | #include "includes.h" | 44 | #include "includes.h" |
43 | RCSID("$OpenBSD: sshd.c,v 1.230 2002/03/18 01:12:14 provos Exp $"); | 45 | RCSID("$OpenBSD: sshd.c,v 1.231 2002/03/18 17:50:31 provos Exp $"); |
44 | 46 | ||
45 | #include <openssl/dh.h> | 47 | #include <openssl/dh.h> |
46 | #include <openssl/bn.h> | 48 | #include <openssl/bn.h> |
47 | #include <openssl/md5.h> | 49 | #include <openssl/md5.h> |
50 | #include <openssl/rand.h> | ||
48 | 51 | ||
49 | #include "ssh.h" | 52 | #include "ssh.h" |
50 | #include "ssh1.h" | 53 | #include "ssh1.h" |
@@ -73,6 +76,10 @@ RCSID("$OpenBSD: sshd.c,v 1.230 2002/03/18 01:12:14 provos Exp $"); | |||
73 | #include "dispatch.h" | 76 | #include "dispatch.h" |
74 | #include "channels.h" | 77 | #include "channels.h" |
75 | #include "session.h" | 78 | #include "session.h" |
79 | #include "monitor_mm.h" | ||
80 | #include "monitor.h" | ||
81 | #include "monitor_wrap.h" | ||
82 | #include "monitor_fdpass.h" | ||
76 | 83 | ||
77 | #ifdef LIBWRAP | 84 | #ifdef LIBWRAP |
78 | #include <tcpd.h> | 85 | #include <tcpd.h> |
@@ -190,8 +197,13 @@ u_int utmp_len = MAXHOSTNAMELEN; | |||
190 | int *startup_pipes = NULL; | 197 | int *startup_pipes = NULL; |
191 | int startup_pipe; /* in child */ | 198 | int startup_pipe; /* in child */ |
192 | 199 | ||
200 | /* variables used for privilege separation */ | ||
201 | extern struct monitor *monitor; | ||
202 | extern int use_privsep; | ||
203 | |||
193 | /* Prototypes for various functions defined later in this file. */ | 204 | /* Prototypes for various functions defined later in this file. */ |
194 | void destroy_sensitive_data(void); | 205 | void destroy_sensitive_data(void); |
206 | void demote_sensitive_data(void); | ||
195 | 207 | ||
196 | static void do_ssh1_kex(void); | 208 | static void do_ssh1_kex(void); |
197 | static void do_ssh2_kex(void); | 209 | static void do_ssh2_kex(void); |
@@ -478,6 +490,115 @@ destroy_sensitive_data(void) | |||
478 | memset(sensitive_data.ssh1_cookie, 0, SSH_SESSION_KEY_LENGTH); | 490 | memset(sensitive_data.ssh1_cookie, 0, SSH_SESSION_KEY_LENGTH); |
479 | } | 491 | } |
480 | 492 | ||
493 | /* Demote private to public keys for network child */ | ||
494 | void | ||
495 | demote_sensitive_data(void) | ||
496 | { | ||
497 | Key *tmp; | ||
498 | int i; | ||
499 | |||
500 | if (sensitive_data.server_key) { | ||
501 | tmp = key_demote(sensitive_data.server_key); | ||
502 | key_free(sensitive_data.server_key); | ||
503 | sensitive_data.server_key = tmp; | ||
504 | } | ||
505 | |||
506 | for (i = 0; i < options.num_host_key_files; i++) { | ||
507 | if (sensitive_data.host_keys[i]) { | ||
508 | tmp = key_demote(sensitive_data.host_keys[i]); | ||
509 | key_free(sensitive_data.host_keys[i]); | ||
510 | sensitive_data.host_keys[i] = tmp; | ||
511 | if (tmp->type == KEY_RSA1) | ||
512 | sensitive_data.ssh1_host_key = tmp; | ||
513 | } | ||
514 | } | ||
515 | |||
516 | /* We do not clear ssh1_host key and cookie. XXX - Okay Niels? */ | ||
517 | } | ||
518 | |||
519 | void | ||
520 | privsep_preauth_child(void) | ||
521 | { | ||
522 | u_int32_t rand[256]; | ||
523 | int i; | ||
524 | |||
525 | /* Enable challenge-response authentication for privilege separation */ | ||
526 | privsep_challenge_enable(); | ||
527 | |||
528 | for (i = 0; i < 256; i++) | ||
529 | rand[i] = arc4random(); | ||
530 | RAND_seed(rand, sizeof(rand)); | ||
531 | |||
532 | /* Demote the private keys to public keys. */ | ||
533 | demote_sensitive_data(); | ||
534 | |||
535 | /* Change our root directory*/ | ||
536 | if (chroot(options.unprivileged_dir) == -1) | ||
537 | fatal("chroot(/var/empty)"); | ||
538 | if (chdir("/") == -1) | ||
539 | fatal("chdir(/)"); | ||
540 | |||
541 | /* Drop our privileges */ | ||
542 | setegid(options.unprivileged_group); | ||
543 | setgid(options.unprivileged_group); | ||
544 | seteuid(options.unprivileged_user); | ||
545 | setuid(options.unprivileged_user); | ||
546 | } | ||
547 | |||
548 | void | ||
549 | privsep_postauth(Authctxt *authctxt, pid_t pid) | ||
550 | { | ||
551 | extern Authctxt *x_authctxt; | ||
552 | int status; | ||
553 | |||
554 | /* Wait for the child's exit status */ | ||
555 | waitpid(pid, &status, 0); | ||
556 | |||
557 | /* XXX - Remote port forwarding */ | ||
558 | x_authctxt = authctxt; | ||
559 | |||
560 | if (authctxt->pw->pw_uid == 0 || options.use_login) { | ||
561 | /* File descriptor passing is broken or root login */ | ||
562 | monitor_apply_keystate(monitor); | ||
563 | use_privsep = 0; | ||
564 | return; | ||
565 | } | ||
566 | |||
567 | /* Authentication complete */ | ||
568 | alarm(0); | ||
569 | if (startup_pipe != -1) { | ||
570 | close(startup_pipe); | ||
571 | startup_pipe = -1; | ||
572 | } | ||
573 | |||
574 | /* New socket pair */ | ||
575 | monitor_reinit(monitor); | ||
576 | |||
577 | monitor->m_pid = fork(); | ||
578 | if (monitor->m_pid == -1) | ||
579 | fatal("fork of unprivileged child failed"); | ||
580 | else if (monitor->m_pid != 0) { | ||
581 | debug2("User child is on pid %d", pid); | ||
582 | close(monitor->m_recvfd); | ||
583 | monitor_child_postauth(monitor); | ||
584 | |||
585 | /* NEVERREACHED */ | ||
586 | exit(0); | ||
587 | } | ||
588 | |||
589 | close(monitor->m_sendfd); | ||
590 | |||
591 | /* Demote the private keys to public keys. */ | ||
592 | demote_sensitive_data(); | ||
593 | |||
594 | /* Drop privileges */ | ||
595 | do_setusercontext(authctxt->pw); | ||
596 | |||
597 | /* It is safe now to apply the key state */ | ||
598 | monitor_apply_keystate(monitor); | ||
599 | } | ||
600 | |||
601 | |||
481 | static char * | 602 | static char * |
482 | list_hostkey_types(void) | 603 | list_hostkey_types(void) |
483 | { | 604 | { |
@@ -507,7 +628,7 @@ list_hostkey_types(void) | |||
507 | return p; | 628 | return p; |
508 | } | 629 | } |
509 | 630 | ||
510 | static Key * | 631 | Key * |
511 | get_hostkey_by_type(int type) | 632 | get_hostkey_by_type(int type) |
512 | { | 633 | { |
513 | int i; | 634 | int i; |
@@ -519,6 +640,25 @@ get_hostkey_by_type(int type) | |||
519 | return NULL; | 640 | return NULL; |
520 | } | 641 | } |
521 | 642 | ||
643 | Key * | ||
644 | get_hostkey_by_index(int ind) | ||
645 | { | ||
646 | if (ind < 0 || ind >= options.num_host_key_files) | ||
647 | return (NULL); | ||
648 | return (sensitive_data.host_keys[ind]); | ||
649 | } | ||
650 | |||
651 | int | ||
652 | get_hostkey_index(Key *key) | ||
653 | { | ||
654 | int i; | ||
655 | for (i = 0; i < options.num_host_key_files; i++) { | ||
656 | if (key == sensitive_data.host_keys[i]) | ||
657 | return (i); | ||
658 | } | ||
659 | return (-1); | ||
660 | } | ||
661 | |||
522 | /* | 662 | /* |
523 | * returns 1 if connection should be dropped, 0 otherwise. | 663 | * returns 1 if connection should be dropped, 0 otherwise. |
524 | * dropping starts at connection #max_startups_begin with a probability | 664 | * dropping starts at connection #max_startups_begin with a probability |
@@ -1233,6 +1373,37 @@ main(int ac, char **av) | |||
1233 | 1373 | ||
1234 | packet_set_nonblocking(); | 1374 | packet_set_nonblocking(); |
1235 | 1375 | ||
1376 | if (!use_privsep) | ||
1377 | goto skip_privilegeseparation; | ||
1378 | |||
1379 | /* Set up unprivileged child process to deal with network data */ | ||
1380 | monitor = monitor_init(); | ||
1381 | /* Store a pointer to the kex for later rekeying */ | ||
1382 | monitor->m_pkex = &xxx_kex; | ||
1383 | |||
1384 | pid = fork(); | ||
1385 | if (pid == -1) | ||
1386 | fatal("fork of unprivileged child failed"); | ||
1387 | else if (pid != 0) { | ||
1388 | debug2("Network child is on pid %d", pid); | ||
1389 | |||
1390 | close(monitor->m_recvfd); | ||
1391 | authctxt = monitor_child_preauth(monitor); | ||
1392 | close(monitor->m_sendfd); | ||
1393 | |||
1394 | /* Sync memory */ | ||
1395 | monitor_sync(monitor); | ||
1396 | goto authenticated; | ||
1397 | } else { | ||
1398 | close(monitor->m_sendfd); | ||
1399 | |||
1400 | /* Demote the child */ | ||
1401 | if (getuid() == 0 || geteuid() == 0) | ||
1402 | privsep_preauth_child(); | ||
1403 | } | ||
1404 | |||
1405 | skip_privilegeseparation: | ||
1406 | |||
1236 | /* perform the key exchange */ | 1407 | /* perform the key exchange */ |
1237 | /* authenticate user and start session */ | 1408 | /* authenticate user and start session */ |
1238 | if (compat20) { | 1409 | if (compat20) { |
@@ -1242,6 +1413,23 @@ main(int ac, char **av) | |||
1242 | do_ssh1_kex(); | 1413 | do_ssh1_kex(); |
1243 | authctxt = do_authentication(); | 1414 | authctxt = do_authentication(); |
1244 | } | 1415 | } |
1416 | if (use_privsep) | ||
1417 | mm_send_keystate(monitor); | ||
1418 | |||
1419 | /* If we use privilege separation, the unprivileged child exits */ | ||
1420 | if (use_privsep) | ||
1421 | exit(0); | ||
1422 | |||
1423 | authenticated: | ||
1424 | /* | ||
1425 | * In privilege separation, we fork another child and prepare | ||
1426 | * file descriptor passing. | ||
1427 | */ | ||
1428 | if (use_privsep) { | ||
1429 | privsep_postauth(authctxt, pid); | ||
1430 | if (!compat20) | ||
1431 | destroy_sensitive_data(); | ||
1432 | } | ||
1245 | 1433 | ||
1246 | /* Perform session preparation. */ | 1434 | /* Perform session preparation. */ |
1247 | do_authenticated(authctxt); | 1435 | do_authenticated(authctxt); |
@@ -1254,6 +1442,10 @@ main(int ac, char **av) | |||
1254 | #endif /* USE_PAM */ | 1442 | #endif /* USE_PAM */ |
1255 | 1443 | ||
1256 | packet_close(); | 1444 | packet_close(); |
1445 | |||
1446 | if (use_privsep) | ||
1447 | mm_terminate(); | ||
1448 | |||
1257 | exit(0); | 1449 | exit(0); |
1258 | } | 1450 | } |
1259 | 1451 | ||
@@ -1261,7 +1453,7 @@ main(int ac, char **av) | |||
1261 | * Decrypt session_key_int using our private server key and private host key | 1453 | * Decrypt session_key_int using our private server key and private host key |
1262 | * (key with larger modulus first). | 1454 | * (key with larger modulus first). |
1263 | */ | 1455 | */ |
1264 | static int | 1456 | int |
1265 | ssh1_session_key(BIGNUM *session_key_int) | 1457 | ssh1_session_key(BIGNUM *session_key_int) |
1266 | { | 1458 | { |
1267 | int rsafail = 0; | 1459 | int rsafail = 0; |
@@ -1417,7 +1609,8 @@ do_ssh1_kex(void) | |||
1417 | packet_check_eom(); | 1609 | packet_check_eom(); |
1418 | 1610 | ||
1419 | /* Decrypt session_key_int using host/server keys */ | 1611 | /* Decrypt session_key_int using host/server keys */ |
1420 | rsafail = ssh1_session_key(session_key_int); | 1612 | rsafail = PRIVSEP(ssh1_session_key(session_key_int)); |
1613 | |||
1421 | /* | 1614 | /* |
1422 | * Extract session key from the decrypted integer. The key is in the | 1615 | * Extract session key from the decrypted integer. The key is in the |
1423 | * least significant 256 bits of the integer; the first byte of the | 1616 | * least significant 256 bits of the integer; the first byte of the |
@@ -1468,9 +1661,12 @@ do_ssh1_kex(void) | |||
1468 | for (i = 0; i < 16; i++) | 1661 | for (i = 0; i < 16; i++) |
1469 | session_id[i] = session_key[i] ^ session_key[i + 16]; | 1662 | session_id[i] = session_key[i] ^ session_key[i + 16]; |
1470 | } | 1663 | } |
1471 | /* Destroy the private and public keys. They will no longer be needed. */ | 1664 | /* Destroy the private and public keys. No longer. */ |
1472 | destroy_sensitive_data(); | 1665 | destroy_sensitive_data(); |
1473 | 1666 | ||
1667 | if (use_privsep) | ||
1668 | mm_ssh1_session_id(session_id); | ||
1669 | |||
1474 | /* Destroy the decrypted integer. It is no longer needed. */ | 1670 | /* Destroy the decrypted integer. It is no longer needed. */ |
1475 | BN_clear_free(session_key_int); | 1671 | BN_clear_free(session_key_int); |
1476 | 1672 | ||
@@ -1517,6 +1713,7 @@ do_ssh2_kex(void) | |||
1517 | kex->client_version_string=client_version_string; | 1713 | kex->client_version_string=client_version_string; |
1518 | kex->server_version_string=server_version_string; | 1714 | kex->server_version_string=server_version_string; |
1519 | kex->load_host_key=&get_hostkey_by_type; | 1715 | kex->load_host_key=&get_hostkey_by_type; |
1716 | kex->host_key_index=&get_hostkey_index; | ||
1520 | 1717 | ||
1521 | xxx_kex = kex; | 1718 | xxx_kex = kex; |
1522 | 1719 | ||