Skip to content

Commit 4857c27

Browse files
committed
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.
1 parent 102d1ed commit 4857c27

File tree

2 files changed

+174
-11
lines changed

2 files changed

+174
-11
lines changed

openbsd-compat/realpath.c

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ char *realpathWin32(const char *path, char resolved[PATH_MAX])
210210
char realpath[PATH_MAX];
211211
char * pch;
212212

213-
path_len = strlcpy(realpath, path, sizeof(realpath));
213+
path_len = strlcpy(realpath, path+1, sizeof(realpath));
214214

215215
char * pchMac;
216216
pchMac = strstr (realpath, "._");
@@ -247,12 +247,69 @@ char *realpathWin32(const char *path, char resolved[PATH_MAX])
247247

248248
if (realpath[1] == ':' && realpath[2] == 0)
249249
{
250-
realpath[2] = '\\';
250+
realpath[2] = '/';
251251
realpath[3] = 0;
252252
}
253-
254-
strncpy (resolved, realpath, sizeof(realpath));
253+
254+
resolved[0] = *path; // will be our first slash in /x:/users/test1 format
255+
strncpy (resolved+1, realpath, sizeof(realpath));
255256
return resolved;
256257
}
257258

259+
char *realpathWin32i(const char *path, char resolved[PATH_MAX])
260+
{
261+
size_t path_len;
262+
unsigned int lastSlash;
263+
char realpath[PATH_MAX];
264+
char * pch;
265+
266+
if (path[0] != '/') {
267+
// absolute form x:/abc/def given, no first slash to take out
268+
path_len = strlcpy(realpath, path, sizeof(realpath));
269+
}
270+
else
271+
path_len = strlcpy(realpath, path + 1, sizeof(realpath));
272+
273+
char * pchMac;
274+
pchMac = strstr(realpath, "._");
275+
if (pchMac != NULL)
276+
{
277+
pchMac[0] = '\0';
278+
pchMac++;
279+
pchMac++;
280+
strcat(realpath, pchMac);
281+
}
282+
283+
pch = strrchr(realpath, '/');
284+
lastSlash = pch - realpath + 1;
285+
if (path_len == lastSlash)
286+
{
287+
realpath[lastSlash - 1] = '\0';
288+
}
289+
290+
pch = strrchr(realpath, '.');
291+
if (pch != NULL)
292+
{
293+
if (realpath[pch - realpath - 1] == '.')
294+
{
295+
realpath[pch - realpath - 2] = '\0';
296+
pch = strrchr(realpath, '/');
297+
if (pch != NULL)
298+
realpath[pch - realpath] = '\0';
299+
}
300+
}
301+
302+
/*
303+
* Store terminating slash in 'X:/' on Windows.
304+
*/
305+
306+
if (realpath[1] == ':' && realpath[2] == 0)
307+
{
308+
realpath[2] = '/';
309+
realpath[3] = 0;
310+
}
311+
312+
strncpy(resolved, realpath, sizeof(realpath));
313+
return resolved;
314+
}
258315
#endif /* WIN32_FIXME */

sftp-server.c

Lines changed: 113 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,15 @@ process_open(u_int32_t id)
772772
debug3("request %u: open flags %d", id, pflags);
773773
flags = flags_from_portable(pflags);
774774
mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a.perm : 0666;
775+
#ifdef WIN32_FIXME
776+
char resolvedname[MAXPATHLEN];
777+
if (realpathWin32i(name, resolvedname))
778+
{
779+
free(name);
780+
name = strdup(resolvedname);
781+
}
782+
#endif
783+
775784
logit("open \"%s\" flags %s mode 0%o",
776785
name, string_from_portable(pflags), mode);
777786
if (readonly &&
@@ -915,10 +924,9 @@ process_do_stat(u_int32_t id, int do_lstat)
915924
if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0)
916925
fatal("%s: buffer error: %s", __func__, ssh_err(r));
917926

918-
if (realpathWin32(name, resolvedname))
927+
if (realpathWin32i(name, resolvedname))
919928
{
920-
free(name);
921-
929+
free(name);
922930
name = strdup(resolvedname);
923931
}
924932

@@ -1130,10 +1138,19 @@ process_opendir(u_int32_t id)
11301138

11311139
if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0)
11321140
fatal("%s: buffer error: %s", __func__, ssh_err(r));
1133-
1141+
1142+
#ifdef WIN32_FIXME
1143+
char resolvedname[MAXPATHLEN];
1144+
if (realpathWin32i(path, resolvedname))
1145+
{
1146+
free(path);
1147+
path = strdup(resolvedname);
1148+
}
1149+
#endif
11341150

