r5902: A rather large change...
[metze/samba/wip.git] / source4 / ntvfs / ipc / vfs_ipc.c
1 /* 
2    Unix SMB/CIFS implementation.
3    default IPC$ NTVFS backend
4
5    Copyright (C) Andrew Tridgell 2003
6    Copyright (C) Stefan (metze) Metzmacher 2004-2005
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 2 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, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22 /*
23   this implements the IPC$ backend, called by the NTVFS subsystem to
24   handle requests on IPC$ shares
25 */
26
27
28 #include "includes.h"
29 #include "system/filesys.h"
30 #include "dlinklist.h"
31 #include "smb_server/smb_server.h"
32
33 #define IPC_BASE_FNUM 0x400
34
35 /* this is the private structure used to keep the state of an open
36    ipc$ connection. It needs to keep information about all open
37    pipes */
38 struct ipc_private {
39         struct idr_context *idtree_fnum;
40
41         struct dcesrv_context *dcesrv;
42
43         /* a list of open pipes */
44         struct pipe_state {
45                 struct pipe_state *next, *prev;
46                 struct ipc_private *private;
47                 const char *pipe_name;
48                 uint16_t fnum;
49                 struct dcesrv_connection *dce_conn;
50                 uint16_t ipc_state;
51                 /* we need to remember the session it was opened on,
52                    as it is illegal to operate on someone elses fnum */
53                 struct smbsrv_session *session;
54
55                 /* we need to remember the client pid that 
56                    opened the file so SMBexit works */
57                 uint16_t smbpid;
58         } *pipe_list;
59
60 };
61
62
63 /*
64   find a open pipe give a file descriptor
65 */
66 static struct pipe_state *pipe_state_find(struct ipc_private *private, uint16_t fnum)
67 {
68         return idr_find(private->idtree_fnum, fnum);
69 }
70
71
72 /*
73   connect to a share - always works 
74 */
75 static NTSTATUS ipc_connect(struct ntvfs_module_context *ntvfs,
76                             struct smbsrv_request *req, const char *sharename)
77 {
78         NTSTATUS status;
79         struct smbsrv_tcon *tcon = req->tcon;
80         struct ipc_private *private;
81
82         tcon->fs_type = talloc_strdup(tcon, "IPC");
83         NT_STATUS_HAVE_NO_MEMORY(tcon->fs_type);
84
85         tcon->dev_type = talloc_strdup(tcon, "IPC");
86         NT_STATUS_HAVE_NO_MEMORY(tcon->dev_type);
87
88         /* prepare the private state for this connection */
89         private = talloc(tcon, struct ipc_private);
90         NT_STATUS_HAVE_NO_MEMORY(private);
91
92         ntvfs->private_data = private;
93
94         private->pipe_list = NULL;
95
96         private->idtree_fnum = idr_init(private);
97         NT_STATUS_HAVE_NO_MEMORY(private->idtree_fnum);
98
99         /* setup the DCERPC server subsystem */
100         status = dcesrv_init_ipc_context(private, &private->dcesrv);
101         NT_STATUS_NOT_OK_RETURN(status);
102
103         return NT_STATUS_OK;
104 }
105
106 /*
107   disconnect from a share
108 */
109 static NTSTATUS ipc_disconnect(struct ntvfs_module_context *ntvfs,
110                                struct smbsrv_tcon *tcon)
111 {
112         return NT_STATUS_OK;
113 }
114
115 /*
116   delete a file
117 */
118 static NTSTATUS ipc_unlink(struct ntvfs_module_context *ntvfs,
119                            struct smbsrv_request *req, struct smb_unlink *unl)
120 {
121         return NT_STATUS_ACCESS_DENIED;
122 }
123
124
125 /*
126   ioctl interface - we don't do any
127 */
128 static NTSTATUS ipc_ioctl(struct ntvfs_module_context *ntvfs,
129                           struct smbsrv_request *req, union smb_ioctl *io)
130 {
131         return NT_STATUS_ACCESS_DENIED;
132 }
133
134 /*
135   check if a directory exists
136 */
137 static NTSTATUS ipc_chkpath(struct ntvfs_module_context *ntvfs,
138                             struct smbsrv_request *req, struct smb_chkpath *cp)
139 {
140         return NT_STATUS_ACCESS_DENIED;
141 }
142
143 /*
144   return info on a pathname
145 */
146 static NTSTATUS ipc_qpathinfo(struct ntvfs_module_context *ntvfs,
147                               struct smbsrv_request *req, union smb_fileinfo *info)
148 {
149         return NT_STATUS_ACCESS_DENIED;
150 }
151
152 /*
153   set info on a pathname
154 */
155 static NTSTATUS ipc_setpathinfo(struct ntvfs_module_context *ntvfs,
156                                 struct smbsrv_request *req, union smb_setfileinfo *st)
157 {
158         return NT_STATUS_ACCESS_DENIED;
159 }
160
161
162 /*
163   destroy a open pipe structure
164 */
165 static int ipc_fd_destructor(void *ptr)
166 {
167         struct pipe_state *p = ptr;
168         idr_remove(p->private->idtree_fnum, p->fnum);
169         DLIST_REMOVE(p->private->pipe_list, p);
170         return 0;
171 }
172
173
174 /*
175   open a file backend - used for MSRPC pipes
176 */
177 static NTSTATUS ipc_open_generic(struct ntvfs_module_context *ntvfs,
178                                  struct smbsrv_request *req, const char *fname, 
179                                  struct pipe_state **ps)
180 {
181         struct pipe_state *p;
182         NTSTATUS status;
183         struct dcerpc_binding *ep_description;
184         struct ipc_private *private = ntvfs->private_data;
185         int fnum;
186         struct stream_connection *srv_conn = req->smb_conn->connection;
187
188         if (!req->session || !req->session->session_info) {
189                 return NT_STATUS_ACCESS_DENIED;
190         }
191
192         p = talloc(req, struct pipe_state);
193         NT_STATUS_HAVE_NO_MEMORY(p);
194
195         ep_description = talloc(req, struct dcerpc_binding);
196         NT_STATUS_HAVE_NO_MEMORY(ep_description);
197
198         while (fname[0] == '\\') fname++;
199
200         p->pipe_name = talloc_asprintf(p, "\\pipe\\%s", fname);
201         NT_STATUS_HAVE_NO_MEMORY(p->pipe_name);
202
203         fnum = idr_get_new_above(private->idtree_fnum, p, IPC_BASE_FNUM, UINT16_MAX);
204         if (fnum == -1) {
205                 return NT_STATUS_TOO_MANY_OPENED_FILES;
206         }
207
208         p->fnum = fnum;
209         p->ipc_state = 0x5ff;
210
211         /*
212           we're all set, now ask the dcerpc server subsystem to open the 
213           endpoint. At this stage the pipe isn't bound, so we don't
214           know what interface the user actually wants, just that they want
215           one of the interfaces attached to this pipe endpoint.
216         */
217         ep_description->transport = NCACN_NP;
218         ep_description->endpoint = talloc_reference(ep_description, p->pipe_name);
219
220         /* The session info is refcount-increased in the 
221          * dcesrv_endpoint_search_connect() function
222          */
223         status = dcesrv_endpoint_search_connect(private->dcesrv,
224                                                 p,
225                                                 ep_description, 
226                                                 req->session->session_info,
227                                                 srv_conn,
228                                                 &p->dce_conn);
229         if (!NT_STATUS_IS_OK(status)) {
230                 idr_remove(private->idtree_fnum, p->fnum);
231                 return status;
232         }
233
234         DLIST_ADD(private->pipe_list, p);
235
236         p->smbpid = req->smbpid;
237         p->session = req->session;
238         p->private = private;
239
240         *ps = p;
241
242         talloc_steal(private, p);
243
244         talloc_set_destructor(p, ipc_fd_destructor);
245
246         return NT_STATUS_OK;
247 }
248
249 /*
250   open a file with ntcreatex - used for MSRPC pipes
251 */
252 static NTSTATUS ipc_open_ntcreatex(struct ntvfs_module_context *ntvfs,
253                                    struct smbsrv_request *req, union smb_open *oi)
254 {
255         struct pipe_state *p;
256         NTSTATUS status;
257
258         status = ipc_open_generic(ntvfs, req, oi->ntcreatex.in.fname, &p);
259         if (!NT_STATUS_IS_OK(status)) {
260                 return status;
261         }
262
263         ZERO_STRUCT(oi->ntcreatex.out);
264         oi->ntcreatex.out.fnum = p->fnum;
265         oi->ntcreatex.out.ipc_state = p->ipc_state;
266         oi->ntcreatex.out.file_type = FILE_TYPE_MESSAGE_MODE_PIPE;
267
268         return status;
269 }
270
271 /*
272   open a file with openx - used for MSRPC pipes
273 */
274 static NTSTATUS ipc_open_openx(struct ntvfs_module_context *ntvfs,
275                                struct smbsrv_request *req, union smb_open *oi)
276 {
277         struct pipe_state *p;
278         NTSTATUS status;
279         const char *fname = oi->openx.in.fname;
280
281         status = ipc_open_generic(ntvfs, req, fname, &p);
282         if (!NT_STATUS_IS_OK(status)) {
283                 return status;
284         }
285
286         ZERO_STRUCT(oi->openx.out);
287         oi->openx.out.fnum = p->fnum;
288         oi->openx.out.ftype = 2;
289         oi->openx.out.devstate = p->ipc_state;
290         
291         return status;
292 }
293
294 /*
295   open a file - used for MSRPC pipes
296 */
297 static NTSTATUS ipc_open(struct ntvfs_module_context *ntvfs,
298                                 struct smbsrv_request *req, union smb_open *oi)
299 {
300         NTSTATUS status;
301
302         switch (oi->generic.level) {
303         case RAW_OPEN_NTCREATEX:
304                 status = ipc_open_ntcreatex(ntvfs, req, oi);
305                 break;
306         case RAW_OPEN_OPENX:
307                 status = ipc_open_openx(ntvfs, req, oi);
308                 break;
309         default:
310                 status = NT_STATUS_NOT_SUPPORTED;
311                 break;
312         }
313
314         return status;
315 }
316
317 /*
318   create a directory
319 */
320 static NTSTATUS ipc_mkdir(struct ntvfs_module_context *ntvfs,
321                           struct smbsrv_request *req, union smb_mkdir *md)
322 {
323         return NT_STATUS_ACCESS_DENIED;
324 }
325
326 /*
327   remove a directory
328 */
329 static NTSTATUS ipc_rmdir(struct ntvfs_module_context *ntvfs,
330                           struct smbsrv_request *req, struct smb_rmdir *rd)
331 {
332         return NT_STATUS_ACCESS_DENIED;
333 }
334
335 /*
336   rename a set of files
337 */
338 static NTSTATUS ipc_rename(struct ntvfs_module_context *ntvfs,
339                            struct smbsrv_request *req, union smb_rename *ren)
340 {
341         return NT_STATUS_ACCESS_DENIED;
342 }
343
344 /*
345   copy a set of files
346 */
347 static NTSTATUS ipc_copy(struct ntvfs_module_context *ntvfs,
348                          struct smbsrv_request *req, struct smb_copy *cp)
349 {
350         return NT_STATUS_ACCESS_DENIED;
351 }
352
353 /*
354   read from a file
355 */
356 static NTSTATUS ipc_read(struct ntvfs_module_context *ntvfs,
357                          struct smbsrv_request *req, union smb_read *rd)
358 {
359         struct ipc_private *private = ntvfs->private_data;
360         DATA_BLOB data;
361         uint16_t fnum;
362         struct pipe_state *p;
363         NTSTATUS status;
364
365         if (rd->generic.level != RAW_READ_GENERIC) {
366                 return ntvfs_map_read(req, rd, ntvfs);
367         }
368
369         fnum = rd->readx.in.fnum;
370
371         p = pipe_state_find(private, fnum);
372         if (!p) {
373                 return NT_STATUS_INVALID_HANDLE;
374         }
375
376         data.length = rd->readx.in.maxcnt;
377         data.data = rd->readx.out.data;
378         if (data.length > UINT16_MAX) {
379                 data.length = 0;
380         }
381
382         if (data.length != 0) {
383                 status = dcesrv_output_blob(p->dce_conn, &data);
384                 if (NT_STATUS_IS_ERR(status)) {
385                         return status;
386                 }
387         }
388
389         rd->readx.out.remaining = 0;
390         rd->readx.out.compaction_mode = 0;
391         rd->readx.out.nread = data.length;
392
393         return status;
394 }
395
396 /*
397   write to a file
398 */
399 static NTSTATUS ipc_write(struct ntvfs_module_context *ntvfs,
400                           struct smbsrv_request *req, union smb_write *wr)
401 {
402         struct ipc_private *private = ntvfs->private_data;
403         DATA_BLOB data;
404         uint16_t fnum;
405         struct pipe_state *p;
406         NTSTATUS status;
407
408         if (wr->generic.level != RAW_WRITE_GENERIC) {
409                 return ntvfs_map_write(req, wr, ntvfs);
410         }
411
412         fnum = wr->writex.in.fnum;
413         data.data = discard_const_p(void, wr->writex.in.data);
414         data.length = wr->writex.in.count;
415
416         p = pipe_state_find(private, fnum);
417         if (!p) {
418                 return NT_STATUS_INVALID_HANDLE;
419         }
420
421         status = dcesrv_input(p->dce_conn, &data);
422         if (!NT_STATUS_IS_OK(status)) {
423                 return status;
424         }
425
426         wr->writex.out.nwritten = data.length;
427         wr->writex.out.remaining = 0;
428
429         return NT_STATUS_OK;
430 }
431
432 /*
433   seek in a file
434 */
435 static NTSTATUS ipc_seek(struct ntvfs_module_context *ntvfs,
436                          struct smbsrv_request *req, struct smb_seek *io)
437 {
438         return NT_STATUS_ACCESS_DENIED;
439 }
440
441 /*
442   flush a file
443 */
444 static NTSTATUS ipc_flush(struct ntvfs_module_context *ntvfs,
445                           struct smbsrv_request *req, struct smb_flush *io)
446 {
447         return NT_STATUS_ACCESS_DENIED;
448 }
449
450 /*
451   close a file
452 */
453 static NTSTATUS ipc_close(struct ntvfs_module_context *ntvfs,
454                           struct smbsrv_request *req, union smb_close *io)
455 {
456         struct ipc_private *private = ntvfs->private_data;
457         struct pipe_state *p;
458
459         if (io->generic.level != RAW_CLOSE_CLOSE) {
460                 return ntvfs_map_close(req, io, ntvfs);
461         }
462
463         p = pipe_state_find(private, io->close.in.fnum);
464         if (!p) {
465                 return NT_STATUS_INVALID_HANDLE;
466         }
467
468         talloc_free(p);
469
470         return NT_STATUS_OK;
471 }
472
473 /*
474   exit - closing files
475 */
476 static NTSTATUS ipc_exit(struct ntvfs_module_context *ntvfs,
477                          struct smbsrv_request *req)
478 {
479         struct ipc_private *private = ntvfs->private_data;
480         struct pipe_state *p, *next;
481         
482         for (p=private->pipe_list; p; p=next) {
483                 next = p->next;
484                 if (p->smbpid == req->smbpid) {
485                         talloc_free(p);
486                 }
487         }
488
489         return NT_STATUS_OK;
490 }
491
492 /*
493   logoff - closing files open by the user
494 */
495 static NTSTATUS ipc_logoff(struct ntvfs_module_context *ntvfs,
496                            struct smbsrv_request *req)
497 {
498         struct ipc_private *private = ntvfs->private_data;
499         struct pipe_state *p, *next;
500         
501         for (p=private->pipe_list; p; p=next) {
502                 next = p->next;
503                 if (p->session == req->session) {
504                         talloc_free(p);
505                 }
506         }
507
508         return NT_STATUS_OK;
509 }
510
511 /*
512   setup for an async call
513 */
514 static NTSTATUS ipc_async_setup(struct ntvfs_module_context *ntvfs,
515                                 struct smbsrv_request *req,
516                                 void *private)
517 {
518         return NT_STATUS_OK;
519 }
520
521 /*
522   cancel an async call
523 */
524 static NTSTATUS ipc_cancel(struct ntvfs_module_context *ntvfs,
525                            struct smbsrv_request *req)
526 {
527         return NT_STATUS_UNSUCCESSFUL;
528 }
529
530 /*
531   lock a byte range
532 */
533 static NTSTATUS ipc_lock(struct ntvfs_module_context *ntvfs,
534                          struct smbsrv_request *req, union smb_lock *lck)
535 {
536         return NT_STATUS_ACCESS_DENIED;
537 }
538
539 /*
540   set info on a open file
541 */
542 static NTSTATUS ipc_setfileinfo(struct ntvfs_module_context *ntvfs,
543                                 struct smbsrv_request *req, union smb_setfileinfo *info)
544 {
545         return NT_STATUS_ACCESS_DENIED;
546 }
547
548 /*
549   query info on a open file
550 */
551 static NTSTATUS ipc_qfileinfo(struct ntvfs_module_context *ntvfs,
552                               struct smbsrv_request *req, union smb_fileinfo *info)
553 {
554         return NT_STATUS_ACCESS_DENIED;
555 }
556
557
558 /*
559   return filesystem info
560 */
561 static NTSTATUS ipc_fsinfo(struct ntvfs_module_context *ntvfs,
562                            struct smbsrv_request *req, union smb_fsinfo *fs)
563 {
564         return NT_STATUS_ACCESS_DENIED;
565 }
566
567 /*
568   return print queue info
569 */
570 static NTSTATUS ipc_lpq(struct ntvfs_module_context *ntvfs,
571                         struct smbsrv_request *req, union smb_lpq *lpq)
572 {
573         return NT_STATUS_ACCESS_DENIED;
574 }
575
576 /* 
577    list files in a directory matching a wildcard pattern
578 */
579 static NTSTATUS ipc_search_first(struct ntvfs_module_context *ntvfs,
580                           struct smbsrv_request *req, union smb_search_first *io,
581                           void *search_private, 
582                           BOOL (*callback)(void *, union smb_search_data *))
583 {
584         return NT_STATUS_ACCESS_DENIED;
585 }
586
587 /* 
588    continue listing files in a directory 
589 */
590 static NTSTATUS ipc_search_next(struct ntvfs_module_context *ntvfs,
591                          struct smbsrv_request *req, union smb_search_next *io,
592                          void *search_private, 
593                          BOOL (*callback)(void *, union smb_search_data *))
594 {
595         return NT_STATUS_ACCESS_DENIED;
596 }
597
598 /* 
599    end listing files in a directory 
600 */
601 static NTSTATUS ipc_search_close(struct ntvfs_module_context *ntvfs,
602                           struct smbsrv_request *req, union smb_search_close *io)
603 {
604         return NT_STATUS_ACCESS_DENIED;
605 }
606
607
608 /* SMBtrans - handle a DCERPC command */
609 static NTSTATUS ipc_dcerpc_cmd(struct ntvfs_module_context *ntvfs,
610                                struct smbsrv_request *req, struct smb_trans2 *trans)
611 {
612         struct pipe_state *p;
613         struct ipc_private *private = ntvfs->private_data;
614         NTSTATUS status;
615
616         /* the fnum is in setup[1] */
617         p = pipe_state_find(private, trans->in.setup[1]);
618         if (!p) {
619                 return NT_STATUS_INVALID_HANDLE;
620         }
621
622         trans->out.data = data_blob_talloc(req, NULL, trans->in.max_data);
623         if (!trans->out.data.data) {
624                 return NT_STATUS_NO_MEMORY;
625         }
626
627         /* pass the data to the dcerpc server. Note that we don't
628            expect this to fail, and things like NDR faults are not
629            reported at this stage. Those sorts of errors happen in the
630            dcesrv_output stage */
631         status = dcesrv_input(p->dce_conn, &trans->in.data);
632         if (!NT_STATUS_IS_OK(status)) {
633                 return status;
634         }
635
636         /*
637           now ask the dcerpc system for some output. This doesn't yet handle
638           async calls. Again, we only expect NT_STATUS_OK. If the call fails then
639           the error is encoded at the dcerpc level
640         */
641         status = dcesrv_output_blob(p->dce_conn, &trans->out.data);
642         if (NT_STATUS_IS_ERR(status)) {
643                 return status;
644         }
645
646         trans->out.setup_count = 0;
647         trans->out.setup = NULL;
648         trans->out.params = data_blob(NULL, 0);
649
650         return status;
651 }
652
653
654 /* SMBtrans - set named pipe state */
655 static NTSTATUS ipc_set_nm_pipe_state(struct ntvfs_module_context *ntvfs,
656                                 struct smbsrv_request *req, struct smb_trans2 *trans)
657 {
658         struct ipc_private *private = ntvfs->private_data;
659         struct pipe_state *p;
660
661         /* the fnum is in setup[1] */
662         p = pipe_state_find(private, trans->in.setup[1]);
663         if (!p) {
664                 return NT_STATUS_INVALID_HANDLE;
665         }
666
667         if (trans->in.params.length != 2) {
668                 return NT_STATUS_INVALID_PARAMETER;
669         }
670         p->ipc_state = SVAL(trans->in.params.data, 0);
671
672         trans->out.setup_count = 0;
673         trans->out.setup = NULL;
674         trans->out.params = data_blob(NULL, 0);
675         trans->out.data = data_blob(NULL, 0);
676
677         return NT_STATUS_OK;
678 }
679
680
681 /* SMBtrans - used to provide access to SMB pipes */
682 static NTSTATUS ipc_trans(struct ntvfs_module_context *ntvfs,
683                                 struct smbsrv_request *req, struct smb_trans2 *trans)
684 {
685         NTSTATUS status;
686
687         if (strequal(trans->in.trans_name, "\\PIPE\\LANMAN"))
688                 return ipc_rap_call(req, trans);
689
690         if (trans->in.setup_count != 2) {
691                 return NT_STATUS_INVALID_PARAMETER;
692         }
693
694         switch (trans->in.setup[0]) {
695         case TRANSACT_SETNAMEDPIPEHANDLESTATE:
696                 status = ipc_set_nm_pipe_state(ntvfs, req, trans);
697                 break;
698         case TRANSACT_DCERPCCMD:
699                 status = ipc_dcerpc_cmd(ntvfs, req, trans);
700                 break;
701         default:
702                 status = NT_STATUS_INVALID_PARAMETER;
703                 break;
704         }
705
706         return status;
707 }
708
709
710
711 /*
712   initialialise the IPC backend, registering ourselves with the ntvfs subsystem
713  */
714 NTSTATUS ntvfs_ipc_init(void)
715 {
716         NTSTATUS ret;
717         struct ntvfs_ops ops;
718
719         ZERO_STRUCT(ops);
720         
721         /* fill in the name and type */
722         ops.name = "default";
723         ops.type = NTVFS_IPC;
724
725         /* fill in all the operations */
726         ops.connect = ipc_connect;
727         ops.disconnect = ipc_disconnect;
728         ops.unlink = ipc_unlink;
729         ops.chkpath = ipc_chkpath;
730         ops.qpathinfo = ipc_qpathinfo;
731         ops.setpathinfo = ipc_setpathinfo;
732         ops.openfile = ipc_open;
733         ops.mkdir = ipc_mkdir;
734         ops.rmdir = ipc_rmdir;
735         ops.rename = ipc_rename;
736         ops.copy = ipc_copy;
737         ops.ioctl = ipc_ioctl;
738         ops.read = ipc_read;
739         ops.write = ipc_write;
740         ops.seek = ipc_seek;
741         ops.flush = ipc_flush;  
742         ops.close = ipc_close;
743         ops.exit = ipc_exit;
744         ops.lock = ipc_lock;
745         ops.setfileinfo = ipc_setfileinfo;
746         ops.qfileinfo = ipc_qfileinfo;
747         ops.fsinfo = ipc_fsinfo;
748         ops.lpq = ipc_lpq;
749         ops.search_first = ipc_search_first;
750         ops.search_next = ipc_search_next;
751         ops.search_close = ipc_search_close;
752         ops.trans = ipc_trans;
753         ops.logoff = ipc_logoff;
754         ops.async_setup = ipc_async_setup;
755         ops.cancel = ipc_cancel;
756
757         /* register ourselves with the NTVFS subsystem. */
758         ret = ntvfs_register(&ops);
759
760         if (!NT_STATUS_IS_OK(ret)) {
761                 DEBUG(0,("Failed to register IPC backend!\n"));
762                 return ret;
763         }
764
765         return ret;
766 }