r2803: allow unixuid module to work with foreign security principles
[amitay/samba.git] / source4 / ntvfs / unixuid / vfs_unixuid.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    a pass-thru NTVFS module to setup a security context using unix
5    uid/gid
6
7    Copyright (C) Andrew Tridgell 2004
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25
26 struct unixuid_private {
27         void *samctx;
28 };
29
30
31 /*
32   map a sid to a unix uid
33 */
34 static NTSTATUS sid_to_unixuid(struct ntvfs_module_context *ntvfs,
35                                struct smbsrv_request *req, struct dom_sid *sid, uid_t *uid)
36 {
37         struct unixuid_private *private = ntvfs->private_data;
38         const char *attrs[] = { "sAMAccountName", "unixID", "unixName", "sAMAccountType", NULL };
39         int ret;
40         const char *s;
41         void *ctx;
42         struct ldb_message **res;
43         const char *sidstr;
44         uint_t atype;
45
46         ctx = talloc(req, 0);
47         sidstr = dom_sid_string(ctx, sid);
48
49         ret = samdb_search(private->samctx, ctx, NULL, &res, attrs, "objectSid=%s", sidstr);
50         if (ret != 1) {
51                 DEBUG(0,("sid_to_unixuid: unable to find sam record for sid %s\n", sidstr));
52                 talloc_free(ctx);
53                 return NT_STATUS_ACCESS_DENIED;
54         }
55
56         /* make sure its a user, not a group */
57         atype = samdb_result_uint(res[0], "sAMAccountType", 0);
58         if (atype && atype != ATYPE_NORMAL_ACCOUNT) {
59                 DEBUG(0,("sid_to_unixuid: sid %s is not ATYPE_NORMAL_ACCOUNT\n", sidstr));
60                 talloc_free(ctx);
61                 return NT_STATUS_ACCESS_DENIED;
62         }
63
64         /* first try to get the uid directly */
65         s = samdb_result_string(res[0], "unixID", NULL);
66         if (s != NULL) {
67                 *uid = strtoul(s, NULL, 0);
68                 talloc_free(ctx);
69                 return NT_STATUS_OK;
70         }
71
72         /* next try via the UnixName attribute */
73         s = samdb_result_string(res[0], "unixName", NULL);
74         if (s != NULL) {
75                 struct passwd *pwd = getpwnam(s);
76                 if (!pwd) {
77                         DEBUG(0,("unixName %s for sid %s does not exist as a local user\n", s, sidstr));
78                         talloc_free(ctx);
79                         return NT_STATUS_ACCESS_DENIED;
80                 }
81                 *uid = pwd->pw_uid;
82                 talloc_free(ctx);
83                 return NT_STATUS_OK;
84         }
85
86         /* finally try via the sAMAccountName attribute */
87         s = samdb_result_string(res[0], "sAMAccountName", NULL);
88         if (s != NULL) {
89                 struct passwd *pwd = getpwnam(s);
90                 if (!pwd) {
91                         DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local user\n", s, sidstr));
92                         talloc_free(ctx);
93                         return NT_STATUS_ACCESS_DENIED;
94                 }
95                 *uid = pwd->pw_uid;
96                 talloc_free(ctx);
97                 return NT_STATUS_OK;
98         }
99
100         DEBUG(0,("sid_to_unixuid: no unixID, unixName or sAMAccountName for sid %s\n", sidstr));
101
102         talloc_free(ctx);
103         return NT_STATUS_ACCESS_DENIED;
104 }
105
106
107 /*
108   map a sid to a unix gid
109 */
110 static NTSTATUS sid_to_unixgid(struct ntvfs_module_context *ntvfs,
111                                struct smbsrv_request *req, struct dom_sid *sid, gid_t *gid)
112 {
113         struct unixuid_private *private = ntvfs->private_data;
114         const char *attrs[] = { "sAMAccountName", "unixID", "unixName", "sAMAccountType", NULL };
115         int ret;
116         const char *s;
117         void *ctx;
118         struct ldb_message **res;
119         const char *sidstr;
120         uint_t atype;
121
122         ctx = talloc(req, 0);
123         sidstr = dom_sid_string(ctx, sid);
124
125         ret = samdb_search(private->samctx, ctx, NULL, &res, attrs, "objectSid=%s", sidstr);
126         if (ret != 1) {
127                 DEBUG(0,("sid_to_unixgid: unable to find sam record for sid %s\n", sidstr));
128                 talloc_free(ctx);
129                 return NT_STATUS_ACCESS_DENIED;
130         }
131
132         /* make sure its not a user */
133         atype = samdb_result_uint(res[0], "sAMAccountType", 0);
134         if (atype && atype == ATYPE_NORMAL_ACCOUNT) {
135                 DEBUG(0,("sid_to_unixgid: sid %s is a ATYPE_NORMAL_ACCOUNT\n", sidstr));
136                 talloc_free(ctx);
137                 return NT_STATUS_ACCESS_DENIED;
138         }
139
140         /* first try to get the gid directly */
141         s = samdb_result_string(res[0], "unixID", NULL);
142         if (s != NULL) {
143                 *gid = strtoul(s, NULL, 0);
144                 talloc_free(ctx);
145                 return NT_STATUS_OK;
146         }
147
148         /* next try via the UnixName attribute */
149         s = samdb_result_string(res[0], "unixName", NULL);
150         if (s != NULL) {
151                 struct group *grp = getgrnam(s);
152                 if (!grp) {
153                         DEBUG(0,("unixName '%s' for sid %s does not exist as a local group\n", s, sidstr));
154                         talloc_free(ctx);
155                         return NT_STATUS_ACCESS_DENIED;
156                 }
157                 *gid = grp->gr_gid;
158                 talloc_free(ctx);
159                 return NT_STATUS_OK;
160         }
161
162         /* finally try via the sAMAccountName attribute */
163         s = samdb_result_string(res[0], "sAMAccountName", NULL);
164         if (s != NULL) {
165                 struct group *grp = getgrnam(s);
166                 if (!grp) {
167                         DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local group\n", s, sidstr));
168                         talloc_free(ctx);
169                         return NT_STATUS_ACCESS_DENIED;
170                 }
171                 *gid = grp->gr_gid;
172                 talloc_free(ctx);
173                 return NT_STATUS_OK;
174         }
175
176         DEBUG(0,("sid_to_unixgid: no unixID, unixName or sAMAccountName for sid %s\n", sidstr));
177
178         talloc_free(ctx);
179         return NT_STATUS_ACCESS_DENIED;
180 }
181
182 struct unix_sec_ctx {
183         uid_t uid;
184         gid_t gid;
185         uint_t ngroups;
186         gid_t *groups;
187 };
188
189 /*
190   pull the current security context into a unix_sec_ctx
191 */
192 static struct unix_sec_ctx *save_unix_security(TALLOC_CTX *mem_ctx)
193 {
194         struct unix_sec_ctx *sec = talloc_p(mem_ctx, struct unix_sec_ctx);
195         if (sec == NULL) {
196                 return NULL;
197         }
198         sec->uid = geteuid();
199         sec->gid = getegid();
200         sec->ngroups = getgroups(0, NULL);
201         if (sec->ngroups == -1) {
202                 talloc_free(sec);
203                 return NULL;
204         }
205         sec->groups = talloc_array_p(sec, gid_t, sec->ngroups);
206         if (sec->groups == NULL) {
207                 talloc_free(sec);
208                 return NULL;
209         }
210
211         if (getgroups(sec->ngroups, sec->groups) != sec->ngroups) {
212                 talloc_free(sec);
213                 return NULL;
214         }
215
216         return sec;
217 }
218
219 /*
220   set the current security context from a unix_sec_ctx
221 */
222 static NTSTATUS set_unix_security(struct unix_sec_ctx *sec)
223 {
224         seteuid(0);
225
226         if (setgroups(sec->ngroups, sec->groups) != 0) {
227                 return NT_STATUS_ACCESS_DENIED;
228         }
229         if (setegid(sec->gid) != 0) {
230                 return NT_STATUS_ACCESS_DENIED;
231         }
232         if (seteuid(sec->uid) != 0) {
233                 return NT_STATUS_ACCESS_DENIED;
234         }
235         return NT_STATUS_OK;
236 }
237
238 /*
239   form a unix_sec_ctx from the current session info
240 */
241 static NTSTATUS authinfo_to_unix_security(struct ntvfs_module_context *ntvfs,
242                                           struct smbsrv_request *req,
243                                           struct auth_serversupplied_info *info,
244                                           struct unix_sec_ctx **sec)
245 {
246         int i;
247         NTSTATUS status;
248         *sec = talloc_p(req, struct unix_sec_ctx);
249
250         status = sid_to_unixuid(ntvfs, req, info->user_sid, &(*sec)->uid);
251         if (!NT_STATUS_IS_OK(status)) {
252                 return status;
253         }
254
255         status = sid_to_unixgid(ntvfs, req, info->primary_group_sid, &(*sec)->gid);
256         if (!NT_STATUS_IS_OK(status)) {
257                 return status;
258         }
259
260         (*sec)->ngroups = info->n_domain_groups;
261         (*sec)->groups = talloc_array_p(*sec, gid_t, (*sec)->ngroups);
262         if ((*sec)->groups == NULL) {
263                 return NT_STATUS_NO_MEMORY;
264         }
265
266         for (i=0;i<(*sec)->ngroups;i++) {
267                 status = sid_to_unixgid(ntvfs, req, info->domain_groups[i], &(*sec)->groups[i]);
268                 if (!NT_STATUS_IS_OK(status)) {
269                         return status;
270                 }
271         }
272
273         return NT_STATUS_OK;
274 }
275
276 /*
277   setup our unix security context according to the session authentication info
278 */
279 static NTSTATUS unixuid_setup_security(struct ntvfs_module_context *ntvfs,
280                                        struct smbsrv_request *req, struct unix_sec_ctx **sec)
281 {
282         struct auth_serversupplied_info *info = req->session->session_info->server_info;
283         void *ctx = talloc(req, 0);
284         struct unix_sec_ctx *newsec;
285         NTSTATUS status;
286
287         *sec = save_unix_security(req);
288         if (*sec == NULL) {
289                 return NT_STATUS_NO_MEMORY;
290         }
291
292         status = authinfo_to_unix_security(ntvfs, req, info, &newsec);
293         if (!NT_STATUS_IS_OK(status)) {
294                 talloc_free(ctx);
295                 return status;
296         }
297
298         status = set_unix_security(newsec);
299         if (!NT_STATUS_IS_OK(status)) {
300                 talloc_free(ctx);
301                 return status;
302         }
303
304         talloc_free(ctx);
305
306         return NT_STATUS_OK;
307 }
308
309 /*
310   this pass through macro operates on request contexts
311 */
312 #define PASS_THRU_REQ(ntvfs, req, op, args) do { \
313         NTSTATUS status2; \
314         struct unix_sec_ctx *sec; \
315         status = unixuid_setup_security(ntvfs, req, &sec); \
316         if (NT_STATUS_IS_OK(status)) status = ntvfs_next_##op args; \
317         status2 = set_unix_security(sec); \
318         if (!NT_STATUS_IS_OK(status2)) smb_panic("Unable to reset security context"); \
319 } while (0)
320
321
322
323 /*
324   connect to a share - used when a tree_connect operation comes in.
325 */
326 static NTSTATUS unixuid_connect(struct ntvfs_module_context *ntvfs,
327                                 struct smbsrv_request *req, const char *sharename)
328 {
329         struct unixuid_private *private;
330         NTSTATUS status;
331
332         private = talloc_p(req->tcon, struct unixuid_private);
333         if (!private) {
334                 return NT_STATUS_NO_MEMORY;
335         }
336
337         private->samctx = samdb_connect(private);
338         if (private->samctx == NULL) {
339                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
340         }
341
342         ntvfs->private_data = private;
343
344         PASS_THRU_REQ(ntvfs, req, connect, (ntvfs, req, sharename));
345
346         return status;
347 }
348
349 /*
350   disconnect from a share
351 */
352 static NTSTATUS unixuid_disconnect(struct ntvfs_module_context *ntvfs,
353                                    struct smbsrv_tcon *tcon)
354 {
355         struct unixuid_private *private = ntvfs->private_data;
356         NTSTATUS status;
357
358         talloc_free(private);
359
360         status = ntvfs_next_disconnect(ntvfs, tcon);
361  
362         return status;
363 }
364
365
366 /*
367   delete a file
368 */
369 static NTSTATUS unixuid_unlink(struct ntvfs_module_context *ntvfs,
370                               struct smbsrv_request *req, struct smb_unlink *unl)
371 {
372         NTSTATUS status;
373
374         PASS_THRU_REQ(ntvfs, req, unlink, (ntvfs, req, unl));
375
376         return status;
377 }
378
379 /*
380   ioctl interface
381 */
382 static NTSTATUS unixuid_ioctl(struct ntvfs_module_context *ntvfs,
383                              struct smbsrv_request *req, union smb_ioctl *io)
384 {
385         NTSTATUS status;
386
387         PASS_THRU_REQ(ntvfs, req, ioctl, (ntvfs, req, io));
388
389         return status;
390 }
391
392 /*
393   check if a directory exists
394 */
395 static NTSTATUS unixuid_chkpath(struct ntvfs_module_context *ntvfs,
396                                struct smbsrv_request *req, struct smb_chkpath *cp)
397 {
398         NTSTATUS status;
399
400         PASS_THRU_REQ(ntvfs, req, chkpath, (ntvfs, req, cp));
401
402         return status;
403 }
404
405 /*
406   return info on a pathname
407 */
408 static NTSTATUS unixuid_qpathinfo(struct ntvfs_module_context *ntvfs,
409                                  struct smbsrv_request *req, union smb_fileinfo *info)
410 {
411         NTSTATUS status;
412
413         PASS_THRU_REQ(ntvfs, req, qpathinfo, (ntvfs, req, info));
414
415         return status;
416 }
417
418 /*
419   query info on a open file
420 */
421 static NTSTATUS unixuid_qfileinfo(struct ntvfs_module_context *ntvfs,
422                                  struct smbsrv_request *req, union smb_fileinfo *info)
423 {
424         NTSTATUS status;
425
426         PASS_THRU_REQ(ntvfs, req, qfileinfo, (ntvfs, req, info));
427
428         return status;
429 }
430
431
432 /*
433   set info on a pathname
434 */
435 static NTSTATUS unixuid_setpathinfo(struct ntvfs_module_context *ntvfs,
436                                    struct smbsrv_request *req, union smb_setfileinfo *st)
437 {
438         NTSTATUS status;
439
440         PASS_THRU_REQ(ntvfs, req, setpathinfo, (ntvfs, req, st));
441
442         return status;
443 }
444
445 /*
446   open a file
447 */
448 static NTSTATUS unixuid_open(struct ntvfs_module_context *ntvfs,
449                             struct smbsrv_request *req, union smb_open *io)
450 {
451         NTSTATUS status;
452
453         PASS_THRU_REQ(ntvfs, req, open, (ntvfs, req, io));
454
455         return status;
456 }
457
458 /*
459   create a directory
460 */
461 static NTSTATUS unixuid_mkdir(struct ntvfs_module_context *ntvfs,
462                              struct smbsrv_request *req, union smb_mkdir *md)
463 {
464         NTSTATUS status;
465
466         PASS_THRU_REQ(ntvfs, req, mkdir, (ntvfs, req, md));
467
468         return status;
469 }
470
471 /*
472   remove a directory
473 */
474 static NTSTATUS unixuid_rmdir(struct ntvfs_module_context *ntvfs,
475                              struct smbsrv_request *req, struct smb_rmdir *rd)
476 {
477         NTSTATUS status;
478
479         PASS_THRU_REQ(ntvfs, req, rmdir, (ntvfs, req, rd));
480
481         return status;
482 }
483
484 /*
485   rename a set of files
486 */
487 static NTSTATUS unixuid_rename(struct ntvfs_module_context *ntvfs,
488                               struct smbsrv_request *req, union smb_rename *ren)
489 {
490         NTSTATUS status;
491
492         PASS_THRU_REQ(ntvfs, req, rename, (ntvfs, req, ren));
493
494         return status;
495 }
496
497 /*
498   copy a set of files
499 */
500 static NTSTATUS unixuid_copy(struct ntvfs_module_context *ntvfs,
501                             struct smbsrv_request *req, struct smb_copy *cp)
502 {
503         NTSTATUS status;
504
505         PASS_THRU_REQ(ntvfs, req, copy, (ntvfs, req, cp));
506
507         return status;
508 }
509
510 /*
511   read from a file
512 */
513 static NTSTATUS unixuid_read(struct ntvfs_module_context *ntvfs,
514                             struct smbsrv_request *req, union smb_read *rd)
515 {
516         NTSTATUS status;
517
518         PASS_THRU_REQ(ntvfs, req, read, (ntvfs, req, rd));
519
520         return status;
521 }
522
523 /*
524   write to a file
525 */
526 static NTSTATUS unixuid_write(struct ntvfs_module_context *ntvfs,
527                              struct smbsrv_request *req, union smb_write *wr)
528 {
529         NTSTATUS status;
530
531         PASS_THRU_REQ(ntvfs, req, write, (ntvfs, req, wr));
532
533         return status;
534 }
535
536 /*
537   seek in a file
538 */
539 static NTSTATUS unixuid_seek(struct ntvfs_module_context *ntvfs,
540                             struct smbsrv_request *req, struct smb_seek *io)
541 {
542         NTSTATUS status;
543
544         PASS_THRU_REQ(ntvfs, req, seek, (ntvfs, req, io));
545
546         return status;
547 }
548
549 /*
550   flush a file
551 */
552 static NTSTATUS unixuid_flush(struct ntvfs_module_context *ntvfs,
553                              struct smbsrv_request *req, struct smb_flush *io)
554 {
555         NTSTATUS status;
556
557         PASS_THRU_REQ(ntvfs, req, flush, (ntvfs, req, io));
558
559         return status;
560 }
561
562 /*
563   close a file
564 */
565 static NTSTATUS unixuid_close(struct ntvfs_module_context *ntvfs,
566                              struct smbsrv_request *req, union smb_close *io)
567 {
568         NTSTATUS status;
569
570         PASS_THRU_REQ(ntvfs, req, close, (ntvfs, req, io));
571
572         return status;
573 }
574
575 /*
576   exit - closing files
577 */
578 static NTSTATUS unixuid_exit(struct ntvfs_module_context *ntvfs,
579                             struct smbsrv_request *req)
580 {
581         NTSTATUS status;
582
583         PASS_THRU_REQ(ntvfs, req, exit, (ntvfs, req));
584
585         return status;
586 }
587
588 /*
589   logoff - closing files
590 */
591 static NTSTATUS unixuid_logoff(struct ntvfs_module_context *ntvfs,
592                               struct smbsrv_request *req)
593 {
594         NTSTATUS status;
595
596         PASS_THRU_REQ(ntvfs, req, logoff, (ntvfs, req));
597
598         return status;
599 }
600
601 /*
602   lock a byte range
603 */
604 static NTSTATUS unixuid_lock(struct ntvfs_module_context *ntvfs,
605                             struct smbsrv_request *req, union smb_lock *lck)
606 {
607         NTSTATUS status;
608
609         PASS_THRU_REQ(ntvfs, req, lock, (ntvfs, req, lck));
610
611         return status;
612 }
613
614 /*
615   set info on a open file
616 */
617 static NTSTATUS unixuid_setfileinfo(struct ntvfs_module_context *ntvfs,
618                                    struct smbsrv_request *req, 
619                                    union smb_setfileinfo *info)
620 {
621         NTSTATUS status;
622
623         PASS_THRU_REQ(ntvfs, req, setfileinfo, (ntvfs, req, info));
624
625         return status;
626 }
627
628
629 /*
630   return filesystem space info
631 */
632 static NTSTATUS unixuid_fsinfo(struct ntvfs_module_context *ntvfs,
633                               struct smbsrv_request *req, union smb_fsinfo *fs)
634 {
635         NTSTATUS status;
636
637         PASS_THRU_REQ(ntvfs, req, fsinfo, (ntvfs, req, fs));
638
639         return status;
640 }
641
642 /*
643   return print queue info
644 */
645 static NTSTATUS unixuid_lpq(struct ntvfs_module_context *ntvfs,
646                            struct smbsrv_request *req, union smb_lpq *lpq)
647 {
648         NTSTATUS status;
649
650         PASS_THRU_REQ(ntvfs, req, lpq, (ntvfs, req, lpq));
651
652         return status;
653 }
654
655 /* 
656    list files in a directory matching a wildcard pattern
657 */
658 static NTSTATUS unixuid_search_first(struct ntvfs_module_context *ntvfs,
659                                     struct smbsrv_request *req, union smb_search_first *io, 
660                                     void *search_private, 
661                                     BOOL (*callback)(void *, union smb_search_data *))
662 {
663         NTSTATUS status;
664
665         PASS_THRU_REQ(ntvfs, req, search_first, (ntvfs, req, io, search_private, callback));
666
667         return status;
668 }
669
670 /* continue a search */
671 static NTSTATUS unixuid_search_next(struct ntvfs_module_context *ntvfs,
672                                    struct smbsrv_request *req, union smb_search_next *io, 
673                                    void *search_private, 
674                                    BOOL (*callback)(void *, union smb_search_data *))
675 {
676         NTSTATUS status;
677
678         PASS_THRU_REQ(ntvfs, req, search_next, (ntvfs, req, io, search_private, callback));
679
680         return status;
681 }
682
683 /* close a search */
684 static NTSTATUS unixuid_search_close(struct ntvfs_module_context *ntvfs,
685                                     struct smbsrv_request *req, union smb_search_close *io)
686 {
687         NTSTATUS status;
688
689         PASS_THRU_REQ(ntvfs, req, search_close, (ntvfs, req, io));
690
691         return status;
692 }
693
694 /* SMBtrans - not used on file shares */
695 static NTSTATUS unixuid_trans(struct ntvfs_module_context *ntvfs,
696                              struct smbsrv_request *req, struct smb_trans2 *trans2)
697 {
698         NTSTATUS status;
699
700         PASS_THRU_REQ(ntvfs, req, trans, (ntvfs, req, trans2));
701
702         return status;
703 }
704
705 /*
706   initialise the unixuid backend, registering ourselves with the ntvfs subsystem
707  */
708 NTSTATUS ntvfs_unixuid_init(void)
709 {
710         NTSTATUS ret;
711         struct ntvfs_ops ops;
712
713         ZERO_STRUCT(ops);
714
715         /* fill in all the operations */
716         ops.connect = unixuid_connect;
717         ops.disconnect = unixuid_disconnect;
718         ops.unlink = unixuid_unlink;
719         ops.chkpath = unixuid_chkpath;
720         ops.qpathinfo = unixuid_qpathinfo;
721         ops.setpathinfo = unixuid_setpathinfo;
722         ops.open = unixuid_open;
723         ops.mkdir = unixuid_mkdir;
724         ops.rmdir = unixuid_rmdir;
725         ops.rename = unixuid_rename;
726         ops.copy = unixuid_copy;
727         ops.ioctl = unixuid_ioctl;
728         ops.read = unixuid_read;
729         ops.write = unixuid_write;
730         ops.seek = unixuid_seek;
731         ops.flush = unixuid_flush;      
732         ops.close = unixuid_close;
733         ops.exit = unixuid_exit;
734         ops.lock = unixuid_lock;
735         ops.setfileinfo = unixuid_setfileinfo;
736         ops.qfileinfo = unixuid_qfileinfo;
737         ops.fsinfo = unixuid_fsinfo;
738         ops.lpq = unixuid_lpq;
739         ops.search_first = unixuid_search_first;
740         ops.search_next = unixuid_search_next;
741         ops.search_close = unixuid_search_close;
742         ops.trans = unixuid_trans;
743         ops.logoff = unixuid_logoff;
744
745         ops.name = "unixuid";
746
747         /* we register under all 3 backend types, as we are not type specific */
748         ops.type = NTVFS_DISK;  
749         ret = register_backend("ntvfs", &ops);
750         if (!NT_STATUS_IS_OK(ret)) goto failed;
751
752         ops.type = NTVFS_PRINT; 
753         ret = register_backend("ntvfs", &ops);
754         if (!NT_STATUS_IS_OK(ret)) goto failed;
755
756         ops.type = NTVFS_IPC;   
757         ret = register_backend("ntvfs", &ops);
758         if (!NT_STATUS_IS_OK(ret)) goto failed;
759         
760 failed:
761         return ret;
762 }