]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/winnps.c
Add support for Windows named pipes.
[PuTTY.git] / windows / winnps.c
1 /*
2  * Windows support module which deals with being a named-pipe server.
3  */
4
5 #include <stdio.h>
6 #include <assert.h>
7
8 #define DEFINE_PLUG_METHOD_MACROS
9 #include "tree234.h"
10 #include "putty.h"
11 #include "network.h"
12 #include "proxy.h"
13 #include "ssh.h"
14
15 #if !defined NO_SECURITY
16
17 #include <aclapi.h>
18
19 Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug,
20                           int overlapped);
21
22 typedef struct Socket_named_pipe_server_tag *Named_Pipe_Server_Socket;
23 struct Socket_named_pipe_server_tag {
24     const struct socket_function_table *fn;
25     /* the above variable absolutely *must* be the first in this structure */
26
27     /* Parameters for (repeated) creation of named pipe objects */
28     PSECURITY_DESCRIPTOR psd;
29     PSID networksid;
30     PACL acl;
31     char *pipename;
32
33     /* The current named pipe object + attempt to connect to it */
34     HANDLE pipehandle;
35     OVERLAPPED connect_ovl;
36
37     /* PuTTY Socket machinery */
38     Plug plug;
39     char *error;
40     void *privptr;
41 };
42
43 static Plug sk_namedpipeserver_plug(Socket s, Plug p)
44 {
45     Named_Pipe_Server_Socket ps = (Named_Pipe_Server_Socket) s;
46     Plug ret = ps->plug;
47     if (p)
48         ps->plug = p;
49     return ret;
50 }
51
52 static void sk_namedpipeserver_close(Socket s)
53 {
54     Named_Pipe_Server_Socket ps = (Named_Pipe_Server_Socket) s;
55
56     CloseHandle(ps->pipehandle);
57     CloseHandle(ps->connect_ovl.hEvent);
58     sfree(ps->error);
59     sfree(ps->pipename);
60     if (ps->networksid)
61         LocalFree(ps->networksid);
62     if (ps->acl)
63         LocalFree(ps->acl);
64     if (ps->psd)
65         LocalFree(ps->psd);
66     sfree(ps);
67 }
68
69 static void sk_namedpipeserver_set_private_ptr(Socket s, void *ptr)
70 {
71     Named_Pipe_Server_Socket ps = (Named_Pipe_Server_Socket) s;
72     ps->privptr = ptr;
73 }
74
75 static void *sk_namedpipeserver_get_private_ptr(Socket s)
76 {
77     Named_Pipe_Server_Socket ps = (Named_Pipe_Server_Socket) s;
78     return ps->privptr;
79 }
80
81 static const char *sk_namedpipeserver_socket_error(Socket s)
82 {
83     Named_Pipe_Server_Socket ps = (Named_Pipe_Server_Socket) s;
84     return ps->error;
85 }
86
87 static int create_named_pipe(Named_Pipe_Server_Socket ps, int first_instance)
88 {
89     SECURITY_ATTRIBUTES sa;
90
91     memset(&sa, 0, sizeof(sa));
92     sa.nLength = sizeof(sa);
93     sa.lpSecurityDescriptor = ps->psd;
94     sa.bInheritHandle = FALSE;
95
96     ps->pipehandle = CreateNamedPipe
97         (/* lpName */
98          ps->pipename,
99
100          /* dwOpenMode */
101          PIPE_ACCESS_DUPLEX |
102          FILE_FLAG_OVERLAPPED |
103          (first_instance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0),
104
105          /* dwPipeMode */
106          PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT
107 #ifdef PIPE_REJECT_REMOTE_CLIENTS
108          | PIPE_REJECT_REMOTE_CLIENTS
109 #endif
110          ,
111
112          /* nMaxInstances */
113          PIPE_UNLIMITED_INSTANCES,
114
115          /* nOutBufferSize, nInBufferSize */
116          4096, 4096,     /* FIXME: think harder about buffer sizes? */
117
118          /* nDefaultTimeOut */
119          0 /* default timeout */,
120
121          /* lpSecurityAttributes */
122          &sa);
123
124     return ps->pipehandle != INVALID_HANDLE_VALUE;
125 }
126
127 static Socket named_pipe_accept(accept_ctx_t ctx, Plug plug)
128 {
129     HANDLE conn = (HANDLE)ctx.p;
130
131     return make_handle_socket(conn, conn, plug, TRUE);
132 }
133
134 static void named_pipe_accept_loop(Named_Pipe_Server_Socket ps,
135                                    int got_one_already)
136 {
137     while (1) {
138         int error;
139         char *errmsg;
140
141         if (got_one_already) {
142             /* If we were called with a connection already waiting,
143              * skip this step. */
144             got_one_already = FALSE;
145             error = 0;
146         } else {
147             /*
148              * Call ConnectNamedPipe, which might succeed or might
149              * tell us that an overlapped operation is in progress and
150              * we should wait for our event object.
151              */
152             if (ConnectNamedPipe(ps->pipehandle, &ps->connect_ovl))
153                 error = 0;
154             else
155                 error = GetLastError();
156
157             if (error == ERROR_IO_PENDING)
158                 return;
159         }
160
161         if (error == 0 || error == ERROR_PIPE_CONNECTED) {
162             /*
163              * We've successfully retrieved an incoming connection, so
164              * ps->pipehandle now refers to that connection. So
165              * convert that handle into a separate connection-type
166              * Socket, and create a fresh one to be the new listening
167              * pipe.
168              */
169             HANDLE conn = ps->pipehandle;
170             accept_ctx_t actx;
171
172             actx.p = (void *)conn;
173             if (plug_accepting(ps->plug, named_pipe_accept, actx)) {
174                 /*
175                  * If the plug didn't want the connection, might as
176                  * well close this handle.
177                  */
178                 CloseHandle(conn);
179             }
180
181             if (!create_named_pipe(ps, FALSE)) {
182                 error = GetLastError();
183             } else {
184                 /*
185                  * Go round again to see if more connections can be
186                  * got, or to begin waiting on the event object.
187                  */
188                 continue;
189             }
190         }
191
192         errmsg = dupprintf("Error while listening to named pipe: %s",
193                            win_strerror(error));
194         plug_log(ps->plug, 1, NULL /* FIXME: appropriate kind of sockaddr */, 0,
195                  errmsg, error);
196         sfree(errmsg);
197         break;
198     }
199 }
200
201 static void named_pipe_connect_callback(void *vps)
202 {
203     Named_Pipe_Server_Socket ps = (Named_Pipe_Server_Socket)vps;
204     named_pipe_accept_loop(ps, TRUE);
205 }
206
207 Socket new_named_pipe_listener(const char *pipename, Plug plug)
208 {
209     /*
210      * This socket type is only used for listening, so it should never
211      * be asked to write or flush or set_frozen.
212      */
213     static const struct socket_function_table socket_fn_table = {
214         sk_namedpipeserver_plug,
215         sk_namedpipeserver_close,
216         NULL /* write */,
217         NULL /* write_oob */,
218         NULL /* write_eof */,
219         NULL /* flush */,
220         sk_namedpipeserver_set_private_ptr,
221         sk_namedpipeserver_get_private_ptr,
222         NULL /* set_frozen */,
223         sk_namedpipeserver_socket_error
224     };
225
226     Named_Pipe_Server_Socket ret;
227     SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY;
228     EXPLICIT_ACCESS ea[2];
229
230     ret = snew(struct Socket_named_pipe_server_tag);
231     ret->fn = &socket_fn_table;
232     ret->plug = plug;
233     ret->error = NULL;
234     ret->privptr = NULL;
235     ret->psd = NULL;
236     ret->pipename = dupstr(pipename);
237     ret->networksid = NULL;
238     ret->acl = NULL;
239
240     assert(strncmp(pipename, "\\\\.\\pipe\\", 9) == 0);
241     assert(strchr(pipename + 9, '\\') == NULL);
242
243     if (!AllocateAndInitializeSid(&nt_auth, 1, SECURITY_NETWORK_RID,
244                                   0, 0, 0, 0, 0, 0, 0, &ret->networksid)) {
245         ret->error = dupprintf("unable to construct SID for rejecting "
246                                "remote pipe connections: %s",
247                                win_strerror(GetLastError()));
248         goto cleanup;
249     }
250
251     memset(ea, 0, sizeof(ea));
252     ea[0].grfAccessPermissions = GENERIC_READ | GENERIC_WRITE;
253     ea[0].grfAccessMode = GRANT_ACCESS;
254     ea[0].grfInheritance = NO_INHERITANCE;
255     ea[0].Trustee.TrusteeForm = TRUSTEE_IS_NAME;
256     ea[0].Trustee.ptstrName = "CURRENT_USER";
257     ea[1].grfAccessPermissions = GENERIC_READ | GENERIC_WRITE;
258     ea[1].grfAccessMode = REVOKE_ACCESS;
259     ea[1].grfInheritance = NO_INHERITANCE;
260     ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
261     ea[1].Trustee.ptstrName = (LPTSTR)ret->networksid;
262
263     if (SetEntriesInAcl(2, ea, NULL, &ret->acl) != ERROR_SUCCESS) {
264         ret->error = dupprintf("unable to construct ACL: %s",
265                                win_strerror(GetLastError()));
266         goto cleanup;
267     }
268
269     ret->psd = (PSECURITY_DESCRIPTOR)
270         LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
271     if (!ret->psd) {
272         ret->error = dupprintf("unable to allocate security descriptor: %s",
273                                win_strerror(GetLastError()));
274         goto cleanup;
275     }
276
277     if (!InitializeSecurityDescriptor(ret->psd,SECURITY_DESCRIPTOR_REVISION)) {
278         ret->error = dupprintf("unable to initialise security descriptor: %s",
279                                win_strerror(GetLastError()));
280         goto cleanup;
281     }
282
283     if (!SetSecurityDescriptorDacl(ret->psd, TRUE, ret->acl, FALSE)) {
284         ret->error = dupprintf("unable to set DACL in security descriptor: %s",
285                                win_strerror(GetLastError()));
286         goto cleanup;
287     }
288
289     if (!create_named_pipe(ret, TRUE)) {
290         ret->error = dupprintf("unable to create named pipe '%s': %s",
291                                pipename, win_strerror(GetLastError()));
292         goto cleanup;
293     }
294
295     memset(&ret->connect_ovl, 0, sizeof(ret->connect_ovl));
296     ret->connect_ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
297     handle_add_foreign_event(ret->connect_ovl.hEvent,
298                              named_pipe_connect_callback, ret);
299     named_pipe_accept_loop(ret, FALSE);
300
301   cleanup:
302     return (Socket) ret;
303 }
304
305 #endif /* !defined NO_SECURITY */