2 Unix SMB/CIFS implementation.
4 a pass-thru NTVFS module to setup a security context using unix
7 Copyright (C) Andrew Tridgell 2004
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.
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.
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/>.
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
31 #include "../lib/util/setid.h"
33 NTSTATUS
ntvfs_unixuid_init(void);
35 struct unixuid_private
{
36 struct security_unix_token
*last_sec_ctx
;
37 struct security_token
*last_token
;
42 pull the current security context into a security_unix_token
44 static struct security_unix_token
*save_unix_security(TALLOC_CTX
*mem_ctx
)
46 struct security_unix_token
*sec
= talloc(mem_ctx
, struct security_unix_token
);
52 sec
->ngroups
= getgroups(0, NULL
);
53 if (sec
->ngroups
== -1) {
57 sec
->groups
= talloc_array(sec
, gid_t
, sec
->ngroups
);
58 if (sec
->groups
== NULL
) {
63 if (getgroups(sec
->ngroups
, sec
->groups
) != sec
->ngroups
) {
72 set the current security context from a security_unix_token
74 static NTSTATUS
set_unix_security(struct security_unix_token
*sec
)
78 if (samba_setgroups(sec
->ngroups
, sec
->groups
) != 0) {
79 return NT_STATUS_ACCESS_DENIED
;
81 if (samba_setegid(sec
->gid
) != 0) {
82 return NT_STATUS_ACCESS_DENIED
;
84 if (samba_seteuid(sec
->uid
) != 0) {
85 return NT_STATUS_ACCESS_DENIED
;
90 static int unixuid_nesting_level
;
93 called at the start and end of a tevent nesting loop. Needs to save/restore
96 static int unixuid_event_nesting_hook(struct tevent_context
*ev
,
101 const char *location
)
103 struct security_unix_token
*sec_ctx
;
105 if (unixuid_nesting_level
== 0) {
106 /* we don't need to do anything unless we are nested
107 inside of a call in this module */
112 sec_ctx
= save_unix_security(ev
);
113 if (sec_ctx
== NULL
) {
114 DEBUG(0,("%s: Failed to save security context\n", location
));
117 *(struct security_unix_token
**)stack_ptr
= sec_ctx
;
118 if (samba_seteuid(0) != 0 || samba_setegid(0) != 0) {
119 DEBUG(0,("%s: Failed to change to root\n", location
));
123 /* called when we come out of a nesting level */
126 sec_ctx
= *(struct security_unix_token
**)stack_ptr
;
127 if (sec_ctx
== NULL
) {
128 /* this happens the first time this function
129 is called, as we install the hook while
130 inside an event in unixuid_connect() */
134 sec_ctx
= talloc_get_type_abort(sec_ctx
, struct security_unix_token
);
135 status
= set_unix_security(sec_ctx
);
136 talloc_free(sec_ctx
);
137 if (!NT_STATUS_IS_OK(status
)) {
138 DEBUG(0,("%s: Failed to revert security context (%s)\n",
139 location
, nt_errstr(status
)));
149 form a security_unix_token from the current security_token
151 static NTSTATUS
nt_token_to_unix_security(struct ntvfs_module_context
*ntvfs
,
152 struct ntvfs_request
*req
,
153 struct security_token
*token
,
154 struct security_unix_token
**sec
)
156 return security_token_to_unix_token(req
,
157 ntvfs
->ctx
->event_ctx
,
162 setup our unix security context according to the session authentication info
164 static NTSTATUS
unixuid_setup_security(struct ntvfs_module_context
*ntvfs
,
165 struct ntvfs_request
*req
, struct security_unix_token
**sec
)
167 struct unixuid_private
*priv
= ntvfs
->private_data
;
168 struct security_token
*token
;
169 struct security_unix_token
*newsec
;
172 /* If we are asked to set up, but have not had a successful
173 * session setup or tree connect, then these may not be filled
174 * in. ACCESS_DENIED is the right error code here */
175 if (req
->session_info
== NULL
|| priv
== NULL
) {
176 return NT_STATUS_ACCESS_DENIED
;
179 token
= req
->session_info
->security_token
;
181 *sec
= save_unix_security(ntvfs
);
183 return NT_STATUS_NO_MEMORY
;
186 if (token
== priv
->last_token
) {
187 newsec
= priv
->last_sec_ctx
;
189 status
= nt_token_to_unix_security(ntvfs
, req
, token
, &newsec
);
190 if (!NT_STATUS_IS_OK(status
)) {
194 if (priv
->last_sec_ctx
) {
195 talloc_free(priv
->last_sec_ctx
);
197 priv
->last_sec_ctx
= newsec
;
198 priv
->last_token
= token
;
199 talloc_steal(priv
, newsec
);
202 status
= set_unix_security(newsec
);
203 if (!NT_STATUS_IS_OK(status
)) {
212 this pass through macro operates on request contexts
214 #define PASS_THRU_REQ(ntvfs, req, op, args) do { \
216 struct security_unix_token *sec; \
217 status = unixuid_setup_security(ntvfs, req, &sec); \
218 NT_STATUS_NOT_OK_RETURN(status); \
219 unixuid_nesting_level++; \
220 status = ntvfs_next_##op args; \
221 unixuid_nesting_level--; \
222 status2 = set_unix_security(sec); \
224 if (!NT_STATUS_IS_OK(status2)) smb_panic("Unable to reset security context"); \
230 connect to a share - used when a tree_connect operation comes in.
232 static NTSTATUS
unixuid_connect(struct ntvfs_module_context
*ntvfs
,
233 struct ntvfs_request
*req
, union smb_tcon
*tcon
)
235 struct unixuid_private
*priv
;
238 priv
= talloc(ntvfs
, struct unixuid_private
);
240 return NT_STATUS_NO_MEMORY
;
243 priv
->last_sec_ctx
= NULL
;
244 priv
->last_token
= NULL
;
245 ntvfs
->private_data
= priv
;
247 tevent_loop_set_nesting_hook(ntvfs
->ctx
->event_ctx
,
248 unixuid_event_nesting_hook
,
249 &unixuid_nesting_level
);
251 /* we don't use PASS_THRU_REQ here, as the connect operation runs with
252 root privileges. This allows the backends to setup any database
253 links they might need during the connect. */
254 status
= ntvfs_next_connect(ntvfs
, req
, tcon
);
260 disconnect from a share
262 static NTSTATUS
unixuid_disconnect(struct ntvfs_module_context
*ntvfs
)
264 struct unixuid_private
*priv
= ntvfs
->private_data
;
268 ntvfs
->private_data
= NULL
;
270 status
= ntvfs_next_disconnect(ntvfs
);
279 static NTSTATUS
unixuid_unlink(struct ntvfs_module_context
*ntvfs
,
280 struct ntvfs_request
*req
,
281 union smb_unlink
*unl
)
285 PASS_THRU_REQ(ntvfs
, req
, unlink
, (ntvfs
, req
, unl
));
293 static NTSTATUS
unixuid_ioctl(struct ntvfs_module_context
*ntvfs
,
294 struct ntvfs_request
*req
, union smb_ioctl
*io
)
298 PASS_THRU_REQ(ntvfs
, req
, ioctl
, (ntvfs
, req
, io
));
304 check if a directory exists
306 static NTSTATUS
unixuid_chkpath(struct ntvfs_module_context
*ntvfs
,
307 struct ntvfs_request
*req
,
308 union smb_chkpath
*cp
)
312 PASS_THRU_REQ(ntvfs
, req
, chkpath
, (ntvfs
, req
, cp
));
318 return info on a pathname
320 static NTSTATUS
unixuid_qpathinfo(struct ntvfs_module_context
*ntvfs
,
321 struct ntvfs_request
*req
, union smb_fileinfo
*info
)
325 PASS_THRU_REQ(ntvfs
, req
, qpathinfo
, (ntvfs
, req
, info
));
331 query info on a open file
333 static NTSTATUS
unixuid_qfileinfo(struct ntvfs_module_context
*ntvfs
,
334 struct ntvfs_request
*req
, union smb_fileinfo
*info
)
338 PASS_THRU_REQ(ntvfs
, req
, qfileinfo
, (ntvfs
, req
, info
));
345 set info on a pathname
347 static NTSTATUS
unixuid_setpathinfo(struct ntvfs_module_context
*ntvfs
,
348 struct ntvfs_request
*req
, union smb_setfileinfo
*st
)
352 PASS_THRU_REQ(ntvfs
, req
, setpathinfo
, (ntvfs
, req
, st
));
360 static NTSTATUS
unixuid_open(struct ntvfs_module_context
*ntvfs
,
361 struct ntvfs_request
*req
, union smb_open
*io
)
365 PASS_THRU_REQ(ntvfs
, req
, open
, (ntvfs
, req
, io
));
373 static NTSTATUS
unixuid_mkdir(struct ntvfs_module_context
*ntvfs
,
374 struct ntvfs_request
*req
, union smb_mkdir
*md
)
378 PASS_THRU_REQ(ntvfs
, req
, mkdir
, (ntvfs
, req
, md
));
386 static NTSTATUS
unixuid_rmdir(struct ntvfs_module_context
*ntvfs
,
387 struct ntvfs_request
*req
, struct smb_rmdir
*rd
)
391 PASS_THRU_REQ(ntvfs
, req
, rmdir
, (ntvfs
, req
, rd
));
397 rename a set of files
399 static NTSTATUS
unixuid_rename(struct ntvfs_module_context
*ntvfs
,
400 struct ntvfs_request
*req
, union smb_rename
*ren
)
404 PASS_THRU_REQ(ntvfs
, req
, rename
, (ntvfs
, req
, ren
));
412 static NTSTATUS
unixuid_copy(struct ntvfs_module_context
*ntvfs
,
413 struct ntvfs_request
*req
, struct smb_copy
*cp
)
417 PASS_THRU_REQ(ntvfs
, req
, copy
, (ntvfs
, req
, cp
));
425 static NTSTATUS
unixuid_read(struct ntvfs_module_context
*ntvfs
,
426 struct ntvfs_request
*req
, union smb_read
*rd
)
430 PASS_THRU_REQ(ntvfs
, req
, read
, (ntvfs
, req
, rd
));
438 static NTSTATUS
unixuid_write(struct ntvfs_module_context
*ntvfs
,
439 struct ntvfs_request
*req
, union smb_write
*wr
)
443 PASS_THRU_REQ(ntvfs
, req
, write
, (ntvfs
, req
, wr
));
451 static NTSTATUS
unixuid_seek(struct ntvfs_module_context
*ntvfs
,
452 struct ntvfs_request
*req
,
457 PASS_THRU_REQ(ntvfs
, req
, seek
, (ntvfs
, req
, io
));
465 static NTSTATUS
unixuid_flush(struct ntvfs_module_context
*ntvfs
,
466 struct ntvfs_request
*req
,
471 PASS_THRU_REQ(ntvfs
, req
, flush
, (ntvfs
, req
, io
));
479 static NTSTATUS
unixuid_close(struct ntvfs_module_context
*ntvfs
,
480 struct ntvfs_request
*req
, union smb_close
*io
)
484 PASS_THRU_REQ(ntvfs
, req
, close
, (ntvfs
, req
, io
));
492 static NTSTATUS
unixuid_exit(struct ntvfs_module_context
*ntvfs
,
493 struct ntvfs_request
*req
)
497 PASS_THRU_REQ(ntvfs
, req
, exit
, (ntvfs
, req
));
503 logoff - closing files
505 static NTSTATUS
unixuid_logoff(struct ntvfs_module_context
*ntvfs
,
506 struct ntvfs_request
*req
)
508 struct unixuid_private
*priv
= ntvfs
->private_data
;
511 PASS_THRU_REQ(ntvfs
, req
, logoff
, (ntvfs
, req
));
513 priv
->last_token
= NULL
;
521 static NTSTATUS
unixuid_async_setup(struct ntvfs_module_context
*ntvfs
,
522 struct ntvfs_request
*req
,
527 PASS_THRU_REQ(ntvfs
, req
, async_setup
, (ntvfs
, req
, private_data
));
533 cancel an async request
535 static NTSTATUS
unixuid_cancel(struct ntvfs_module_context
*ntvfs
,
536 struct ntvfs_request
*req
)
540 PASS_THRU_REQ(ntvfs
, req
, cancel
, (ntvfs
, req
));
548 static NTSTATUS
unixuid_notify(struct ntvfs_module_context
*ntvfs
,
549 struct ntvfs_request
*req
, union smb_notify
*info
)
553 PASS_THRU_REQ(ntvfs
, req
, notify
, (ntvfs
, req
, info
));
561 static NTSTATUS
unixuid_lock(struct ntvfs_module_context
*ntvfs
,
562 struct ntvfs_request
*req
, union smb_lock
*lck
)
566 PASS_THRU_REQ(ntvfs
, req
, lock
, (ntvfs
, req
, lck
));
572 set info on a open file
574 static NTSTATUS
unixuid_setfileinfo(struct ntvfs_module_context
*ntvfs
,
575 struct ntvfs_request
*req
,
576 union smb_setfileinfo
*info
)
580 PASS_THRU_REQ(ntvfs
, req
, setfileinfo
, (ntvfs
, req
, info
));
587 return filesystem space info
589 static NTSTATUS
unixuid_fsinfo(struct ntvfs_module_context
*ntvfs
,
590 struct ntvfs_request
*req
, union smb_fsinfo
*fs
)
594 PASS_THRU_REQ(ntvfs
, req
, fsinfo
, (ntvfs
, req
, fs
));
600 return print queue info
602 static NTSTATUS
unixuid_lpq(struct ntvfs_module_context
*ntvfs
,
603 struct ntvfs_request
*req
, union smb_lpq
*lpq
)
607 PASS_THRU_REQ(ntvfs
, req
, lpq
, (ntvfs
, req
, lpq
));
613 list files in a directory matching a wildcard pattern
615 static NTSTATUS
unixuid_search_first(struct ntvfs_module_context
*ntvfs
,
616 struct ntvfs_request
*req
, union smb_search_first
*io
,
617 void *search_private
,
618 bool (*callback
)(void *, const union smb_search_data
*))
622 PASS_THRU_REQ(ntvfs
, req
, search_first
, (ntvfs
, req
, io
, search_private
, callback
));
627 /* continue a search */
628 static NTSTATUS
unixuid_search_next(struct ntvfs_module_context
*ntvfs
,
629 struct ntvfs_request
*req
, union smb_search_next
*io
,
630 void *search_private
,
631 bool (*callback
)(void *, const union smb_search_data
*))
635 PASS_THRU_REQ(ntvfs
, req
, search_next
, (ntvfs
, req
, io
, search_private
, callback
));
641 static NTSTATUS
unixuid_search_close(struct ntvfs_module_context
*ntvfs
,
642 struct ntvfs_request
*req
, union smb_search_close
*io
)
646 PASS_THRU_REQ(ntvfs
, req
, search_close
, (ntvfs
, req
, io
));
651 /* SMBtrans - not used on file shares */
652 static NTSTATUS
unixuid_trans(struct ntvfs_module_context
*ntvfs
,
653 struct ntvfs_request
*req
, struct smb_trans2
*trans2
)
657 PASS_THRU_REQ(ntvfs
, req
, trans
, (ntvfs
, req
, trans2
));
663 initialise the unixuid backend, registering ourselves with the ntvfs subsystem
665 NTSTATUS
ntvfs_unixuid_init(void)
668 struct ntvfs_ops ops
;
669 NTVFS_CURRENT_CRITICAL_SIZES(vers
);
673 /* fill in all the operations */
674 ops
.connect_fn
= unixuid_connect
;
675 ops
.disconnect_fn
= unixuid_disconnect
;
676 ops
.unlink_fn
= unixuid_unlink
;
677 ops
.chkpath_fn
= unixuid_chkpath
;
678 ops
.qpathinfo_fn
= unixuid_qpathinfo
;
679 ops
.setpathinfo_fn
= unixuid_setpathinfo
;
680 ops
.open_fn
= unixuid_open
;
681 ops
.mkdir_fn
= unixuid_mkdir
;
682 ops
.rmdir_fn
= unixuid_rmdir
;
683 ops
.rename_fn
= unixuid_rename
;
684 ops
.copy_fn
= unixuid_copy
;
685 ops
.ioctl_fn
= unixuid_ioctl
;
686 ops
.read_fn
= unixuid_read
;
687 ops
.write_fn
= unixuid_write
;
688 ops
.seek_fn
= unixuid_seek
;
689 ops
.flush_fn
= unixuid_flush
;
690 ops
.close_fn
= unixuid_close
;
691 ops
.exit_fn
= unixuid_exit
;
692 ops
.lock_fn
= unixuid_lock
;
693 ops
.setfileinfo_fn
= unixuid_setfileinfo
;
694 ops
.qfileinfo_fn
= unixuid_qfileinfo
;
695 ops
.fsinfo_fn
= unixuid_fsinfo
;
696 ops
.lpq_fn
= unixuid_lpq
;
697 ops
.search_first_fn
= unixuid_search_first
;
698 ops
.search_next_fn
= unixuid_search_next
;
699 ops
.search_close_fn
= unixuid_search_close
;
700 ops
.trans_fn
= unixuid_trans
;
701 ops
.logoff_fn
= unixuid_logoff
;
702 ops
.async_setup_fn
= unixuid_async_setup
;
703 ops
.cancel_fn
= unixuid_cancel
;
704 ops
.notify_fn
= unixuid_notify
;
706 ops
.name
= "unixuid";
708 /* we register under all 3 backend types, as we are not type specific */
709 ops
.type
= NTVFS_DISK
;
710 ret
= ntvfs_register(&ops
, &vers
);
711 if (!NT_STATUS_IS_OK(ret
)) goto failed
;
713 ops
.type
= NTVFS_PRINT
;
714 ret
= ntvfs_register(&ops
, &vers
);
715 if (!NT_STATUS_IS_OK(ret
)) goto failed
;
717 ops
.type
= NTVFS_IPC
;
718 ret
= ntvfs_register(&ops
, &vers
);
719 if (!NT_STATUS_IS_OK(ret
)) goto failed
;