Fix bug #1254 - write list not working under share-level security
[Samba.git] / source / smbd / uid.c
blob07a42fa7fee9407ab1a68a835358fa2f2abbc5eb
1 /*
2 Unix SMB/CIFS implementation.
3 uid/user handling
4 Copyright (C) Andrew Tridgell 1992-1998
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "includes.h"
23 /* what user is current? */
24 extern struct current_user current_user;
26 /****************************************************************************
27 Iterator functions for getting all gid's from current_user.
28 ****************************************************************************/
30 gid_t get_current_user_gid_first(int *piterator)
32 *piterator = 0;
33 return current_user.ut.gid;
36 gid_t get_current_user_gid_next(int *piterator)
38 gid_t ret;
40 if (!current_user.ut.groups || *piterator >= current_user.ut.ngroups) {
41 return (gid_t)-1;
44 ret = current_user.ut.groups[*piterator];
45 (*piterator) += 1;
46 return ret;
49 /****************************************************************************
50 Become the guest user without changing the security context stack.
51 ****************************************************************************/
53 BOOL change_to_guest(void)
55 static struct passwd *pass=NULL;
57 if (!pass) {
58 /* Don't need to free() this as its stored in a static */
59 pass = getpwnam_alloc(NULL, lp_guestaccount());
60 if (!pass)
61 return(False);
64 #ifdef AIX
65 /* MWW: From AIX FAQ patch to WU-ftpd: call initgroups before
66 setting IDs */
67 initgroups(pass->pw_name, pass->pw_gid);
68 #endif
70 set_sec_ctx(pass->pw_uid, pass->pw_gid, 0, NULL, NULL);
72 current_user.conn = NULL;
73 current_user.vuid = UID_FIELD_INVALID;
75 TALLOC_FREE(pass);
76 pass = NULL;
78 return True;
81 /*******************************************************************
82 Check if a username is OK.
83 ********************************************************************/
85 static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum)
87 unsigned int i;
88 struct vuid_cache_entry *ent = NULL;
89 BOOL readonly_share;
90 NT_USER_TOKEN *token;
92 for (i=0;i<conn->vuid_cache.entries && i< VUID_CACHE_SIZE;i++) {
93 if (conn->vuid_cache.array[i].vuid == vuser->vuid) {
94 ent = &conn->vuid_cache.array[i];
95 conn->read_only = ent->read_only;
96 conn->admin_user = ent->admin_user;
97 return(True);
101 if (!user_ok_token(vuser->user.unix_name, vuser->nt_user_token, snum))
102 return(False);
104 readonly_share = is_share_read_only_for_token(vuser->user.unix_name,
105 vuser->nt_user_token,
106 SNUM(conn));
108 token = conn->nt_user_token ?
109 conn->nt_user_token : vuser->nt_user_token;
111 if (!readonly_share &&
112 !share_access_check(token, lp_servicename(snum),
113 FILE_WRITE_DATA)) {
114 /* smb.conf allows r/w, but the security descriptor denies
115 * write. Fall back to looking at readonly. */
116 readonly_share = True;
117 DEBUG(5,("falling back to read-only access-evaluation due to "
118 "security descriptor\n"));
121 if (!share_access_check(token, lp_servicename(snum),
122 readonly_share ?
123 FILE_READ_DATA : FILE_WRITE_DATA)) {
124 return False;
127 i = conn->vuid_cache.entries % VUID_CACHE_SIZE;
128 if (conn->vuid_cache.entries < VUID_CACHE_SIZE)
129 conn->vuid_cache.entries++;
131 ent = &conn->vuid_cache.array[i];
132 ent->vuid = vuser->vuid;
133 ent->read_only = readonly_share;
135 ent->admin_user = token_contains_name_in_list(
136 vuser->user.unix_name, NULL, vuser->nt_user_token,
137 lp_admin_users(SNUM(conn)));
139 conn->read_only = ent->read_only;
140 conn->admin_user = ent->admin_user;
142 return(True);
145 /*******************************************************************
146 Check if a username is OK in share level security.
147 ********************************************************************/
149 static bool check_user_ok_sharelevel_security(connection_struct *conn,
150 const char *unix_name,
151 int snum)
153 NT_USER_TOKEN *token = conn->nt_user_token;
155 if (!user_ok_token(unix_name, token, snum)) {
156 return false;
159 conn->read_only = is_share_read_only_for_token(unix_name,
160 token,
161 snum);
163 if (!conn->read_only &&
164 !share_access_check(token, lp_servicename(snum),
165 FILE_WRITE_DATA)) {
166 /* smb.conf allows r/w, but the security descriptor denies
167 * write. Fall back to looking at readonly. */
168 conn->read_only = true;
169 DEBUG(5,("falling back to read-only access-evaluation due to "
170 "security descriptor\n"));
173 if (!share_access_check(token, lp_servicename(snum),
174 conn->read_only ?
175 FILE_READ_DATA : FILE_WRITE_DATA)) {
176 return false;
179 conn->admin_user = token_contains_name_in_list(
180 unix_name, NULL, token,
181 lp_admin_users(SNUM(conn)));
183 return true;
187 /****************************************************************************
188 Become the user of a connection number without changing the security context
189 stack, but modify the current_user entries.
190 ****************************************************************************/
192 BOOL change_to_user(connection_struct *conn, uint16 vuid)
194 enum security_types sec = (enum security_types)lp_security();
195 user_struct *vuser = get_valid_user_struct(vuid);
196 int snum;
197 gid_t gid;
198 uid_t uid;
199 char group_c;
200 BOOL must_free_token = False;
201 NT_USER_TOKEN *token = NULL;
202 int num_groups = 0;
203 gid_t *group_list = NULL;
205 if (!conn) {
206 DEBUG(2,("change_to_user: Connection not open\n"));
207 return(False);
211 * We need a separate check in security=share mode due to vuid
212 * always being UID_FIELD_INVALID. If we don't do this then
213 * in share mode security we are *always* changing uid's between
214 * SMB's - this hurts performance - Badly.
217 if((sec == SEC_SHARE) && (current_user.conn == conn) &&
218 (current_user.ut.uid == conn->uid)) {
219 DEBUG(4,("change_to_user: Skipping user change - already "
220 "user\n"));
221 return(True);
222 } else if ((current_user.conn == conn) &&
223 (vuser != 0) && (current_user.vuid == vuid) &&
224 (current_user.ut.uid == vuser->uid)) {
225 DEBUG(4,("change_to_user: Skipping user change - already "
226 "user\n"));
227 return(True);
230 snum = SNUM(conn);
232 if ((vuser) && !check_user_ok(conn, vuser, snum)) {
233 DEBUG(2,("change_to_user: SMB user %s (unix user %s, vuid %d) "
234 "not permitted access to share %s.\n",
235 vuser->user.smb_name, vuser->user.unix_name, vuid,
236 lp_servicename(snum)));
237 return False;
238 } else if ((sec == SEC_SHARE) && !check_user_ok_sharelevel_security(conn,
239 conn->user, snum)) {
240 DEBUG(2,("change_to_user: unix user %s "
241 "not permitted access to share %s.\n",
242 conn->user,
243 lp_servicename(snum)));
244 return false;
247 if (conn->force_user) /* security = share sets this too */ {
248 uid = conn->uid;
249 gid = conn->gid;
250 group_list = conn->groups;
251 num_groups = conn->ngroups;
252 token = conn->nt_user_token;
253 } else if (vuser) {
254 uid = conn->admin_user ? 0 : vuser->uid;
255 gid = vuser->gid;
256 num_groups = vuser->n_groups;
257 group_list = vuser->groups;
258 token = vuser->nt_user_token;
259 } else {
260 DEBUG(2,("change_to_user: Invalid vuid used %d in accessing "
261 "share %s.\n",vuid, lp_servicename(snum) ));
262 return False;
266 * See if we should force group for this service.
267 * If so this overrides any group set in the force
268 * user code.
271 if((group_c = *lp_force_group(snum))) {
273 token = dup_nt_token(NULL, token);
274 if (token == NULL) {
275 DEBUG(0, ("dup_nt_token failed\n"));
276 return False;
278 must_free_token = True;
280 if(group_c == '+') {
283 * Only force group if the user is a member of
284 * the service group. Check the group memberships for
285 * this user (we already have this) to
286 * see if we should force the group.
289 int i;
290 for (i = 0; i < num_groups; i++) {
291 if (group_list[i] == conn->gid) {
292 gid = conn->gid;
293 gid_to_sid(&token->user_sids[1], gid);
294 break;
297 } else {
298 gid = conn->gid;
299 gid_to_sid(&token->user_sids[1], gid);
303 /* Now set current_user since we will immediately also call
304 set_sec_ctx() */
306 current_user.ut.ngroups = num_groups;
307 current_user.ut.groups = group_list;
309 set_sec_ctx(uid, gid, current_user.ut.ngroups, current_user.ut.groups,
310 token);
313 * Free the new token (as set_sec_ctx copies it).
316 if (must_free_token)
317 TALLOC_FREE(token);
319 current_user.conn = conn;
320 current_user.vuid = vuid;
322 DEBUG(5,("change_to_user uid=(%d,%d) gid=(%d,%d)\n",
323 (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
325 return(True);
328 /****************************************************************************
329 Go back to being root without changing the security context stack,
330 but modify the current_user entries.
331 ****************************************************************************/
333 BOOL change_to_root_user(void)
335 set_root_sec_ctx();
337 DEBUG(5,("change_to_root_user: now uid=(%d,%d) gid=(%d,%d)\n",
338 (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
340 current_user.conn = NULL;
341 current_user.vuid = UID_FIELD_INVALID;
343 return(True);
346 /****************************************************************************
347 Become the user of an authenticated connected named pipe.
348 When this is called we are currently running as the connection
349 user. Doesn't modify current_user.
350 ****************************************************************************/
352 BOOL become_authenticated_pipe_user(pipes_struct *p)
354 if (!push_sec_ctx())
355 return False;
357 set_sec_ctx(p->pipe_user.ut.uid, p->pipe_user.ut.gid,
358 p->pipe_user.ut.ngroups, p->pipe_user.ut.groups,
359 p->pipe_user.nt_user_token);
361 return True;
364 /****************************************************************************
365 Unbecome the user of an authenticated connected named pipe.
366 When this is called we are running as the authenticated pipe
367 user and need to go back to being the connection user. Doesn't modify
368 current_user.
369 ****************************************************************************/
371 BOOL unbecome_authenticated_pipe_user(void)
373 return pop_sec_ctx();
376 /****************************************************************************
377 Utility functions used by become_xxx/unbecome_xxx.
378 ****************************************************************************/
380 struct conn_ctx {
381 connection_struct *conn;
382 uint16 vuid;
385 /* A stack of current_user connection contexts. */
387 static struct conn_ctx conn_ctx_stack[MAX_SEC_CTX_DEPTH];
388 static int conn_ctx_stack_ndx;
390 static void push_conn_ctx(void)
392 struct conn_ctx *ctx_p;
394 /* Check we don't overflow our stack */
396 if (conn_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
397 DEBUG(0, ("Connection context stack overflow!\n"));
398 smb_panic("Connection context stack overflow!\n");
401 /* Store previous user context */
402 ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx];
404 ctx_p->conn = current_user.conn;
405 ctx_p->vuid = current_user.vuid;
407 DEBUG(3, ("push_conn_ctx(%u) : conn_ctx_stack_ndx = %d\n",
408 (unsigned int)ctx_p->vuid, conn_ctx_stack_ndx ));
410 conn_ctx_stack_ndx++;
413 static void pop_conn_ctx(void)
415 struct conn_ctx *ctx_p;
417 /* Check for stack underflow. */
419 if (conn_ctx_stack_ndx == 0) {
420 DEBUG(0, ("Connection context stack underflow!\n"));
421 smb_panic("Connection context stack underflow!\n");
424 conn_ctx_stack_ndx--;
425 ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx];
427 current_user.conn = ctx_p->conn;
428 current_user.vuid = ctx_p->vuid;
430 ctx_p->conn = NULL;
431 ctx_p->vuid = UID_FIELD_INVALID;
434 /****************************************************************************
435 Temporarily become a root user. Must match with unbecome_root(). Saves and
436 restores the connection context.
437 ****************************************************************************/
439 void become_root(void)
441 push_sec_ctx();
442 push_conn_ctx();
443 set_root_sec_ctx();
446 /* Unbecome the root user */
448 void unbecome_root(void)
450 pop_sec_ctx();
451 pop_conn_ctx();
454 /****************************************************************************
455 Push the current security context then force a change via change_to_user().
456 Saves and restores the connection context.
457 ****************************************************************************/
459 BOOL become_user(connection_struct *conn, uint16 vuid)
461 if (!push_sec_ctx())
462 return False;
464 push_conn_ctx();
466 if (!change_to_user(conn, vuid)) {
467 pop_sec_ctx();
468 pop_conn_ctx();
469 return False;
472 return True;
475 BOOL unbecome_user(void)
477 pop_sec_ctx();
478 pop_conn_ctx();
479 return True;