diff --git a/README.md b/README.md index 6b620651..93b61d2c 100644 --- a/README.md +++ b/README.md @@ -326,9 +326,12 @@ port forwarding support in wolfSSH. The echoserver will handle local and remote port forwarding when started with `-F`; without it every forwarding request is refused. Local forwards may connect only to loopback destinations unless `-L` names the allowed -`host` or `host:port` entries, comma separated. To connect with -the ssh tool, using one of the following command lines. You can run either of -the ssh command lines from anywhere: +`host` or `host:port` entries, comma separated. Started as root with +`-F`, the echoserver runs as `nobody` once it is listening; `-u` names +another user. Shells run as that user too, so a login whose home +directory has mode 0700 gets no shell. To connect with the ssh tool, use +one of the following command lines. You can run either of the ssh command +lines from anywhere: src/wolfssl$ ./examples/server/server src/wolfssh$ ./examples/echoserver/echoserver -F diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index 12c62908..dc879f09 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -103,6 +103,18 @@ #include #endif /* WOLFSSH_AGENT */ +#if defined(WOLFSSH_FWD) && !defined(USE_WINDOWS_API) && \ + !defined(WOLFSSL_NUCLEUS) && !defined(WOLFSSH_ZEPHYR) && \ + !defined(NO_MAIN_DRIVER) + /* Gives up root before serving; forwarding opens sockets for the + * peer with the server's credentials. Standalone only: the tests run + * echoserver_test() inside their own process. */ + #define ECHOSERVER_DROP_PRIVS + #include + #include + #include +#endif + #ifdef HAVE_SYS_SELECT_H #include #endif @@ -3834,6 +3846,36 @@ static int SetDefaultSftpPath(WOLFSSH* ssh, const char* defaultSftpPath, #endif +#ifdef ECHOSERVER_DROP_PRIVS +/* Runs the process as userName for good. Without root this only checks + * that userName is the current user. Returns 0, or -1. */ +static int DropPrivileges(const char* userName) +{ + struct passwd* pw; + gid_t gid; + uid_t uid; + + pw = getpwnam(userName); + if (pw == NULL) + return -1; + gid = pw->pw_gid; + uid = pw->pw_uid; + + if (geteuid() != 0) + return (uid == geteuid()) ? 0 : -1; + + if (setgroups(1, &gid) != 0 || setgid(gid) != 0 || setuid(uid) != 0) + return -1; + + /* Make sure root cannot be taken back. */ + if (uid != 0 && (setuid(0) == 0 || seteuid(0) == 0)) + return -1; + + return 0; +} +#endif + + static void ShowUsage(void) { printf("echoserver %s linked with wolfSSL %s\n", LIBWOLFSSH_VERSION_STRING, @@ -3854,6 +3896,10 @@ static void ShowUsage(void) " destinations the peer may connect to, in place\n" " of the default of loopback only; repeatable\n"); #endif +#ifdef ECHOSERVER_DROP_PRIVS + printf(" -u run as user once listening; started as root with\n" + " -F, the default is nobody\n"); +#endif #ifdef WOLFSSH_SFTP printf(" -d set the home directory for SFTP connections\n"); printf(" -D confine SFTP connections to the home directory," @@ -3905,7 +3951,7 @@ static void ShowUsage(void) } -#define ECHOSERVER_OPTLIST "?1a:Ad:DefEFL:p:R:Ni:j:i:I:J:K:P:k:b:x:m:c:s:G:HW:" +#define ECHOSERVER_OPTLIST "?1a:Ad:DefEFL:p:R:Ni:j:i:I:J:K:P:k:b:x:m:c:s:G:HW:u:" #ifdef WOLFSSH_WINDOWS_CERT_STORE /* Detects whether argv or the environment requests a host key from the @@ -4039,6 +4085,9 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) int appChannels = 0; #ifdef WOLFSSH_FWD int allowFwd = 0; +#endif +#ifdef ECHOSERVER_DROP_PRIVS + const char* dropUser = NULL; #endif int ch; word16 port = wolfSshPort; @@ -4114,6 +4163,15 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) #endif break; + case 'u': + #ifdef ECHOSERVER_DROP_PRIVS + dropUser = myoptarg; + #else + fprintf(stderr, "Warning: -u is ignored in this " + "build\n"); + #endif + break; + case 'L': #ifdef WOLFSSH_FWD if (fwdAllowArgsSz >= FWD_ALLOW_ARGS_MAX) { @@ -4796,6 +4854,23 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) #endif } +#ifdef ECHOSERVER_DROP_PRIVS + /* After the listener and the keys, before any peer is served. */ + if (dropUser == NULL && allowFwd && geteuid() == 0) { + dropUser = "nobody"; + fprintf(stderr, "Warning: running as nobody for -F; -u picks the " + "user.\n"); + #ifdef WOLFSSH_SHELL + fprintf(stderr, "Warning: shells run as nobody too; a login whose " + "home has mode 0700 gets no shell.\n"); + #endif + } + if (dropUser != NULL && DropPrivileges(dropUser) != 0) { + WCLOSESOCKET(listenFd); + ES_ERROR("Couldn't run as user %s.\n", dropUser); + } +#endif + SignalTcpReady(serverArgs->signal, port); do {