s4-smb: Migrate named_pipe_server to tsocket.
[ira/wip.git] / source4 / smbd / service_named_pipe.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    helper functions for NAMED PIPE servers
5
6    Copyright (C) Stefan (metze) Metzmacher      2008
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include <tevent.h>
24 #include "smbd/service.h"
25 #include "param/param.h"
26 #include "auth/session.h"
27 #include "auth/auth_sam_reply.h"
28 #include "lib/socket/socket.h"
29 #include "lib/tsocket/tsocket.h"
30 #include "libcli/util/tstream.h"
31 #include "librpc/gen_ndr/ndr_named_pipe_auth.h"
32 #include "system/passwd.h"
33 #include "system/network.h"
34 #include "libcli/raw/smb.h"
35 #include "auth/credentials/credentials.h"
36 #include "auth/credentials/credentials_krb5.h"
37
38 struct named_pipe_socket {
39         const char *pipe_name;
40         const char *pipe_path;
41         const struct stream_server_ops *ops;
42         void *private_data;
43 };
44
45 struct named_pipe_connection {
46         struct stream_connection *connection;
47         const struct named_pipe_socket *pipe_sock;
48         struct tstream_context *tstream;
49 };
50
51 static void named_pipe_terminate_connection(struct named_pipe_connection *pipe_conn, const char *reason)
52 {
53         stream_terminate_connection(pipe_conn->connection, reason);
54 }
55
56 static NTSTATUS named_pipe_full_request(void *private_data, DATA_BLOB blob, size_t *size)
57 {
58         if (blob.length < 8) {
59                 return STATUS_MORE_ENTRIES;
60         }
61
62         if (memcmp(NAMED_PIPE_AUTH_MAGIC, &blob.data[4], 4) != 0) {
63                 DEBUG(0,("named_pipe_full_request: wrong protocol\n"));
64                 *size = blob.length;
65                 /* the error will be handled in named_pipe_recv_auth_request */
66                 return NT_STATUS_OK;
67         }
68
69         *size = 4 + RIVAL(blob.data, 0);
70         if (*size > blob.length) {
71                 return STATUS_MORE_ENTRIES;
72         }
73
74         return NT_STATUS_OK;
75 }
76
77 static void named_pipe_auth_request(struct tevent_req *subreq);
78
79 static void named_pipe_accept(struct stream_connection *conn)
80 {
81         struct named_pipe_socket *pipe_sock = talloc_get_type(conn->private_data,
82                                                 struct named_pipe_socket);
83         struct named_pipe_connection *pipe_conn;
84         struct tevent_req *subreq;
85         int rc, fd;
86
87         pipe_conn = talloc_zero(conn, struct named_pipe_connection);
88         if (pipe_conn == NULL) {
89                 stream_terminate_connection(conn,
90                                 "named_pipe_accept: out of memory");
91                 return;
92         }
93
94         TALLOC_FREE(conn->event.fde);
95
96         /*
97          * We have to duplicate the fd, cause it gets closed when the tstream
98          * is freed and you shouldn't work a fd the tstream is based on.
99          */
100         fd = dup(socket_get_fd(conn->socket));
101         if (fd == -1) {
102                 char *reason;
103
104                 reason = talloc_asprintf(conn,
105                                          "named_pipe_accept: failed to duplicate the file descriptor - %s",
106                                          strerror(errno));
107                 if (reason == NULL) {
108                         reason = strerror(errno);
109                 }
110                 stream_terminate_connection(conn, reason);
111         }
112         rc = tstream_bsd_existing_socket(pipe_conn,
113                                          fd,
114                                          &pipe_conn->tstream);
115         if (rc < 0) {
116                 stream_terminate_connection(conn,
117                                 "named_pipe_accept: out of memory");
118                 return;
119         }
120
121         pipe_conn->connection = conn;
122         pipe_conn->pipe_sock = pipe_sock;
123         conn->private_data = pipe_conn;
124
125         /*
126          * The named pipe pdu's have the length as 8 byte (initial_read_size),
127          * named_pipe_full_request provides the pdu length then.
128          */
129         subreq = tstream_read_pdu_blob_send(pipe_conn,
130                                             pipe_conn->connection->event.ctx,
131                                             pipe_conn->tstream,
132                                             8, /* initial_read_size */
133                                             named_pipe_full_request,
134                                             pipe_conn);
135         if (subreq == NULL) {
136                 named_pipe_terminate_connection(pipe_conn,
137                                 "named_pipe_accept: "
138                                 "no memory for tstream_read_pdu_blob_send");
139                 return;
140         }
141         tevent_req_set_callback(subreq, named_pipe_auth_request, pipe_conn);
142 }
143
144 struct named_pipe_call {
145         struct named_pipe_connection *pipe_conn;
146         DATA_BLOB in;
147         DATA_BLOB out;
148         struct iovec out_iov[1];
149         NTSTATUS status;
150 };
151
152 static void named_pipe_handover_connection(struct tevent_req *subreq);
153
154 static void named_pipe_auth_request(struct tevent_req *subreq)
155 {
156         struct named_pipe_connection *pipe_conn = tevent_req_callback_data(subreq,
157                                       struct named_pipe_connection);
158         struct stream_connection *conn = pipe_conn->connection;
159         struct named_pipe_call *call;
160         enum ndr_err_code ndr_err;
161         union netr_Validation val;
162         struct auth_serversupplied_info *server_info;
163         struct named_pipe_auth_req pipe_request;
164         struct named_pipe_auth_rep pipe_reply;
165         NTSTATUS status;
166
167         call = talloc(pipe_conn, struct named_pipe_call);
168         if (call == NULL) {
169                 named_pipe_terminate_connection(pipe_conn,
170                                 "named_pipe_auth_request: "
171                                 "no memory for named_pipe_call");
172                 return;
173         }
174         call->pipe_conn = pipe_conn;
175
176         status = tstream_read_pdu_blob_recv(subreq,
177                                             call,
178                                             &call->in);
179         TALLOC_FREE(subreq);
180         if (!NT_STATUS_IS_OK(status)) {
181                 const char *reason;
182
183                 reason = talloc_asprintf(call, "named_pipe_call_loop: "
184                                          "tstream_read_pdu_blob_recv() - %s",
185                                          nt_errstr(status));
186                 if (reason == NULL) {
187                         reason = nt_errstr(status);
188                 }
189
190                 named_pipe_terminate_connection(pipe_conn, reason);
191                 return;
192         }
193
194         DEBUG(10,("Received named_pipe packet of length %lu from %s\n",
195                  (long) call->in.length,
196                  tsocket_address_string(pipe_conn->connection->remote_address, call)));
197         dump_data(11, call->in.data, call->in.length);
198
199         /*
200          * TODO: check it's a root (uid == 0) pipe
201          */
202
203         ZERO_STRUCT(pipe_reply);
204         pipe_reply.level = 0;
205         pipe_reply.status = NT_STATUS_INTERNAL_ERROR;
206
207         /* parse the passed credentials */
208         ndr_err = ndr_pull_struct_blob_all(
209                         &call->in,
210                         pipe_conn,
211                         lp_iconv_convenience(conn->lp_ctx),
212                         &pipe_request,
213                         (ndr_pull_flags_fn_t) ndr_pull_named_pipe_auth_req);
214         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
215                 pipe_reply.status = ndr_map_error2ntstatus(ndr_err);
216                 DEBUG(2, ("Could not unmarshall named_pipe_auth_req: %s\n",
217                           nt_errstr(pipe_reply.status)));
218                 goto reply;
219         }
220
221         if (DEBUGLVL(10)) {
222                 NDR_PRINT_DEBUG(named_pipe_auth_req, &pipe_request);
223         }
224
225         if (strcmp(NAMED_PIPE_AUTH_MAGIC, pipe_request.magic) != 0) {
226                 DEBUG(2, ("named_pipe_auth_req: invalid magic '%s' != %s\n",
227                           pipe_request.magic, NAMED_PIPE_AUTH_MAGIC));
228                 pipe_reply.status = NT_STATUS_INVALID_PARAMETER;
229                 goto reply;
230         }
231
232         switch (pipe_request.level) {
233         case 0:
234                 /*
235                  * anon connection, we don't create a session info
236                  * and leave it NULL
237                  */
238                 pipe_reply.level = 0;
239                 pipe_reply.status = NT_STATUS_OK;
240                 break;
241         case 1:
242                 val.sam3 = &pipe_request.info.info1;
243
244                 pipe_reply.level = 1;
245                 pipe_reply.status = make_server_info_netlogon_validation(pipe_conn,
246                                                                          "TODO",
247                                                                          3, &val,
248                                                                          &server_info);
249                 if (!NT_STATUS_IS_OK(pipe_reply.status)) {
250                         DEBUG(2, ("make_server_info_netlogon_validation returned "
251                                   "%s\n", nt_errstr(pipe_reply.status)));
252                         goto reply;
253                 }
254
255                 /* setup the session_info on the connection */
256                 pipe_reply.status = auth_generate_session_info(conn,
257                                                                conn->event.ctx,
258                                                                conn->lp_ctx,
259                                                                server_info,
260                                                                &conn->session_info);
261                 if (!NT_STATUS_IS_OK(pipe_reply.status)) {
262                         DEBUG(2, ("auth_generate_session_info failed: %s\n",
263                                   nt_errstr(pipe_reply.status)));
264                         goto reply;
265                 }
266
267                 break;
268         case 2:
269                 pipe_reply.level = 2;
270                 pipe_reply.info.info2.file_type = FILE_TYPE_MESSAGE_MODE_PIPE;
271                 pipe_reply.info.info2.device_state = 0xff | 0x0400 | 0x0100;
272                 pipe_reply.info.info2.allocation_size = 4096;
273
274                 if (pipe_request.info.info2.sam_info3 == NULL) {
275                         /*
276                          * anon connection, we don't create a session info
277                          * and leave it NULL
278                          */
279                         pipe_reply.status = NT_STATUS_OK;
280                         break;
281                 }
282
283                 val.sam3 = pipe_request.info.info2.sam_info3;
284
285                 pipe_reply.status = make_server_info_netlogon_validation(pipe_conn,
286                                                 val.sam3->base.account_name.string,
287                                                 3, &val, &server_info);
288                 if (!NT_STATUS_IS_OK(pipe_reply.status)) {
289                         DEBUG(2, ("make_server_info_netlogon_validation returned "
290                                   "%s\n", nt_errstr(pipe_reply.status)));
291                         goto reply;
292                 }
293
294                 /* setup the session_info on the connection */
295                 pipe_reply.status = auth_generate_session_info(conn,
296                                                         conn->event.ctx,
297                                                         conn->lp_ctx,
298                                                         server_info,
299                                                         &conn->session_info);
300                 if (!NT_STATUS_IS_OK(pipe_reply.status)) {
301                         DEBUG(2, ("auth_generate_session_info failed: %s\n",
302                                   nt_errstr(pipe_reply.status)));
303                         goto reply;
304                 }
305
306                 conn->session_info->session_key = data_blob_const(pipe_request.info.info2.session_key,
307                                                         pipe_request.info.info2.session_key_length);
308                 talloc_steal(conn->session_info, pipe_request.info.info2.session_key);
309
310                 break;
311         case 3:
312                 pipe_reply.level = 3;
313                 pipe_reply.info.info3.file_type = FILE_TYPE_MESSAGE_MODE_PIPE;
314                 pipe_reply.info.info3.device_state = 0xff | 0x0400 | 0x0100;
315                 pipe_reply.info.info3.allocation_size = 4096;
316
317                 if (pipe_request.info.info3.sam_info3 == NULL) {
318                         /*
319                          * anon connection, we don't create a session info
320                          * and leave it NULL
321                          */
322                         pipe_reply.status = NT_STATUS_OK;
323                         break;
324                 }
325
326                 val.sam3 = pipe_request.info.info3.sam_info3;
327
328                 pipe_reply.status = make_server_info_netlogon_validation(pipe_conn,
329                                                 val.sam3->base.account_name.string,
330                                                 3, &val, &server_info);
331                 if (!NT_STATUS_IS_OK(pipe_reply.status)) {
332                         DEBUG(2, ("make_server_info_netlogon_validation returned "
333                                   "%s\n", nt_errstr(pipe_reply.status)));
334                         goto reply;
335                 }
336
337                 /* setup the session_info on the connection */
338                 pipe_reply.status = auth_generate_session_info(conn,
339                                                                conn->event.ctx,
340                                                                conn->lp_ctx,
341                                                                server_info,
342                                                                &conn->session_info);
343                 if (!NT_STATUS_IS_OK(pipe_reply.status)) {
344                         DEBUG(2, ("auth_generate_session_info failed: %s\n",
345                                   nt_errstr(pipe_reply.status)));
346                         goto reply;
347                 }
348
349                 if (pipe_request.info.info3.gssapi_delegated_creds_length) {
350                         OM_uint32 minor_status;
351                         gss_buffer_desc cred_token;
352                         gss_cred_id_t cred_handle;
353                         int ret;
354                         const char *error_string;
355
356                         DEBUG(10, ("named_pipe_auth: delegated credentials supplied by client\n"));
357
358                         cred_token.value = pipe_request.info.info3.gssapi_delegated_creds;
359                         cred_token.length = pipe_request.info.info3.gssapi_delegated_creds_length;
360
361                         ret = gss_import_cred(&minor_status,
362                                                &cred_token,
363                                                &cred_handle);
364                         if (ret != GSS_S_COMPLETE) {
365                                 pipe_reply.status = NT_STATUS_INTERNAL_ERROR;
366                                 goto reply;
367                         }
368
369                         conn->session_info->credentials = cli_credentials_init(conn->session_info);
370                         if (conn->session_info->credentials == NULL) {
371                                 pipe_reply.status = NT_STATUS_NO_MEMORY;
372                                 goto reply;
373                         }
374
375                         cli_credentials_set_conf(conn->session_info->credentials,
376                                                  conn->lp_ctx);
377                         /* Just so we don't segfault trying to get at a username */
378                         cli_credentials_set_anonymous(conn->session_info->credentials);
379
380                         ret = cli_credentials_set_client_gss_creds(conn->session_info->credentials,
381                                                                    conn->event.ctx,
382                                                                    conn->lp_ctx,
383                                                                    cred_handle,
384                                                                    CRED_SPECIFIED, &error_string);
385                         if (ret) {
386                                 pipe_reply.status = NT_STATUS_INTERNAL_ERROR;
387                                 DEBUG(2, ("Failed to set pipe forwarded creds: %s\n", error_string));
388                                 goto reply;
389                         }
390
391                         /* This credential handle isn't useful for password authentication, so ensure nobody tries to do that */
392                         cli_credentials_set_kerberos_state(conn->session_info->credentials,
393                                                            CRED_MUST_USE_KERBEROS);
394                 }
395
396                 conn->session_info->session_key = data_blob_const(pipe_request.info.info3.session_key,
397                                                         pipe_request.info.info3.session_key_length);
398                 talloc_steal(conn->session_info, pipe_request.info.info3.session_key);
399
400                 break;
401         default:
402                 DEBUG(0, ("named_pipe_auth_req: unknown level %u\n",
403                           pipe_request.level));
404                 pipe_reply.level = 0;
405                 pipe_reply.status = NT_STATUS_INVALID_LEVEL;
406                 goto reply;
407         }
408
409 reply:
410         /* create the output */
411         ndr_err = ndr_push_struct_blob(&call->out, pipe_conn,
412                         lp_iconv_convenience(conn->lp_ctx),
413                         &pipe_reply,
414                         (ndr_push_flags_fn_t)ndr_push_named_pipe_auth_rep);
415         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
416                 const char *reason;
417                 status = ndr_map_error2ntstatus(ndr_err);
418
419                 reason = talloc_asprintf(pipe_conn, "named_pipe_auth_request: could not marshall named_pipe_auth_rep: %s\n",
420                                          nt_errstr(status));
421                 if (reason == NULL) {
422                         reason = "named_pipe_auth_request: could not marshall named_pipe_auth_rep";
423                 }
424                 named_pipe_terminate_connection(pipe_conn, reason);
425                 return;
426         }
427
428         DEBUG(10,("named_pipe_auth_request: named_pipe_auth reply[%u]\n",
429                   (unsigned) call->out.length));
430         dump_data(11, call->out.data, call->out.length);
431         if (DEBUGLVL(10)) {
432                 NDR_PRINT_DEBUG(named_pipe_auth_rep, &pipe_reply);
433         }
434
435         call->status = pipe_reply.status;
436
437         call->out_iov[0].iov_base = call->out.data;
438         call->out_iov[0].iov_len = call->out.length;
439
440         subreq = tstream_writev_send(call,
441                                      pipe_conn->connection->event.ctx,
442                                      pipe_conn->tstream,
443                                      call->out_iov, 1);
444         if (subreq == NULL) {
445                 named_pipe_terminate_connection(pipe_conn, "named_pipe_auth_request: "
446                                 "no memory for tstream_writev_send");
447                 return;
448         }
449
450         tevent_req_set_callback(subreq, named_pipe_handover_connection, call);
451 }
452
453 static void named_pipe_handover_connection(struct tevent_req *subreq)
454 {
455         struct named_pipe_call *call = tevent_req_callback_data(subreq,
456                         struct named_pipe_call);
457         struct named_pipe_connection *pipe_conn = call->pipe_conn;
458         struct stream_connection *conn = pipe_conn->connection;
459         int sys_errno;
460         int rc;
461
462         rc = tstream_writev_recv(subreq, &sys_errno);
463         TALLOC_FREE(subreq);
464         if (rc == -1) {
465                 const char *reason;
466
467                 reason = talloc_asprintf(call, "named_pipe_handover_connection: "
468                                          "tstream_writev_recv() - %d:%s",
469                                          sys_errno, strerror(sys_errno));
470                 if (reason == NULL) {
471                         reason = "named_pipe_handover_connection: "
472                                  "tstream_writev_recv() failed";
473                 }
474
475                 named_pipe_terminate_connection(pipe_conn, reason);
476                 return;
477         }
478
479         if (!NT_STATUS_IS_OK(call->status)) {
480                 const char *reason;
481
482                 reason = talloc_asprintf(call, "named_pipe_handover_connection: "
483                                         "reply status - %s", nt_errstr(call->status));
484                 if (reason == NULL) {
485                         reason = nt_errstr(call->status);
486                 }
487
488                 named_pipe_terminate_connection(pipe_conn, reason);
489                 return;
490         }
491
492         /*
493          * remove the named_pipe layer together with its packet layer
494          */
495         conn->ops               = pipe_conn->pipe_sock->ops;
496         conn->private_data      = pipe_conn->pipe_sock->private_data;
497         talloc_unlink(conn, pipe_conn);
498
499         conn->event.fde = tevent_add_fd(conn->event.ctx,
500                                         conn,
501                                         socket_get_fd(conn->socket),
502                                         TEVENT_FD_READ,
503                                         stream_io_handler_fde,
504                                         conn);
505         if (conn->event.fde == NULL) {
506                 named_pipe_terminate_connection(pipe_conn, "named_pipe_handover_connection: "
507                                 "setting up the stream_io_handler_fde failed");
508                 return;
509         }
510
511         /*
512          * hand over to the real pipe implementation,
513          * now that we have setup the transport session_info
514          */
515         conn->ops->accept_connection(conn);
516
517         DEBUG(10,("named_pipe_handover_connection[%s]: succeeded\n",
518               conn->ops->name));
519
520         /* we don't have to free call here as the connection got closed */
521 }
522
523 /*
524   called when a pipe socket becomes readable
525 */
526 static void named_pipe_recv(struct stream_connection *conn, uint16_t flags)
527 {
528         struct named_pipe_connection *pipe_conn = talloc_get_type(
529                 conn->private_data, struct named_pipe_connection);
530
531         named_pipe_terminate_connection(pipe_conn,
532                                         "named_pipe_recv: called");
533 }
534
535 /*
536   called when a pipe socket becomes writable
537 */
538 static void named_pipe_send(struct stream_connection *conn, uint16_t flags)
539 {
540         struct named_pipe_connection *pipe_conn = talloc_get_type(
541                 conn->private_data, struct named_pipe_connection);
542
543         named_pipe_terminate_connection(pipe_conn,
544                                         "named_pipe_send: called");
545 }
546
547 static const struct stream_server_ops named_pipe_stream_ops = {
548         .name                   = "named_pipe",
549         .accept_connection      = named_pipe_accept,
550         .recv_handler           = named_pipe_recv,
551         .send_handler           = named_pipe_send,
552 };
553
554 NTSTATUS stream_setup_named_pipe(struct tevent_context *event_context,
555                                  struct loadparm_context *lp_ctx,
556                                  const struct model_ops *model_ops,
557                                  const struct stream_server_ops *stream_ops,
558                                  const char *pipe_name,
559                                  void *private_data)
560 {
561         char *dirname;
562         struct named_pipe_socket *pipe_sock;
563         NTSTATUS status = NT_STATUS_NO_MEMORY;;
564
565         pipe_sock = talloc(event_context, struct named_pipe_socket);
566         if (pipe_sock == NULL) {
567                 goto fail;
568         }
569
570         /* remember the details about the pipe */
571         pipe_sock->pipe_name    = talloc_strdup(pipe_sock, pipe_name);
572         if (pipe_sock->pipe_name == NULL) {
573                 goto fail;
574         }
575
576         dirname = talloc_asprintf(pipe_sock, "%s/np", lp_ncalrpc_dir(lp_ctx));
577         if (dirname == NULL) {
578                 goto fail;
579         }
580
581         if (!directory_create_or_exist(dirname, geteuid(), 0700)) {
582                 status = map_nt_error_from_unix(errno);
583                 DEBUG(0,(__location__ ": Failed to create stream pipe directory %s - %s\n",
584                          dirname, nt_errstr(status)));
585                 goto fail;
586         }
587
588         if (strncmp(pipe_name, "\\pipe\\", 6) == 0) {
589                 pipe_name += 6;
590         }
591
592         pipe_sock->pipe_path = talloc_asprintf(pipe_sock, "%s/%s", dirname,
593                                                pipe_name);
594         if (pipe_sock->pipe_path == NULL) {
595                 goto fail;
596         }
597
598         talloc_free(dirname);
599
600         pipe_sock->ops          = stream_ops;
601         pipe_sock->private_data = talloc_reference(pipe_sock, private_data);
602
603         status = stream_setup_socket(event_context,
604                                      lp_ctx,
605                                      model_ops,
606                                      &named_pipe_stream_ops,
607                                      "unix",
608                                      pipe_sock->pipe_path,
609                                      NULL,
610                                      NULL,
611                                      pipe_sock);
612         if (!NT_STATUS_IS_OK(status)) {
613                 goto fail;
614         }
615         return NT_STATUS_OK;
616
617  fail:
618         talloc_free(pipe_sock);
619         return status;
620 }