diff options
author | Damien Miller <djm@mindrot.org> | 2008-02-10 22:40:12 +1100 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2008-02-10 22:40:12 +1100 |
commit | d8cb1f184f9acaae02bb4d15ce1e00ffbeeeac88 (patch) | |
tree | fb0100a74a6c870e835706aa487b54500510c5e1 /session.c | |
parent | dfc24258a75a06ea8a3f56d99d3669e1a012a1dc (diff) |
- djm@cvs.openbsd.org 2008/02/08 23:24:07
[servconf.c servconf.h session.c sftp-server.c sftp.h sshd_config]
[sshd_config.5]
add sshd_config ChrootDirectory option to chroot(2) users to a directory
and tweak internal sftp server to work with it (no special files in
chroot required). ok markus@
Diffstat (limited to 'session.c')
-rw-r--r-- | session.c | 106 |
1 files changed, 93 insertions, 13 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: session.c,v 1.225 2008/02/04 21:53:00 markus Exp $ */ | 1 | /* $OpenBSD: session.c,v 1.226 2008/02/08 23:24:07 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
4 | * All rights reserved | 4 | * All rights reserved |
@@ -84,6 +84,7 @@ | |||
84 | #include "sshlogin.h" | 84 | #include "sshlogin.h" |
85 | #include "serverloop.h" | 85 | #include "serverloop.h" |
86 | #include "canohost.h" | 86 | #include "canohost.h" |
87 | #include "misc.h" | ||
87 | #include "session.h" | 88 | #include "session.h" |
88 | #include "kex.h" | 89 | #include "kex.h" |
89 | #include "monitor_wrap.h" | 90 | #include "monitor_wrap.h" |
@@ -93,6 +94,9 @@ | |||
93 | #include <kafs.h> | 94 | #include <kafs.h> |
94 | #endif | 95 | #endif |
95 | 96 | ||
97 | /* Magic name for internal sftp-server */ | ||
98 | #define INTERNAL_SFTP_NAME "internal-sftp" | ||
99 | |||
96 | /* func */ | 100 | /* func */ |
97 | 101 | ||
98 | Session *session_new(void); | 102 | Session *session_new(void); |
@@ -130,7 +134,7 @@ extern Buffer loginmsg; | |||
130 | const char *original_command = NULL; | 134 | const char *original_command = NULL; |
131 | 135 | ||
132 | /* data */ | 136 | /* data */ |
133 | #define MAX_SESSIONS 10 | 137 | #define MAX_SESSIONS 20 |
134 | Session sessions[MAX_SESSIONS]; | 138 | Session sessions[MAX_SESSIONS]; |
135 | 139 | ||
136 | #define SUBSYSTEM_NONE 0 | 140 | #define SUBSYSTEM_NONE 0 |
@@ -688,13 +692,17 @@ do_exec(Session *s, const char *command) | |||
688 | if (options.adm_forced_command) { | 692 | if (options.adm_forced_command) { |
689 | original_command = command; | 693 | original_command = command; |
690 | command = options.adm_forced_command; | 694 | command = options.adm_forced_command; |
691 | if (s->is_subsystem) | 695 | if (strcmp(INTERNAL_SFTP_NAME, command) == 0) |
696 | s->is_subsystem = SUBSYSTEM_INT_SFTP; | ||
697 | else if (s->is_subsystem) | ||
692 | s->is_subsystem = SUBSYSTEM_EXT; | 698 | s->is_subsystem = SUBSYSTEM_EXT; |
693 | debug("Forced command (config) '%.900s'", command); | 699 | debug("Forced command (config) '%.900s'", command); |
694 | } else if (forced_command) { | 700 | } else if (forced_command) { |
695 | original_command = command; | 701 | original_command = command; |
696 | command = forced_command; | 702 | command = forced_command; |
697 | if (s->is_subsystem) | 703 | if (strcmp(INTERNAL_SFTP_NAME, command) == 0) |
704 | s->is_subsystem = SUBSYSTEM_INT_SFTP; | ||
705 | else if (s->is_subsystem) | ||
698 | s->is_subsystem = SUBSYSTEM_EXT; | 706 | s->is_subsystem = SUBSYSTEM_EXT; |
699 | debug("Forced command (key option) '%.900s'", command); | 707 | debug("Forced command (key option) '%.900s'", command); |
700 | } | 708 | } |
@@ -710,7 +718,6 @@ do_exec(Session *s, const char *command) | |||
710 | PRIVSEP(audit_run_command(shell)); | 718 | PRIVSEP(audit_run_command(shell)); |
711 | } | 719 | } |
712 | #endif | 720 | #endif |
713 | |||
714 | if (s->ttyfd != -1) | 721 | if (s->ttyfd != -1) |
715 | do_exec_pty(s, command); | 722 | do_exec_pty(s, command); |
716 | else | 723 | else |
@@ -1293,6 +1300,61 @@ do_nologin(struct passwd *pw) | |||
1293 | } | 1300 | } |
1294 | } | 1301 | } |
1295 | 1302 | ||
1303 | /* | ||
1304 | * Chroot into a directory after checking it for safety: all path components | ||
1305 | * must be root-owned directories with strict permissions. | ||
1306 | */ | ||
1307 | static void | ||
1308 | safely_chroot(const char *path, uid_t uid) | ||
1309 | { | ||
1310 | const char *cp; | ||
1311 | char component[MAXPATHLEN]; | ||
1312 | struct stat st; | ||
1313 | |||
1314 | if (*path != '/') | ||
1315 | fatal("chroot path does not begin at root"); | ||
1316 | if (strlen(path) >= sizeof(component)) | ||
1317 | fatal("chroot path too long"); | ||
1318 | |||
1319 | /* | ||
1320 | * Descend the path, checking that each component is a | ||
1321 | * root-owned directory with strict permissions. | ||
1322 | */ | ||
1323 | for (cp = path; cp != NULL;) { | ||
1324 | if ((cp = strchr(cp, '/')) == NULL) | ||
1325 | strlcpy(component, path, sizeof(component)); | ||
1326 | else { | ||
1327 | cp++; | ||
1328 | memcpy(component, path, cp - path); | ||
1329 | component[cp - path] = '\0'; | ||
1330 | } | ||
1331 | |||
1332 | debug3("%s: checking '%s'", __func__, component); | ||
1333 | |||
1334 | if (stat(component, &st) != 0) | ||
1335 | fatal("%s: stat(\"%s\"): %s", __func__, | ||
1336 | component, strerror(errno)); | ||
1337 | if (st.st_uid != 0 || (st.st_mode & 022) != 0) | ||
1338 | fatal("bad ownership or modes for chroot " | ||
1339 | "directory %s\"%s\"", | ||
1340 | cp == NULL ? "" : "component ", component); | ||
1341 | if (!S_ISDIR(st.st_mode)) | ||
1342 | fatal("chroot path %s\"%s\" is not a directory", | ||
1343 | cp == NULL ? "" : "component ", component); | ||
1344 | |||
1345 | } | ||
1346 | |||
1347 | if (chdir(path) == -1) | ||
1348 | fatal("Unable to chdir to chroot path \"%s\": " | ||
1349 | "%s", path, strerror(errno)); | ||
1350 | if (chroot(path) == -1) | ||
1351 | fatal("chroot(\"%s\"): %s", path, strerror(errno)); | ||
1352 | if (chdir("/") == -1) | ||
1353 | fatal("%s: chdir(/) after chroot: %s", | ||
1354 | __func__, strerror(errno)); | ||
1355 | verbose("Changed root directory to \"%s\"", path); | ||
1356 | } | ||
1357 | |||
1296 | /* Set login name, uid, gid, and groups. */ | 1358 | /* Set login name, uid, gid, and groups. */ |
1297 | void | 1359 | void |
1298 | do_setusercontext(struct passwd *pw) | 1360 | do_setusercontext(struct passwd *pw) |
@@ -1324,7 +1386,7 @@ do_setusercontext(struct passwd *pw) | |||
1324 | } | 1386 | } |
1325 | # endif /* USE_PAM */ | 1387 | # endif /* USE_PAM */ |
1326 | if (setusercontext(lc, pw, pw->pw_uid, | 1388 | if (setusercontext(lc, pw, pw->pw_uid, |
1327 | (LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) { | 1389 | (LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETUSER))) < 0) { |
1328 | perror("unable to set user context"); | 1390 | perror("unable to set user context"); |
1329 | exit(1); | 1391 | exit(1); |
1330 | } | 1392 | } |
@@ -1347,13 +1409,13 @@ do_setusercontext(struct passwd *pw) | |||
1347 | exit(1); | 1409 | exit(1); |
1348 | } | 1410 | } |
1349 | endgrent(); | 1411 | endgrent(); |
1350 | #ifdef GSSAPI | 1412 | # ifdef GSSAPI |
1351 | if (options.gss_authentication) { | 1413 | if (options.gss_authentication) { |
1352 | temporarily_use_uid(pw); | 1414 | temporarily_use_uid(pw); |
1353 | ssh_gssapi_storecreds(); | 1415 | ssh_gssapi_storecreds(); |
1354 | restore_uid(); | 1416 | restore_uid(); |
1355 | } | 1417 | } |
1356 | #endif | 1418 | # endif |
1357 | # ifdef USE_PAM | 1419 | # ifdef USE_PAM |
1358 | /* | 1420 | /* |
1359 | * PAM credentials may take the form of supplementary groups. | 1421 | * PAM credentials may take the form of supplementary groups. |
@@ -1367,15 +1429,33 @@ do_setusercontext(struct passwd *pw) | |||
1367 | # endif /* USE_PAM */ | 1429 | # endif /* USE_PAM */ |
1368 | # if defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) | 1430 | # if defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) |
1369 | irix_setusercontext(pw); | 1431 | irix_setusercontext(pw); |
1370 | # endif /* defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) */ | 1432 | # endif /* defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) */ |
1371 | # ifdef _AIX | 1433 | # ifdef _AIX |
1372 | aix_usrinfo(pw); | 1434 | aix_usrinfo(pw); |
1373 | # endif /* _AIX */ | 1435 | # endif /* _AIX */ |
1374 | #ifdef USE_LIBIAF | 1436 | # ifdef USE_LIBIAF |
1375 | if (set_id(pw->pw_name) != 0) { | 1437 | if (set_id(pw->pw_name) != 0) { |
1376 | exit(1); | 1438 | exit(1); |
1377 | } | 1439 | } |
1378 | #endif /* USE_LIBIAF */ | 1440 | # endif /* USE_LIBIAF */ |
1441 | #endif | ||
1442 | |||
1443 | if (options.chroot_directory != NULL && | ||
1444 | strcasecmp(options.chroot_directory, "none") != 0) { | ||
1445 | char *chroot_path; | ||
1446 | |||
1447 | chroot_path = percent_expand(options.chroot_directory, | ||
1448 | "h", pw->pw_dir, "u", pw->pw_name, (char *)NULL); | ||
1449 | safely_chroot(chroot_path, pw->pw_uid); | ||
1450 | free(chroot_path); | ||
1451 | } | ||
1452 | |||
1453 | #ifdef HAVE_LOGIN_CAP | ||
1454 | if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUSER) < 0) { | ||
1455 | perror("unable to set user context (setuser)"); | ||
1456 | exit(1); | ||
1457 | } | ||
1458 | #else | ||
1379 | /* Permanently switch to the desired uid. */ | 1459 | /* Permanently switch to the desired uid. */ |
1380 | permanently_set_uid(pw); | 1460 | permanently_set_uid(pw); |
1381 | #endif | 1461 | #endif |
@@ -1625,7 +1705,7 @@ do_child(Session *s, const char *command) | |||
1625 | argv[i] = NULL; | 1705 | argv[i] = NULL; |
1626 | optind = optreset = 1; | 1706 | optind = optreset = 1; |
1627 | __progname = argv[0]; | 1707 | __progname = argv[0]; |
1628 | exit(sftp_server_main(i, argv)); | 1708 | exit(sftp_server_main(i, argv, s->pw)); |
1629 | } | 1709 | } |
1630 | 1710 | ||
1631 | if (options.use_login) { | 1711 | if (options.use_login) { |
@@ -1900,7 +1980,7 @@ session_subsystem_req(Session *s) | |||
1900 | if (strcmp(subsys, options.subsystem_name[i]) == 0) { | 1980 | if (strcmp(subsys, options.subsystem_name[i]) == 0) { |
1901 | prog = options.subsystem_command[i]; | 1981 | prog = options.subsystem_command[i]; |
1902 | cmd = options.subsystem_args[i]; | 1982 | cmd = options.subsystem_args[i]; |
1903 | if (!strcmp("internal-sftp", prog)) { | 1983 | if (!strcmp(INTERNAL_SFTP_NAME, prog)) { |
1904 | s->is_subsystem = SUBSYSTEM_INT_SFTP; | 1984 | s->is_subsystem = SUBSYSTEM_INT_SFTP; |
1905 | } else if (stat(prog, &st) < 0) { | 1985 | } else if (stat(prog, &st) < 0) { |
1906 | error("subsystem: cannot stat %s: %s", prog, | 1986 | error("subsystem: cannot stat %s: %s", prog, |