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