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