11351151
debug3("request %u: opendir", id);
11361152
logit("opendir \"%s\"", path);
1153+
11371154
dirp = opendir(path);
11381155
if (dirp == NULL) {
11391156
status = errno_to_portable(errno);
@@ -1238,6 +1255,17 @@ process_remove(u_int32_t id)
12381255

12391256
debug3("request %u: remove", id);
12401257
logit("remove name \"%s\"", name);
1258+
1259+
#ifdef WIN32_FIXME
1260+
char resolvedname[MAXPATHLEN];
1261+
if (realpathWin32i(name, resolvedname))
1262+
{
1263+
free(name);
1264+
1265+
name = strdup(resolvedname);
1266+
}
1267+
#endif
1268+
12411269
r = unlink(name);
12421270
status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
12431271
send_status(id, status);
@@ -1261,6 +1289,16 @@ process_mkdir(u_int32_t id)
12611289
a.perm & 07777 : 0777;
12621290
debug3("request %u: mkdir", id);
12631291
logit("mkdir name \"%s\" mode 0%o", name, mode);
1292+
1293+
#ifdef WIN32_FIXME
1294+
char resolvedname[MAXPATHLEN];
1295+
if (realpathWin32i(name, resolvedname))
1296+
{
1297+
free(name);
1298+
1299+
name = strdup(resolvedname);
1300+
}
1301+
#endif
12641302
r = mkdir(name, mode);
12651303
status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
12661304
send_status(id, status);
@@ -1278,6 +1316,15 @@ process_rmdir(u_int32_t id)
12781316

12791317
debug3("request %u: rmdir", id);
12801318
logit("rmdir name \"%s\"", name);
1319+
#ifdef WIN32_FIXME
1320+
char resolvedname[MAXPATHLEN];
1321+
if (realpathWin32i(name, resolvedname))
1322+
{
1323+
free(name);
1324+
1325+
name = strdup(resolvedname);
1326+
}
1327+
#endif
12811328
r = rmdir(name);
12821329
status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
12831330
send_status(id, status);
@@ -1306,16 +1353,48 @@ process_realpath(u_int32_t id)
13061353
#else
13071354
if ( (path[0] == '\0') || ( strcmp(path, ".")== 0 ) ) {
13081355
free(path);
1309-
_getcwd(resolvedname, sizeof(resolvedname));
1356+
// add an extra / in front of paths to make them sftp spec compliant
1357+
// c:/users/test1 will become /c:/users/test1
1358+
resolvedname[0] = '/';
1359+
1360+
_getcwd(&resolvedname[1], sizeof(resolvedname));
13101361
// convert back slashes to forward slashes to be compatibale with unix naming
13111362
char *cptr = resolvedname;
13121363
while (*cptr) {
13131364
if (*cptr == '\\')
13141365
*cptr = '/' ;
13151366
cptr++;
13161367
}
1317-
path = xstrdup(resolvedname);
1368+
path = strdup(resolvedname);
13181369
}
1370+
else {
1371+
// see if we were given rooted form /dir or /x:/home/x:/dir
1372+
if (path[2] != ':') {
1373+
// absolute form given /dir
1374+
// no drive letter, so was given in absolute form like cd /debug and we got "/debug" to process
1375+
// we have to attach current drive letter in front
1376+
resolvedname[0] = '/';
1377+
resolvedname[1] = _getdrive() + 'A' - 1; // convert current drive letter to Windows driver Char
1378+
resolvedname[2] = ':';
1379+
strcpy(&resolvedname[3], path);
1380+
free(path);
1381+
path = strdup(resolvedname);
1382+
}
1383+
else {
1384+
char *pch = strchr(path, ':');
1385+
if (pch != NULL && (pch = strrchr(pch+1, ':')) ) {
1386+
if (path[0] == '/') { // it was /x:/home/x:/dir form, use last drive letter part
1387+
pch--;
1388+
resolvedname[0] = '/';
1389+
strcpy(resolvedname+1, pch);
1390+
free(path);
1391+
path = strdup(resolvedname);
1392+
}
1393+
}
1394+
}
1395+
1396+
}
1397+
13191398
#endif
13201399

13211400
debug3("request %u: realpath", id);
@@ -1341,7 +1420,19 @@ process_rename(u_int32_t id)
13411420
if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 ||
13421421
(r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0)
13431422
fatal("%s: buffer error: %s", __func__, ssh_err(r));
1344-
1423+
#ifdef WIN32_FIXME
1424+
char resolvedname[MAXPATHLEN];
1425+
if (realpathWin32i(oldpath, resolvedname))
1426+
{
1427+
free(oldpath);
1428+
oldpath = strdup(resolvedname);
1429+
}
1430+
if (realpathWin32i(newpath, resolvedname))
1431+
{
1432+
free(newpath);
1433+
newpath = strdup(resolvedname);
1434+
}
1435+
#endif
13451436

13461437
debug3("request %u: rename", id);
13471438
logit("rename old \"%s\" new \"%s\"", oldpath, newpath);
@@ -1502,6 +1593,19 @@ process_extended_posix_rename(u_int32_t id)
15021593
(r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0)
15031594
fatal("%s: buffer error: %s", __func__, ssh_err(r));
15041595

1596+
#ifdef WIN32_FIXME
1597+
char resolvedname[MAXPATHLEN];
1598+
if (realpathWin32i(oldpath, resolvedname))
1599+
{
1600+
free(oldpath);
1601+
oldpath = strdup(resolvedname);
1602+
}
1603+
if (realpathWin32i(newpath, resolvedname))
1604+
{
1605+
free(newpath);
1606+
newpath = strdup(resolvedname);
1607+
}
1608+
#endif
15051609

15061610
debug3("request %u: posix-rename", id);
15071611
logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath);
@@ -1707,6 +1811,8 @@ if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 ||
17071811
#ifndef WIN32_FIXME
17081812
r = link(oldpath, newpath);
17091813
status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1814+
#else
1815+
status = SSH2_FX_OP_UNSUPPORTED;
17101816
#endif
17111817
send_status(id, status);
17121818
free(oldpath);

0 commit comments

Comments
 (0)