Add driver letter support to sftp-server and let it form external path with / to meet spec

sftp-server now conforms to sftp rfc spec and creates external path with
/ as the first character so that programs like Winscp will now work.
Driver letters are kept below it like /x:/users/user1homedir format;
driver letters are now supported.  cd /users or cd c:/users or cd
D:/users will all work now. Windows security enforces what directory or
files one can view/access.
This commit is contained in:
quamrulmina 2015-12-29 03:12:56 -06:00
parent 102d1ed6b6
commit 4857c272b9
2 changed files with 174 additions and 11 deletions

View File

@ -210,7 +210,7 @@ char *realpathWin32(const char *path, char resolved[PATH_MAX])
char realpath[PATH_MAX]; char realpath[PATH_MAX];
char * pch; char * pch;
path_len = strlcpy(realpath, path, sizeof(realpath)); path_len = strlcpy(realpath, path+1, sizeof(realpath));
char * pchMac; char * pchMac;
pchMac = strstr (realpath, "._"); pchMac = strstr (realpath, "._");
@ -247,12 +247,69 @@ char *realpathWin32(const char *path, char resolved[PATH_MAX])
if (realpath[1] == ':' && realpath[2] == 0) if (realpath[1] == ':' && realpath[2] == 0)
{ {
realpath[2] = '\\'; realpath[2] = '/';
realpath[3] = 0; realpath[3] = 0;
} }
strncpy (resolved, realpath, sizeof(realpath)); resolved[0] = *path; // will be our first slash in /x:/users/test1 format
strncpy (resolved+1, realpath, sizeof(realpath));
return resolved; return resolved;
} }
char *realpathWin32i(const char *path, char resolved[PATH_MAX])
{
size_t path_len;
unsigned int lastSlash;
char realpath[PATH_MAX];
char * pch;
if (path[0] != '/') {
// absolute form x:/abc/def given, no first slash to take out
path_len = strlcpy(realpath, path, sizeof(realpath));
}
else
path_len = strlcpy(realpath, path + 1, sizeof(realpath));
char * pchMac;
pchMac = strstr(realpath, "._");
if (pchMac != NULL)
{
pchMac[0] = '\0';
pchMac++;
pchMac++;
strcat(realpath, pchMac);
}
pch = strrchr(realpath, '/');
lastSlash = pch - realpath + 1;
if (path_len == lastSlash)
{
realpath[lastSlash - 1] = '\0';
}
pch = strrchr(realpath, '.');
if (pch != NULL)
{
if (realpath[pch - realpath - 1] == '.')
{
realpath[pch - realpath - 2] = '\0';
pch = strrchr(realpath, '/');
if (pch != NULL)
realpath[pch - realpath] = '\0';
}
}
/*
* Store terminating slash in 'X:/' on Windows.
*/
if (realpath[1] == ':' && realpath[2] == 0)
{
realpath[2] = '/';
realpath[3] = 0;
}
strncpy(resolved, realpath, sizeof(realpath));
return resolved;
}
#endif /* WIN32_FIXME */ #endif /* WIN32_FIXME */

View File

