diff --git a/apps/wolfsshd/auth.c b/apps/wolfsshd/auth.c index 6f915f63..2e520b83 100644 --- a/apps/wolfsshd/auth.c +++ b/apps/wolfsshd/auth.c @@ -51,6 +51,7 @@ #ifndef _WIN32 #include #include +#include #include #endif @@ -937,4 +938,33 @@ long wolfSSHD_AuthGetGraceTime(const WOLFSSHD_AUTH* auth) return ret; } + + +/* return the user configuration */ +WOLFSSHD_CONFIG* wolfSSHD_AuthGetUserConf(const WOLFSSHD_AUTH* auth, + const char* usr, const char* host, + const char* localAdr, word16* localPort, const char* RDomain, + const char* adr) +{ + struct group* g = NULL; + WOLFSSHD_CONFIG* ret = NULL; + + if (auth != NULL) { + struct passwd *p_passwd; + + p_passwd = getpwnam((const char *)usr); + if (p_passwd == NULL) { + return NULL; + } + + g = getgrgid(p_passwd->pw_gid); + if (g == NULL) { + return NULL; + } + + ret = wolfSSHD_GetUserConf(auth->conf, usr, g->gr_name, host, localAdr, + localPort, RDomain, adr); + } + return ret; +} #endif /* WOLFSSH_SSHD */ diff --git a/apps/wolfsshd/auth.h b/apps/wolfsshd/auth.h index 4fb0c894..9274652f 100644 --- a/apps/wolfsshd/auth.h +++ b/apps/wolfsshd/auth.h @@ -61,4 +61,8 @@ int wolfSSHD_AuthRaisePermissions(WOLFSSHD_AUTH* auth); int wolfSSHD_AuthReducePermissionsUser(WOLFSSHD_AUTH* auth, WUID_T uid, WGID_T gid); long wolfSSHD_AuthGetGraceTime(const WOLFSSHD_AUTH* auth); +WOLFSSHD_CONFIG* wolfSSHD_AuthGetUserConf(const WOLFSSHD_AUTH* auth, + const char* usr, const char* host, + const char* localAdr, word16* localPort, const char* RDomain, + const char* adr); #endif /* WOLFAUTH_H */ diff --git a/apps/wolfsshd/configuration.c b/apps/wolfsshd/configuration.c index dc92b82c..3d2308cd 100644 --- a/apps/wolfsshd/configuration.c +++ b/apps/wolfsshd/configuration.c @@ -50,6 +50,8 @@ struct WOLFSSHD_CONFIG { void* heap; + char* usrAppliesTo; /* NULL means all users */ + char* groupAppliesTo; /* NULL means all groups */ char* banner; char* chrootDir; char* ciphers; @@ -58,6 +60,8 @@ struct WOLFSSHD_CONFIG { char* kekAlgos; char* listenAddress; char* authKeysFile; + char* forceCmd; + WOLFSSHD_CONFIG* next; /* next config in list */ long loginTimer; word16 port; byte usePrivilegeSeparation:2; @@ -67,6 +71,7 @@ struct WOLFSSHD_CONFIG { byte permitEmptyPasswords:1; }; +int CountWhitespace(const char* in, int inSz, byte inv); /* convert a string into seconds, handles if 'm' for minutes follows the string * number, i.e. 2m @@ -111,11 +116,20 @@ static long GetConfigInt(const char* in, int inSz, int isTime, void* heap) return ret; } -/* returns WS_SUCCESS on success */ +/* returns WS_SUCCESS on success, removes trailng newlines */ static int CreateString(char** out, const char* in, int inSz, void* heap) { int ret = WS_SUCCESS; - int idx = 0; + int idx = 0, tail, sz = 0; + + if (in == NULL && inSz != 0) { + return WS_BAD_ARGUMENT; + } + + if (in == NULL) { + /* "created" an empty string */ + return ret; + } /* remove leading white spaces */ while (idx < inSz && in[idx] == ' ') idx++; @@ -124,15 +138,28 @@ static int CreateString(char** out, const char* in, int inSz, void* heap) ret = WS_BAD_ARGUMENT; } + if (ret == WS_SUCCESS) { + for (tail = inSz - 1; tail > idx; tail--) { + if (in[tail] != '\n' && in[tail] != ' ' && in[tail] != '\r') { + break; + } + } + + sz = tail - idx + 1; /* +1 to account for index of 0 */ + if (sz > inSz - idx) { + ret = WS_BAD_ARGUMENT; + } + } + /* malloc new string and set it */ if (ret == WS_SUCCESS) { - *out = (char*)WMALLOC((inSz - idx) + 1, heap, DYNTYPE_SSHD); + *out = (char*)WMALLOC(sz + 1, heap, DYNTYPE_SSHD); if (*out == NULL) { ret = WS_MEMORY_E; } else { - XMEMCPY(*out, in + idx, inSz - idx); - *(*out + (inSz - idx)) = '\0'; + XMEMCPY(*out, in + idx, sz); + *(*out + sz) = '\0'; } } @@ -170,17 +197,101 @@ WOLFSSHD_CONFIG* wolfSSHD_ConfigNew(void* heap) } + +/* on success return a newly create WOLFSSHD_CONFIG structure that has the + * same values set as the input 'conf'. User and group match values are not + * copied */ +static WOLFSSHD_CONFIG* wolfSSHD_ConfigCopy(WOLFSSHD_CONFIG* conf) +{ + int ret = WS_SUCCESS; + WOLFSSHD_CONFIG* newConf; + + newConf = wolfSSHD_ConfigNew(conf->heap); + if (newConf != NULL) { + if (conf->banner) { + ret = CreateString(&newConf->banner, conf->banner, + (int)WSTRLEN(conf->banner), + newConf->heap); + } + + if (ret == WS_SUCCESS && conf->chrootDir) { + ret = CreateString(&newConf->chrootDir, conf->chrootDir, + (int)WSTRLEN(conf->chrootDir), + newConf->heap); + } + + if (ret == WS_SUCCESS && conf->ciphers) { + ret = CreateString(&newConf->ciphers, conf->ciphers, + (int)WSTRLEN(conf->ciphers), + newConf->heap); + } + + if (ret == WS_SUCCESS && conf->hostKeyFile) { + ret = CreateString(&newConf->hostKeyFile, conf->hostKeyFile, + (int)WSTRLEN(conf->hostKeyFile), + newConf->heap); + } + + if (ret == WS_SUCCESS && conf->hostKeyAlgos) { + ret = CreateString(&newConf->hostKeyAlgos, conf->hostKeyAlgos, + (int)WSTRLEN(conf->hostKeyAlgos), + newConf->heap); + } + + if (ret == WS_SUCCESS && conf->kekAlgos) { + ret = CreateString(&newConf->kekAlgos, conf->kekAlgos, + (int)WSTRLEN(conf->kekAlgos), + newConf->heap); + } + + if (ret == WS_SUCCESS && conf->listenAddress) { + ret = CreateString(&newConf->listenAddress, conf->listenAddress, + (int)WSTRLEN(conf->listenAddress), + newConf->heap); + } + + if (ret == WS_SUCCESS && conf->authKeysFile) { + ret = CreateString(&newConf->authKeysFile, conf->authKeysFile, + (int)WSTRLEN(conf->authKeysFile), + newConf->heap); + } + + if (ret == WS_SUCCESS) { + newConf->loginTimer = conf->loginTimer; + newConf->port = conf->port; + newConf->passwordAuth = conf->passwordAuth; + newConf->pubKeyAuth = conf->pubKeyAuth; + newConf->usePrivilegeSeparation = conf->usePrivilegeSeparation; + newConf->permitRootLogin = conf->permitRootLogin; + newConf->permitEmptyPasswords = conf->permitEmptyPasswords; + } + } + + return newConf; +} + + void wolfSSHD_ConfigFree(WOLFSSHD_CONFIG* conf) { + WOLFSSHD_CONFIG* current; void* heap; - if (conf != NULL) { - heap = conf->heap; + current = conf; + while (current != NULL) { + WOLFSSHD_CONFIG* next = current->next; + heap = current->heap; - FreeString(&conf->authKeysFile, heap); - FreeString(&conf->hostKeyFile, heap); + FreeString(¤t->banner, heap); + FreeString(¤t->chrootDir, heap); + FreeString(¤t->ciphers, heap); + FreeString(¤t->kekAlgos, heap); + FreeString(¤t->hostKeyAlgos, heap); + FreeString(¤t->listenAddress, heap); + FreeString(¤t->authKeysFile, heap); + FreeString(¤t->hostKeyFile, heap); - WFREE(conf, heap, DYNTYPE_SSHD); + WFREE(current, heap, DYNTYPE_SSHD); + current = next; } } @@ -208,10 +319,13 @@ enum { OPT_PORT = 13, OPT_PERMIT_ROOT = 14, OPT_USE_DNS = 15, - OPT_INCLUDE = 16 + OPT_INCLUDE = 16, + OPT_CHROOT_DIR = 17, + OPT_MATCH = 18, + OPT_FORCE_CMD = 19, }; enum { - NUM_OPTIONS = 17 + NUM_OPTIONS = 20 }; static const CONFIG_OPTION options[NUM_OPTIONS] = { @@ -231,7 +345,10 @@ static const CONFIG_OPTION options[NUM_OPTIONS] = { {OPT_PORT, "Port"}, {OPT_PERMIT_ROOT, "PermitRootLogin"}, {OPT_USE_DNS, "UseDNS"}, - {OPT_INCLUDE, "Include"} + {OPT_INCLUDE, "Include"}, + {OPT_CHROOT_DIR, "ChrootDirectory"}, + {OPT_MATCH, "Match"}, + {OPT_FORCE_CMD, "ForceCommand"}, }; /* returns WS_SUCCESS on success */ @@ -637,20 +754,154 @@ static int HandleInclude(WOLFSSHD_CONFIG *conf, const char *value) return ret; } + /* returns WS_SUCCESS on success */ -static int HandleConfigOption(WOLFSSHD_CONFIG* conf, int opt, const char* value) +static int HandleChrootDir(WOLFSSHD_CONFIG* conf, const char* value) +{ + int ret = WS_SUCCESS; + + if (conf == NULL || value == NULL) { + ret = WS_BAD_ARGUMENT; + } + + if (ret == WS_SUCCESS) { + if (conf->chrootDir != NULL) { + FreeString(&conf->chrootDir, conf->heap); + conf->chrootDir = NULL; + } + ret = CreateString(&conf->chrootDir, value, + (int)WSTRLEN(value), conf->heap); + } + + return ret; +} + + +/* returns WS_SUCCESS on success, helps with adding a restricted case to the + * config */ +static int AddRestrictedCase(WOLFSSHD_CONFIG* config, const char* mtch, + const char* value, char** out) +{ + int ret = WS_SUCCESS; + char* pt; + + pt = XSTRSTR(value, mtch); + if (pt != NULL) { + int sz, i; + + pt += (int)XSTRLEN(mtch); + sz = (int)XSTRLEN(pt); + + /* remove spaces between 'mtch' and the user name */ + for (i = 0; i < sz; i++) { + if (pt[i] != ' ') break; + } + if (i == sz) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] No valid input found with Match"); + ret = WS_FATAL_ERROR; + } + + if (ret == WS_SUCCESS) { + pt += i; + sz -= i; + + /* get the actual size of the user name */ + for (i = 0; i < sz; i++) { + if (pt[i] == ' ' || pt[i] == '\r' || pt[i] == '\n') break; + } + sz = i; + + ret = CreateString(out, pt, sz, config->heap); + } + } + return ret; +} + + +/* returns WS_SUCCESS on success, on success it update the conf pointed to + * and makes it point to the newly created conf node */ +static int HandleMatch(WOLFSSHD_CONFIG** conf, const char* value, int valueSz) +{ + WOLFSSHD_CONFIG* newConf; + int ret = WS_SUCCESS; + + if (conf == NULL || *conf == NULL || value == NULL) { + ret = WS_BAD_ARGUMENT; + } + + /* create new configure for altered options specific to the match */ + if (ret == WS_SUCCESS) { + newConf = wolfSSHD_ConfigCopy(*conf); + if (newConf == NULL) { + ret = WS_MEMORY_E; + } + } + + /* users the settings apply to */ + if (ret == WS_SUCCESS) { + ret = AddRestrictedCase(newConf, "User", value, + &newConf->usrAppliesTo); + } + + /* groups the settings apply to */ + if (ret == WS_SUCCESS) { + ret = AddRestrictedCase(newConf, "Group", value, + &newConf->groupAppliesTo); + } + + /* @TODO handle , seperated user/group list */ + + /* update current config being processed */ + if (ret == WS_SUCCESS) { + (*conf)->next = newConf; + (*conf) = newConf; + } + + (void)valueSz; + return ret; +} + + +/* returns WS_SUCCESS on success */ +static int HandleForcedCommand(WOLFSSHD_CONFIG* conf, const char* value, + int valueSz) +{ + int ret = WS_SUCCESS; + + if (conf == NULL || value == NULL) { + ret = WS_BAD_ARGUMENT; + } + + if (ret == WS_SUCCESS) { + if (conf->forceCmd != NULL) { + FreeString(&conf->forceCmd, conf->heap); + conf->forceCmd = NULL; + } + + ret = CreateString(&conf->forceCmd, value, valueSz, conf->heap); + } + + + (void)valueSz; + return ret; +} + +/* returns WS_SUCCESS on success */ +static int HandleConfigOption(WOLFSSHD_CONFIG** conf, int opt, + const char* value, const char* full, int fullSz) { int ret = WS_BAD_ARGUMENT; switch (opt) { case OPT_AUTH_KEYS_FILE: - ret = wolfSSHD_ConfigSetAuthKeysFile(conf, value); + ret = wolfSSHD_ConfigSetAuthKeysFile(*conf, value); break; case OPT_PRIV_SEP: - ret = HandlePrivSep(conf, value); + ret = HandlePrivSep(*conf, value); break; case OPT_PERMIT_EMPTY_PW: - ret = HandlePermitEmptyPw(conf, value); + ret = HandlePermitEmptyPw(*conf, value); break; case OPT_SUBSYSTEM: /* TODO */ @@ -678,30 +929,40 @@ static int HandleConfigOption(WOLFSSHD_CONFIG* conf, int opt, const char* value) break; case OPT_PROTOCOL: /* TODO */ - ret = HandleProtocol(conf, value); + ret = HandleProtocol(*conf, value); break; case OPT_LOGIN_GRACE_TIME: - ret = HandleLoginGraceTime(conf, value); + ret = HandleLoginGraceTime(*conf, value); break; case OPT_HOST_KEY: /* TODO: Add logic to check if file exists? */ - ret = wolfSSHD_ConfigSetHostKeyFile(conf, value); + ret = wolfSSHD_ConfigSetHostKeyFile(*conf, value); break; case OPT_PASSWORD_AUTH: - ret = HandlePwAuth(conf, value); + ret = HandlePwAuth(*conf, value); break; case OPT_PORT: - ret = HandlePort(conf, value); + ret = HandlePort(*conf, value); break; case OPT_PERMIT_ROOT: - ret = HandlePermitRoot(conf, value); + ret = HandlePermitRoot(*conf, value); break; case OPT_USE_DNS: /* TODO */ ret = WS_SUCCESS; break; case OPT_INCLUDE: - ret = HandleInclude(conf, value); + ret = HandleInclude(*conf, value); + break; + case OPT_CHROOT_DIR: + ret = HandleChrootDir(*conf, value); + break; + case OPT_MATCH: + /* makes new config and appends it to the list */ + ret = HandleMatch(conf, full, fullSz); + break; + case OPT_FORCE_CMD: + ret = HandleForcedCommand(*conf, full, fullSz); break; default: break; @@ -712,7 +973,7 @@ static int HandleConfigOption(WOLFSSHD_CONFIG* conf, int opt, const char* value) /* helper function to count white spaces, returns the number of white spaces on * success */ -static int CountWhitespace(const char* in, int inSz, byte inv) +int CountWhitespace(const char* in, int inSz, byte inv) { int i = 0; @@ -736,8 +997,9 @@ static int CountWhitespace(const char* in, int inSz, byte inv) /* returns WS_SUCCESS on success * Fails if any option is found that is unknown/unsupported + * Match command will create new configs for specific matching cases */ -WOLFSSHD_STATIC int ParseConfigLine(WOLFSSHD_CONFIG* conf, const char* l, +WOLFSSHD_STATIC int ParseConfigLine(WOLFSSHD_CONFIG** conf, const char* l, int lSz) { int ret = WS_BAD_ARGUMENT; @@ -769,7 +1031,7 @@ WOLFSSHD_STATIC int ParseConfigLine(WOLFSSHD_CONFIG* conf, const char* l, else { WMEMCPY(tmp, l + idx, sz); tmp[sz] = 0; - ret = HandleConfigOption(conf, found->tag, tmp); + ret = HandleConfigOption(conf, found->tag, tmp, l + idx, lSz - idx); } } else { @@ -787,6 +1049,7 @@ WOLFSSHD_STATIC int ParseConfigLine(WOLFSSHD_CONFIG* conf, const char* l, int wolfSSHD_ConfigLoad(WOLFSSHD_CONFIG* conf, const char* filename) { XFILE f; + WOLFSSHD_CONFIG* currentConfig; int ret = WS_SUCCESS; char buf[MAX_LINE_SIZE]; const char* current; @@ -802,6 +1065,7 @@ int wolfSSHD_ConfigLoad(WOLFSSHD_CONFIG* conf, const char* filename) } wolfSSH_Log(WS_LOG_INFO, "[SSHD] parsing config file %s", filename); + currentConfig = conf; while ((current = XFGETS(buf, MAX_LINE_SIZE, f)) != NULL) { int currentSz = (int)XSTRLEN(current); @@ -819,7 +1083,7 @@ int wolfSSHD_ConfigLoad(WOLFSSHD_CONFIG* conf, const char* filename) continue; /* commented out line */ } - ret = ParseConfigLine(conf, current, currentSz); + ret = ParseConfigLine(¤tConfig, current, currentSz); if (ret != WS_SUCCESS) { fprintf(stderr, "Unable to parse config line : %s\n", current); break; @@ -832,6 +1096,60 @@ int wolfSSHD_ConfigLoad(WOLFSSHD_CONFIG* conf, const char* filename) return ret; } + +/* returns the config associated with the user */ +WOLFSSHD_CONFIG* wolfSSHD_GetUserConf(const WOLFSSHD_CONFIG* conf, + const char* usr, const char* grp, const char* host, + const char* localAdr, word16* localPort, const char* RDomain, + const char* adr) +{ + WOLFSSHD_CONFIG* ret; + WOLFSSHD_CONFIG* current; + + /* default to return head of list */ + ret = current = (WOLFSSHD_CONFIG*)conf; + while (current != NULL) { + /* compare current configs user */ + if (usr != NULL && current->usrAppliesTo != NULL) { + if (XSTRCMP(current->usrAppliesTo, usr) == 0) { + ret = current; + break; + } + } + + /* compare current configs group */ + if (grp != NULL && current->groupAppliesTo != NULL) { + if (XSTRCMP(current->groupAppliesTo, grp) == 0) { + ret = current; + break; + } + } + + current = current->next; + } + + /* @TODO */ + (void)host; + (void)localAdr; + (void)localPort; + (void)RDomain; + (void)adr; + + return ret; +} + + +char* wolfSSHD_ConfigGetForcedCmd(const WOLFSSHD_CONFIG* conf) +{ + char* ret = NULL; + + if (conf != NULL) { + ret = conf->forceCmd; + } + + return ret; +} + char* wolfSSHD_ConfigGetAuthKeysFile(const WOLFSSHD_CONFIG* conf) { char* ret = NULL; @@ -877,6 +1195,17 @@ char* wolfSSHD_ConfigGetBanner(const WOLFSSHD_CONFIG* conf) return ret; } +char* wolfSSHD_ConfigGetChroot(const WOLFSSHD_CONFIG* conf) +{ + char* ret = NULL; + + if (conf != NULL) { + ret = conf->chrootDir; + } + + return ret; +} + char* wolfSSHD_ConfigGetHostKeyFile(const WOLFSSHD_CONFIG* conf) { char* ret = NULL; diff --git a/apps/wolfsshd/configuration.h b/apps/wolfsshd/configuration.h index 3d352408..7504f210 100644 --- a/apps/wolfsshd/configuration.h +++ b/apps/wolfsshd/configuration.h @@ -34,7 +34,9 @@ WOLFSSHD_CONFIG* wolfSSHD_ConfigNew(void* heap); void wolfSSHD_ConfigFree(WOLFSSHD_CONFIG* conf); int wolfSSHD_ConfigLoad(WOLFSSHD_CONFIG* conf, const char* filename); +char* wolfSSHD_ConfigGetForcedCmd(const WOLFSSHD_CONFIG* conf); char* wolfSSHD_ConfigGetBanner(const WOLFSSHD_CONFIG* conf); +char* wolfSSHD_ConfigGetChroot(const WOLFSSHD_CONFIG* conf); char* wolfSSHD_ConfigGetHostKeyFile(const WOLFSSHD_CONFIG* conf); int wolfSSHD_ConfigSetHostKeyFile(WOLFSSHD_CONFIG* conf, const char* file); word16 wolfSSHD_ConfigGetPort(const WOLFSSHD_CONFIG* conf); @@ -45,9 +47,13 @@ byte wolfSSHD_ConfigGetPermitRoot(const WOLFSSHD_CONFIG* conf); byte wolfSSHD_ConfigGetPrivilegeSeparation(const WOLFSSHD_CONFIG* conf); long wolfSSHD_ConfigGetGraceTime(const WOLFSSHD_CONFIG* conf); byte wolfSSHD_ConfigGetPwAuth(const WOLFSSHD_CONFIG* conf); +WOLFSSHD_CONFIG* wolfSSHD_GetUserConf(const WOLFSSHD_CONFIG* conf, + const char* usr, const char* grp, const char* host, + const char* localAdr, word16* localPort, const char* RDomain, + const char* adr); #ifdef WOLFSSHD_UNIT_TEST -int ParseConfigLine(WOLFSSHD_CONFIG* conf, const char* l, int lSz); +int ParseConfigLine(WOLFSSHD_CONFIG** conf, const char* l, int lSz); #endif #endif /* WOLFSSHD_H */ diff --git a/apps/wolfsshd/test/test_configuration.c b/apps/wolfsshd/test/test_configuration.c index df31cbf7..043eab91 100644 --- a/apps/wolfsshd/test/test_configuration.c +++ b/apps/wolfsshd/test/test_configuration.c @@ -200,7 +200,7 @@ static int test_ParseConfigLine(void) for (i = 0; i < numVectors; ++i) { Log(" Testing scenario: %s.", vectors[i].desc); - ret = ParseConfigLine(conf, vectors[i].line, + ret = ParseConfigLine(&conf, vectors[i].line, (int)WSTRLEN(vectors[i].line)); if ((ret == WS_SUCCESS && !vectors[i].shouldFail) || diff --git a/apps/wolfsshd/wolfsshd.c b/apps/wolfsshd/wolfsshd.c index 24aff073..3bd61784 100644 --- a/apps/wolfsshd/wolfsshd.c +++ b/apps/wolfsshd/wolfsshd.c @@ -281,27 +281,41 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx) } +/* return 1 if set, 0 if not set and negative values on error */ +static int SetupChroot(WOLFSSHD_CONFIG* usrConf) +{ + int ret = 0; + char* chrootPath; + + /* check for chroot set */ + chrootPath = wolfSSHD_ConfigGetChroot(usrConf); + if (chrootPath != NULL) { + ret = 1; + wolfSSH_Log(WS_LOG_INFO, "[SSHD] chroot to path %s", chrootPath); + if (chroot(chrootPath) != 0) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] chroot failed to path %s", chrootPath); + ret = WS_FATAL_ERROR; + } + } + return ret; +} + + #ifdef WOLFSSH_SFTP #define TEST_SFTP_TIMEOUT 1 /* handle SFTP operations - * returns 0 on success + * returns WS_SUCCESS on success */ -static int SFTP_Subsystem(WOLFSSH* ssh, WOLFSSHD_CONNECTION* conn) +static int SFTP_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, + WPASSWD* pPasswd, WOLFSSHD_CONFIG* usrConf) { byte tmp[1]; int ret = WS_SUCCESS; int error = WS_SUCCESS; WS_SOCKET_T sockfd; int select_ret = 0; - const char *userName; - struct passwd *p_passwd; - - userName = wolfSSH_GetUsername(ssh); - if (userName == NULL) { - wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Failure get user name"); - return WS_FATAL_ERROR; - } /* temporarily elevate permissions to get users information */ if (wolfSSHD_AuthRaisePermissions(conn->auth) != WS_SUCCESS) { @@ -309,18 +323,39 @@ static int SFTP_Subsystem(WOLFSSH* ssh, WOLFSSHD_CONNECTION* conn) return WS_FATAL_ERROR; } - p_passwd = getpwnam((const char *)userName); - if (p_passwd == NULL) { - if (wolfSSHD_AuthReducePermissions(conn->auth) != WS_SUCCESS) { - /* stop everything if not able to reduce permissions level */ - exit(1); + if (ret == WS_SUCCESS) { + error = SetupChroot(usrConf); + if (error == 1) { + /* chroot was executed */ + if (wolfSSH_SFTP_SetDefaultPath(ssh, "/") != WS_SUCCESS) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Error setting SFTP default path"); + ret = WS_FATAL_ERROR; + } + } + else if (error < 0) { + ret = error; /* error case with setup chroot */ } - - return WS_FATAL_ERROR; } - if (wolfSSHD_AuthReducePermissionsUser(conn->auth, p_passwd->pw_uid, - p_passwd->pw_gid) != WS_SUCCESS) { + /* set starting SFTP directory */ + if (ret == WS_SUCCESS) { + WDIR dir; + + /* if home directory exists than set it as the default */ + if (WOPENDIR(NULL, NULL, &dir, pPasswd->pw_dir) == 0) { + if (wolfSSH_SFTP_SetDefaultPath(ssh, pPasswd->pw_dir) + != WS_SUCCESS) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Error setting SFTP default home path"); + ret = WS_FATAL_ERROR; + } + WCLOSEDIR(&dir); + } + } + + if (wolfSSHD_AuthReducePermissionsUser(conn->auth, pPasswd->pw_uid, + pPasswd->pw_gid) != WS_SUCCESS) { wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error setting user ID"); if (wolfSSHD_AuthReducePermissions(conn->auth) != WS_SUCCESS) { /* stop everything if not able to reduce permissions level */ @@ -378,12 +413,14 @@ static int SFTP_Subsystem(WOLFSSH* ssh, WOLFSSHD_CONNECTION* conn) #define MAX_COMMAND_SZ 80 #endif -static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh) +/* handles creating a new shell env. and maintains SSH connection for incoming + * user input as well as output of the shell. + * return WS_SUCCESS on success */ +static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, + WPASSWD* pPasswd, WOLFSSHD_CONFIG* usrConf) { WS_SOCKET_T sshFd = 0; int rc; - const char *userName; - struct passwd *p_passwd; WS_SOCKET_T childFd = 0; pid_t childPid; #ifndef EXAMPLE_BUFFER_SZ @@ -391,10 +428,13 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh) #endif byte shellBuffer[EXAMPLE_BUFFER_SZ]; byte channelBuffer[EXAMPLE_BUFFER_SZ]; + char* forcedCmd; - userName = wolfSSH_GetUsername(ssh); - if (userName == NULL) { - wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Failure get user name"); + forcedCmd = wolfSSHD_ConfigGetForcedCmd(usrConf); + if (forcedCmd != NULL && XSTRCMP(forcedCmd, "internal-sftp") == 0) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Only SFTP connections allowed for user " + "%s", wolfSSH_GetUsername(ssh)); return WS_FATAL_ERROR; } @@ -404,30 +444,7 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh) return WS_FATAL_ERROR; } - p_passwd = getpwnam((const char *)userName); - if (p_passwd == NULL) { - /* Not actually a user on the system. */ - wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Invalid user name found"); - if (wolfSSHD_AuthReducePermissions(conn->auth) != WS_SUCCESS) { - /* stop everything if not able to reduce permissions level */ - exit(1); - } - - return WS_FATAL_ERROR; - } - ChildRunning = 1; - if (wolfSSHD_AuthReducePermissionsUser(conn->auth, p_passwd->pw_uid, - p_passwd->pw_gid) != WS_SUCCESS) { - wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error setting user ID"); - if (wolfSSHD_AuthReducePermissions(conn->auth) != WS_SUCCESS) { - /* stop everything if not able to reduce permissions level */ - exit(1); - } - - return WS_FATAL_ERROR; - } - childPid = forkpty(&childFd, NULL, NULL, NULL); if (childPid < 0) { /* forkpty failed, so return */ @@ -444,31 +461,85 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh) signal(SIGINT, SIG_DFL); signal(SIGCHLD, SIG_DFL); - setenv("HOME", p_passwd->pw_dir, 1); - setenv("LOGNAME", p_passwd->pw_name, 1); - rc = chdir(p_passwd->pw_dir); - if (rc != 0) { + rc = SetupChroot(usrConf); + if (rc < 0) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error setting chroot"); + if (wolfSSHD_AuthReducePermissions(conn->auth) != WS_SUCCESS) { + /* stop everything if not able to reduce permissions level */ + exit(1); + } + return WS_FATAL_ERROR; } + else if (rc == 1) { + rc = chdir("/"); + if (rc != 0) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Error going to / after chroot"); + if (wolfSSHD_AuthReducePermissions(conn->auth) != WS_SUCCESS) { + /* stop everything if not able to reduce permissions level */ + exit(1); + } + + return WS_FATAL_ERROR; + } + } + + if (wolfSSHD_AuthReducePermissionsUser(conn->auth, pPasswd->pw_uid, + pPasswd->pw_gid) != WS_SUCCESS) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error setting user ID"); + if (wolfSSHD_AuthReducePermissions(conn->auth) != WS_SUCCESS) { + /* stop everything if not able to reduce permissions level */ + exit(1); + } + + return WS_FATAL_ERROR; + } + + setenv("HOME", pPasswd->pw_dir, 1); + setenv("LOGNAME", pPasswd->pw_name, 1); + + rc = chdir(pPasswd->pw_dir); + if (rc != 0) { + /* not error'ing out if unable to find home directory */ + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error going to user home dir %s", + pPasswd->pw_dir); + } /* default to /bin/sh if user shell is not set */ WMEMSET(cmd, 0, sizeof(cmd)); - if (XSTRLEN(p_passwd->pw_shell) == 0) { + if (XSTRLEN(pPasswd->pw_shell) == 0) { XSNPRINTF(cmd, sizeof(cmd), "%s", "/bin/sh"); } else { - XSNPRINTF(cmd, sizeof(cmd),"%s", p_passwd->pw_shell); + XSNPRINTF(cmd, sizeof(cmd),"%s", pPasswd->pw_shell); } errno = 0; - ret = execv(cmd, (char**)args); + if (forcedCmd) { + args[0] = NULL; + ret = execv(forcedCmd, (char**)args); + } + else { + ret = execv(cmd, (char**)args); + } if (ret && errno) { - perror("error executing shell command "); + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue opening shell"); exit(1); } exit(0); /* exit child process and close down SSH connection */ } + if (wolfSSHD_AuthReducePermissionsUser(conn->auth, pPasswd->pw_uid, + pPasswd->pw_gid) != WS_SUCCESS) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error setting user ID"); + if (wolfSSHD_AuthReducePermissions(conn->auth) != WS_SUCCESS) { + /* stop everything if not able to reduce permissions level */ + exit(1); + } + + return WS_FATAL_ERROR; + } sshFd = wolfSSH_get_fd(ssh); struct termios tios; @@ -634,7 +705,8 @@ static void* HandleConnection(void* arg) error = WS_FATAL_ERROR; } - if (ret != WS_SUCCESS && ret != WS_SFTP_COMPLETE) { + if (ret != WS_SUCCESS && ret != WS_SFTP_COMPLETE && + ret != WS_SCP_COMPLETE) { wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Failed to accept WOLFSSH connection from %s", conn->ip); @@ -642,46 +714,74 @@ static void* HandleConnection(void* arg) } if (ret == WS_SUCCESS || ret == WS_SFTP_COMPLETE) { - switch (wolfSSH_GetSessionType(ssh)) { - case WOLFSSH_SESSION_SHELL: - #ifdef WOLFSSH_SHELL - if (ret == WS_SUCCESS) { - wolfSSH_Log(WS_LOG_INFO, "[SSHD] Entering new shell"); - SHELL_Subsystem(conn, ssh); - } - #else - wolfSSH_Log(WS_LOG_ERROR, - "[SSHD] Shell support is disabled"); - ret = WS_NOT_COMPILED; - #endif - break; + WPASSWD* pPasswd; + WOLFSSHD_CONFIG* usrConf; + char* usr; - case WOLFSSH_SESSION_SUBSYSTEM: - /* test for known subsystems */ - switch (ret) { - case WS_SFTP_COMPLETE: - #ifdef WOLFSSH_SFTP - ret = SFTP_Subsystem(ssh, conn); - #else - err_sys("SFTP not compiled in. Please use --enable-sftp"); - #endif - break; + /* get configuration for user */ + usr = wolfSSH_GetUsername(ssh); + usrConf = wolfSSHD_AuthGetUserConf(conn->auth, usr, NULL, NULL, + NULL, NULL, NULL); + if (usrConf == NULL) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Error getting user configuration"); + ret = WS_FATAL_ERROR; + } - default: - wolfSSH_Log(WS_LOG_ERROR, - "[SSHD] Unknown or build not supporting subsystem" - " found [%s]", wolfSSH_GetSessionCommand(ssh)); - ret = WS_NOT_COMPILED; - } - break; + if (ret == WS_SUCCESS || ret == WS_SFTP_COMPLETE) { + pPasswd = getpwnam((const char *)usr); + if (pPasswd == NULL) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error igetting user info"); + ret = WS_FATAL_ERROR; + } + } - case WOLFSSH_SESSION_UNKNOWN: - case WOLFSSH_SESSION_EXEC: - case WOLFSSH_SESSION_TERMINAL: - default: - wolfSSH_Log(WS_LOG_ERROR, - "[SSHD] Unknown or build not supporting session type found"); - ret = WS_NOT_COMPILED; + if (ret != WS_FATAL_ERROR) { + /* check for any forced command set for the user */ + switch (wolfSSH_GetSessionType(ssh)) { + case WOLFSSH_SESSION_SHELL: + #ifdef WOLFSSH_SHELL + if (ret == WS_SUCCESS) { + wolfSSH_Log(WS_LOG_INFO, "[SSHD] Entering new shell"); + SHELL_Subsystem(conn, ssh, pPasswd, usrConf); + } + #else + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Shell support is disabled"); + ret = WS_NOT_COMPILED; + #endif + break; + + case WOLFSSH_SESSION_SUBSYSTEM: + /* test for known subsystems */ + switch (ret) { + case WS_SFTP_COMPLETE: + #ifdef WOLFSSH_SFTP + ret = SFTP_Subsystem(conn, ssh, pPasswd, usrConf); + #else + err_sys("SFTP not compiled in. Please use " + "--enable-sftp"); + #endif + break; + + default: + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Unknown or build not supporting sub" + "system found [%s]", + wolfSSH_GetSessionCommand(ssh)); + ret = WS_NOT_COMPILED; + } + break; + + case WOLFSSH_SESSION_UNKNOWN: + case WOLFSSH_SESSION_EXEC: + case WOLFSSH_SESSION_TERMINAL: + default: + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Unknown or build not supporting session type " + "found"); + ret = WS_NOT_COMPILED; + } } } diff --git a/src/wolfsftp.c b/src/wolfsftp.c index fa9465e3..ab5af5fb 100644 --- a/src/wolfsftp.c +++ b/src/wolfsftp.c @@ -1230,6 +1230,12 @@ static int wolfSSH_SFTP_RecvRealPath(WOLFSSH* ssh, int reqId, byte* data, rSz -= 1; } + /* special case of /. */ + if (rSz == 2 && r[0] == WS_DELIM && r[1] == '.') { + r[1] = '\0'; + rSz -= 1; + } + /* for real path always send '/' chars */ for (i = 0; i < rSz; i++) { if (r[i] == WS_DELIM) r[i] = '/'; diff --git a/wolfssh/port.h b/wolfssh/port.h index acda08ba..55f99eff 100644 --- a/wolfssh/port.h +++ b/wolfssh/port.h @@ -50,6 +50,7 @@ extern "C" { #endif #define WUID_T uid_t #define WGID_T gid_t + #define WPASSWD struct passwd #endif /* setup memory handling */