]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/winsftp.c
Turn off Windows process ACL restriction by default.
[PuTTY.git] / windows / winsftp.c
1 /*
2  * winsftp.c: the Windows-specific parts of PSFTP and PSCP.
3  */
4
5 #include <assert.h>
6
7 #include "putty.h"
8 #include "psftp.h"
9 #include "ssh.h"
10 #include "int64.h"
11 #include "winsecur.h"
12
13 char *get_ttymode(void *frontend, const char *mode) { return NULL; }
14
15 int get_userpass_input(prompts_t *p, const unsigned char *in, int inlen)
16 {
17     int ret;
18     ret = cmdline_get_passwd_input(p, in, inlen);
19     if (ret == -1)
20         ret = console_get_userpass_input(p, in, inlen);
21     return ret;
22 }
23
24 void platform_get_x11_auth(struct X11Display *display, Conf *conf)
25 {
26     /* Do nothing, therefore no auth. */
27 }
28 const int platform_uses_x11_unix_by_default = TRUE;
29
30 /* ----------------------------------------------------------------------
31  * File access abstraction.
32  */
33
34 /*
35  * Set local current directory. Returns NULL on success, or else an
36  * error message which must be freed after printing.
37  */
38 char *psftp_lcd(char *dir)
39 {
40     char *ret = NULL;
41
42     if (!SetCurrentDirectory(dir)) {
43         LPVOID message;
44         int i;
45         FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
46                       FORMAT_MESSAGE_FROM_SYSTEM |
47                       FORMAT_MESSAGE_IGNORE_INSERTS,
48                       NULL, GetLastError(),
49                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
50                       (LPTSTR)&message, 0, NULL);
51         i = strcspn((char *)message, "\n");
52         ret = dupprintf("%.*s", i, (LPCTSTR)message);
53         LocalFree(message);
54     }
55
56     return ret;
57 }
58
59 /*
60  * Get local current directory. Returns a string which must be
61  * freed.
62  */
63 char *psftp_getcwd(void)
64 {
65     char *ret = snewn(256, char);
66     int len = GetCurrentDirectory(256, ret);
67     if (len > 256)
68         ret = sresize(ret, len, char);
69     GetCurrentDirectory(len, ret);
70     return ret;
71 }
72
73 #define TIME_POSIX_TO_WIN(t, ft) do { \
74     ULARGE_INTEGER uli; \
75     uli.QuadPart = ((ULONGLONG)(t) + 11644473600ull) * 10000000ull; \
76     (ft).dwLowDateTime  = uli.LowPart; \
77     (ft).dwHighDateTime = uli.HighPart; \
78 } while(0)
79 #define TIME_WIN_TO_POSIX(ft, t) do { \
80     ULARGE_INTEGER uli; \
81     uli.LowPart  = (ft).dwLowDateTime; \
82     uli.HighPart = (ft).dwHighDateTime; \
83     uli.QuadPart = uli.QuadPart / 10000000ull - 11644473600ull; \
84     (t) = (unsigned long) uli.QuadPart; \
85 } while(0)
86
87 struct RFile {
88     HANDLE h;
89 };
90
91 RFile *open_existing_file(const char *name, uint64 *size,
92                           unsigned long *mtime, unsigned long *atime,
93                           long *perms)
94 {
95     HANDLE h;
96     RFile *ret;
97
98     h = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL,
99                    OPEN_EXISTING, 0, 0);
100     if (h == INVALID_HANDLE_VALUE)
101         return NULL;
102
103     ret = snew(RFile);
104     ret->h = h;
105
106     if (size) {
107         DWORD lo, hi;
108         lo = GetFileSize(h, &hi);
109         size->lo = lo;
110         size->hi = hi;
111     }
112
113     if (mtime || atime) {
114         FILETIME actime, wrtime;
115         GetFileTime(h, NULL, &actime, &wrtime);
116         if (atime)
117             TIME_WIN_TO_POSIX(actime, *atime);
118         if (mtime)
119             TIME_WIN_TO_POSIX(wrtime, *mtime);
120     }
121
122     if (perms)
123         *perms = -1;
124
125     return ret;
126 }
127
128 int read_from_file(RFile *f, void *buffer, int length)
129 {
130     int ret;
131     DWORD read;
132     ret = ReadFile(f->h, buffer, length, &read, NULL);
133     if (!ret)
134         return -1;                     /* error */
135     else
136         return read;
137 }
138
139 void close_rfile(RFile *f)
140 {
141     CloseHandle(f->h);
142     sfree(f);
143 }
144
145 struct WFile {
146     HANDLE h;
147 };
148
149 WFile *open_new_file(const char *name, long perms)
150 {
151     HANDLE h;
152     WFile *ret;
153
154     h = CreateFile(name, GENERIC_WRITE, 0, NULL,
155                    CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
156     if (h == INVALID_HANDLE_VALUE)
157         return NULL;
158
159     ret = snew(WFile);
160     ret->h = h;
161
162     return ret;
163 }
164
165 WFile *open_existing_wfile(const char *name, uint64 *size)
166 {
167     HANDLE h;
168     WFile *ret;
169
170     h = CreateFile(name, GENERIC_WRITE, FILE_SHARE_READ, NULL,
171                    OPEN_EXISTING, 0, 0);
172     if (h == INVALID_HANDLE_VALUE)
173         return NULL;
174
175     ret = snew(WFile);
176     ret->h = h;
177
178     if (size) {
179         DWORD lo, hi;
180         lo = GetFileSize(h, &hi);
181         size->lo = lo;
182         size->hi = hi;
183     }
184
185     return ret;
186 }
187
188 int write_to_file(WFile *f, void *buffer, int length)
189 {
190     int ret;
191     DWORD written;
192     ret = WriteFile(f->h, buffer, length, &written, NULL);
193     if (!ret)
194         return -1;                     /* error */
195     else
196         return written;
197 }
198
199 void set_file_times(WFile *f, unsigned long mtime, unsigned long atime)
200 {
201     FILETIME actime, wrtime;
202     TIME_POSIX_TO_WIN(atime, actime);
203     TIME_POSIX_TO_WIN(mtime, wrtime);
204     SetFileTime(f->h, NULL, &actime, &wrtime);
205 }
206
207 void close_wfile(WFile *f)
208 {
209     CloseHandle(f->h);
210     sfree(f);
211 }
212
213 /* Seek offset bytes through file, from whence, where whence is
214    FROM_START, FROM_CURRENT, or FROM_END */
215 int seek_file(WFile *f, uint64 offset, int whence)
216 {
217     DWORD movemethod;
218
219     switch (whence) {
220     case FROM_START:
221         movemethod = FILE_BEGIN;
222         break;
223     case FROM_CURRENT:
224         movemethod = FILE_CURRENT;
225         break;
226     case FROM_END:
227         movemethod = FILE_END;
228         break;
229     default:
230         return -1;
231     }
232
233     {
234         LONG lo = offset.lo, hi = offset.hi;
235         SetFilePointer(f->h, lo, &hi, movemethod);
236     }
237     
238     if (GetLastError() != NO_ERROR)
239         return -1;
240     else 
241         return 0;
242 }
243
244 uint64 get_file_posn(WFile *f)
245 {
246     uint64 ret;
247     LONG lo, hi = 0;
248
249     lo = SetFilePointer(f->h, 0L, &hi, FILE_CURRENT);
250     ret.lo = lo;
251     ret.hi = hi;
252
253     return ret;
254 }
255
256 int file_type(const char *name)
257 {
258     DWORD attr;
259     attr = GetFileAttributes(name);
260     /* We know of no `weird' files under Windows. */
261     if (attr == (DWORD)-1)
262         return FILE_TYPE_NONEXISTENT;
263     else if (attr & FILE_ATTRIBUTE_DIRECTORY)
264         return FILE_TYPE_DIRECTORY;
265     else
266         return FILE_TYPE_FILE;
267 }
268
269 struct DirHandle {
270     HANDLE h;
271     char *name;
272 };
273
274 DirHandle *open_directory(const char *name)
275 {
276     HANDLE h;
277     WIN32_FIND_DATA fdat;
278     char *findfile;
279     DirHandle *ret;
280
281     /* Enumerate files in dir `foo'. */
282     findfile = dupcat(name, "/*", NULL);
283     h = FindFirstFile(findfile, &fdat);
284     if (h == INVALID_HANDLE_VALUE)
285         return NULL;
286     sfree(findfile);
287
288     ret = snew(DirHandle);
289     ret->h = h;
290     ret->name = dupstr(fdat.cFileName);
291     return ret;
292 }
293
294 char *read_filename(DirHandle *dir)
295 {
296     do {
297
298         if (!dir->name) {
299             WIN32_FIND_DATA fdat;
300             int ok = FindNextFile(dir->h, &fdat);
301             if (!ok)
302                 return NULL;
303             else
304                 dir->name = dupstr(fdat.cFileName);
305         }
306
307         assert(dir->name);
308         if (dir->name[0] == '.' &&
309             (dir->name[1] == '\0' ||
310              (dir->name[1] == '.' && dir->name[2] == '\0'))) {
311             sfree(dir->name);
312             dir->name = NULL;
313         }
314
315     } while (!dir->name);
316
317     if (dir->name) {
318         char *ret = dir->name;
319         dir->name = NULL;
320         return ret;
321     } else
322         return NULL;
323 }
324
325 void close_directory(DirHandle *dir)
326 {
327     FindClose(dir->h);
328     if (dir->name)
329         sfree(dir->name);
330     sfree(dir);
331 }
332
333 int test_wildcard(const char *name, int cmdline)
334 {
335     HANDLE fh;
336     WIN32_FIND_DATA fdat;
337
338     /* First see if the exact name exists. */
339     if (GetFileAttributes(name) != (DWORD)-1)
340         return WCTYPE_FILENAME;
341
342     /* Otherwise see if a wildcard match finds anything. */
343     fh = FindFirstFile(name, &fdat);
344     if (fh == INVALID_HANDLE_VALUE)
345         return WCTYPE_NONEXISTENT;
346
347     FindClose(fh);
348     return WCTYPE_WILDCARD;
349 }
350
351 struct WildcardMatcher {
352     HANDLE h;
353     char *name;
354     char *srcpath;
355 };
356
357 char *stripslashes(const char *str, int local)
358 {
359     char *p;
360
361     /*
362      * On Windows, \ / : are all path component separators.
363      */
364
365     if (local) {
366         p = strchr(str, ':');
367         if (p) str = p+1;
368     }
369
370     p = strrchr(str, '/');
371     if (p) str = p+1;
372
373     if (local) {
374         p = strrchr(str, '\\');
375         if (p) str = p+1;
376     }
377
378     return (char *)str;
379 }
380
381 WildcardMatcher *begin_wildcard_matching(const char *name)
382 {
383     HANDLE h;
384     WIN32_FIND_DATA fdat;
385     WildcardMatcher *ret;
386     char *last;
387
388     h = FindFirstFile(name, &fdat);
389     if (h == INVALID_HANDLE_VALUE)
390         return NULL;
391
392     ret = snew(WildcardMatcher);
393     ret->h = h;
394     ret->srcpath = dupstr(name);
395     last = stripslashes(ret->srcpath, 1);
396     *last = '\0';
397     if (fdat.cFileName[0] == '.' &&
398         (fdat.cFileName[1] == '\0' ||
399          (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0')))
400         ret->name = NULL;
401     else
402         ret->name = dupcat(ret->srcpath, fdat.cFileName, NULL);
403
404     return ret;
405 }
406
407 char *wildcard_get_filename(WildcardMatcher *dir)
408 {
409     while (!dir->name) {
410         WIN32_FIND_DATA fdat;
411         int ok = FindNextFile(dir->h, &fdat);
412
413         if (!ok)
414             return NULL;
415
416         if (fdat.cFileName[0] == '.' &&
417             (fdat.cFileName[1] == '\0' ||
418              (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0')))
419             dir->name = NULL;
420         else
421             dir->name = dupcat(dir->srcpath, fdat.cFileName, NULL);
422     }
423
424     if (dir->name) {
425         char *ret = dir->name;
426         dir->name = NULL;
427         return ret;
428     } else
429         return NULL;
430 }
431
432 void finish_wildcard_matching(WildcardMatcher *dir)
433 {
434     FindClose(dir->h);
435     if (dir->name)
436         sfree(dir->name);
437     sfree(dir->srcpath);
438     sfree(dir);
439 }
440
441 int vet_filename(const char *name)
442 {
443     if (strchr(name, '/') || strchr(name, '\\') || strchr(name, ':'))
444         return FALSE;
445
446     if (!name[strspn(name, ".")])      /* entirely composed of dots */
447         return FALSE;
448
449     return TRUE;
450 }
451
452 int create_directory(const char *name)
453 {
454     return CreateDirectory(name, NULL) != 0;
455 }
456
457 char *dir_file_cat(const char *dir, const char *file)
458 {
459     return dupcat(dir, "\\", file, NULL);
460 }
461
462 /* ----------------------------------------------------------------------
463  * Platform-specific network handling.
464  */
465
466 /*
467  * Be told what socket we're supposed to be using.
468  */
469 static SOCKET sftp_ssh_socket = INVALID_SOCKET;
470 static HANDLE netevent = INVALID_HANDLE_VALUE;
471 char *do_select(SOCKET skt, int startup)
472 {
473     int events;
474     if (startup)
475         sftp_ssh_socket = skt;
476     else
477         sftp_ssh_socket = INVALID_SOCKET;
478
479     if (p_WSAEventSelect) {
480         if (startup) {
481             events = (FD_CONNECT | FD_READ | FD_WRITE |
482                       FD_OOB | FD_CLOSE | FD_ACCEPT);
483             netevent = CreateEvent(NULL, FALSE, FALSE, NULL);
484         } else {
485             events = 0;
486         }
487         if (p_WSAEventSelect(skt, netevent, events) == SOCKET_ERROR) {
488             switch (p_WSAGetLastError()) {
489               case WSAENETDOWN:
490                 return "Network is down";
491               default:
492                 return "WSAEventSelect(): unknown error";
493             }
494         }
495     }
496     return NULL;
497 }
498 extern int select_result(WPARAM, LPARAM);
499
500 int do_eventsel_loop(HANDLE other_event)
501 {
502     int n, nhandles, nallhandles, netindex, otherindex;
503     unsigned long next, then;
504     long ticks;
505     HANDLE *handles;
506     SOCKET *sklist;
507     int skcount;
508     unsigned long now = GETTICKCOUNT();
509
510     if (toplevel_callback_pending()) {
511         ticks = 0;
512         next = now;
513     } else if (run_timers(now, &next)) {
514         then = now;
515         now = GETTICKCOUNT();
516         if (now - then > next - then)
517             ticks = 0;
518         else
519             ticks = next - now;
520     } else {
521         ticks = INFINITE;
522         /* no need to initialise next here because we can never get
523          * WAIT_TIMEOUT */
524     }
525
526     handles = handle_get_events(&nhandles);
527     handles = sresize(handles, nhandles+2, HANDLE);
528     nallhandles = nhandles;
529
530     if (netevent != INVALID_HANDLE_VALUE)
531         handles[netindex = nallhandles++] = netevent;
532     else
533         netindex = -1;
534     if (other_event != INVALID_HANDLE_VALUE)
535         handles[otherindex = nallhandles++] = other_event;
536     else
537         otherindex = -1;
538
539     n = WaitForMultipleObjects(nallhandles, handles, FALSE, ticks);
540
541     if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) {
542         handle_got_event(handles[n - WAIT_OBJECT_0]);
543     } else if (netindex >= 0 && n == WAIT_OBJECT_0 + netindex) {
544         WSANETWORKEVENTS things;
545         SOCKET socket;
546         extern SOCKET first_socket(int *), next_socket(int *);
547         extern int select_result(WPARAM, LPARAM);
548         int i, socketstate;
549
550         /*
551          * We must not call select_result() for any socket
552          * until we have finished enumerating within the
553          * tree. This is because select_result() may close
554          * the socket and modify the tree.
555          */
556         /* Count the active sockets. */
557         i = 0;
558         for (socket = first_socket(&socketstate);
559              socket != INVALID_SOCKET;
560              socket = next_socket(&socketstate)) i++;
561
562         /* Expand the buffer if necessary. */
563         sklist = snewn(i, SOCKET);
564
565         /* Retrieve the sockets into sklist. */
566         skcount = 0;
567         for (socket = first_socket(&socketstate);
568              socket != INVALID_SOCKET;
569              socket = next_socket(&socketstate)) {
570             sklist[skcount++] = socket;
571         }
572
573         /* Now we're done enumerating; go through the list. */
574         for (i = 0; i < skcount; i++) {
575             WPARAM wp;
576             socket = sklist[i];
577             wp = (WPARAM) socket;
578             if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) {
579                 static const struct { int bit, mask; } eventtypes[] = {
580                     {FD_CONNECT_BIT, FD_CONNECT},
581                     {FD_READ_BIT, FD_READ},
582                     {FD_CLOSE_BIT, FD_CLOSE},
583                     {FD_OOB_BIT, FD_OOB},
584                     {FD_WRITE_BIT, FD_WRITE},
585                     {FD_ACCEPT_BIT, FD_ACCEPT},
586                 };
587                 int e;
588
589                 noise_ultralight(socket);
590                 noise_ultralight(things.lNetworkEvents);
591
592                 for (e = 0; e < lenof(eventtypes); e++)
593                     if (things.lNetworkEvents & eventtypes[e].mask) {
594                         LPARAM lp;
595                         int err = things.iErrorCode[eventtypes[e].bit];
596                         lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err);
597                         select_result(wp, lp);
598                     }
599             }
600         }
601
602         sfree(sklist);
603     }
604
605     sfree(handles);
606
607     run_toplevel_callbacks();
608
609     if (n == WAIT_TIMEOUT) {
610         now = next;
611     } else {
612         now = GETTICKCOUNT();
613     }
614
615     if (otherindex >= 0 && n == WAIT_OBJECT_0 + otherindex)
616         return 1;
617
618     return 0;
619 }
620
621 /*
622  * Wait for some network data and process it.
623  *
624  * We have two variants of this function. One uses select() so that
625  * it's compatible with WinSock 1. The other uses WSAEventSelect
626  * and MsgWaitForMultipleObjects, so that we can consistently use
627  * WSAEventSelect throughout; this enables us to also implement
628  * ssh_sftp_get_cmdline() using a parallel mechanism.
629  */
630 int ssh_sftp_loop_iteration(void)
631 {
632     if (p_WSAEventSelect == NULL) {
633         fd_set readfds;
634         int ret;
635         unsigned long now = GETTICKCOUNT(), then;
636
637         if (sftp_ssh_socket == INVALID_SOCKET)
638             return -1;                 /* doom */
639
640         if (socket_writable(sftp_ssh_socket))
641             select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_WRITE);
642
643         do {
644             unsigned long next;
645             long ticks;
646             struct timeval tv, *ptv;
647
648             if (run_timers(now, &next)) {
649                 then = now;
650                 now = GETTICKCOUNT();
651                 if (now - then > next - then)
652                     ticks = 0;
653                 else
654                     ticks = next - now;
655                 tv.tv_sec = ticks / 1000;
656                 tv.tv_usec = ticks % 1000 * 1000;
657                 ptv = &tv;
658             } else {
659                 ptv = NULL;
660             }
661
662             FD_ZERO(&readfds);
663             FD_SET(sftp_ssh_socket, &readfds);
664             ret = p_select(1, &readfds, NULL, NULL, ptv);
665
666             if (ret < 0)
667                 return -1;                     /* doom */
668             else if (ret == 0)
669                 now = next;
670             else
671                 now = GETTICKCOUNT();
672
673         } while (ret == 0);
674
675         select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ);
676
677         return 0;
678     } else {
679         return do_eventsel_loop(INVALID_HANDLE_VALUE);
680     }
681 }
682
683 /*
684  * Read a command line from standard input.
685  * 
686  * In the presence of WinSock 2, we can use WSAEventSelect to
687  * mediate between the socket and stdin, meaning we can send
688  * keepalives and respond to server events even while waiting at
689  * the PSFTP command prompt. Without WS2, we fall back to a simple
690  * fgets.
691  */
692 struct command_read_ctx {
693     HANDLE event;
694     char *line;
695 };
696
697 static DWORD WINAPI command_read_thread(void *param)
698 {
699     struct command_read_ctx *ctx = (struct command_read_ctx *) param;
700
701     ctx->line = fgetline(stdin);
702
703     SetEvent(ctx->event);
704
705     return 0;
706 }
707
708 char *ssh_sftp_get_cmdline(const char *prompt, int no_fds_ok)
709 {
710     int ret;
711     struct command_read_ctx actx, *ctx = &actx;
712     DWORD threadid;
713     HANDLE hThread;
714
715     fputs(prompt, stdout);
716     fflush(stdout);
717
718     if ((sftp_ssh_socket == INVALID_SOCKET && no_fds_ok) ||
719         p_WSAEventSelect == NULL) {
720         return fgetline(stdin);        /* very simple */
721     }
722
723     /*
724      * Create a second thread to read from stdin. Process network
725      * and timing events until it terminates.
726      */
727     ctx->event = CreateEvent(NULL, FALSE, FALSE, NULL);
728     ctx->line = NULL;
729
730     hThread = CreateThread(NULL, 0, command_read_thread, ctx, 0, &threadid);
731     if (!hThread) {
732         CloseHandle(ctx->event);
733         fprintf(stderr, "Unable to create command input thread\n");
734         cleanup_exit(1);
735     }
736
737     do {
738         ret = do_eventsel_loop(ctx->event);
739
740         /* Error return can only occur if netevent==NULL, and it ain't. */
741         assert(ret >= 0);
742     } while (ret == 0);
743
744     CloseHandle(hThread);
745     CloseHandle(ctx->event);
746
747     return ctx->line;
748 }
749
750 void platform_psftp_post_option_setup(void)
751 {
752 }
753
754 /* ----------------------------------------------------------------------
755  * Main program. Parse arguments etc.
756  */
757 int main(int argc, char *argv[])
758 {
759     int ret;
760
761     dll_hijacking_protection();
762
763     ret = psftp_main(argc, argv);
764
765     return ret;
766 }