@ -772,6 +772,15 @@ process_open(u_int32_t id)
debug3("request %u: open flags %d", id, pflags); debug3("request %u: open flags %d", id, pflags);
flags = flags_from_portable(pflags); flags = flags_from_portable(pflags);
mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a.perm : 0666; mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a.perm : 0666;
#ifdef WIN32_FIXME
char resolvedname[MAXPATHLEN];
if (realpathWin32i(name, resolvedname))
{
free(name);
name = strdup(resolvedname);
}
#endif
logit("open \"%s\" flags %s mode 0%o", logit("open \"%s\" flags %s mode 0%o",
name, string_from_portable(pflags), mode); name, string_from_portable(pflags), mode);
if (readonly && if (readonly &&
@ -915,10 +924,9 @@ process_do_stat(u_int32_t id, int do_lstat)
if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0)
fatal("%s: buffer error: %s", __func__, ssh_err(r)); fatal("%s: buffer error: %s", __func__, ssh_err(r));
if (realpathWin32(name, resolvedname)) if (realpathWin32i(name, resolvedname))
{ {
free(name); free(name);
name = strdup(resolvedname); name = strdup(resolvedname);
} }
@ -1131,9 +1139,18 @@ process_opendir(u_int32_t id)
if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0)
fatal("%s: buffer error: %s", __func__, ssh_err(r)); fatal("%s: buffer error: %s", __func__, ssh_err(r));
#ifdef WIN32_FIXME
char resolvedname[MAXPATHLEN];
if (realpathWin32i(path, resolvedname))
{
free(path);
path = strdup(resolvedname);
}
#endif
debug3("request %u: opendir", id); debug3("request %u: opendir", id);
logit("opendir \"%s\"", path); logit("opendir \"%s\"", path);
dirp = opendir(path); dirp = opendir(path);
if (dirp == NULL) { if (dirp == NULL) {
status = errno_to_portable(errno); status = errno_to_portable(errno);
@ -1238,6 +1255,17 @@ process_remove(u_int32_t id)
debug3("request %u: remove", id); debug3("request %u: remove", id);
logit("remove name \"%s\"", name); logit("remove name \"%s\"", name);
#ifdef WIN32_FIXME
char resolvedname[MAXPATHLEN];
if (realpathWin32i(name, resolvedname))
{
free(name);
name = strdup(resolvedname);
}
#endif
r = unlink(name); r = unlink(name);
status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
send_status(id, status); send_status(id, status);
@ -1261,6 +1289,16 @@ process_mkdir(u_int32_t id)
a.perm & 07777 : 0777; a.perm & 07777 : 0777;
debug3("request %u: mkdir", id); debug3("request %u: mkdir", id);
logit("mkdir name \"%s\" mode 0%o", name, mode); logit("mkdir name \"%s\" mode 0%o", name, mode);
#ifdef WIN32_FIXME
char resolvedname[MAXPATHLEN];
if (realpathWin32i(name, resolvedname))
{
free(name);
name = strdup(resolvedname);
}
#endif
r = mkdir(name, mode); r = mkdir(name, mode);
status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
send_status(id, status); send_status(id, status);
@ -1278,6 +1316,15 @@ process_rmdir(u_int32_t id)
debug3("request %u: rmdir", id); debug3("request %u: rmdir", id);
logit("rmdir name \"%s\"", name); logit("rmdir name \"%s\"", name);
#ifdef WIN32_FIXME
char resolvedname[MAXPATHLEN];
if (realpathWin32i(name, resolvedname))
{
free(name);
name = strdup(resolvedname);
}
#endif
r = rmdir(name); r = rmdir(name);
status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
send_status(id, status); send_status(id, status);
@ -1306,7 +1353,11 @@ process_realpath(u_int32_t id)
#else #else
if ( (path[0] == '\0') || ( strcmp(path, ".")== 0 ) ) { if ( (path[0] == '\0') || ( strcmp(path, ".")== 0 ) ) {
free(path); free(path);
_getcwd(resolvedname, sizeof(resolvedname)); // add an extra / in front of paths to make them sftp spec compliant
// c:/users/test1 will become /c:/users/test1
resolvedname[0] = '/';
_getcwd(&resolvedname[1], sizeof(resolvedname));
// convert back slashes to forward slashes to be compatibale with unix naming // convert back slashes to forward slashes to be compatibale with unix naming
char *cptr = resolvedname; char *cptr = resolvedname;
while (*cptr) { while (*cptr) {
@ -1314,8 +1365,36 @@ process_realpath(u_int32_t id)
*cptr = '/' ; *cptr = '/' ;
cptr++; cptr++;
} }
path = xstrdup(resolvedname); path = strdup(resolvedname);
} }
else {
// see if we were given rooted form /dir or /x:/home/x:/dir
if (path[2] != ':') {
// absolute form given /dir
// no drive letter, so was given in absolute form like cd /debug and we got "/debug" to process
// we have to attach current drive letter in front
resolvedname[0] = '/';
resolvedname[1] = _getdrive() + 'A' - 1; // convert current drive letter to Windows driver Char
resolvedname[2] = ':';
strcpy(&resolvedname[3], path);
free(path);
path = strdup(resolvedname);
}
else {
char *pch = strchr(path, ':');
if (pch != NULL && (pch = strrchr(pch+1, ':')) ) {
if (path[0] == '/') { // it was /x:/home/x:/dir form, use last drive letter part
pch--;
resolvedname[0] = '/';
strcpy(resolvedname+1, pch);
free(path);
path = strdup(resolvedname);
}
}
}
}
#endif #endif
debug3("request %u: realpath", id); debug3("request %u: realpath", id);
@ -1341,7 +1420,19 @@ process_rename(u_int32_t id)
if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 ||
(r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0)
fatal("%s: buffer error: %s", __func__, ssh_err(r)); fatal("%s: buffer error: %s", __func__, ssh_err(r));
#ifdef WIN32_FIXME
char resolvedname[MAXPATHLEN];
if (realpathWin32i(oldpath, resolvedname))
{
free(oldpath);
oldpath = strdup(resolvedname);
}
if (realpathWin32i(newpath, resolvedname))
{
free(newpath);
newpath = strdup(resolvedname);
}
#endif
debug3("request %u: rename", id); debug3("request %u: rename", id);
logit("rename old \"%s\" new \"%s\"", oldpath, newpath); logit("rename old \"%s\" new \"%s\"", oldpath, newpath);
@ -1502,6 +1593,19 @@ process_extended_posix_rename(u_int32_t id)
(r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0)
fatal("%s: buffer error: %s", __func__, ssh_err(r)); fatal("%s: buffer error: %s", __func__, ssh_err(r));
#ifdef WIN32_FIXME
char resolvedname[MAXPATHLEN];
if (realpathWin32i(oldpath, resolvedname))
{
free(oldpath);
oldpath = strdup(resolvedname);
}
if (realpathWin32i(newpath, resolvedname))
{
free(newpath);
newpath = strdup(resolvedname);
}
#endif
debug3("request %u: posix-rename", id); debug3("request %u: posix-rename", id);
logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath);
@ -1707,6 +1811,8 @@ if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 ||
#ifndef WIN32_FIXME #ifndef WIN32_FIXME
r = link(oldpath, newpath); r = link(oldpath, newpath);
status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
#else
status = SSH2_FX_OP_UNSUPPORTED;
#endif #endif
send_status(id, status); send_status(id, status);
free(oldpath); free(oldpath);