/** * XXX: This is temporary and there should be no callers of this once * smb_filename is plumbed through all path based operations. */ NTSTATUS create_synthetic_smb_fname_split(TALLOC_CTX *ctx, const char *fname, const SMB_STRUCT_STAT *psbuf, struct smb_filename **smb_fname_out) { NTSTATUS status; const char *stream_name = NULL; char *base_name = NULL; if (!lp_posix_pathnames()) { stream_name = strchr_m(fname, ':'); } /* Setup the base_name/stream_name. */ if (stream_name) { base_name = talloc_strndup(ctx, fname, PTR_DIFF(stream_name, fname)); } else { base_name = talloc_strdup(ctx, fname); } if (!base_name) { return NT_STATUS_NO_MEMORY; } status = create_synthetic_smb_fname(ctx, base_name, stream_name, psbuf, smb_fname_out); TALLOC_FREE(base_name); return status; }
static int streams_xattr_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf) { struct smb_filename *smb_fname_base = NULL; NTSTATUS status; int ret = -1; struct stream_io *io = (struct stream_io *) VFS_FETCH_FSP_EXTENSION(handle, fsp); DEBUG(10, ("streams_xattr_fstat called for %d\n", fsp->fh->fd)); if (io == NULL || fsp->base_fsp == NULL) { return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf); } if (!streams_xattr_recheck(io)) { return -1; } /* Create an smb_filename with stream_name == NULL. */ status = create_synthetic_smb_fname(talloc_tos(), io->base, NULL, NULL, &smb_fname_base); if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); return -1; } if (lp_posix_pathnames()) { ret = SMB_VFS_LSTAT(handle->conn, smb_fname_base); } else { ret = SMB_VFS_STAT(handle->conn, smb_fname_base); } *sbuf = smb_fname_base->st; TALLOC_FREE(smb_fname_base); if (ret == -1) { return -1; } sbuf->st_ex_size = get_xattr_size(handle->conn, fsp->base_fsp, io->base, io->xattr_name); if (sbuf->st_ex_size == -1) { return -1; } DEBUG(10, ("sbuf->st_ex_size = %d\n", (int)sbuf->st_ex_size)); sbuf->st_ex_ino = stream_inode(sbuf, io->xattr_name); sbuf->st_ex_mode &= ~S_IFMT; sbuf->st_ex_mode |= S_IFREG; sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1; return 0; }
NTSTATUS onefs_stream_prep_smb_fname(TALLOC_CTX *ctx, const struct smb_filename *smb_fname_in, struct smb_filename **smb_fname_out) { char *stream_name = NULL; NTSTATUS status; /* * Only attempt to strip off the trailing :$DATA if there is an actual * stream there. If it is the default stream, the smb_fname_out will * just have a NULL stream so the base file is opened. */ if (smb_fname_in->stream_name && !is_ntfs_default_stream_smb_fname(smb_fname_in)) { char *str_tmp = smb_fname_in->stream_name; /* First strip off the leading ':' */ if (str_tmp[0] == ':') { str_tmp++; } /* Create a new copy of the stream_name. */ stream_name = talloc_strdup(ctx, str_tmp); if (stream_name == NULL) { return NT_STATUS_NO_MEMORY; } /* Strip off the :$DATA if one exists. */ str_tmp = strrchr_m(stream_name, ':'); if (str_tmp) { if (strcasecmp_m(str_tmp, ":$DATA") != 0) { return NT_STATUS_INVALID_PARAMETER; } str_tmp[0] = '\0'; } } /* * If there was a stream that wasn't the default stream the leading * colon and trailing :$DATA has now been stripped off. Create a new * smb_filename to pass back. */ status = create_synthetic_smb_fname(ctx, smb_fname_in->base_name, stream_name, &smb_fname_in->st, smb_fname_out); TALLOC_FREE(stream_name); if (!NT_STATUS_IS_OK(status)) { DEBUG(5, ("Failed to prep stream name for %s: %s\n", *smb_fname_out ? smb_fname_str_dbg(*smb_fname_out) : "NULL", nt_errstr(status))); } return status; }
NTSTATUS open_np_file(struct smb_request *smb_req, const char *name, struct files_struct **pfsp) { struct connection_struct *conn = smb_req->conn; struct files_struct *fsp; struct smb_filename *smb_fname = NULL; NTSTATUS status; status = file_new(smb_req, conn, &fsp); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("file_new failed: %s\n", nt_errstr(status))); return status; } fsp->conn = conn; fsp->fh->fd = -1; fsp->vuid = smb_req->vuid; fsp->can_lock = false; fsp->access_mask = FILE_READ_DATA | FILE_WRITE_DATA; status = create_synthetic_smb_fname(talloc_tos(), name, NULL, NULL, &smb_fname); if (!NT_STATUS_IS_OK(status)) { file_free(smb_req, fsp); return status; } status = fsp_set_smb_fname(fsp, smb_fname); TALLOC_FREE(smb_fname); if (!NT_STATUS_IS_OK(status)) { file_free(smb_req, fsp); return status; } status = np_open(fsp, name, conn->sconn->local_address, conn->sconn->remote_address, &conn->sconn->client_id, conn->session_info, conn->sconn->msg_ctx, &fsp->fake_file_handle); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("np_open(%s) returned %s\n", name, nt_errstr(status))); file_free(smb_req, fsp); return status; } *pfsp = fsp; return NT_STATUS_OK; }
static SMB_STRUCT_DIR *dirsort_opendir(vfs_handle_struct *handle, const char *fname, const char *mask, uint32 attr) { NTSTATUS status; struct dirsort_privates *data = NULL; /* set up our private data about this directory */ data = talloc_zero(handle->conn, struct dirsort_privates); if (!data) { return NULL; } status = create_synthetic_smb_fname(data, fname, NULL, NULL, &data->smb_fname); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(data); return NULL; } /* Open the underlying directory and count the number of entries */ data->source_directory = SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr); if (data->source_directory == NULL) { TALLOC_FREE(data); return NULL; } if (!open_and_sort_dir(handle, data)) { SMB_VFS_NEXT_CLOSEDIR(handle,data->source_directory); TALLOC_FREE(data); return NULL; } SMB_VFS_HANDLE_SET_DATA(handle, data, free_dirsort_privates, struct dirsort_privates, return NULL); return data->source_directory; }
connection_struct *make_connection_snum(struct smbd_server_connection *sconn, int snum, user_struct *vuser, DATA_BLOB password, const char *pdev, NTSTATUS *pstatus) { connection_struct *conn; struct smb_filename *smb_fname_cpath = NULL; fstring dev; int ret; char addr[INET6_ADDRSTRLEN]; bool on_err_call_dis_hook = false; NTSTATUS status; fstrcpy(dev, pdev); if (NT_STATUS_IS_ERR(*pstatus = share_sanity_checks(snum, dev))) { return NULL; } conn = conn_new(sconn); if (!conn) { DEBUG(0,("Couldn't find free connection.\n")); *pstatus = NT_STATUS_INSUFFICIENT_RESOURCES; return NULL; } conn->params->service = snum; status = create_connection_server_info(sconn, conn, snum, vuser ? vuser->server_info : NULL, password, &conn->server_info); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("create_connection_server_info failed: %s\n", nt_errstr(status))); *pstatus = status; conn_free(conn); return NULL; } if ((lp_guest_only(snum)) || (lp_security() == SEC_SHARE)) { conn->force_user = true; } add_session_user(sconn, conn->server_info->unix_name); safe_strcpy(conn->client_address, client_addr(get_client_fd(),addr,sizeof(addr)), sizeof(conn->client_address)-1); conn->num_files_open = 0; conn->lastused = conn->lastused_count = time(NULL); conn->used = True; conn->printer = (strncmp(dev,"LPT",3) == 0); conn->ipc = ( (strncmp(dev,"IPC",3) == 0) || ( lp_enable_asu_support() && strequal(dev,"ADMIN$")) ); /* Case options for the share. */ if (lp_casesensitive(snum) == Auto) { /* We will be setting this per packet. Set to be case * insensitive for now. */ conn->case_sensitive = False; } else { conn->case_sensitive = (bool)lp_casesensitive(snum); } conn->case_preserve = lp_preservecase(snum); conn->short_case_preserve = lp_shortpreservecase(snum); conn->encrypt_level = lp_smb_encrypt(snum); conn->veto_list = NULL; conn->hide_list = NULL; conn->veto_oplock_list = NULL; conn->aio_write_behind_list = NULL; conn->read_only = lp_readonly(SNUM(conn)); conn->admin_user = False; if (*lp_force_user(snum)) { /* * Replace conn->server_info with a completely faked up one * from the username we are forced into :-) */ char *fuser; struct auth_serversupplied_info *forced_serverinfo; fuser = talloc_string_sub(conn, lp_force_user(snum), "%S", lp_servicename(snum)); if (fuser == NULL) { conn_free(conn); *pstatus = NT_STATUS_NO_MEMORY; return NULL; } status = make_serverinfo_from_username( conn, fuser, conn->server_info->guest, &forced_serverinfo); if (!NT_STATUS_IS_OK(status)) { conn_free(conn); *pstatus = status; return NULL; } TALLOC_FREE(conn->server_info); conn->server_info = forced_serverinfo; conn->force_user = True; DEBUG(3,("Forced user %s\n", fuser)); } /* * If force group is true, then override * any groupid stored for the connecting user. */ if (*lp_force_group(snum)) { status = find_forced_group( conn->force_user, snum, conn->server_info->unix_name, &conn->server_info->ptok->user_sids[1], &conn->server_info->utok.gid); if (!NT_STATUS_IS_OK(status)) { conn_free(conn); *pstatus = status; return NULL; } /* * We need to cache this gid, to use within * change_to_user() separately from the conn->server_info * struct. We only use conn->server_info directly if * "force_user" was set. */ conn->force_group_gid = conn->server_info->utok.gid; } conn->vuid = (vuser != NULL) ? vuser->vuid : UID_FIELD_INVALID; { char *s = talloc_sub_advanced(talloc_tos(), lp_servicename(SNUM(conn)), conn->server_info->unix_name, conn->connectpath, conn->server_info->utok.gid, conn->server_info->sanitized_username, pdb_get_domain(conn->server_info->sam_account), lp_pathname(snum)); if (!s) { conn_free(conn); *pstatus = NT_STATUS_NO_MEMORY; return NULL; } if (!set_conn_connectpath(conn,s)) { TALLOC_FREE(s); conn_free(conn); *pstatus = NT_STATUS_NO_MEMORY; return NULL; } DEBUG(3,("Connect path is '%s' for service [%s]\n",s, lp_servicename(snum))); TALLOC_FREE(s); } /* * New code to check if there's a share security descripter * added from NT server manager. This is done after the * smb.conf checks are done as we need a uid and token. JRA. * */ { bool can_write = False; can_write = share_access_check(conn->server_info->ptok, lp_servicename(snum), FILE_WRITE_DATA); if (!can_write) { if (!share_access_check(conn->server_info->ptok, lp_servicename(snum), FILE_READ_DATA)) { /* No access, read or write. */ DEBUG(0,("make_connection: connection to %s " "denied due to security " "descriptor.\n", lp_servicename(snum))); conn_free(conn); *pstatus = NT_STATUS_ACCESS_DENIED; return NULL; } else { conn->read_only = True; } } } /* Initialise VFS function pointers */ if (!smbd_vfs_init(conn)) { DEBUG(0, ("vfs_init failed for service %s\n", lp_servicename(snum))); conn_free(conn); *pstatus = NT_STATUS_BAD_NETWORK_NAME; return NULL; } /* * If widelinks are disallowed we need to canonicalise the connect * path here to ensure we don't have any symlinks in the * connectpath. We will be checking all paths on this connection are * below this directory. We must do this after the VFS init as we * depend on the realpath() pointer in the vfs table. JRA. */ if (!lp_widelinks(snum)) { if (!canonicalize_connect_path(conn)) { DEBUG(0, ("canonicalize_connect_path failed " "for service %s, path %s\n", lp_servicename(snum), conn->connectpath)); conn_free(conn); *pstatus = NT_STATUS_BAD_NETWORK_NAME; return NULL; } } if ((!conn->printer) && (!conn->ipc)) { conn->notify_ctx = notify_init(conn, server_id_self(), smbd_messaging_context(), smbd_event_context(), conn); } /* ROOT Activities: */ /* * Enforce the max connections parameter. */ if ((lp_max_connections(snum) > 0) && (count_current_connections(lp_servicename(SNUM(conn)), True) >= lp_max_connections(snum))) { DEBUG(1, ("Max connections (%d) exceeded for %s\n", lp_max_connections(snum), lp_servicename(snum))); conn_free(conn); *pstatus = NT_STATUS_INSUFFICIENT_RESOURCES; return NULL; } /* * Get us an entry in the connections db */ if (!claim_connection(conn, lp_servicename(snum), 0)) { DEBUG(1, ("Could not store connections entry\n")); conn_free(conn); *pstatus = NT_STATUS_INTERNAL_DB_ERROR; return NULL; } /* Preexecs are done here as they might make the dir we are to ChDir * to below */ /* execute any "root preexec = " line */ if (*lp_rootpreexec(snum)) { char *cmd = talloc_sub_advanced(talloc_tos(), lp_servicename(SNUM(conn)), conn->server_info->unix_name, conn->connectpath, conn->server_info->utok.gid, conn->server_info->sanitized_username, pdb_get_domain(conn->server_info->sam_account), lp_rootpreexec(snum)); DEBUG(5,("cmd=%s\n",cmd)); ret = smbrun(cmd,NULL); TALLOC_FREE(cmd); if (ret != 0 && lp_rootpreexec_close(snum)) { DEBUG(1,("root preexec gave %d - failing " "connection\n", ret)); yield_connection(conn, lp_servicename(snum)); conn_free(conn); *pstatus = NT_STATUS_ACCESS_DENIED; return NULL; } } /* USER Activites: */ if (!change_to_user(conn, conn->vuid)) { /* No point continuing if they fail the basic checks */ DEBUG(0,("Can't become connected user!\n")); yield_connection(conn, lp_servicename(snum)); conn_free(conn); *pstatus = NT_STATUS_LOGON_FAILURE; return NULL; } /* Remember that a different vuid can connect later without these * checks... */ /* Preexecs are done here as they might make the dir we are to ChDir * to below */ /* execute any "preexec = " line */ if (*lp_preexec(snum)) { char *cmd = talloc_sub_advanced(talloc_tos(), lp_servicename(SNUM(conn)), conn->server_info->unix_name, conn->connectpath, conn->server_info->utok.gid, conn->server_info->sanitized_username, pdb_get_domain(conn->server_info->sam_account), lp_preexec(snum)); ret = smbrun(cmd,NULL); TALLOC_FREE(cmd); if (ret != 0 && lp_preexec_close(snum)) { DEBUG(1,("preexec gave %d - failing connection\n", ret)); *pstatus = NT_STATUS_ACCESS_DENIED; goto err_root_exit; } } #ifdef WITH_FAKE_KASERVER if (lp_afs_share(snum)) { afs_login(conn); } #endif /* Add veto/hide lists */ if (!IS_IPC(conn) && !IS_PRINT(conn)) { set_namearray( &conn->veto_list, lp_veto_files(snum)); set_namearray( &conn->hide_list, lp_hide_files(snum)); set_namearray( &conn->veto_oplock_list, lp_veto_oplocks(snum)); set_namearray( &conn->aio_write_behind_list, lp_aio_write_behind(snum)); } /* Invoke VFS make connection hook - do this before the VFS_STAT call to allow any filesystems needing user credentials to initialize themselves. */ if (SMB_VFS_CONNECT(conn, lp_servicename(snum), conn->server_info->unix_name) < 0) { DEBUG(0,("make_connection: VFS make connection failed!\n")); *pstatus = NT_STATUS_UNSUCCESSFUL; goto err_root_exit; } /* Any error exit after here needs to call the disconnect hook. */ on_err_call_dis_hook = true; status = create_synthetic_smb_fname(talloc_tos(), conn->connectpath, NULL, NULL, &smb_fname_cpath); if (!NT_STATUS_IS_OK(status)) { *pstatus = status; goto err_root_exit; } /* win2000 does not check the permissions on the directory during the tree connect, instead relying on permission check during individual operations. To match this behaviour I have disabled this chdir check (tridge) */ /* the alternative is just to check the directory exists */ if ((ret = SMB_VFS_STAT(conn, smb_fname_cpath)) != 0 || !S_ISDIR(smb_fname_cpath->st.st_ex_mode)) { if (ret == 0 && !S_ISDIR(smb_fname_cpath->st.st_ex_mode)) { DEBUG(0,("'%s' is not a directory, when connecting to " "[%s]\n", conn->connectpath, lp_servicename(snum))); } else { DEBUG(0,("'%s' does not exist or permission denied " "when connecting to [%s] Error was %s\n", conn->connectpath, lp_servicename(snum), strerror(errno) )); } *pstatus = NT_STATUS_BAD_NETWORK_NAME; goto err_root_exit; } string_set(&conn->origpath,conn->connectpath); #if SOFTLINK_OPTIMISATION /* resolve any soft links early if possible */ if (vfs_ChDir(conn,conn->connectpath) == 0) { TALLOC_CTX *ctx = talloc_tos(); char *s = vfs_GetWd(ctx,s); if (!s) { *status = map_nt_error_from_unix(errno); goto err_root_exit; } if (!set_conn_connectpath(conn,s)) { *status = NT_STATUS_NO_MEMORY; goto err_root_exit; } vfs_ChDir(conn,conn->connectpath); } #endif if (lp_unix_extensions() && lp_widelinks(snum)) { DEBUG(0,("Share '%s' has wide links and unix extensions enabled. " "These parameters are incompatible. " "Disabling wide links for this share.\n", lp_servicename(snum) )); lp_do_parameter(snum, "wide links", "False"); } /* Figure out the characteristics of the underlying filesystem. This * assumes that all the filesystem mounted withing a share path have * the same characteristics, which is likely but not guaranteed. */ conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res); /* * Print out the 'connected as' stuff here as we need * to know the effective uid and gid we will be using * (at least initially). */ if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) { dbgtext( "%s (%s) ", get_remote_machine_name(), conn->client_address ); dbgtext( "%s", srv_is_signing_active(smbd_server_conn) ? "signed " : ""); dbgtext( "connect to service %s ", lp_servicename(snum) ); dbgtext( "initially as user %s ", conn->server_info->unix_name ); dbgtext( "(uid=%d, gid=%d) ", (int)geteuid(), (int)getegid() ); dbgtext( "(pid %d)\n", (int)sys_getpid() ); } /* we've finished with the user stuff - go back to root */ change_to_root_user(); return(conn); err_root_exit: TALLOC_FREE(smb_fname_cpath); change_to_root_user(); if (on_err_call_dis_hook) { /* Call VFS disconnect hook */ SMB_VFS_DISCONNECT(conn); } yield_connection(conn, lp_servicename(snum)); conn_free(conn); return NULL; }
static NTSTATUS rmdir_internals(TALLOC_CTX *ctx, files_struct *fsp) { connection_struct *conn = fsp->conn; struct smb_filename *smb_dname = fsp->fsp_name; int ret; SMB_ASSERT(!is_ntfs_stream_smb_fname(smb_dname)); /* Might be a symlink. */ if(SMB_VFS_LSTAT(conn, smb_dname) != 0) { return map_nt_error_from_unix(errno); } if (S_ISLNK(smb_dname->st.st_ex_mode)) { /* Is what it points to a directory ? */ if(SMB_VFS_STAT(conn, smb_dname) != 0) { return map_nt_error_from_unix(errno); } if (!(S_ISDIR(smb_dname->st.st_ex_mode))) { return NT_STATUS_NOT_A_DIRECTORY; } ret = SMB_VFS_UNLINK(conn, smb_dname); } else { ret = SMB_VFS_RMDIR(conn, smb_dname->base_name); } if (ret == 0) { notify_fname(conn, NOTIFY_ACTION_REMOVED, FILE_NOTIFY_CHANGE_DIR_NAME, smb_dname->base_name); return NT_STATUS_OK; } if(((errno == ENOTEMPTY)||(errno == EEXIST)) && lp_veto_files(SNUM(conn))) { /* * Check to see if the only thing in this directory are * vetoed files/directories. If so then delete them and * retry. If we fail to delete any of them (and we *don't* * do a recursive delete) then fail the rmdir. */ SMB_STRUCT_STAT st; const char *dname = NULL; char *talloced = NULL; long dirpos = 0; struct smb_Dir *dir_hnd = OpenDir(talloc_tos(), conn, smb_dname->base_name, NULL, 0); if(dir_hnd == NULL) { errno = ENOTEMPTY; goto err; } while ((dname = ReadDirName(dir_hnd, &dirpos, &st, &talloced)) != NULL) { if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) { TALLOC_FREE(talloced); continue; } if (!is_visible_file(conn, smb_dname->base_name, dname, &st, false)) { TALLOC_FREE(talloced); continue; } if(!IS_VETO_PATH(conn, dname)) { TALLOC_FREE(dir_hnd); TALLOC_FREE(talloced); errno = ENOTEMPTY; goto err; } TALLOC_FREE(talloced); } /* We only have veto files/directories. * Are we allowed to delete them ? */ if(!lp_recursive_veto_delete(SNUM(conn))) { TALLOC_FREE(dir_hnd); errno = ENOTEMPTY; goto err; } /* Do a recursive delete. */ RewindDir(dir_hnd,&dirpos); while ((dname = ReadDirName(dir_hnd, &dirpos, &st, &talloced)) != NULL) { struct smb_filename *smb_dname_full = NULL; char *fullname = NULL; bool do_break = true; NTSTATUS status; if (ISDOT(dname) || ISDOTDOT(dname)) { TALLOC_FREE(talloced); continue; } if (!is_visible_file(conn, smb_dname->base_name, dname, &st, false)) { TALLOC_FREE(talloced); continue; } fullname = talloc_asprintf(ctx, "%s/%s", smb_dname->base_name, dname); if(!fullname) { errno = ENOMEM; goto err_break; } status = create_synthetic_smb_fname(talloc_tos(), fullname, NULL, NULL, &smb_dname_full); if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); goto err_break; } if(SMB_VFS_LSTAT(conn, smb_dname_full) != 0) { goto err_break; } if(smb_dname_full->st.st_ex_mode & S_IFDIR) { if(!recursive_rmdir(ctx, conn, smb_dname_full)) { goto err_break; } if(SMB_VFS_RMDIR(conn, smb_dname_full->base_name) != 0) { goto err_break; } } else if(SMB_VFS_UNLINK(conn, smb_dname_full) != 0) { goto err_break; } /* Successful iteration. */ do_break = false; err_break: TALLOC_FREE(fullname); TALLOC_FREE(smb_dname_full); TALLOC_FREE(talloced); if (do_break) break; } TALLOC_FREE(dir_hnd); /* Retry the rmdir */ ret = SMB_VFS_RMDIR(conn, smb_dname->base_name); } err: if (ret != 0) { DEBUG(3,("rmdir_internals: couldn't remove directory %s : " "%s\n", smb_fname_str_dbg(smb_dname), strerror(errno))); return map_nt_error_from_unix(errno); } notify_fname(conn, NOTIFY_ACTION_REMOVED, FILE_NOTIFY_CHANGE_DIR_NAME, smb_dname->base_name); return NT_STATUS_OK; }
static bool recursive_rmdir(TALLOC_CTX *ctx, connection_struct *conn, struct smb_filename *smb_dname) { const char *dname = NULL; char *talloced = NULL; bool ret = True; long offset = 0; SMB_STRUCT_STAT st; struct smb_Dir *dir_hnd; SMB_ASSERT(!is_ntfs_stream_smb_fname(smb_dname)); dir_hnd = OpenDir(talloc_tos(), conn, smb_dname->base_name, NULL, 0); if(dir_hnd == NULL) return False; while((dname = ReadDirName(dir_hnd, &offset, &st, &talloced))) { struct smb_filename *smb_dname_full = NULL; char *fullname = NULL; bool do_break = true; NTSTATUS status; if (ISDOT(dname) || ISDOTDOT(dname)) { TALLOC_FREE(talloced); continue; } if (!is_visible_file(conn, smb_dname->base_name, dname, &st, false)) { TALLOC_FREE(talloced); continue; } /* Construct the full name. */ fullname = talloc_asprintf(ctx, "%s/%s", smb_dname->base_name, dname); if (!fullname) { errno = ENOMEM; goto err_break; } status = create_synthetic_smb_fname(talloc_tos(), fullname, NULL, NULL, &smb_dname_full); if (!NT_STATUS_IS_OK(status)) { goto err_break; } if(SMB_VFS_LSTAT(conn, smb_dname_full) != 0) { goto err_break; } if(smb_dname_full->st.st_ex_mode & S_IFDIR) { if(!recursive_rmdir(ctx, conn, smb_dname_full)) { goto err_break; } if(SMB_VFS_RMDIR(conn, smb_dname_full->base_name) != 0) { goto err_break; } } else if(SMB_VFS_UNLINK(conn, smb_dname_full) != 0) { goto err_break; } /* Successful iteration. */ do_break = false; err_break: TALLOC_FREE(smb_dname_full); TALLOC_FREE(fullname); TALLOC_FREE(talloced); if (do_break) { ret = false; break; } } TALLOC_FREE(dir_hnd); return ret; }
NTSTATUS delete_all_streams(connection_struct *conn, const char *fname) { struct stream_struct *stream_info = NULL; int i; unsigned int num_streams = 0; TALLOC_CTX *frame = talloc_stackframe(); NTSTATUS status; status = vfs_streaminfo(conn, NULL, fname, talloc_tos(), &num_streams, &stream_info); if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) { DEBUG(10, ("no streams around\n")); TALLOC_FREE(frame); return NT_STATUS_OK; } if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("vfs_streaminfo failed: %s\n", nt_errstr(status))); goto fail; } DEBUG(10, ("delete_all_streams found %d streams\n", num_streams)); if (num_streams == 0) { TALLOC_FREE(frame); return NT_STATUS_OK; } for (i=0; i<num_streams; i++) { int res; struct smb_filename *smb_fname_stream = NULL; if (strequal(stream_info[i].name, "::$DATA")) { continue; } status = create_synthetic_smb_fname(talloc_tos(), fname, stream_info[i].name, NULL, &smb_fname_stream); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("talloc_aprintf failed\n")); goto fail; } res = SMB_VFS_UNLINK(conn, smb_fname_stream); if (res == -1) { status = map_nt_error_from_unix(errno); DEBUG(10, ("Could not delete stream %s: %s\n", smb_fname_str_dbg(smb_fname_stream), strerror(errno))); TALLOC_FREE(smb_fname_stream); break; } TALLOC_FREE(smb_fname_stream); } fail: TALLOC_FREE(frame); return status; }
bool can_delete_file_in_directory(connection_struct *conn, const struct smb_filename *smb_fname) { TALLOC_CTX *ctx = talloc_tos(); char *dname = NULL; struct smb_filename *smb_fname_parent = NULL; NTSTATUS status; bool ret; if (!CAN_WRITE(conn)) { return False; } if (!lp_acl_check_permissions(SNUM(conn))) { /* This option means don't check. */ return true; } /* Get the parent directory permission mask and owners. */ if (!parent_dirname(ctx, smb_fname->base_name, &dname, NULL)) { return False; } status = create_synthetic_smb_fname(ctx, dname, NULL, NULL, &smb_fname_parent); if (!NT_STATUS_IS_OK(status)) { ret = false; goto out; } if(SMB_VFS_STAT(conn, smb_fname_parent) != 0) { ret = false; goto out; } /* fast paths first */ if (!S_ISDIR(smb_fname_parent->st.st_ex_mode)) { ret = false; goto out; } if (get_current_uid(conn) == (uid_t)0) { /* I'm sorry sir, I didn't know you were root... */ ret = true; goto out; } #ifdef S_ISVTX /* sticky bit means delete only by owner of file or by root or * by owner of directory. */ if (smb_fname_parent->st.st_ex_mode & S_ISVTX) { if (!VALID_STAT(smb_fname->st)) { /* If the file doesn't already exist then * yes we'll be able to delete it. */ ret = true; goto out; } /* * Patch from SATOH Fumiyasu <*****@*****.**> * for bug #3348. Don't assume owning sticky bit * directory means write access allowed. * Fail to delete if we're not the owner of the file, * or the owner of the directory as we have no possible * chance of deleting. Otherwise, go on and check the ACL. */ if ((get_current_uid(conn) != smb_fname_parent->st.st_ex_uid) && (get_current_uid(conn) != smb_fname->st.st_ex_uid)) { DEBUG(10,("can_delete_file_in_directory: not " "owner of file %s or directory %s", smb_fname_str_dbg(smb_fname), smb_fname_str_dbg(smb_fname_parent))); ret = false; goto out; } } #endif /* now for ACL checks */ /* * There's two ways to get the permission to delete a file: First by * having the DELETE bit on the file itself and second if that does * not help, by the DELETE_CHILD bit on the containing directory. * * Here we only check the directory permissions, we will * check the file DELETE permission separately. */ ret = NT_STATUS_IS_OK(smbd_check_access_rights(conn, smb_fname_parent, FILE_DELETE_CHILD)); out: TALLOC_FREE(dname); TALLOC_FREE(smb_fname_parent); return ret; }
int hpuxacl_sys_acl_set_file(vfs_handle_struct *handle, const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T theacl) { int ret = -1; HPUX_ACL_T hpux_acl = NULL; int count; struct smb_filename *smb_fname = NULL; NTSTATUS status; DEBUG(10, ("hpuxacl_sys_acl_set_file called for file '%s'\n", name)); status = create_synthetic_smb_fname(talloc_tos(), name, NULL, NULL, &smb_fname); if (!NT_STATUS_IS_OK(status)) { goto done; } if(hpux_acl_call_present() == False) { /* Looks like we don't have the acl() system call on HPUX. * May be the system doesn't have the latest version of JFS. */ goto done; } if ((type != SMB_ACL_TYPE_ACCESS) && (type != SMB_ACL_TYPE_DEFAULT)) { errno = EINVAL; DEBUG(10, ("invalid smb acl type given (%d).\n", type)); goto done; } DEBUGADD(10, ("setting %s acl\n", ((type == SMB_ACL_TYPE_ACCESS) ? "access" : "default"))); if(!smb_acl_to_hpux_acl(theacl, &hpux_acl, &count, type)) { DEBUG(10, ("conversion smb_acl -> hpux_acl failed (%s).\n", strerror(errno))); goto done; } /* * if the file is a directory, there is extra work to do: * since the hpux acl call stores both the access acl and * the default acl as provided, we have to get the acl part * that has _not_ been specified in "type" from the file first * and concatenate it with the acl provided. */ if (lp_posix_pathnames()) { ret = SMB_VFS_LSTAT(handle->conn, smb_fname); } else { ret = SMB_VFS_STAT(handle->conn, smb_fname); } if (ret != 0) { DEBUG(10, ("Error in stat call: %s\n", strerror(errno))); goto done; } if (S_ISDIR(smb_fname->st.st_ex_mode)) { HPUX_ACL_T other_acl; int other_count; SMB_ACL_TYPE_T other_type; other_type = (type == SMB_ACL_TYPE_ACCESS) ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS; DEBUGADD(10, ("getting acl from filesystem\n")); if (!hpux_acl_get_file(smb_fname->base_name, &other_acl, &other_count)) { DEBUG(10, ("error getting acl from directory\n")); goto done; } DEBUG(10, ("adding %s part of fs acl to given acl\n", ((other_type == SMB_ACL_TYPE_ACCESS) ? "access" : "default"))); if (!hpux_add_to_acl(&hpux_acl, &count, other_acl, other_count, other_type)) { DEBUG(10, ("error adding other acl.\n")); SAFE_FREE(other_acl); goto done; } SAFE_FREE(other_acl); } else if (type != SMB_ACL_TYPE_ACCESS) { errno = EINVAL; goto done; } if (!hpux_acl_sort(hpux_acl, count)) { DEBUG(10, ("resulting acl is not valid!\n")); goto done; } DEBUG(10, ("resulting acl is valid.\n")); ret = acl(discard_const_p(char, smb_fname->base_name), ACL_SET, count, hpux_acl); if (ret != 0) { DEBUG(0, ("ERROR calling acl: %s\n", strerror(errno))); } done: DEBUG(10, ("hpuxacl_sys_acl_set_file %s.\n", ((ret != 0) ? "failed" : "succeeded"))); TALLOC_FREE(smb_fname); SAFE_FREE(hpux_acl); return ret; }
NTSTATUS file_new(struct smb_request *req, connection_struct *conn, files_struct **result) { int i; files_struct *fsp; NTSTATUS status; /* we want to give out file handles differently on each new connection because of a common bug in MS clients where they try to reuse a file descriptor from an earlier smb connection. This code increases the chance that the errant client will get an error rather than causing corruption */ if (first_file == 0) { first_file = (sys_getpid() ^ (int)time(NULL)) % real_max_open_files; } /* TODO: Port the id-tree implementation from Samba4 */ i = bitmap_find(file_bmap, first_file); if (i == -1) { DEBUG(0,("ERROR! Out of file structures\n")); /* TODO: We have to unconditionally return a DOS error here, * W2k3 even returns ERRDOS/ERRnofids for ntcreate&x with * NTSTATUS negotiated */ return NT_STATUS_TOO_MANY_OPENED_FILES; } /* * Make a child of the connection_struct as an fsp can't exist * indepenedent of a connection. */ fsp = talloc_zero(conn, struct files_struct); if (!fsp) { return NT_STATUS_NO_MEMORY; } /* * This can't be a child of fsp because the file_handle can be ref'd * when doing a dos/fcb open, which will then share the file_handle * across multiple fsps. */ fsp->fh = talloc_zero(conn, struct fd_handle); if (!fsp->fh) { TALLOC_FREE(fsp); return NT_STATUS_NO_MEMORY; } fsp->fh->ref_count = 1; fsp->fh->fd = -1; fsp->conn = conn; fsp->fh->gen_id = get_gen_count(); GetTimeOfDay(&fsp->open_time); first_file = (i+1) % real_max_open_files; bitmap_set(file_bmap, i); files_used++; fsp->fnum = i + FILE_HANDLE_OFFSET; SMB_ASSERT(fsp->fnum < 65536); /* * Create an smb_filename with "" for the base_name. There are very * few NULL checks, so make sure it's initialized with something. to * be safe until an audit can be done. */ status = create_synthetic_smb_fname(fsp, "", NULL, NULL, &fsp->fsp_name); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(fsp); TALLOC_FREE(fsp->fh); } DLIST_ADD(Files, fsp); DEBUG(5,("allocated file structure %d, fnum = %d (%d used)\n", i, fsp->fnum, files_used)); if (req != NULL) { req->chain_fsp = fsp; } /* A new fsp invalidates the positive and negative fsp_fi_cache as the new fsp is pushed at the start of the list and we search from a cache hit to the *end* of the list. */ ZERO_STRUCT(fsp_fi_cache); conn->num_files_open++; *result = fsp; return NT_STATUS_OK; }
NTSTATUS print_spool_open(files_struct *fsp, const char *fname, uint16_t current_vuid) { NTSTATUS status; TALLOC_CTX *tmp_ctx; struct print_file_data *pf; struct dcerpc_binding_handle *b = NULL; struct spoolss_DevmodeContainer devmode_ctr; union spoolss_DocumentInfo info; int fd = -1; WERROR werr; tmp_ctx = talloc_new(fsp); if (!tmp_ctx) { return NT_STATUS_NO_MEMORY; } pf = talloc_zero(fsp, struct print_file_data); if (!pf) { status = NT_STATUS_NO_MEMORY; goto done; } pf->svcname = talloc_strdup(pf, lp_servicename(SNUM(fsp->conn))); /* the document name is derived from the file name. * "Remote Downlevel Document" is added in front to * mimic what windows does in this case */ pf->docname = talloc_strdup(pf, DOCNAME_DEFAULT); if (!pf->docname) { status = NT_STATUS_NO_MEMORY; goto done; } if (fname) { const char *p = strrchr(fname, '/'); if (!p) { p = fname; } pf->docname = talloc_asprintf_append(pf->docname, " %s", p); if (!pf->docname) { status = NT_STATUS_NO_MEMORY; goto done; } } /* * Ok, now we have to open an actual file. * Here is the reason: * We want to write the spool job to this file in * smbd for scalability reason (and also because * apparently window printer drivers can seek when * spooling to a file). * So we first create a file, and then we pass it * to spoolss in output_file so it can monitor and * take over once we call EndDocPrinter(). * Of course we will not start writing until * StartDocPrinter() actually gives the ok. * smbd spooler files do not include a print jobid * path component, as the jobid is only known after * calling StartDocPrinter(). */ pf->filename = talloc_asprintf(pf, "%s/%sXXXXXX", lp_pathname(SNUM(fsp->conn)), PRINT_SPOOL_PREFIX); if (!pf->filename) { status = NT_STATUS_NO_MEMORY; goto done; } errno = 0; fd = mkstemp(pf->filename); if (fd == -1) { if (errno == EACCES) { /* Common setup error, force a report. */ DEBUG(0, ("Insufficient permissions " "to open spool file %s.\n", pf->filename)); } else { /* Normal case, report at level 3 and above. */ DEBUG(3, ("can't open spool file %s,\n", pf->filename)); DEBUGADD(3, ("errno = %d (%s).\n", errno, strerror(errno))); } status = map_nt_error_from_unix(errno); goto done; } /* now open a document over spoolss so that it does * all printer verification, and eventually assigns * a job id */ status = rpc_pipe_open_interface(fsp->conn, &ndr_table_spoolss.syntax_id, fsp->conn->session_info, &fsp->conn->sconn->client_id, fsp->conn->sconn->msg_ctx, &fsp->conn->spoolss_pipe); if (!NT_STATUS_IS_OK(status)) { goto done; } b = fsp->conn->spoolss_pipe->binding_handle; ZERO_STRUCT(devmode_ctr); status = dcerpc_spoolss_OpenPrinter(b, pf, pf->svcname, "RAW", devmode_ctr, SEC_FLAG_MAXIMUM_ALLOWED, &pf->handle, &werr); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!W_ERROR_IS_OK(werr)) { status = werror_to_ntstatus(werr); goto done; } info.info1 = talloc(tmp_ctx, struct spoolss_DocumentInfo1); if (!info.info1) { status = NT_STATUS_NO_MEMORY; goto done; } info.info1->document_name = pf->docname; info.info1->output_file = pf->filename; info.info1->datatype = "RAW"; status = dcerpc_spoolss_StartDocPrinter(b, tmp_ctx, &pf->handle, 1, info, &pf->jobid, &werr); if (!NT_STATUS_IS_OK(status)) { goto done; } if (!W_ERROR_IS_OK(werr)) { status = werror_to_ntstatus(werr); goto done; } /* Convert to RAP id. */ pf->rap_jobid = pjobid_to_rap(pf->svcname, pf->jobid); if (pf->rap_jobid == 0) { /* No errno around here */ status = NT_STATUS_ACCESS_DENIED; goto done; } /* setup a full fsp */ status = create_synthetic_smb_fname(fsp, pf->filename, NULL, NULL, &fsp->fsp_name); if (!NT_STATUS_IS_OK(status)) { goto done; } if (sys_fstat(fd, &fsp->fsp_name->st, false) != 0) { status = map_nt_error_from_unix(errno); goto done; } fsp->file_id = vfs_file_id_from_sbuf(fsp->conn, &fsp->fsp_name->st); fsp->mode = fsp->fsp_name->st.st_ex_mode; fsp->fh->fd = fd; fsp->vuid = current_vuid; fsp->can_lock = false; fsp->can_read = false; fsp->access_mask = FILE_GENERIC_WRITE; fsp->can_write = true; fsp->modified = false; fsp->oplock_type = NO_OPLOCK; fsp->sent_oplock_break = NO_BREAK_SENT; fsp->is_directory = false; fsp->print_file = pf; status = NT_STATUS_OK; done: if (!NT_STATUS_IS_OK(status)) { if (fd != -1) { close(fd); if (fsp->print_file) { unlink(fsp->print_file->filename); } } /* We need to delete the job from spoolss too */ if (pf->jobid) { print_spool_terminate(fsp->conn, pf); } } talloc_free(tmp_ctx); return status; }
/** * Check if file should be recycled **/ static int recycle_unlink(vfs_handle_struct *handle, const struct smb_filename *smb_fname) { connection_struct *conn = handle->conn; char *path_name = NULL; char *temp_name = NULL; char *final_name = NULL; struct smb_filename *smb_fname_final = NULL; const char *base; char *repository = NULL; int i = 1; SMB_OFF_T maxsize, minsize; SMB_OFF_T file_size; /* space_avail; */ bool exist; NTSTATUS status; int rc = -1; repository = talloc_sub_advanced(NULL, lp_servicename(SNUM(conn)), conn->session_info->unix_name, conn->connectpath, conn->session_info->utok.gid, conn->session_info->sanitized_username, conn->session_info->info3->base.domain.string, recycle_repository(handle)); ALLOC_CHECK(repository, done); /* shouldn't we allow absolute path names here? --metze */ /* Yes :-). JRA. */ trim_char(repository, '\0', '/'); if(!repository || *(repository) == '\0') { DEBUG(3, ("recycle: repository path not set, purging %s...\n", smb_fname_str_dbg(smb_fname))); rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); goto done; } /* we don't recycle the recycle bin... */ if (strncmp(smb_fname->base_name, repository, strlen(repository)) == 0) { DEBUG(3, ("recycle: File is within recycling bin, unlinking ...\n")); rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); goto done; } file_size = recycle_get_file_size(handle, smb_fname); /* it is wrong to purge filenames only because they are empty imho * --- simo * if(fsize == 0) { DEBUG(3, ("recycle: File %s is empty, purging...\n", file_name)); rc = SMB_VFS_NEXT_UNLINK(handle,file_name); goto done; } */ /* FIXME: this is wrong, we should check the whole size of the recycle bin is * not greater then maxsize, not the size of the single file, also it is better * to remove older files */ maxsize = recycle_maxsize(handle); if(maxsize > 0 && file_size > maxsize) { DEBUG(3, ("recycle: File %s exceeds maximum recycle size, " "purging... \n", smb_fname_str_dbg(smb_fname))); rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); goto done; } minsize = recycle_minsize(handle); if(minsize > 0 && file_size < minsize) { DEBUG(3, ("recycle: File %s lowers minimum recycle size, " "purging... \n", smb_fname_str_dbg(smb_fname))); rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); goto done; } /* FIXME: this is wrong: moving files with rename does not change the disk space * allocation * space_avail = SMB_VFS_NEXT_DISK_FREE(handle, ".", True, &bsize, &dfree, &dsize) * 1024L; DEBUG(5, ("space_avail = %Lu, file_size = %Lu\n", space_avail, file_size)); if(space_avail < file_size) { DEBUG(3, ("recycle: Not enough diskspace, purging file %s\n", file_name)); rc = SMB_VFS_NEXT_UNLINK(handle, file_name); goto done; } */ /* extract filename and path */ base = strrchr(smb_fname->base_name, '/'); if (base == NULL) { base = smb_fname->base_name; path_name = SMB_STRDUP("/"); ALLOC_CHECK(path_name, done); } else { path_name = SMB_STRDUP(smb_fname->base_name); ALLOC_CHECK(path_name, done); path_name[base - smb_fname->base_name] = '\0'; base++; } /* original filename with path */ DEBUG(10, ("recycle: fname = %s\n", smb_fname_str_dbg(smb_fname))); /* original path */ DEBUG(10, ("recycle: fpath = %s\n", path_name)); /* filename without path */ DEBUG(10, ("recycle: base = %s\n", base)); if (matchparam(recycle_exclude(handle), base)) { DEBUG(3, ("recycle: file %s is excluded \n", base)); rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); goto done; } if (matchdirparam(recycle_exclude_dir(handle), path_name)) { DEBUG(3, ("recycle: directory %s is excluded \n", path_name)); rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); goto done; } if (recycle_keep_dir_tree(handle) == True) { if (asprintf(&temp_name, "%s/%s", repository, path_name) == -1) { ALLOC_CHECK(temp_name, done); } } else { temp_name = SMB_STRDUP(repository); } ALLOC_CHECK(temp_name, done); exist = recycle_directory_exist(handle, temp_name); if (exist) { DEBUG(10, ("recycle: Directory already exists\n")); } else { DEBUG(10, ("recycle: Creating directory %s\n", temp_name)); if (recycle_create_dir(handle, temp_name) == False) { DEBUG(3, ("recycle: Could not create directory, " "purging %s...\n", smb_fname_str_dbg(smb_fname))); rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); goto done; } } if (asprintf(&final_name, "%s/%s", temp_name, base) == -1) { ALLOC_CHECK(final_name, done); } /* Create smb_fname with final base name and orig stream name. */ status = create_synthetic_smb_fname(talloc_tos(), final_name, smb_fname->stream_name, NULL, &smb_fname_final); if (!NT_STATUS_IS_OK(status)) { rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); goto done; } /* new filename with path */ DEBUG(10, ("recycle: recycled file name: %s\n", smb_fname_str_dbg(smb_fname_final))); /* check if we should delete file from recycle bin */ if (recycle_file_exist(handle, smb_fname_final)) { if (recycle_versions(handle) == False || matchparam(recycle_noversions(handle), base) == True) { DEBUG(3, ("recycle: Removing old file %s from recycle " "bin\n", smb_fname_str_dbg(smb_fname_final))); if (SMB_VFS_NEXT_UNLINK(handle, smb_fname_final) != 0) { DEBUG(1, ("recycle: Error deleting old file: %s\n", strerror(errno))); } } } /* rename file we move to recycle bin */ i = 1; while (recycle_file_exist(handle, smb_fname_final)) { SAFE_FREE(final_name); if (asprintf(&final_name, "%s/Copy #%d of %s", temp_name, i++, base) == -1) { ALLOC_CHECK(final_name, done); } TALLOC_FREE(smb_fname_final->base_name); smb_fname_final->base_name = talloc_strdup(smb_fname_final, final_name); if (smb_fname_final->base_name == NULL) { rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); goto done; } } DEBUG(10, ("recycle: Moving %s to %s\n", smb_fname_str_dbg(smb_fname), smb_fname_str_dbg(smb_fname_final))); rc = SMB_VFS_NEXT_RENAME(handle, smb_fname, smb_fname_final); if (rc != 0) { DEBUG(3, ("recycle: Move error %d (%s), purging file %s " "(%s)\n", errno, strerror(errno), smb_fname_str_dbg(smb_fname), smb_fname_str_dbg(smb_fname_final))); rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname); goto done; } /* touch access date of moved file */ if (recycle_touch(handle) == True || recycle_touch_mtime(handle)) recycle_do_touch(handle, smb_fname_final, recycle_touch_mtime(handle)); done: SAFE_FREE(path_name); SAFE_FREE(temp_name); SAFE_FREE(final_name); TALLOC_FREE(smb_fname_final); TALLOC_FREE(repository); return rc; }
connection_struct *make_connection_snum(struct smbd_server_connection *sconn, int snum, user_struct *vuser, DATA_BLOB password, const char *pdev, NTSTATUS *pstatus) { connection_struct *conn = NULL; struct smb_filename *smb_fname_cpath = NULL; fstring dev; int ret; bool on_err_call_dis_hook = false; bool claimed_connection = false; uid_t effuid; gid_t effgid; NTSTATUS status; fstrcpy(dev, pdev); *pstatus = share_sanity_checks(sconn->remote_address, sconn->remote_hostname, snum, dev); if (NT_STATUS_IS_ERR(*pstatus)) { goto err_root_exit; } conn = conn_new(sconn); if (!conn) { DEBUG(0,("Couldn't find free connection.\n")); *pstatus = NT_STATUS_INSUFFICIENT_RESOURCES; goto err_root_exit; } conn->params->service = snum; status = create_connection_session_info(sconn, conn, snum, vuser ? vuser->session_info : NULL, password, &conn->session_info); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("create_connection_session_info failed: %s\n", nt_errstr(status))); *pstatus = status; goto err_root_exit; } if ((lp_guest_only(snum)) || (lp_security() == SEC_SHARE)) { conn->force_user = true; } add_session_user(sconn, conn->session_info->unix_info->unix_name); conn->num_files_open = 0; conn->lastused = conn->lastused_count = time(NULL); conn->used = True; conn->printer = (strncmp(dev,"LPT",3) == 0); conn->ipc = ( (strncmp(dev,"IPC",3) == 0) || ( lp_enable_asu_support() && strequal(dev,"ADMIN$")) ); /* Case options for the share. */ if (lp_casesensitive(snum) == Auto) { /* We will be setting this per packet. Set to be case * insensitive for now. */ conn->case_sensitive = False; } else { conn->case_sensitive = (bool)lp_casesensitive(snum); } conn->case_preserve = lp_preservecase(snum); conn->short_case_preserve = lp_shortpreservecase(snum); conn->encrypt_level = lp_smb_encrypt(snum); conn->veto_list = NULL; conn->hide_list = NULL; conn->veto_oplock_list = NULL; conn->aio_write_behind_list = NULL; conn->read_only = lp_readonly(SNUM(conn)); status = set_conn_force_user_group(conn, snum); if (!NT_STATUS_IS_OK(status)) { conn_free(conn); *pstatus = status; return NULL; } conn->vuid = (vuser != NULL) ? vuser->vuid : UID_FIELD_INVALID; { char *s = talloc_sub_advanced(talloc_tos(), lp_servicename(SNUM(conn)), conn->session_info->unix_info->unix_name, conn->connectpath, conn->session_info->unix_token->gid, conn->session_info->unix_info->sanitized_username, conn->session_info->info->domain_name, lp_pathname(snum)); if (!s) { *pstatus = NT_STATUS_NO_MEMORY; goto err_root_exit; } if (!set_conn_connectpath(conn,s)) { TALLOC_FREE(s); *pstatus = NT_STATUS_NO_MEMORY; goto err_root_exit; } DEBUG(3,("Connect path is '%s' for service [%s]\n",s, lp_servicename(snum))); TALLOC_FREE(s); } /* * New code to check if there's a share security descripter * added from NT server manager. This is done after the * smb.conf checks are done as we need a uid and token. JRA. * */ share_access_check(conn->session_info->security_token, lp_servicename(snum), MAXIMUM_ALLOWED_ACCESS, &conn->share_access); if ((conn->share_access & FILE_WRITE_DATA) == 0) { if ((conn->share_access & FILE_READ_DATA) == 0) { /* No access, read or write. */ DEBUG(0,("make_connection: connection to %s " "denied due to security " "descriptor.\n", lp_servicename(snum))); *pstatus = NT_STATUS_ACCESS_DENIED; goto err_root_exit; } else { conn->read_only = True; } } /* Initialise VFS function pointers */ if (!smbd_vfs_init(conn)) { DEBUG(0, ("vfs_init failed for service %s\n", lp_servicename(snum))); *pstatus = NT_STATUS_BAD_NETWORK_NAME; goto err_root_exit; } /* ROOT Activities: */ /* explicitly check widelinks here so that we can correctly warn * in the logs. */ widelinks_warning(snum); /* * Enforce the max connections parameter. */ if ((lp_max_connections(snum) > 0) && (count_current_connections(lp_servicename(SNUM(conn)), True) >= lp_max_connections(snum))) { DEBUG(1, ("Max connections (%d) exceeded for %s\n", lp_max_connections(snum), lp_servicename(snum))); *pstatus = NT_STATUS_INSUFFICIENT_RESOURCES; goto err_root_exit; } /* * Get us an entry in the connections db */ if (!claim_connection(conn, lp_servicename(snum))) { DEBUG(1, ("Could not store connections entry\n")); *pstatus = NT_STATUS_INTERNAL_DB_ERROR; goto err_root_exit; } claimed_connection = true; /* Invoke VFS make connection hook - this must be the first filesystem operation that we do. */ if (SMB_VFS_CONNECT(conn, lp_servicename(snum), conn->session_info->unix_info->unix_name) < 0) { DEBUG(0,("make_connection: VFS make connection failed!\n")); *pstatus = NT_STATUS_UNSUCCESSFUL; goto err_root_exit; } /* Any error exit after here needs to call the disconnect hook. */ on_err_call_dis_hook = true; if ((!conn->printer) && (!conn->ipc)) { conn->notify_ctx = notify_init(conn, messaging_server_id(sconn->msg_ctx), sconn->msg_ctx, sconn->ev_ctx, conn); } /* * Fix compatibility issue pointed out by Volker. * We pass the conn->connectpath to the preexec * scripts as a parameter, so attempt to canonicalize * it here before calling the preexec scripts. * We ignore errors here, as it is possible that * the conn->connectpath doesn't exist yet and * the preexec scripts will create them. */ (void)canonicalize_connect_path(conn); /* Preexecs are done here as they might make the dir we are to ChDir * to below */ /* execute any "root preexec = " line */ if (*lp_rootpreexec(snum)) { char *cmd = talloc_sub_advanced(talloc_tos(), lp_servicename(SNUM(conn)), conn->session_info->unix_info->unix_name, conn->connectpath, conn->session_info->unix_token->gid, conn->session_info->unix_info->sanitized_username, conn->session_info->info->domain_name, lp_rootpreexec(snum)); DEBUG(5,("cmd=%s\n",cmd)); ret = smbrun(cmd,NULL); TALLOC_FREE(cmd); if (ret != 0 && lp_rootpreexec_close(snum)) { DEBUG(1,("root preexec gave %d - failing " "connection\n", ret)); *pstatus = NT_STATUS_ACCESS_DENIED; goto err_root_exit; } } /* USER Activites: */ if (!change_to_user(conn, conn->vuid)) { /* No point continuing if they fail the basic checks */ DEBUG(0,("Can't become connected user!\n")); *pstatus = NT_STATUS_LOGON_FAILURE; goto err_root_exit; } effuid = geteuid(); effgid = getegid(); /* Remember that a different vuid can connect later without these * checks... */ /* Preexecs are done here as they might make the dir we are to ChDir * to below */ /* execute any "preexec = " line */ if (*lp_preexec(snum)) { char *cmd = talloc_sub_advanced(talloc_tos(), lp_servicename(SNUM(conn)), conn->session_info->unix_info->unix_name, conn->connectpath, conn->session_info->unix_token->gid, conn->session_info->unix_info->sanitized_username, conn->session_info->info->domain_name, lp_preexec(snum)); ret = smbrun(cmd,NULL); TALLOC_FREE(cmd); if (ret != 0 && lp_preexec_close(snum)) { DEBUG(1,("preexec gave %d - failing connection\n", ret)); *pstatus = NT_STATUS_ACCESS_DENIED; goto err_root_exit; } } #ifdef WITH_FAKE_KASERVER if (lp_afs_share(snum)) { afs_login(conn); } #endif /* * we've finished with the user stuff - go back to root * so the SMB_VFS_STAT call will only fail on path errors, * not permission problems. */ change_to_root_user(); /* ROOT Activites: */ /* * If widelinks are disallowed we need to canonicalise the connect * path here to ensure we don't have any symlinks in the * connectpath. We will be checking all paths on this connection are * below this directory. We must do this after the VFS init as we * depend on the realpath() pointer in the vfs table. JRA. */ if (!lp_widelinks(snum)) { if (!canonicalize_connect_path(conn)) { DEBUG(0, ("canonicalize_connect_path failed " "for service %s, path %s\n", lp_servicename(snum), conn->connectpath)); *pstatus = NT_STATUS_BAD_NETWORK_NAME; goto err_root_exit; } } /* Add veto/hide lists */ if (!IS_IPC(conn) && !IS_PRINT(conn)) { set_namearray( &conn->veto_list, lp_veto_files(snum)); set_namearray( &conn->hide_list, lp_hide_files(snum)); set_namearray( &conn->veto_oplock_list, lp_veto_oplocks(snum)); set_namearray( &conn->aio_write_behind_list, lp_aio_write_behind(snum)); } status = create_synthetic_smb_fname(talloc_tos(), conn->connectpath, NULL, NULL, &smb_fname_cpath); if (!NT_STATUS_IS_OK(status)) { *pstatus = status; goto err_root_exit; } /* win2000 does not check the permissions on the directory during the tree connect, instead relying on permission check during individual operations. To match this behaviour I have disabled this chdir check (tridge) */ /* the alternative is just to check the directory exists */ if ((ret = SMB_VFS_STAT(conn, smb_fname_cpath)) != 0 || !S_ISDIR(smb_fname_cpath->st.st_ex_mode)) { if (ret == 0 && !S_ISDIR(smb_fname_cpath->st.st_ex_mode)) { DEBUG(0,("'%s' is not a directory, when connecting to " "[%s]\n", conn->connectpath, lp_servicename(snum))); } else { DEBUG(0,("'%s' does not exist or permission denied " "when connecting to [%s] Error was %s\n", conn->connectpath, lp_servicename(snum), strerror(errno) )); } *pstatus = NT_STATUS_BAD_NETWORK_NAME; goto err_root_exit; } conn->base_share_dev = smb_fname_cpath->st.st_ex_dev; string_set(&conn->origpath,conn->connectpath); /* Figure out the characteristics of the underlying filesystem. This * assumes that all the filesystem mounted withing a share path have * the same characteristics, which is likely but not guaranteed. */ conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res); /* * Print out the 'connected as' stuff here as we need * to know the effective uid and gid we will be using * (at least initially). */ if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) { dbgtext( "%s (%s) ", get_remote_machine_name(), tsocket_address_string(conn->sconn->remote_address, talloc_tos()) ); dbgtext( "%s", srv_is_signing_active(sconn) ? "signed " : ""); dbgtext( "connect to service %s ", lp_servicename(snum) ); dbgtext( "initially as user %s ", conn->session_info->unix_info->unix_name ); dbgtext( "(uid=%d, gid=%d) ", (int)effuid, (int)effgid ); dbgtext( "(pid %d)\n", (int)sys_getpid() ); } return(conn); err_root_exit: TALLOC_FREE(smb_fname_cpath); /* We must exit this function as root. */ if (geteuid() != 0) { change_to_root_user(); } if (on_err_call_dis_hook) { /* Call VFS disconnect hook */ SMB_VFS_DISCONNECT(conn); } if (claimed_connection) { yield_connection(conn, lp_servicename(snum)); } if (conn) { conn_free(conn); } return NULL; }
static ssize_t vfs_my_module_pwrite(vfs_handle_struct *handle, files_struct *fsp, const void *data, size_t n, off_t offset) { connection_struct *conn = handle->conn; char *path_name = NULL; char *temp_name = NULL; char *final_name = NULL; const struct smb_filename *smb_fname = fsp->fsp_name; struct smb_filename *smb_fname_final = NULL; const char *base; char *repository,*repositoryTemp = NULL; int i = 1; SMB_OFF_T maxsize, minsize; SMB_OFF_T file_size; /* space_avail; */ bool exist; NTSTATUS status; ssize_t rc = -1; int count = 0; const char *share = "/mnt/share"; repository = talloc_sub_advanced(NULL, lp_servicename(SNUM(conn)), conn->session_info->unix_name, conn->connectpath, conn->session_info->utok.gid, conn->session_info->sanitized_username, conn->session_info->info3->base.domain.string, sharevolume(handle)); ALLOC_CHECK(repository, done); trim_char(repository, '\0', '/'); if(!repository || *(repository) == '\0') { rc = SMB_VFS_NEXT_PWRITE(handle, fsp,data,n,offset); goto done; } file_size = vfs_my_module_get_file_size(handle, smb_fname); maxsize = vfs_my_module_maxsize(handle); if(maxsize > 0 && file_size > maxsize){ repository = talloc_sub_advanced(NULL, lp_servicename(SNUM(conn)), conn->session_info->unix_name, conn->connectpath, conn->session_info->utok.gid, conn->session_info->sanitized_username, conn->session_info->info3->base.domain.string, stripevolume(handle)); ALLOC_CHECK(repository, done); trim_char(repository, '\0', '/'); if(!repository || *(repository) == '\0') { rc = SMB_VFS_NEXT_PWRITE(handle, fsp,data,n,offset); goto done; } } if (strncmp(smb_fname->base_name, repository, strlen(repository)) == 0) { rc = SMB_VFS_NEXT_PWRITE(handle, fsp,data,n,offset); goto done; } base = strrchr(smb_fname->base_name, '/'); if (base == NULL) { base = smb_fname->base_name; path_name = SMB_STRDUP("/"); ALLOC_CHECK(path_name, done); } else { path_name = SMB_STRDUP(smb_fname->base_name); ALLOC_CHECK(path_name, done); path_name[base - smb_fname->base_name] = '\0'; base++; } /* original filename with path */ DEBUG(10, ("file transaction: fname = %s\n", smb_fname_str_dbg(smb_fname))); /* original path */ DEBUG(10, ("file transaction: fpath = %s\n", path_name)); /* filename without path */ DEBUG(10, ("file transaction: base = %s\n", base)); if (vfs_my_module_keep_dir_tree(handle) == True) { if (asprintf(&temp_name, "%s/%s", repository, path_name) == -1) { ALLOC_CHECK(temp_name, done); } } else { temp_name = SMB_STRDUP(repository); } ALLOC_CHECK(temp_name, done); exist = vfs_my_module_directory_exist(handle, temp_name); if (exist) { DEBUG(10, ("file transaction: Directory already exists\n")); } else { DEBUG(10, ("file transaction: Creating directory %s\n", temp_name)); count = vfs_my_module_create_dir(handle, temp_name, smb_fname); } if (asprintf(&final_name, "%s/%s", temp_name, base) == -1) { ALLOC_CHECK(final_name, done); } /* Create smb_fname with final base name and orig stream name. */ status = create_synthetic_smb_fname(talloc_tos(), final_name, smb_fname->stream_name, NULL, &smb_fname_final); if (!NT_STATUS_IS_OK(status)) { rc = SMB_VFS_NEXT_PWRITE(handle, fsp,data,n,offset); goto done; } TALLOC_FREE(smb_fname_final->base_name); smb_fname_final->base_name = talloc_strdup(smb_fname_final, final_name); if (smb_fname_final->base_name == NULL) { rc = SMB_VFS_NEXT_PWRITE(handle, fsp,data,n,offset); goto done; } SMB_VFS_NEXT_RENAME(handle, smb_fname, smb_fname_final); rc = SMB_VFS_NEXT_PWRITE(handle, fsp,data,n,offset); if (rc != 0) { rc = SMB_VFS_NEXT_PWRITE(handle, fsp,data,n,offset); goto done; } done: vfs_my_module_delete_dir(handle,path_name,smb_fname); while(count !=0){ vfs_my_module_delete_dir(handle,path_name,smb_fname); SMB_VFS_NEXT_RMDIR(handle, path_name); count--; } SAFE_FREE(path_name); SAFE_FREE(temp_name); SAFE_FREE(final_name); TALLOC_FREE(smb_fname_final); TALLOC_FREE(repository); return rc; }