f6b73d747358707417e55f656a9c4fb5cdf4bdfe
[sfrench/samba-autobuild/.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 3 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, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "system/filesys.h"
25 #include "system/passwd.h"
26 #include "auth/auth.h"
27 #include "ntvfs/ntvfs.h"
28 #include "libcli/wbclient/wbclient.h"
29 #define TEVENT_DEPRECATED
30 #include <tevent.h>
31
32 #if defined(UID_WRAPPER)
33 #if !defined(UID_WRAPPER_REPLACE) && !defined(UID_WRAPPER_NOT_REPLACE)
34 #define UID_WRAPPER_REPLACE
35 #include "../uid_wrapper/uid_wrapper.h"
36 #endif
37 #else
38 #define uwrap_enabled() 0
39 #endif
40
41
42 struct unixuid_private {
43         struct wbc_context *wbc_ctx;
44         struct unix_sec_ctx *last_sec_ctx;
45         struct security_token *last_token;
46 };
47
48
49
50 struct unix_sec_ctx {
51         uid_t uid;
52         gid_t gid;
53         unsigned int ngroups;
54         gid_t *groups;
55 };
56
57 /*
58   pull the current security context into a unix_sec_ctx
59 */
60 static struct unix_sec_ctx *save_unix_security(TALLOC_CTX *mem_ctx)
61 {
62         struct unix_sec_ctx *sec = talloc(mem_ctx, struct unix_sec_ctx);
63         if (sec == NULL) {
64                 return NULL;
65         }
66         sec->uid = geteuid();
67         sec->gid = getegid();
68         sec->ngroups = getgroups(0, NULL);
69         if (sec->ngroups == -1) {
70                 talloc_free(sec);
71                 return NULL;
72         }
73         sec->groups = talloc_array(sec, gid_t, sec->ngroups);
74         if (sec->groups == NULL) {
75                 talloc_free(sec);
76                 return NULL;
77         }
78
79         if (getgroups(sec->ngroups, sec->groups) != sec->ngroups) {
80                 talloc_free(sec);
81                 return NULL;
82         }
83
84         return sec;
85 }
86
87 /*
88   set the current security context from a unix_sec_ctx
89 */
90 static NTSTATUS set_unix_security(struct unix_sec_ctx *sec)
91 {
92         seteuid(0);
93
94         if (setgroups(sec->ngroups, sec->groups) != 0) {
95                 return NT_STATUS_ACCESS_DENIED;
96         }
97         if (setegid(sec->gid) != 0) {
98                 return NT_STATUS_ACCESS_DENIED;
99         }
100         if (seteuid(sec->uid) != 0) {
101                 return NT_STATUS_ACCESS_DENIED;
102         }
103         return NT_STATUS_OK;
104 }
105
106 static int unixuid_nesting_level;
107
108 /*
109   called at the start and end of a tevent nesting loop. Needs to save/restore
110   unix security context
111  */
112 static int unixuid_event_nesting_hook(struct tevent_context *ev,
113                                       void *private_data,
114                                       uint32_t level,
115                                       bool begin,
116                                       void *stack_ptr,
117                                       const char *location)
118 {
119         struct unix_sec_ctx *sec_ctx;
120
121         if (unixuid_nesting_level == 0) {
122                 /* we don't need to do anything unless we are nested
123                    inside of a call in this module */
124                 return 0;
125         }
126
127         if (begin) {
128                 sec_ctx = save_unix_security(ev);
129                 if (sec_ctx == NULL) {
130                         DEBUG(0,("%s: Failed to save security context\n", location));
131                         return -1;
132                 }
133                 *(struct unix_sec_ctx **)stack_ptr = sec_ctx;
134                 if (seteuid(0) != 0 || setegid(0) != 0) {
135                         DEBUG(0,("%s: Failed to change to root\n", location));
136                         return -1;                      
137                 }
138         } else {
139                 /* called when we come out of a nesting level */
140                 NTSTATUS status;
141
142                 sec_ctx = *(struct unix_sec_ctx **)stack_ptr;
143                 if (sec_ctx == NULL) {
144                         /* this happens the first time this function
145                            is called, as we install the hook while
146                            inside an event in unixuid_connect() */
147                         return 0;
148                 }
149
150                 sec_ctx = talloc_get_type_abort(sec_ctx, struct unix_sec_ctx);
151                 status = set_unix_security(sec_ctx);
152                 talloc_free(sec_ctx);
153                 if (!NT_STATUS_IS_OK(status)) {
154                         DEBUG(0,("%s: Failed to revert security context (%s)\n", 
155                                  location, nt_errstr(status)));
156                         return -1;
157                 }
158         }
159
160         return 0;
161 }
162
163
164 /*
165   form a unix_sec_ctx from the current security_token
166 */
167 static NTSTATUS nt_token_to_unix_security(struct ntvfs_module_context *ntvfs,
168                                           struct ntvfs_request *req,
169                                           struct security_token *token,
170                                           struct unix_sec_ctx **sec)
171 {
172         struct unixuid_private *priv = ntvfs->private_data;
173         int i;
174         NTSTATUS status;
175         struct id_map *ids;
176         struct composite_context *ctx;
177         *sec = talloc(req, struct unix_sec_ctx);
178
179         /* we can't do unix security without a user and group */
180         if (token->num_sids < 2) {
181                 return NT_STATUS_ACCESS_DENIED;
182         }
183
184         ids = talloc_array(req, struct id_map, token->num_sids);
185         NT_STATUS_HAVE_NO_MEMORY(ids);
186
187         ids[0].unixid = NULL;
188         ids[0].sid = token->user_sid;
189         ids[0].status = ID_UNKNOWN;
190
191         ids[1].unixid = NULL;
192         ids[1].sid = token->group_sid;
193         ids[1].status = ID_UNKNOWN;
194
195         (*sec)->ngroups = token->num_sids - 2;
196         (*sec)->groups = talloc_array(*sec, gid_t, (*sec)->ngroups);
197         NT_STATUS_HAVE_NO_MEMORY((*sec)->groups);
198
199         for (i=0;i<(*sec)->ngroups;i++) {
200                 ids[i+2].unixid = NULL;
201                 ids[i+2].sid = token->sids[i+2];
202                 ids[i+2].status = ID_UNKNOWN;
203         }
204
205         ctx = wbc_sids_to_xids_send(priv->wbc_ctx, ids, token->num_sids, ids);
206         NT_STATUS_HAVE_NO_MEMORY(ctx);
207
208         status = wbc_sids_to_xids_recv(ctx, &ids);
209         NT_STATUS_NOT_OK_RETURN(status);
210
211         if (ids[0].unixid->type == ID_TYPE_BOTH ||
212             ids[0].unixid->type == ID_TYPE_UID) {
213                 (*sec)->uid = ids[0].unixid->id;
214         } else {
215                 return NT_STATUS_INVALID_SID;
216         }
217
218         if (ids[1].unixid->type == ID_TYPE_BOTH ||
219             ids[1].unixid->type == ID_TYPE_GID) {
220                 (*sec)->gid = ids[1].unixid->id;
221         } else {
222                 return NT_STATUS_INVALID_SID;
223         }
224
225         for (i=0;i<(*sec)->ngroups;i++) {
226                 if (ids[i+2].unixid->type == ID_TYPE_BOTH ||
227                     ids[i+2].unixid->type == ID_TYPE_GID) {
228                         (*sec)->groups[i] = ids[i+2].unixid->id;
229                 } else {
230                         return NT_STATUS_INVALID_SID;
231                 }
232         }
233
234         return NT_STATUS_OK;
235 }
236
237 /*
238   setup our unix security context according to the session authentication info
239 */
240 static NTSTATUS unixuid_setup_security(struct ntvfs_module_context *ntvfs,
241                                        struct ntvfs_request *req, struct unix_sec_ctx **sec)
242 {
243         struct unixuid_private *priv = ntvfs->private_data;
244         struct security_token *token;
245         struct unix_sec_ctx *newsec;
246         NTSTATUS status;
247
248         /* If we are asked to set up, but have not had a successful
249          * session setup or tree connect, then these may not be filled
250          * in.  ACCESS_DENIED is the right error code here */
251         if (req->session_info == NULL || priv == NULL) {
252                 return NT_STATUS_ACCESS_DENIED;
253         }
254
255         token = req->session_info->security_token;
256
257         *sec = save_unix_security(ntvfs);
258         if (*sec == NULL) {
259                 return NT_STATUS_NO_MEMORY;
260         }
261
262         if (token == priv->last_token) {
263                 newsec = priv->last_sec_ctx;
264         } else {
265                 status = nt_token_to_unix_security(ntvfs, req, token, &newsec);
266                 if (!NT_STATUS_IS_OK(status)) {
267                         talloc_free(*sec);
268                         return status;
269                 }
270                 if (priv->last_sec_ctx) {
271                         talloc_free(priv->last_sec_ctx);
272                 }
273                 priv->last_sec_ctx = newsec;
274                 priv->last_token = token;
275                 talloc_steal(priv, newsec);
276         }
277
278         status = set_unix_security(newsec);
279         if (!NT_STATUS_IS_OK(status)) {
280                 talloc_free(*sec);
281                 return status;
282         }
283
284         return NT_STATUS_OK;
285 }
286
287 /*
288   this pass through macro operates on request contexts
289 */
290 #define PASS_THRU_REQ(ntvfs, req, op, args) do { \
291         NTSTATUS status2; \
292         struct unix_sec_ctx *sec; \
293         status = unixuid_setup_security(ntvfs, req, &sec); \
294         NT_STATUS_NOT_OK_RETURN(status); \
295         unixuid_nesting_level++; \
296         status = ntvfs_next_##op args; \
297         unixuid_nesting_level--; \
298         status2 = set_unix_security(sec); \
299         talloc_free(sec); \
300         if (!NT_STATUS_IS_OK(status2)) smb_panic("Unable to reset security context"); \
301 } while (0)
302
303
304
305 /*
306   connect to a share - used when a tree_connect operation comes in.
307 */
308 static NTSTATUS unixuid_connect(struct ntvfs_module_context *ntvfs,
309                                 struct ntvfs_request *req, union smb_tcon *tcon)
310 {
311         struct unixuid_private *priv;
312         NTSTATUS status;
313
314         priv = talloc(ntvfs, struct unixuid_private);
315         if (!priv) {
316                 return NT_STATUS_NO_MEMORY;
317         }
318
319         priv->wbc_ctx = wbc_init(priv, ntvfs->ctx->msg_ctx,
320                                     ntvfs->ctx->event_ctx);
321         if (priv->wbc_ctx == NULL) {
322                 talloc_free(priv);
323                 return NT_STATUS_INTERNAL_ERROR;
324         }
325
326         priv->last_sec_ctx = NULL;
327         priv->last_token = NULL;
328         ntvfs->private_data = priv;
329
330         tevent_loop_set_nesting_hook(ntvfs->ctx->event_ctx, 
331                                      unixuid_event_nesting_hook,
332                                      &unixuid_nesting_level);
333
334         /* we don't use PASS_THRU_REQ here, as the connect operation runs with 
335            root privileges. This allows the backends to setup any database
336            links they might need during the connect. */
337         status = ntvfs_next_connect(ntvfs, req, tcon);
338
339         return status;
340 }
341
342 /*
343   disconnect from a share
344 */
345 static NTSTATUS unixuid_disconnect(struct ntvfs_module_context *ntvfs)
346 {
347         struct unixuid_private *priv = ntvfs->private_data;
348         NTSTATUS status;
349
350         talloc_free(priv);
351         ntvfs->private_data = NULL;
352
353         status = ntvfs_next_disconnect(ntvfs);
354  
355         return status;
356 }
357
358
359 /*
360   delete a file
361 */
362 static NTSTATUS unixuid_unlink(struct ntvfs_module_context *ntvfs,
363                               struct ntvfs_request *req,
364                               union smb_unlink *unl)
365 {
366         NTSTATUS status;
367
368         PASS_THRU_REQ(ntvfs, req, unlink, (ntvfs, req, unl));
369
370         return status;
371 }
372
373 /*
374   ioctl interface
375 */
376 static NTSTATUS unixuid_ioctl(struct ntvfs_module_context *ntvfs,
377                              struct ntvfs_request *req, union smb_ioctl *io)
378 {
379         NTSTATUS status;
380
381         PASS_THRU_REQ(ntvfs, req, ioctl, (ntvfs, req, io));
382
383         return status;
384 }
385
386 /*
387   check if a directory exists
388 */
389 static NTSTATUS unixuid_chkpath(struct ntvfs_module_context *ntvfs,
390                                 struct ntvfs_request *req,
391                                 union smb_chkpath *cp)
392 {
393         NTSTATUS status;
394
395         PASS_THRU_REQ(ntvfs, req, chkpath, (ntvfs, req, cp));
396
397         return status;
398 }
399
400 /*
401   return info on a pathname
402 */
403 static NTSTATUS unixuid_qpathinfo(struct ntvfs_module_context *ntvfs,
404                                  struct ntvfs_request *req, union smb_fileinfo *info)
405 {
406         NTSTATUS status;
407
408         PASS_THRU_REQ(ntvfs, req, qpathinfo, (ntvfs, req, info));
409
410         return status;
411 }
412
413 /*
414   query info on a open file
415 */
416 static NTSTATUS unixuid_qfileinfo(struct ntvfs_module_context *ntvfs,
417                                  struct ntvfs_request *req, union smb_fileinfo *info)
418 {
419         NTSTATUS status;
420
421         PASS_THRU_REQ(ntvfs, req, qfileinfo, (ntvfs, req, info));
422
423         return status;
424 }
425
426
427 /*
428   set info on a pathname
429 */
430 static NTSTATUS unixuid_setpathinfo(struct ntvfs_module_context *ntvfs,
431                                    struct ntvfs_request *req, union smb_setfileinfo *st)
432 {
433         NTSTATUS status;
434
435         PASS_THRU_REQ(ntvfs, req, setpathinfo, (ntvfs, req, st));
436
437         return status;
438 }
439
440 /*
441   open a file
442 */
443 static NTSTATUS unixuid_open(struct ntvfs_module_context *ntvfs,
444                              struct ntvfs_request *req, union smb_open *io)
445 {
446         NTSTATUS status;
447
448         PASS_THRU_REQ(ntvfs, req, open, (ntvfs, req, io));
449
450         return status;
451 }
452
453 /*
454   create a directory
455 */
456 static NTSTATUS unixuid_mkdir(struct ntvfs_module_context *ntvfs,
457                              struct ntvfs_request *req, union smb_mkdir *md)
458 {
459         NTSTATUS status;
460
461         PASS_THRU_REQ(ntvfs, req, mkdir, (ntvfs, req, md));
462
463         return status;
464 }
465
466 /*
467   remove a directory
468 */
469 static NTSTATUS unixuid_rmdir(struct ntvfs_module_context *ntvfs,
470                              struct ntvfs_request *req, struct smb_rmdir *rd)
471 {
472         NTSTATUS status;
473
474         PASS_THRU_REQ(ntvfs, req, rmdir, (ntvfs, req, rd));
475
476         return status;
477 }
478
479 /*
480   rename a set of files
481 */
482 static NTSTATUS unixuid_rename(struct ntvfs_module_context *ntvfs,
483                               struct ntvfs_request *req, union smb_rename *ren)
484 {
485         NTSTATUS status;
486
487         PASS_THRU_REQ(ntvfs, req, rename, (ntvfs, req, ren));
488
489         return status;
490 }
491
492 /*
493   copy a set of files
494 */
495 static NTSTATUS unixuid_copy(struct ntvfs_module_context *ntvfs,
496                             struct ntvfs_request *req, struct smb_copy *cp)
497 {
498         NTSTATUS status;
499
500         PASS_THRU_REQ(ntvfs, req, copy, (ntvfs, req, cp));
501
502         return status;
503 }
504
505 /*
506   read from a file
507 */
508 static NTSTATUS unixuid_read(struct ntvfs_module_context *ntvfs,
509                             struct ntvfs_request *req, union smb_read *rd)
510 {
511         NTSTATUS status;
512
513         PASS_THRU_REQ(ntvfs, req, read, (ntvfs, req, rd));
514
515         return status;
516 }
517
518 /*
519   write to a file
520 */
521 static NTSTATUS unixuid_write(struct ntvfs_module_context *ntvfs,
522                              struct ntvfs_request *req, union smb_write *wr)
523 {
524         NTSTATUS status;
525
526         PASS_THRU_REQ(ntvfs, req, write, (ntvfs, req, wr));
527
528         return status;
529 }
530
531 /*
532   seek in a file
533 */
534 static NTSTATUS unixuid_seek(struct ntvfs_module_context *ntvfs,
535                              struct ntvfs_request *req,
536                              union smb_seek *io)
537 {
538         NTSTATUS status;
539
540         PASS_THRU_REQ(ntvfs, req, seek, (ntvfs, req, io));
541
542         return status;
543 }
544
545 /*
546   flush a file
547 */
548 static NTSTATUS unixuid_flush(struct ntvfs_module_context *ntvfs,
549                               struct ntvfs_request *req,
550                               union smb_flush *io)
551 {
552         NTSTATUS status;
553
554         PASS_THRU_REQ(ntvfs, req, flush, (ntvfs, req, io));
555
556         return status;
557 }
558
559 /*
560   close a file
561 */
562 static NTSTATUS unixuid_close(struct ntvfs_module_context *ntvfs,
563                              struct ntvfs_request *req, union smb_close *io)
564 {
565         NTSTATUS status;
566
567         PASS_THRU_REQ(ntvfs, req, close, (ntvfs, req, io));
568
569         return status;
570 }
571
572 /*
573   exit - closing files
574 */
575 static NTSTATUS unixuid_exit(struct ntvfs_module_context *ntvfs,
576                             struct ntvfs_request *req)
577 {
578         NTSTATUS status;
579
580         PASS_THRU_REQ(ntvfs, req, exit, (ntvfs, req));
581
582         return status;
583 }
584
585 /*
586   logoff - closing files
587 */
588 static NTSTATUS unixuid_logoff(struct ntvfs_module_context *ntvfs,
589                               struct ntvfs_request *req)
590 {
591         struct unixuid_private *priv = ntvfs->private_data;
592         NTSTATUS status;
593
594         PASS_THRU_REQ(ntvfs, req, logoff, (ntvfs, req));
595
596         priv->last_token = NULL;
597
598         return status;
599 }
600
601 /*
602   async setup
603 */
604 static NTSTATUS unixuid_async_setup(struct ntvfs_module_context *ntvfs,
605                                     struct ntvfs_request *req, 
606                                     void *private_data)
607 {
608         NTSTATUS status;
609
610         PASS_THRU_REQ(ntvfs, req, async_setup, (ntvfs, req, private_data));
611
612         return status;
613 }
614
615 /*
616   cancel an async request
617 */
618 static NTSTATUS unixuid_cancel(struct ntvfs_module_context *ntvfs,
619                                struct ntvfs_request *req)
620 {
621         NTSTATUS status;
622
623         PASS_THRU_REQ(ntvfs, req, cancel, (ntvfs, req));
624
625         return status;
626 }
627
628 /*
629   change notify
630 */
631 static NTSTATUS unixuid_notify(struct ntvfs_module_context *ntvfs,
632                                struct ntvfs_request *req, union smb_notify *info)
633 {
634         NTSTATUS status;
635
636         PASS_THRU_REQ(ntvfs, req, notify, (ntvfs, req, info));
637
638         return status;
639 }
640
641 /*
642   lock a byte range
643 */
644 static NTSTATUS unixuid_lock(struct ntvfs_module_context *ntvfs,
645                             struct ntvfs_request *req, union smb_lock *lck)
646 {
647         NTSTATUS status;
648
649         PASS_THRU_REQ(ntvfs, req, lock, (ntvfs, req, lck));
650
651         return status;
652 }
653
654 /*
655   set info on a open file
656 */
657 static NTSTATUS unixuid_setfileinfo(struct ntvfs_module_context *ntvfs,
658                                    struct ntvfs_request *req, 
659                                    union smb_setfileinfo *info)
660 {
661         NTSTATUS status;
662
663         PASS_THRU_REQ(ntvfs, req, setfileinfo, (ntvfs, req, info));
664
665         return status;
666 }
667
668
669 /*
670   return filesystem space info
671 */
672 static NTSTATUS unixuid_fsinfo(struct ntvfs_module_context *ntvfs,
673                               struct ntvfs_request *req, union smb_fsinfo *fs)
674 {
675         NTSTATUS status;
676
677         PASS_THRU_REQ(ntvfs, req, fsinfo, (ntvfs, req, fs));
678
679         return status;
680 }
681
682 /*
683   return print queue info
684 */
685 static NTSTATUS unixuid_lpq(struct ntvfs_module_context *ntvfs,
686                            struct ntvfs_request *req, union smb_lpq *lpq)
687 {
688         NTSTATUS status;
689
690         PASS_THRU_REQ(ntvfs, req, lpq, (ntvfs, req, lpq));
691
692         return status;
693 }
694
695 /* 
696    list files in a directory matching a wildcard pattern
697 */
698 static NTSTATUS unixuid_search_first(struct ntvfs_module_context *ntvfs,
699                                     struct ntvfs_request *req, union smb_search_first *io, 
700                                     void *search_private, 
701                                     bool (*callback)(void *, const union smb_search_data *))
702 {
703         NTSTATUS status;
704
705         PASS_THRU_REQ(ntvfs, req, search_first, (ntvfs, req, io, search_private, callback));
706
707         return status;
708 }
709
710 /* continue a search */
711 static NTSTATUS unixuid_search_next(struct ntvfs_module_context *ntvfs,
712                                    struct ntvfs_request *req, union smb_search_next *io, 
713                                    void *search_private, 
714                                    bool (*callback)(void *, const union smb_search_data *))
715 {
716         NTSTATUS status;
717
718         PASS_THRU_REQ(ntvfs, req, search_next, (ntvfs, req, io, search_private, callback));
719
720         return status;
721 }
722
723 /* close a search */
724 static NTSTATUS unixuid_search_close(struct ntvfs_module_context *ntvfs,
725                                     struct ntvfs_request *req, union smb_search_close *io)
726 {
727         NTSTATUS status;
728
729         PASS_THRU_REQ(ntvfs, req, search_close, (ntvfs, req, io));
730
731         return status;
732 }
733
734 /* SMBtrans - not used on file shares */
735 static NTSTATUS unixuid_trans(struct ntvfs_module_context *ntvfs,
736                              struct ntvfs_request *req, struct smb_trans2 *trans2)
737 {
738         NTSTATUS status;
739
740         PASS_THRU_REQ(ntvfs, req, trans, (ntvfs, req, trans2));
741
742         return status;
743 }
744
745 /*
746   initialise the unixuid backend, registering ourselves with the ntvfs subsystem
747  */
748 NTSTATUS ntvfs_unixuid_init(void)
749 {
750         NTSTATUS ret;
751         struct ntvfs_ops ops;
752         NTVFS_CURRENT_CRITICAL_SIZES(vers);
753
754         ZERO_STRUCT(ops);
755
756         /* fill in all the operations */
757         ops.connect = unixuid_connect;
758         ops.disconnect = unixuid_disconnect;
759         ops.unlink = unixuid_unlink;
760         ops.chkpath = unixuid_chkpath;
761         ops.qpathinfo = unixuid_qpathinfo;
762         ops.setpathinfo = unixuid_setpathinfo;
763         ops.open = unixuid_open;
764         ops.mkdir = unixuid_mkdir;
765         ops.rmdir = unixuid_rmdir;
766         ops.rename = unixuid_rename;
767         ops.copy = unixuid_copy;
768         ops.ioctl = unixuid_ioctl;
769         ops.read = unixuid_read;
770         ops.write = unixuid_write;
771         ops.seek = unixuid_seek;
772         ops.flush = unixuid_flush;      
773         ops.close = unixuid_close;
774         ops.exit = unixuid_exit;
775         ops.lock = unixuid_lock;
776         ops.setfileinfo = unixuid_setfileinfo;
777         ops.qfileinfo = unixuid_qfileinfo;
778         ops.fsinfo = unixuid_fsinfo;
779         ops.lpq = unixuid_lpq;
780         ops.search_first = unixuid_search_first;
781         ops.search_next = unixuid_search_next;
782         ops.search_close = unixuid_search_close;
783         ops.trans = unixuid_trans;
784         ops.logoff = unixuid_logoff;
785         ops.async_setup = unixuid_async_setup;
786         ops.cancel = unixuid_cancel;
787         ops.notify = unixuid_notify;
788
789         ops.name = "unixuid";
790
791         /* we register under all 3 backend types, as we are not type specific */
792         ops.type = NTVFS_DISK;  
793         ret = ntvfs_register(&ops, &vers);
794         if (!NT_STATUS_IS_OK(ret)) goto failed;
795
796         ops.type = NTVFS_PRINT; 
797         ret = ntvfs_register(&ops, &vers);
798         if (!NT_STATUS_IS_OK(ret)) goto failed;
799
800         ops.type = NTVFS_IPC;   
801         ret = ntvfs_register(&ops, &vers);
802         if (!NT_STATUS_IS_OK(ret)) goto failed;
803         
804 failed:
805         return ret;
806 }