}
}
+ if ((perms & S_IWUSR) && lp_dos_filemode(snum)) {
+ nt_mask |= (SEC_STD_WRITE_DAC|SEC_STD_WRITE_OWNER|DELETE_ACCESS);
+ }
+
DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
(unsigned int)perms, (unsigned int)nt_mask ));
Map NT perms to a UNIX mode_t.
****************************************************************************/
-#define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
-#define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
+#define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA)
+#define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA)
#define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
static mode_t map_nt_perms( uint32 *mask, int type)
****************************************************************************/
static bool ensure_canon_entry_valid(canon_ace **pp_ace,
- const struct share_params *params,
- const bool is_directory,
- const DOM_SID *pfile_owner_sid,
- const DOM_SID *pfile_grp_sid,
- const SMB_STRUCT_STAT *pst,
- bool setting_acl)
+ bool is_default_acl,
+ const struct share_params *params,
+ const bool is_directory,
+ const DOM_SID *pfile_owner_sid,
+ const DOM_SID *pfile_grp_sid,
+ const SMB_STRUCT_STAT *pst,
+ bool setting_acl)
{
canon_ace *pace;
bool got_user = False;
for (pace = *pp_ace; pace; pace = pace->next) {
if (pace->type == SMB_ACL_USER_OBJ) {
- if (setting_acl)
+ if (setting_acl) {
+ /*
+ * Ensure we have default parameters for the
+ * user (owner) even on default ACLs.
+ */
apply_default_perms(params, is_directory, pace, S_IRUSR);
+ }
got_user = True;
} else if (pace->type == SMB_ACL_GROUP_OBJ) {
* Ensure create mask/force create mode is respected on set.
*/
- if (setting_acl)
+ if (setting_acl && !is_default_acl) {
apply_default_perms(params, is_directory, pace, S_IRGRP);
+ }
got_grp = True;
} else if (pace->type == SMB_ACL_OTHER) {
* Ensure create mask/force create mode is respected on set.
*/
- if (setting_acl)
+ if (setting_acl && !is_default_acl) {
apply_default_perms(params, is_directory, pace, S_IROTH);
+ }
got_other = True;
pace_other = pace;
+
+ } else if (pace->type == SMB_ACL_USER || pace->type == SMB_ACL_GROUP) {
+
+ /*
+ * Ensure create mask/force create mode is respected on set.
+ */
+
+ if (setting_acl && !is_default_acl) {
+ apply_default_perms(params, is_directory, pace, S_IRGRP);
+ }
}
}
pace->unix_ug.uid = pst->st_ex_uid;
pace->trustee = *pfile_owner_sid;
pace->attr = ALLOW_ACE;
+ /* Start with existing permissions, principle of least
+ surprises for the user. */
+ pace->perms = pst->st_ex_mode;
if (setting_acl) {
/* See if the owning user is in any of the other groups in
- the ACE. If so, OR in the permissions from that group. */
+ the ACE, or if there's a matching user entry.
+ If so, OR in the permissions from that entry. */
- bool group_matched = False;
canon_ace *pace_iter;
for (pace_iter = *pp_ace; pace_iter; pace_iter = pace_iter->next) {
- if (pace_iter->type == SMB_ACL_GROUP_OBJ || pace_iter->type == SMB_ACL_GROUP) {
+ if (pace_iter->type == SMB_ACL_USER &&
+ pace_iter->unix_ug.uid == pace->unix_ug.uid) {
+ pace->perms |= pace_iter->perms;
+ } else if (pace_iter->type == SMB_ACL_GROUP_OBJ || pace_iter->type == SMB_ACL_GROUP) {
if (uid_entry_in_group(pace, pace_iter)) {
pace->perms |= pace_iter->perms;
- group_matched = True;
}
}
}
- /* If we only got an "everyone" perm, just use that. */
- if (!group_matched) {
+ if (pace->perms == 0) {
+ /* If we only got an "everyone" perm, just use that. */
if (got_other)
pace->perms = pace_other->perms;
- else
- pace->perms = 0;
}
+ /*
+ * Ensure we have default parameters for the
+ * user (owner) even on default ACLs.
+ */
apply_default_perms(params, is_directory, pace, S_IRUSR);
} else {
pace->perms = unix_perms_to_acl_perms(pst->st_ex_mode, S_IRUSR, S_IWUSR, S_IXUSR);
pace->perms = pace_other->perms;
else
pace->perms = 0;
- apply_default_perms(params, is_directory, pace, S_IRGRP);
+ if (!is_default_acl) {
+ apply_default_perms(params, is_directory, pace, S_IRGRP);
+ }
} else {
pace->perms = unix_perms_to_acl_perms(pst->st_ex_mode, S_IRGRP, S_IWGRP, S_IXGRP);
}
pace->attr = ALLOW_ACE;
if (setting_acl) {
pace->perms = 0;
- apply_default_perms(params, is_directory, pace, S_IROTH);
+ if (!is_default_acl) {
+ apply_default_perms(params, is_directory, pace, S_IROTH);
+ }
} else
pace->perms = unix_perms_to_acl_perms(pst->st_ex_mode, S_IROTH, S_IWOTH, S_IXOTH);
Check if a POSIX ACL has the required SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries.
If it does not have them, check if there are any entries where the trustee is the
file owner or the owning group, and map these to SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ.
+ Note we must not do this to default directory ACLs.
****************************************************************************/
static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID *pfile_grp_sid)
DEBUG(10,("check_owning_objs: ACL is missing an owning group entry.\n"));
}
-/****************************************************************************
- If an ACE entry is SMB_ACL_USER_OBJ and not CREATOR_OWNER, map to SMB_ACL_USER.
- If an ACE entry is SMB_ACL_GROUP_OBJ and not CREATOR_GROUP, map to SMB_ACL_GROUP
-****************************************************************************/
-
-static bool dup_owning_ace(canon_ace *dir_ace, canon_ace *ace)
-{
- /* dir ace must be followings.
- SMB_ACL_USER_OBJ : trustee(CREATOR_OWNER) -> Posix ACL d:u::perm
- SMB_ACL_USER : not trustee -> Posix ACL u:user:perm
- SMB_ACL_USER_OBJ : trustee -> convert to SMB_ACL_USER : trustee
- Posix ACL u:trustee:perm
-
- SMB_ACL_GROUP_OBJ: trustee(CREATOR_GROUP) -> Posix ACL d:g::perm
- SMB_ACL_GROUP : not trustee -> Posix ACL g:group:perm
- SMB_ACL_GROUP_OBJ: trustee -> convert to SMB_ACL_GROUP : trustee
- Posix ACL g:trustee:perm
- */
-
- if (ace->type == SMB_ACL_USER_OBJ &&
- !(sid_equal(&ace->trustee, &global_sid_Creator_Owner))) {
- canon_ace *dup_ace = dup_canon_ace(ace);
-
- if (dup_ace == NULL) {
- return false;
- }
- dup_ace->type = SMB_ACL_USER;
- DLIST_ADD_END(dir_ace, dup_ace, canon_ace *);
- }
-
- if (ace->type == SMB_ACL_GROUP_OBJ &&
- !(sid_equal(&ace->trustee, &global_sid_Creator_Group))) {
- canon_ace *dup_ace = dup_canon_ace(ace);
-
- if (dup_ace == NULL) {
- return false;
- }
- dup_ace->type = SMB_ACL_GROUP;
- DLIST_ADD_END(dir_ace, dup_ace, canon_ace *);
- }
-
- return true;
-}
-
/****************************************************************************
Unpack a SEC_DESC into two canonical ace lists.
****************************************************************************/
continue;
}
+ if (lp_force_unknown_acl_user(SNUM(fsp->conn))) {
+ DEBUG(10, ("create_canon_ace_lists: ignoring "
+ "unknown or foreign SID %s\n",
+ sid_string_dbg(&psa->trustee)));
+ SAFE_FREE(current_ace);
+ continue;
+ }
+
free_canon_ace_list(file_ace);
free_canon_ace_list(dir_ace);
DEBUG(0, ("create_canon_ace_lists: unable to map SID "
if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
(SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+ canon_ace *current_dir_ace = current_ace;
DLIST_ADD_END(dir_ace, current_ace, canon_ace *);
/*
print_canon_ace( current_ace, 0);
}
- /*
- * We have a lossy mapping: directory ACE entries
- * CREATOR_OWNER ------\
- * (map to) +---> SMB_ACL_USER_OBJ
- * owning sid ------/
- *
- * CREATOR_GROUP ------\
- * (map to) +---> SMB_ACL_GROUP_OBJ
- * primary group sid --/
- *
- * on set. And on read of a directory ACL
- *
- * SMB_ACL_USER_OBJ ----> CREATOR_OWNER
- * SMB_ACL_GROUP_OBJ ---> CREATOR_GROUP.
- *
- * Deal with this on set by duplicating
- * owning sid and primary group sid ACE
- * entries into the directory ACL.
- * Fix from Tsukasa Hamano <hamano@osstech.co.jp>.
- */
-
- if (!dup_owning_ace(dir_ace, current_ace)) {
- DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
- free_canon_ace_list(file_ace);
- free_canon_ace_list(dir_ace);
- return false;
- }
-
/*
* If this is not an inherit only ACE we need to add a duplicate
* to the file acl.
*/
current_ace = NULL;
}
+
+ /*
+ * current_ace is now either owned by file_ace
+ * or is NULL. We can safely operate on current_dir_ace
+ * to treat mapping for default acl entries differently
+ * than access acl entries.
+ */
+
+ if (current_dir_ace->owner_type == UID_ACE) {
+ /*
+ * We already decided above this is a uid,
+ * for default acls ace's only CREATOR_OWNER
+ * maps to ACL_USER_OBJ. All other uid
+ * ace's are ACL_USER.
+ */
+ if (sid_equal(¤t_dir_ace->trustee,
+ &global_sid_Creator_Owner)) {
+ current_dir_ace->type = SMB_ACL_USER_OBJ;
+ } else {
+ current_dir_ace->type = SMB_ACL_USER;
+ }
+ }
+
+ if (current_dir_ace->owner_type == GID_ACE) {
+ /*
+ * We already decided above this is a gid,
+ * for default acls ace's only CREATOR_GROUP
+ * maps to ACL_GROUP_OBJ. All other uid
+ * ace's are ACL_GROUP.
+ */
+ if (sid_equal(¤t_dir_ace->trustee,
+ &global_sid_Creator_Group)) {
+ current_dir_ace->type = SMB_ACL_GROUP_OBJ;
+ } else {
+ current_dir_ace->type = SMB_ACL_GROUP;
+ }
+ }
}
}
dir_ace = NULL;
} else {
/*
- * Check if we have SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries in each
- * ACL. If we don't have them, check if any SMB_ACL_USER/SMB_ACL_GROUP
- * entries can be converted to *_OBJ. Usually we will already have these
- * entries in the Default ACL, and the Access ACL will not have them.
+ * Check if we have SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries in
+ * the file ACL. If we don't have them, check if any SMB_ACL_USER/SMB_ACL_GROUP
+ * entries can be converted to *_OBJ. Don't do this for the default
+ * ACL, we will create them separately for this if needed inside
+ * ensure_canon_entry_valid().
*/
if (file_ace) {
check_owning_objs(file_ace, pfile_owner_sid, pfile_grp_sid);
}
- if (dir_ace) {
- check_owning_objs(dir_ace, pfile_owner_sid, pfile_grp_sid);
- }
}
*ppfile_ace = file_ace;
*pp_ace_list = ace_list;
}
-/****************************************************************************
- Create a default mode that will be used if a security descriptor entry has
- no user/group/world entries.
-****************************************************************************/
-
-static mode_t create_default_mode(files_struct *fsp, bool interitable_mode)
-{
- int snum = SNUM(fsp->conn);
- mode_t and_bits = (mode_t)0;
- mode_t or_bits = (mode_t)0;
- mode_t mode;
-
- if (interitable_mode) {
- mode = unix_mode(fsp->conn, FILE_ATTRIBUTE_ARCHIVE,
- fsp->fsp_name, NULL);
- } else {
- mode = S_IRUSR;
- }
-
- if (fsp->is_directory)
- mode |= (S_IWUSR|S_IXUSR);
-
- /*
- * Now AND with the create mode/directory mode bits then OR with the
- * force create mode/force directory mode bits.
- */
-
- if (fsp->is_directory) {
- and_bits = lp_dir_security_mask(snum);
- or_bits = lp_force_dir_security_mode(snum);
- } else {
- and_bits = lp_security_mask(snum);
- or_bits = lp_force_security_mode(snum);
- }
-
- return ((mode & and_bits)|or_bits);
-}
-
/****************************************************************************
Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
succeeding.
uint32 security_info_sent,
const SEC_DESC *psd)
{
- SMB_STRUCT_STAT st;
canon_ace *file_ace = NULL;
canon_ace *dir_ace = NULL;
print_canon_ace_list( "file ace - before valid", file_ace);
- st = *pst;
-
- /*
- * A default 3 element mode entry for a file should be r-- --- ---.
- * A default 3 element mode entry for a directory should be rwx --- ---.
- */
-
- st.st_ex_mode = create_default_mode(fsp, False);
-
- if (!ensure_canon_entry_valid(&file_ace, fsp->conn->params,
- fsp->is_directory, pfile_owner_sid, pfile_grp_sid, &st, True)) {
+ if (!ensure_canon_entry_valid(&file_ace, false, fsp->conn->params,
+ fsp->is_directory, pfile_owner_sid, pfile_grp_sid, pst, True)) {
free_canon_ace_list(file_ace);
free_canon_ace_list(dir_ace);
return False;
print_canon_ace_list( "dir ace - before valid", dir_ace);
- /*
- * A default inheritable 3 element mode entry for a directory should be the
- * mode Samba will use to create a file within. Ensure user rwx bits are set if
- * it's a directory.
- */
-
- st.st_ex_mode = create_default_mode(fsp, True);
-
- if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp->conn->params,
- fsp->is_directory, pfile_owner_sid, pfile_grp_sid, &st, True)) {
+ if (dir_ace && !ensure_canon_entry_valid(&dir_ace, true, fsp->conn->params,
+ fsp->is_directory, pfile_owner_sid, pfile_grp_sid, pst, True)) {
free_canon_ace_list(file_ace);
free_canon_ace_list(dir_ace);
return False;
canon_ace *ace = NULL;
canon_ace *next_ace = NULL;
int entry_id = SMB_ACL_FIRST_ENTRY;
+ bool is_default_acl = (the_acl_type == SMB_ACL_TYPE_DEFAULT);
SMB_ACL_ENTRY_T entry;
size_t ace_count;
ace->trustee = sid;
ace->unix_ug = unix_ug;
ace->owner_type = owner_type;
- ace->ace_flags = get_pai_flags(pal, ace, (the_acl_type == SMB_ACL_TYPE_DEFAULT));
+ ace->ace_flags = get_pai_flags(pal, ace, is_default_acl);
DLIST_ADD(l_head, ace);
}
* This next call will ensure we have at least a user/group/world set.
*/
- if (!ensure_canon_entry_valid(&l_head, conn->params,
+ if (!ensure_canon_entry_valid(&l_head, is_default_acl, conn->params,
S_ISDIR(psbuf->st_ex_mode), powner, pgroup,
psbuf, False))
goto fail;
* acl_mask. Ensure all DENY Entries are at the start of the list.
*/
- DEBUG(10,("canonicalise_acl: %s ace entries before arrange :\n", the_acl_type == SMB_ACL_TYPE_ACCESS ? "Access" : "Default" ));
+ DEBUG(10,("canonicalise_acl: %s ace entries before arrange :\n", is_default_acl ? "Default" : "Access"));
for ( ace_count = 0, ace = l_head; ace; ace = next_ace, ace_count++) {
next_ace = ace->next;
Check if the current user group list contains a given group.
****************************************************************************/
-static bool current_user_in_group(gid_t gid)
+bool current_user_in_group(gid_t gid)
{
int i;
mode_t mask_perms = 0;
/* Use the psbuf that was passed in. */
- fsp->fsp_name->st = *psbuf;
+ if (psbuf != &fsp->fsp_name->st) {
+ fsp->fsp_name->st = *psbuf;
+ }
#if defined(POSIX_ACL_NEEDS_MASK)
/* HP-UX always wants to have a mask (called "class" there). */
return -1;
}
- if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, smb_fname, &fsp))) {
+ if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname, &fsp))) {
return -1;
}
}
unbecome_root();
- close_file_fchmod(NULL, fsp);
+ close_file(NULL, fsp, NORMAL_CLOSE);
return ret;
}
This should be the only external function needed for the UNIX style set ACL.
****************************************************************************/
-NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC *psd)
+NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC *psd_orig)
{
connection_struct *conn = fsp->conn;
uid_t user = (uid_t)-1;
bool set_acl_as_root = false;
bool acl_set_support = false;
bool ret = false;
+ SEC_DESC *psd = NULL;
DEBUG(10,("set_nt_acl: called for file %s\n",
fsp_str_dbg(fsp)));
return NT_STATUS_MEDIA_WRITE_PROTECTED;
}
+ if (!psd_orig) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ psd = dup_sec_desc(talloc_tos(), psd_orig);
+ if (!psd) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
/*
* Get the current state of the file.
*/
* Unpack the user/group/world id's.
*/
+ /* POSIX can't cope with missing owner/group. */
+ if ((security_info_sent & SECINFO_OWNER) && (psd->owner_sid == NULL)) {
+ security_info_sent &= ~SECINFO_OWNER;
+ }
+ if ((security_info_sent & SECINFO_GROUP) && (psd->group_sid == NULL)) {
+ security_info_sent &= ~SECINFO_GROUP;
+ }
+
status = unpack_nt_owners( SNUM(conn), &user, &grp, security_info_sent, psd);
if (!NT_STATUS_IS_OK(status)) {
return status;
create_file_sids(&fsp->fsp_name->st, &file_owner_sid, &file_grp_sid);
+ if((security_info_sent & SECINFO_DACL) &&
+ (psd->type & SEC_DESC_DACL_PRESENT) &&
+ (psd->dacl == NULL)) {
+ SEC_ACE ace[3];
+
+ /* We can't have NULL DACL in POSIX.
+ Use owner/group/Everyone -> full access. */
+
+ init_sec_ace(&ace[0],
+ &file_owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ GENERIC_ALL_ACCESS,
+ 0);
+ init_sec_ace(&ace[1],
+ &file_grp_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ GENERIC_ALL_ACCESS,
+ 0);
+ init_sec_ace(&ace[2],
+ &global_sid_World,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ GENERIC_ALL_ACCESS,
+ 0);
+ psd->dacl = make_sec_acl(talloc_tos(),
+ NT4_ACL_REVISION,
+ 3,
+ ace);
+ if (psd->dacl == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ security_acl_map_generic(psd->dacl, &file_generic_mapping);
+ }
+
acl_perms = unpack_canon_ace(fsp, &fsp->fsp_name->st, &file_owner_sid,
&file_grp_sid, &file_ace_list,
&dir_ace_list, security_info_sent, psd);
return ret_sd;
}
+
+/* Stolen shamelessly from pvfs_default_acl() in source4 :-). */
+
+NTSTATUS make_default_filesystem_acl(TALLOC_CTX *ctx,
+ const char *name,
+ SMB_STRUCT_STAT *psbuf,
+ SEC_DESC **ppdesc)
+{
+ struct dom_sid owner_sid, group_sid;
+ size_t size = 0;
+ SEC_ACE aces[4];
+ uint32_t access_mask = 0;
+ mode_t mode = psbuf->st_ex_mode;
+ SEC_ACL *new_dacl = NULL;
+ int idx = 0;
+
+ DEBUG(10,("make_default_filesystem_acl: file %s mode = 0%o\n",
+ name, (int)mode ));
+
+ uid_to_sid(&owner_sid, psbuf->st_ex_uid);
+ gid_to_sid(&group_sid, psbuf->st_ex_gid);
+
+ /*
+ We provide up to 4 ACEs
+ - Owner
+ - Group
+ - Everyone
+ - NT System
+ */
+
+ if (mode & S_IRUSR) {
+ if (mode & S_IWUSR) {
+ access_mask |= SEC_RIGHTS_FILE_ALL;
+ } else {
+ access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+ }
+ }
+ if (mode & S_IWUSR) {
+ access_mask |= SEC_RIGHTS_FILE_WRITE | SEC_STD_DELETE;
+ }
+
+ init_sec_ace(&aces[idx],
+ &owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ access_mask,
+ 0);
+ idx++;
+
+ access_mask = 0;
+ if (mode & S_IRGRP) {
+ access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+ }
+ if (mode & S_IWGRP) {
+ /* note that delete is not granted - this matches posix behaviour */
+ access_mask |= SEC_RIGHTS_FILE_WRITE;
+ }
+ if (access_mask) {
+ init_sec_ace(&aces[idx],
+ &group_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ access_mask,
+ 0);
+ idx++;
+ }
+
+ access_mask = 0;
+ if (mode & S_IROTH) {
+ access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+ }
+ if (mode & S_IWOTH) {
+ access_mask |= SEC_RIGHTS_FILE_WRITE;
+ }
+ if (access_mask) {
+ init_sec_ace(&aces[idx],
+ &global_sid_World,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ access_mask,
+ 0);
+ idx++;
+ }
+
+ init_sec_ace(&aces[idx],
+ &global_sid_System,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_RIGHTS_FILE_ALL,
+ 0);
+ idx++;
+
+ new_dacl = make_sec_acl(ctx,
+ NT4_ACL_REVISION,
+ idx,
+ aces);
+
+ if (!new_dacl) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *ppdesc = make_sec_desc(ctx,
+ SECURITY_DESCRIPTOR_REVISION_1,
+ SEC_DESC_SELF_RELATIVE|SEC_DESC_DACL_PRESENT,
+ &owner_sid,
+ &group_sid,
+ NULL,
+ new_dacl,
+ &size);
+ if (!*ppdesc) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}