BOOL change_to_guest(void) { static struct passwd *pass=NULL; if (!pass) { /* Don't need to free() this as its stored in a static */ pass = getpwnam_alloc(lp_guestaccount()); if (!pass) return(False); } #ifdef AIX /* MWW: From AIX FAQ patch to WU-ftpd: call initgroups before setting IDs */ initgroups(pass->pw_name, pass->pw_gid); #endif set_sec_ctx(pass->pw_uid, pass->pw_gid, 0, NULL, NULL); current_user.conn = NULL; current_user.vuid = UID_FIELD_INVALID; passwd_free(&pass); return True; }
BOOL change_to_guest(void) { static struct passwd *pass=NULL; static uid_t guest_uid = (uid_t)-1; static gid_t guest_gid = (gid_t)-1; static fstring guest_name; if (!pass) { pass = Get_Pwnam(lp_guestaccount(-1),True); if (!pass) return(False); guest_uid = pass->pw_uid; guest_gid = pass->pw_gid; fstrcpy(guest_name, pass->pw_name); } #ifdef AIX /* MWW: From AIX FAQ patch to WU-ftpd: call initgroups before setting IDs */ initgroups(guest_name, guest_gid); #endif set_sec_ctx(guest_uid, guest_gid, 0, NULL, NULL); current_user.conn = NULL; current_user.vuid = UID_FIELD_INVALID; return True; }
BOOL become_authenticated_pipe_user(pipes_struct *p) { if (!push_sec_ctx()) return False; set_sec_ctx(p->pipe_user.uid, p->pipe_user.gid, p->pipe_user.ngroups, p->pipe_user.groups, p->pipe_user.nt_user_token); return True; }
bool become_authenticated_pipe_user(struct auth_serversupplied_info *session_info) { if (!push_sec_ctx()) return False; set_sec_ctx(session_info->utok.uid, session_info->utok.gid, session_info->utok.ngroups, session_info->utok.groups, session_info->security_token); return True; }
bool smbd_become_authenticated_pipe_user(struct auth_session_info *session_info) { if (!push_sec_ctx()) return False; set_sec_ctx(session_info->unix_token->uid, session_info->unix_token->gid, session_info->unix_token->ngroups, session_info->unix_token->groups, session_info->security_token); DEBUG(5, ("Impersonated user: uid=(%d,%d), gid=(%d,%d)\n", (int)getuid(), (int)geteuid(), (int)getgid(), (int)getegid())); return True; }
int main (int argc, char **argv) { init_sec_ctx(); /* Become a non-root user */ setuid(1); setgid(1); /* Try to push a security context. This should fail with a smb_assert() error. */ push_sec_ctx(); set_sec_ctx(2, 2, 0, NULL); printf("FAIL\n"); return 0; }
int main (int argc, char **argv) { int ngroups, actual_ngroups; gid_t *groups, *actual_groups; extern struct current_user current_user; init_sec_ctx(); /* Initialise a security context */ get_random_grouplist(&ngroups, &groups); set_sec_ctx(1, 1, ngroups, groups); /* Become root and check */ set_root_sec_ctx(); actual_ngroups = getgroups(0, NULL); actual_groups = (gid_t *)malloc(actual_ngroups * sizeof(gid_t)); getgroups(actual_ngroups, actual_groups); if (geteuid() != 0 || getegid() != 0 || actual_ngroups != 0) { printf("FAIL: root id not set\n"); return 1; } if (current_user.uid != 0 || current_user.gid != 0 || current_user.ngroups != 0 || current_user.groups) { printf("FAIL: current_user not set correctly\n"); return 1; } printf("PASS\n"); return 0; }
bool change_to_guest(void) { struct passwd *pass; pass = Get_Pwnam_alloc(talloc_tos(), lp_guestaccount()); if (!pass) { return false; } #ifdef AIX /* MWW: From AIX FAQ patch to WU-ftpd: call initgroups before setting IDs */ initgroups(pass->pw_name, pass->pw_gid); #endif set_sec_ctx(pass->pw_uid, pass->pw_gid, 0, NULL, NULL); current_user.conn = NULL; current_user.vuid = UID_FIELD_INVALID; TALLOC_FREE(pass); return true; }
static NTSTATUS close_directory(files_struct *fsp, enum file_close_type close_type) { struct share_mode_lock *lck = 0; BOOL delete_dir = False; NTSTATUS status = NT_STATUS_OK; /* * NT can set delete_on_close of the last open * reference to a directory also. */ lck = get_share_mode_lock(NULL, fsp->dev, fsp->inode, NULL, NULL); if (lck == NULL) { DEBUG(0, ("close_directory: Could not get share mode lock for %s\n", fsp->fsp_name)); return NT_STATUS_INVALID_PARAMETER; } if (!del_share_mode(lck, fsp)) { DEBUG(0, ("close_directory: Could not delete share entry for %s\n", fsp->fsp_name)); } if (fsp->initial_delete_on_close) { BOOL became_user = False; /* Initial delete on close was set - for * directories we don't care if anyone else * wrote a real delete on close. */ if (current_user.vuid != fsp->vuid) { become_user(fsp->conn, fsp->vuid); became_user = True; } send_stat_cache_delete_message(fsp->fsp_name); set_delete_on_close_lck(lck, True, ¤t_user.ut); if (became_user) { unbecome_user(); } } delete_dir = lck->delete_on_close; if (delete_dir) { int i; /* See if others still have the dir open. If this is the * case, then don't delete. If all opens are POSIX delete now. */ for (i=0; i<lck->num_share_modes; i++) { struct share_mode_entry *e = &lck->share_modes[i]; if (is_valid_share_mode_entry(e)) { if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) { continue; } delete_dir = False; break; } } } if ((close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) && delete_dir && lck->delete_token) { /* Become the user who requested the delete. */ if (!push_sec_ctx()) { smb_panic("close_directory: failed to push sec_ctx.\n"); } set_sec_ctx(lck->delete_token->uid, lck->delete_token->gid, lck->delete_token->ngroups, lck->delete_token->groups, NULL); TALLOC_FREE(lck); status = rmdir_internals(fsp->conn, fsp->fsp_name); DEBUG(5,("close_directory: %s. Delete on close was set - " "deleting directory returned %s.\n", fsp->fsp_name, nt_errstr(status))); /* unbecome user. */ pop_sec_ctx(); /* * Ensure we remove any change notify requests that would * now fail as the directory has been deleted. */ if(NT_STATUS_IS_OK(status)) { remove_pending_change_notify_requests_by_fid(fsp, NT_STATUS_DELETE_PENDING); } } else { TALLOC_FREE(lck); remove_pending_change_notify_requests_by_fid( fsp, NT_STATUS_OK); } /* * Do the code common to files and directories. */ close_filestruct(fsp); file_free(fsp); return status; }
static NTSTATUS close_remove_share_mode(files_struct *fsp, enum file_close_type close_type) { connection_struct *conn = fsp->conn; BOOL delete_file = False; struct share_mode_lock *lck; SMB_STRUCT_STAT sbuf; NTSTATUS status = NT_STATUS_OK; int ret; /* * Lock the share entries, and determine if we should delete * on close. If so delete whilst the lock is still in effect. * This prevents race conditions with the file being created. JRA. */ lck = get_share_mode_lock(NULL, fsp->dev, fsp->inode, NULL, NULL); if (lck == NULL) { DEBUG(0, ("close_remove_share_mode: Could not get share mode " "lock for file %s\n", fsp->fsp_name)); return NT_STATUS_INVALID_PARAMETER; } if (!del_share_mode(lck, fsp)) { DEBUG(0, ("close_remove_share_mode: Could not delete share " "entry for file %s\n", fsp->fsp_name)); } if (fsp->initial_delete_on_close && (lck->delete_token == NULL)) { BOOL became_user = False; /* Initial delete on close was set and no one else * wrote a real delete on close. */ if (current_user.vuid != fsp->vuid) { become_user(conn, fsp->vuid); became_user = True; } set_delete_on_close_lck(lck, True, ¤t_user.ut); if (became_user) { unbecome_user(); } } delete_file = lck->delete_on_close; if (delete_file) { int i; /* See if others still have the file open. If this is the * case, then don't delete. If all opens are POSIX delete now. */ for (i=0; i<lck->num_share_modes; i++) { struct share_mode_entry *e = &lck->share_modes[i]; if (is_valid_share_mode_entry(e)) { if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) { continue; } delete_file = False; break; } } } /* Notify any deferred opens waiting on this close. */ notify_deferred_opens(lck); reply_to_oplock_break_requests(fsp); /* * NT can set delete_on_close of the last open * reference to a file. */ if (!(close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) || !delete_file || (lck->delete_token == NULL)) { TALLOC_FREE(lck); return NT_STATUS_OK; } /* * Ok, we have to delete the file */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set " "- deleting file.\n", fsp->fsp_name)); /* Become the user who requested the delete. */ if (!push_sec_ctx()) { smb_panic("close_remove_share_mode: file %s. failed to push " "sec_ctx.\n"); } set_sec_ctx(lck->delete_token->uid, lck->delete_token->gid, lck->delete_token->ngroups, lck->delete_token->groups, NULL); /* We can only delete the file if the name we have is still valid and hasn't been renamed. */ if (fsp->posix_open) { ret = SMB_VFS_LSTAT(conn,fsp->fsp_name,&sbuf); } else { ret = SMB_VFS_STAT(conn,fsp->fsp_name,&sbuf); } if (ret != 0) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and stat failed with error %s\n", fsp->fsp_name, strerror(errno) )); /* * Don't save the errno here, we ignore this error */ goto done; } if(sbuf.st_dev != fsp->dev || sbuf.st_ino != fsp->inode) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and dev and/or inode does not match\n", fsp->fsp_name )); DEBUG(5,("close_remove_share_mode: file %s. stored dev = %x, " "inode = %.0f stat dev = %x, inode = %.0f\n", fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, (unsigned int)sbuf.st_dev, (double)sbuf.st_ino )); /* * Don't save the errno here, we ignore this error */ goto done; } if (SMB_VFS_UNLINK(conn,fsp->fsp_name) != 0) { /* * This call can potentially fail as another smbd may * have had the file open with delete on close set and * deleted it when its last reference to this file * went away. Hence we log this but not at debug level * zero. */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and unlink failed with error %s\n", fsp->fsp_name, strerror(errno) )); status = map_nt_error_from_unix(errno); } notify_fname(conn, NOTIFY_ACTION_REMOVED, FILE_NOTIFY_CHANGE_FILE_NAME, fsp->fsp_name); /* As we now have POSIX opens which can unlink * with other open files we may have taken * this code path with more than one share mode * entry - ensure we only delete once by resetting * the delete on close flag. JRA. */ set_delete_on_close_lck(lck, False, NULL); done: /* unbecome user. */ pop_sec_ctx(); TALLOC_FREE(lck); return status; }
static NTSTATUS close_remove_share_mode(files_struct *fsp, enum file_close_type close_type) { connection_struct *conn = fsp->conn; bool delete_file = false; bool changed_user = false; struct share_mode_lock *lck = NULL; NTSTATUS status = NT_STATUS_OK; NTSTATUS tmp_status; struct file_id id; /* Ensure any pending write time updates are done. */ if (fsp->update_write_time_event) { update_write_time_handler(smbd_event_context(), fsp->update_write_time_event, timeval_current(), (void *)fsp); } /* * Lock the share entries, and determine if we should delete * on close. If so delete whilst the lock is still in effect. * This prevents race conditions with the file being created. JRA. */ lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL, NULL); if (lck == NULL) { DEBUG(0, ("close_remove_share_mode: Could not get share mode " "lock for file %s\n", fsp_str_dbg(fsp))); status = NT_STATUS_INVALID_PARAMETER; goto done; } if (fsp->write_time_forced) { DEBUG(10,("close_remove_share_mode: write time forced " "for file %s\n", fsp_str_dbg(fsp))); set_close_write_time(fsp, lck->changed_write_time); } else if (fsp->update_write_time_on_close) { /* Someone had a pending write. */ if (null_timespec(fsp->close_write_time)) { DEBUG(10,("close_remove_share_mode: update to current time " "for file %s\n", fsp_str_dbg(fsp))); /* Update to current time due to "normal" write. */ set_close_write_time(fsp, timespec_current()); } else { DEBUG(10,("close_remove_share_mode: write time pending " "for file %s\n", fsp_str_dbg(fsp))); /* Update to time set on close call. */ set_close_write_time(fsp, fsp->close_write_time); } } if (!del_share_mode(lck, fsp)) { DEBUG(0, ("close_remove_share_mode: Could not delete share " "entry for file %s\n", fsp_str_dbg(fsp))); } if (fsp->initial_delete_on_close && (lck->delete_token == NULL)) { bool became_user = False; /* Initial delete on close was set and no one else * wrote a real delete on close. */ if (current_user.vuid != fsp->vuid) { become_user(conn, fsp->vuid); became_user = True; } fsp->delete_on_close = true; set_delete_on_close_lck(lck, True, ¤t_user.ut); if (became_user) { unbecome_user(); } } delete_file = lck->delete_on_close; if (delete_file) { int i; /* See if others still have the file open. If this is the * case, then don't delete. If all opens are POSIX delete now. */ for (i=0; i<lck->num_share_modes; i++) { struct share_mode_entry *e = &lck->share_modes[i]; if (is_valid_share_mode_entry(e)) { if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) { continue; } delete_file = False; break; } } } /* Notify any deferred opens waiting on this close. */ notify_deferred_opens(lck); reply_to_oplock_break_requests(fsp); /* * NT can set delete_on_close of the last open * reference to a file. */ if (!(close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) || !delete_file || (lck->delete_token == NULL)) { TALLOC_FREE(lck); return NT_STATUS_OK; } /* * Ok, we have to delete the file */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set " "- deleting file.\n", fsp_str_dbg(fsp))); /* * Don't try to update the write time when we delete the file */ fsp->update_write_time_on_close = false; if (!unix_token_equal(lck->delete_token, ¤t_user.ut)) { /* Become the user who requested the delete. */ DEBUG(5,("close_remove_share_mode: file %s. " "Change user to uid %u\n", fsp_str_dbg(fsp), (unsigned int)lck->delete_token->uid)); if (!push_sec_ctx()) { smb_panic("close_remove_share_mode: file %s. failed to push " "sec_ctx.\n"); } set_sec_ctx(lck->delete_token->uid, lck->delete_token->gid, lck->delete_token->ngroups, lck->delete_token->groups, NULL); changed_user = true; } /* We can only delete the file if the name we have is still valid and hasn't been renamed. */ tmp_status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(tmp_status)) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and stat failed with error %s\n", fsp_str_dbg(fsp), nt_errstr(tmp_status))); /* * Don't save the errno here, we ignore this error */ goto done; } id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st); if (!file_id_equal(&fsp->file_id, &id)) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and dev and/or inode does not match\n", fsp_str_dbg(fsp))); DEBUG(5,("close_remove_share_mode: file %s. stored file_id %s, " "stat file_id %s\n", fsp_str_dbg(fsp), file_id_string_tos(&fsp->file_id), file_id_string_tos(&id))); /* * Don't save the errno here, we ignore this error */ goto done; } if ((conn->fs_capabilities & FILE_NAMED_STREAMS) && !is_ntfs_stream_smb_fname(fsp->fsp_name)) { status = delete_all_streams(conn, fsp->fsp_name->base_name); if (!NT_STATUS_IS_OK(status)) { DEBUG(5, ("delete_all_streams failed: %s\n", nt_errstr(status))); goto done; } } if (SMB_VFS_UNLINK(conn, fsp->fsp_name) != 0) { /* * This call can potentially fail as another smbd may * have had the file open with delete on close set and * deleted it when its last reference to this file * went away. Hence we log this but not at debug level * zero. */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and unlink failed with error %s\n", fsp_str_dbg(fsp), strerror(errno))); status = map_nt_error_from_unix(errno); } notify_fname(conn, NOTIFY_ACTION_REMOVED, FILE_NOTIFY_CHANGE_FILE_NAME, fsp->fsp_name->base_name); /* As we now have POSIX opens which can unlink * with other open files we may have taken * this code path with more than one share mode * entry - ensure we only delete once by resetting * the delete on close flag. JRA. */ fsp->delete_on_close = false; set_delete_on_close_lck(lck, False, NULL); done: if (changed_user) { /* unbecome user. */ pop_sec_ctx(); } TALLOC_FREE(lck); return status; }
BOOL change_to_user(connection_struct *conn, uint16 vuid) { user_struct *vuser = get_valid_user_struct(vuid); int snum; gid_t gid; uid_t uid; char group_c; BOOL must_free_token = False; NT_USER_TOKEN *token = NULL; if (!conn) { DEBUG(2,("change_to_user: Connection not open\n")); return(False); } /* * We need a separate check in security=share mode due to vuid * always being UID_FIELD_INVALID. If we don't do this then * in share mode security we are *always* changing uid's between * SMB's - this hurts performance - Badly. */ if((lp_security() == SEC_SHARE) && (current_user.conn == conn) && (current_user.uid == conn->uid)) { DEBUG(4,("change_to_user: Skipping user change - already user\n")); return(True); } else if ((current_user.conn == conn) && (vuser != 0) && (current_user.vuid == vuid) && (current_user.uid == vuser->uid)) { DEBUG(4,("change_to_user: Skipping user change - already user\n")); return True; } snum = SNUM(conn); if((vuser != NULL) && !check_user_ok(conn, vuser, snum)) return False; if (conn->force_user || conn->admin_user || (lp_security() == SEC_SHARE)) { uid = conn->uid; gid = conn->gid; current_user.groups = conn->groups; current_user.ngroups = conn->ngroups; token = conn->nt_user_token; } else { if (!vuser) { DEBUG(2,("change_to_user: Invalid vuid used %d\n",vuid)); return(False); } uid = vuser->uid; gid = vuser->gid; current_user.ngroups = vuser->n_groups; current_user.groups = vuser->groups; token = vuser->nt_user_token; } /* * See if we should force group for this service. * If so this overrides any group set in the force * user code. */ if((group_c = *lp_force_group(snum))) { BOOL is_guest = False; if(group_c == '+') { /* * Only force group if the user is a member of * the service group. Check the group memberships for * this user (we already have this) to * see if we should force the group. */ int i; for (i = 0; i < current_user.ngroups; i++) { if (current_user.groups[i] == conn->gid) { gid = conn->gid; break; } } } else { gid = conn->gid; } /* * We've changed the group list in the token - we must * re-create it. */ if (vuser && vuser->guest) is_guest = True; token = create_nt_token(uid, gid, current_user.ngroups, current_user.groups, is_guest, NULL); must_free_token = True; } set_sec_ctx(uid, gid, current_user.ngroups, current_user.groups, token); /* * Free the new token (as set_sec_ctx copies it). */ if (must_free_token) delete_nt_token(&token); current_user.conn = conn; current_user.vuid = vuid; DEBUG(5,("change_to_user uid=(%d,%d) gid=(%d,%d)\n", (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid())); return(True); }
BOOL change_to_user(connection_struct *conn, uint16 vuid) { user_struct *vuser = get_valid_user_struct(vuid); int snum; gid_t gid; uid_t uid; char group_c; BOOL must_free_token = False; NT_USER_TOKEN *token = NULL; if (!conn) { DEBUG(2,("change_to_user: Connection not open\n")); return(False); } /* * We need a separate check in security=share mode due to vuid * always being UID_FIELD_INVALID. If we don't do this then * in share mode security we are *always* changing uid's between * SMB's - this hurts performance - Badly. */ if((lp_security() == SEC_SHARE) && (current_user.conn == conn) && (current_user.ut.uid == conn->uid)) { DEBUG(4,("change_to_user: Skipping user change - already " "user\n")); return(True); } else if ((current_user.conn == conn) && (vuser != 0) && (current_user.vuid == vuid) && (current_user.ut.uid == vuser->uid)) { DEBUG(4,("change_to_user: Skipping user change - already " "user\n")); return(True); } snum = SNUM(conn); if ((vuser) && !check_user_ok(conn, vuser, snum)) { DEBUG(2,("change_to_user: SMB user %s (unix user %s, vuid %d) " "not permitted access to share %s.\n", vuser->user.smb_name, vuser->user.unix_name, vuid, lp_servicename(snum))); return False; } if (conn->force_user) /* security = share sets this too */ { uid = conn->uid; gid = conn->gid; current_user.ut.groups = conn->groups; current_user.ut.ngroups = conn->ngroups; token = conn->nt_user_token; } else if (vuser) { uid = conn->admin_user ? 0 : vuser->uid; gid = vuser->gid; current_user.ut.ngroups = vuser->n_groups; current_user.ut.groups = vuser->groups; token = vuser->nt_user_token; } else { DEBUG(2,("change_to_user: Invalid vuid used %d in accessing " "share %s.\n",vuid, lp_servicename(snum) )); return False; } /* * See if we should force group for this service. * If so this overrides any group set in the force * user code. */ if((group_c = *lp_force_group(snum))) { token = dup_nt_token(NULL, token); if (token == NULL) { DEBUG(0, ("dup_nt_token failed\n")); return False; } must_free_token = True; if(group_c == '+') { /* * Only force group if the user is a member of * the service group. Check the group memberships for * this user (we already have this) to * see if we should force the group. */ int i; for (i = 0; i < current_user.ut.ngroups; i++) { if (current_user.ut.groups[i] == conn->gid) { gid = conn->gid; gid_to_sid(&token->user_sids[1], gid); break; } } } else { gid = conn->gid; gid_to_sid(&token->user_sids[1], gid); } } set_sec_ctx(uid, gid, current_user.ut.ngroups, current_user.ut.groups, token); /* * Free the new token (as set_sec_ctx copies it). */ if (must_free_token) TALLOC_FREE(token); current_user.conn = conn; current_user.vuid = vuid; DEBUG(5,("change_to_user uid=(%d,%d) gid=(%d,%d)\n", (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid())); return(True); }
static int close_remove_share_mode(files_struct *fsp, enum file_close_type close_type) { connection_struct *conn = fsp->conn; BOOL delete_file = False; struct share_mode_lock *lck; /* * Lock the share entries, and determine if we should delete * on close. If so delete whilst the lock is still in effect. * This prevents race conditions with the file being created. JRA. */ lck = get_share_mode_lock(NULL, fsp->dev, fsp->inode, NULL, NULL); if (lck == NULL) { DEBUG(0, ("close_remove_share_mode: Could not get share mode lock for file %s\n", fsp->fsp_name)); return EINVAL; } if (!del_share_mode(lck, fsp)) { DEBUG(0, ("close_remove_share_mode: Could not delete share entry for file %s\n", fsp->fsp_name)); } delete_file = (lck->delete_on_close | lck->initial_delete_on_close); if (delete_file) { int i; /* See if others still have the file open. If this is the * case, then don't delete */ for (i=0; i<lck->num_share_modes; i++) { if (is_valid_share_mode_entry(&lck->share_modes[i])) { delete_file = False; break; } } } /* Notify any deferred opens waiting on this close. */ notify_deferred_opens(lck); reply_to_oplock_break_requests(fsp); /* * NT can set delete_on_close of the last open * reference to a file. */ if ((close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) && delete_file && lck->delete_token) { SMB_STRUCT_STAT sbuf; DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set - deleting file.\n", fsp->fsp_name)); /* Become the user who requested the delete. */ if (!push_sec_ctx()) { smb_panic("close_remove_share_mode: file %s. failed to push sec_ctx.\n"); } set_sec_ctx(lck->delete_token->uid, lck->delete_token->gid, lck->delete_token->ngroups, lck->delete_token->groups, NULL); /* We can only delete the file if the name we have is still valid and hasn't been renamed. */ if(SMB_VFS_STAT(conn,fsp->fsp_name,&sbuf) != 0) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set " "and stat failed with error %s\n", fsp->fsp_name, strerror(errno) )); } else { if(sbuf.st_dev != fsp->dev || sbuf.st_ino != fsp->inode) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set and " "dev and/or inode does not match\n", fsp->fsp_name )); DEBUG(5,("close_remove_share_mode: file %s. stored dev = %x, inode = %.0f " "stat dev = %x, inode = %.0f\n", fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, (unsigned int)sbuf.st_dev, (double)sbuf.st_ino )); } else if(SMB_VFS_UNLINK(conn,fsp->fsp_name) != 0) { /* * This call can potentially fail as another smbd may have * had the file open with delete on close set and deleted * it when its last reference to this file went away. Hence * we log this but not at debug level zero. */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set " "and unlink failed with error %s\n", fsp->fsp_name, strerror(errno) )); } } /* unbecome user. */ pop_sec_ctx(); process_pending_change_notify_queue((time_t)0); } TALLOC_FREE(lck); return 0; }
static int close_directory(files_struct *fsp, enum file_close_type close_type) { struct share_mode_lock *lck = 0; BOOL delete_dir = False; /* * NT can set delete_on_close of the last open * reference to a directory also. */ lck = get_share_mode_lock(NULL, fsp->dev, fsp->inode, NULL, NULL); if (lck == NULL) { DEBUG(0, ("close_directory: Could not get share mode lock for %s\n", fsp->fsp_name)); return EINVAL; } if (!del_share_mode(lck, fsp)) { DEBUG(0, ("close_directory: Could not delete share entry for %s\n", fsp->fsp_name)); } delete_dir = (lck->delete_on_close | lck->initial_delete_on_close); if ((close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) && delete_dir && lck->delete_token) { BOOL ok; /* Become the user who requested the delete. */ if (!push_sec_ctx()) { smb_panic("close_directory: failed to push sec_ctx.\n"); } set_sec_ctx(lck->delete_token->uid, lck->delete_token->gid, lck->delete_token->ngroups, lck->delete_token->groups, NULL); TALLOC_FREE(lck); ok = rmdir_internals(fsp->conn, fsp->fsp_name); DEBUG(5,("close_directory: %s. Delete on close was set - deleting directory %s.\n", fsp->fsp_name, ok ? "succeeded" : "failed" )); /* unbecome user. */ pop_sec_ctx(); /* * Ensure we remove any change notify requests that would * now fail as the directory has been deleted. */ if(ok) { remove_pending_change_notify_requests_by_fid(fsp, NT_STATUS_DELETE_PENDING); remove_pending_change_notify_requests_by_filename(fsp, NT_STATUS_DELETE_PENDING); } process_pending_change_notify_queue((time_t)0); } else { TALLOC_FREE(lck); remove_pending_change_notify_requests_by_fid(fsp, NT_STATUS_CANCELLED); } /* * Do the code common to files and directories. */ close_filestruct(fsp); file_free(fsp); return 0; }
static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp, enum file_close_type close_type) { struct server_id self = messaging_server_id(fsp->conn->sconn->msg_ctx); struct share_mode_lock *lck = NULL; bool delete_dir = False; NTSTATUS status = NT_STATUS_OK; NTSTATUS status1 = NT_STATUS_OK; const struct security_token *del_nt_token = NULL; const struct security_unix_token *del_token = NULL; NTSTATUS notify_status; if (fsp->conn->sconn->using_smb2) { notify_status = STATUS_NOTIFY_CLEANUP; } else { notify_status = NT_STATUS_OK; } /* * NT can set delete_on_close of the last open * reference to a directory also. */ lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id); if (lck == NULL) { DEBUG(0, ("close_directory: Could not get share mode lock for " "%s\n", fsp_str_dbg(fsp))); return NT_STATUS_INVALID_PARAMETER; } if (fsp->initial_delete_on_close) { bool became_user = False; /* Initial delete on close was set - for * directories we don't care if anyone else * wrote a real delete on close. */ if (get_current_vuid(fsp->conn) != fsp->vuid) { become_user(fsp->conn, fsp->vuid); became_user = True; } send_stat_cache_delete_message(fsp->conn->sconn->msg_ctx, fsp->fsp_name->base_name); set_delete_on_close_lck(fsp, lck, get_current_nttok(fsp->conn), get_current_utok(fsp->conn)); fsp->delete_on_close = true; if (became_user) { unbecome_user(); } } delete_dir = get_delete_on_close_token(lck, fsp->name_hash, &del_nt_token, &del_token); if (delete_dir) { int i; /* See if others still have the dir open. If this is the * case, then don't delete. If all opens are POSIX delete now. */ for (i=0; i<lck->data->num_share_modes; i++) { struct share_mode_entry *e = &lck->data->share_modes[i]; if (is_valid_share_mode_entry(e) && e->name_hash == fsp->name_hash) { if ((fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) { continue; } if (serverid_equal(&self, &e->pid) && (e->share_file_id == fsp->fh->gen_id)) { continue; } if (share_mode_stale_pid(lck->data, i)) { continue; } delete_dir = False; break; } } } if ((close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) && delete_dir) { /* Become the user who requested the delete. */ if (!push_sec_ctx()) { smb_panic("close_directory: failed to push sec_ctx.\n"); } set_sec_ctx(del_token->uid, del_token->gid, del_token->ngroups, del_token->groups, del_nt_token); if (!del_share_mode(lck, fsp)) { DEBUG(0, ("close_directory: Could not delete share entry for " "%s\n", fsp_str_dbg(fsp))); } TALLOC_FREE(lck); if ((fsp->conn->fs_capabilities & FILE_NAMED_STREAMS) && !is_ntfs_stream_smb_fname(fsp->fsp_name)) { status = delete_all_streams(fsp->conn, fsp->fsp_name->base_name); if (!NT_STATUS_IS_OK(status)) { DEBUG(5, ("delete_all_streams failed: %s\n", nt_errstr(status))); return status; } } status = rmdir_internals(talloc_tos(), fsp); DEBUG(5,("close_directory: %s. Delete on close was set - " "deleting directory returned %s.\n", fsp_str_dbg(fsp), nt_errstr(status))); /* unbecome user. */ pop_sec_ctx(); /* * Ensure we remove any change notify requests that would * now fail as the directory has been deleted. */ if (NT_STATUS_IS_OK(status)) { notify_status = NT_STATUS_DELETE_PENDING; } } else { if (!del_share_mode(lck, fsp)) { DEBUG(0, ("close_directory: Could not delete share entry for " "%s\n", fsp_str_dbg(fsp))); } TALLOC_FREE(lck); } remove_pending_change_notify_requests_by_fid(fsp, notify_status); status1 = fd_close(fsp); if (!NT_STATUS_IS_OK(status1)) { DEBUG(0, ("Could not close dir! fname=%s, fd=%d, err=%d=%s\n", fsp_str_dbg(fsp), fsp->fh->fd, errno, strerror(errno))); } /* * Do the code common to files and directories. */ close_filestruct(fsp); file_free(req, fsp); if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(status1)) { status = status1; } return status; }
static bool change_to_user_internal(connection_struct *conn, const struct auth_serversupplied_info *session_info, uint16_t vuid) { int snum; gid_t gid; uid_t uid; char group_c; int num_groups = 0; gid_t *group_list = NULL; bool ok; snum = SNUM(conn); ok = check_user_ok(conn, vuid, session_info, snum); if (!ok) { DEBUG(2,("SMB user %s (unix user %s) " "not permitted access to share %s.\n", session_info->sanitized_username, session_info->unix_name, lp_servicename(snum))); return false; } uid = conn->session_info->utok.uid; gid = conn->session_info->utok.gid; num_groups = conn->session_info->utok.ngroups; group_list = conn->session_info->utok.groups; /* * See if we should force group for this service. If so this overrides * any group set in the force user code. */ if((group_c = *lp_force_group(snum))) { SMB_ASSERT(conn->force_group_gid != (gid_t)-1); if (group_c == '+') { int i; /* * Only force group if the user is a member of the * service group. Check the group memberships for this * user (we already have this) to see if we should force * the group. */ for (i = 0; i < num_groups; i++) { if (group_list[i] == conn->force_group_gid) { conn->session_info->utok.gid = conn->force_group_gid; gid = conn->force_group_gid; gid_to_sid(&conn->session_info->security_token ->sids[1], gid); break; } } } else { conn->session_info->utok.gid = conn->force_group_gid; gid = conn->force_group_gid; gid_to_sid(&conn->session_info->security_token->sids[1], gid); } } /*Set current_user since we will immediately also call set_sec_ctx() */ current_user.ut.ngroups = num_groups; current_user.ut.groups = group_list; set_sec_ctx(uid, gid, current_user.ut.ngroups, current_user.ut.groups, conn->session_info->security_token); current_user.conn = conn; current_user.vuid = vuid; DEBUG(5, ("Impersonated user: uid=(%d,%d), gid=(%d,%d)\n", (int)getuid(), (int)geteuid(), (int)getgid(), (int)getegid())); return true; }
int main(int argc, char **argv) { extern struct current_user current_user; uid_t initial_uid = current_user.uid; gid_t initial_gid = current_user.gid; int ngroups; gid_t *groups; init_sec_ctx(); /* Check initial id */ if (initial_uid != 0 || initial_gid != 0) { printf("FAIL: current_user not initialised to root\n"); return 1; } /* Push a context and check current user is updated */ if (!push_sec_ctx()) { printf("FAIL: push_sec_ctx\n"); return 1; } set_sec_ctx(1, 2, 0, NULL); if (current_user.uid != 1 || current_user.gid != 2) { printf("FAIL: current_user id not updated after push\n"); return 1; } if (current_user.ngroups != 0 || current_user.groups) { printf("FAIL: current_user groups not updated after push\n"); return 1; } /* Push another */ get_random_grouplist(&ngroups, &groups); if (!push_sec_ctx()) { printf("FAIL: push_sec_ctx\n"); return 1; } set_sec_ctx(2, 3, ngroups, groups); if (current_user.uid != 2 || current_user.gid != 3) { printf("FAIL: current_user id not updated after second " "push\n"); return 1; } if (current_user.ngroups != ngroups || (memcmp(current_user.groups, groups, sizeof(gid_t) * ngroups) != 0)) { printf("FAIL: current_user groups not updated\n"); return 1; } /* Pop them both off */ if (!pop_sec_ctx()) { printf("FAIL: pop_sec_ctx\n"); return 1; } if (current_user.uid != 1 || current_user.gid != 2) { printf("FAIL: current_user not updaded pop\n"); return 1; } if (!pop_sec_ctx()) { printf("FAIL: pop_sec_ctx\n"); return 1; } /* Check initial state was returned */ if (current_user.uid != initial_uid || current_user.gid != initial_gid) { printf("FAIL: current_user not updaded pop\n"); return 1; } /* Everything's cool */ printf("PASS\n"); return 0; }
static NTSTATUS close_remove_share_mode(files_struct *fsp, enum file_close_type close_type) { connection_struct *conn = fsp->conn; struct server_id self = messaging_server_id(conn->sconn->msg_ctx); bool delete_file = false; bool changed_user = false; struct share_mode_lock *lck = NULL; NTSTATUS status = NT_STATUS_OK; NTSTATUS tmp_status; struct file_id id; const struct security_unix_token *del_token = NULL; const struct security_token *del_nt_token = NULL; bool got_tokens = false; bool normal_close; int ret_flock, retries = 1; /* Ensure any pending write time updates are done. */ if (fsp->update_write_time_event) { update_write_time_handler(fsp->conn->sconn->ev_ctx, fsp->update_write_time_event, timeval_current(), (void *)fsp); } /* * Lock the share entries, and determine if we should delete * on close. If so delete whilst the lock is still in effect. * This prevents race conditions with the file being created. JRA. */ lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id); if (lck == NULL) { DEBUG(0, ("close_remove_share_mode: Could not get share mode " "lock for file %s\n", fsp_str_dbg(fsp))); return NT_STATUS_INVALID_PARAMETER; } if (fsp->write_time_forced) { DEBUG(10,("close_remove_share_mode: write time forced " "for file %s\n", fsp_str_dbg(fsp))); set_close_write_time(fsp, lck->data->changed_write_time); } else if (fsp->update_write_time_on_close) { /* Someone had a pending write. */ if (null_timespec(fsp->close_write_time)) { DEBUG(10,("close_remove_share_mode: update to current time " "for file %s\n", fsp_str_dbg(fsp))); /* Update to current time due to "normal" write. */ set_close_write_time(fsp, timespec_current()); } else { DEBUG(10,("close_remove_share_mode: write time pending " "for file %s\n", fsp_str_dbg(fsp))); /* Update to time set on close call. */ set_close_write_time(fsp, fsp->close_write_time); } } if (fsp->initial_delete_on_close && !is_delete_on_close_set(lck, fsp->name_hash)) { bool became_user = False; /* Initial delete on close was set and no one else * wrote a real delete on close. */ if (get_current_vuid(conn) != fsp->vuid) { become_user(conn, fsp->vuid); became_user = True; } fsp->delete_on_close = true; set_delete_on_close_lck(fsp, lck, get_current_nttok(conn), get_current_utok(conn)); if (became_user) { unbecome_user(); } } delete_file = is_delete_on_close_set(lck, fsp->name_hash); if (delete_file) { int i; /* See if others still have the file open via this pathname. If this is the case, then don't delete. If all opens are POSIX delete now. */ for (i=0; i<lck->data->num_share_modes; i++) { struct share_mode_entry *e = &lck->data->share_modes[i]; if (!is_valid_share_mode_entry(e)) { continue; } if (e->name_hash != fsp->name_hash) { continue; } if ((fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) { continue; } if (serverid_equal(&self, &e->pid) && (e->share_file_id == fsp->fh->gen_id)) { continue; } if (share_mode_stale_pid(lck->data, i)) { continue; } delete_file = False; break; } } /* * NT can set delete_on_close of the last open * reference to a file. */ normal_close = (close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE); if (!normal_close || !delete_file) { status = NT_STATUS_OK; goto done; } /* * Ok, we have to delete the file */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set " "- deleting file.\n", fsp_str_dbg(fsp))); /* * Don't try to update the write time when we delete the file */ fsp->update_write_time_on_close = false; got_tokens = get_delete_on_close_token(lck, fsp->name_hash, &del_nt_token, &del_token); SMB_ASSERT(got_tokens); if (!unix_token_equal(del_token, get_current_utok(conn))) { /* Become the user who requested the delete. */ DEBUG(5,("close_remove_share_mode: file %s. " "Change user to uid %u\n", fsp_str_dbg(fsp), (unsigned int)del_token->uid)); if (!push_sec_ctx()) { smb_panic("close_remove_share_mode: file %s. failed to push " "sec_ctx.\n"); } set_sec_ctx(del_token->uid, del_token->gid, del_token->ngroups, del_token->groups, del_nt_token); changed_user = true; } /* We can only delete the file if the name we have is still valid and hasn't been renamed. */ tmp_status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(tmp_status)) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and stat failed with error %s\n", fsp_str_dbg(fsp), nt_errstr(tmp_status))); /* * Don't save the errno here, we ignore this error */ goto done; } id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st); if (!file_id_equal(&fsp->file_id, &id)) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and dev and/or inode does not match\n", fsp_str_dbg(fsp))); DEBUG(5,("close_remove_share_mode: file %s. stored file_id %s, " "stat file_id %s\n", fsp_str_dbg(fsp), file_id_string_tos(&fsp->file_id), file_id_string_tos(&id))); /* * Don't save the errno here, we ignore this error */ goto done; } if ((conn->fs_capabilities & FILE_NAMED_STREAMS) && !is_ntfs_stream_smb_fname(fsp->fsp_name)) { status = delete_all_streams(conn, fsp->fsp_name->base_name); if (!NT_STATUS_IS_OK(status)) { DEBUG(5, ("delete_all_streams failed: %s\n", nt_errstr(status))); goto done; } } retry_delete: /* temporary files with delete on close set will not be deleted on a * cifs share using a netapp backend since they are opened with * read + write access mask. * close the file to allow the delete. */ if (fsp->can_write && !S_ISDIR(fsp->fsp_name->st.st_ex_mode) && fsp->fh->ref_count == 1 && retries) { status = fd_close(fsp); if (!NT_STATUS_IS_OK(status)) { DEBUG(3, ("close_remove_share_mode: Error %s closing %s\n", nt_errstr(status), smb_fname_str_dbg(fsp->fsp_name))); goto skip_retry; } if (SMB_VFS_UNLINK(conn, fsp->fsp_name) != 0) { /* * This call can potentially fail as another smbd may * have had the file open with delete on close set and * deleted it when its last reference to this file * went away. Hence we log this but not at debug level * zero. */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and unlink failed with error %s\n", fsp_str_dbg(fsp), strerror(errno))); status = map_nt_error_from_unix(errno); retries = 0; goto retry_delete; } } else { if (SMB_VFS_UNLINK(conn, fsp->fsp_name) != 0) { /* * This call can potentially fail as another smbd may * have had the file open with delete on close set and * deleted it when its last reference to this file * went away. Hence we log this but not at debug level * zero. */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and unlink failed with error %s\n", fsp_str_dbg(fsp), strerror(errno))); status = map_nt_error_from_unix(errno); } } /* As we now have POSIX opens which can unlink * with other open files we may have taken * this code path with more than one share mode * entry - ensure we only delete once by resetting * the delete on close flag. JRA. */ skip_retry: fsp->delete_on_close = false; reset_delete_on_close_lck(fsp, lck); done: if (changed_user) { /* unbecome user. */ pop_sec_ctx(); } /* remove filesystem sharemodes */ ret_flock = SMB_VFS_KERNEL_FLOCK(fsp, 0, 0); if (ret_flock == -1) { DEBUG(2, ("close_remove_share_mode: removing kernel flock for " "%s failed: %s\n", fsp_str_dbg(fsp), strerror(errno))); } if (!del_share_mode(lck, fsp)) { DEBUG(0, ("close_remove_share_mode: Could not delete share " "entry for file %s\n", fsp_str_dbg(fsp))); } TALLOC_FREE(lck); if (delete_file) { /* * Do the notification after we released the share * mode lock. Inside notify_fname we take out another * tdb lock. With ctdb also accessing our databases, * this can lead to deadlocks. Putting this notify * after the TALLOC_FREE(lck) above we avoid locking * two records simultaneously. Notifies are async and * informational only, so calling the notify_fname * without holding the share mode lock should not do * any harm. */ notify_fname(conn, NOTIFY_ACTION_REMOVED, FILE_NOTIFY_CHANGE_FILE_NAME, fsp->fsp_name->base_name); } return status; }
void set_root_sec_ctx(void) { /* May need to worry about supplementary groups at some stage */ set_sec_ctx(0, 0, 0, NULL, NULL); }