static struct security_descriptor *file_get_sd( struct object *obj ) { struct file *file = (struct file *)obj; struct stat st; int unix_fd; struct security_descriptor *sd; assert( obj->ops == &file_ops ); unix_fd = get_file_unix_fd( file ); if (unix_fd == -1 || fstat( unix_fd, &st ) == -1) return obj->sd; /* mode and uid the same? if so, no need to re-generate security descriptor */ if (obj->sd && (st.st_mode & (S_IRWXU|S_IRWXO)) == (file->mode & (S_IRWXU|S_IRWXO)) && (st.st_uid == file->uid)) return obj->sd; sd = mode_to_sd( st.st_mode, security_unix_uid_to_sid( st.st_uid ), token_get_primary_group( current->process->token )); if (!sd) return obj->sd; file->mode = st.st_mode; file->uid = st.st_uid; free( obj->sd ); obj->sd = sd; return sd; }
struct security_descriptor *get_file_sd( struct object *obj, struct fd *fd, mode_t *mode, uid_t *uid ) { int unix_fd = get_unix_fd( fd ); struct stat st; struct security_descriptor *sd; const SID *user, *group; if (unix_fd == -1 || fstat( unix_fd, &st ) == -1) return obj->sd; /* mode and uid the same? if so, no need to re-generate security descriptor */ if (obj->sd && (st.st_mode & (S_IRWXU|S_IRWXO)) == (*mode & (S_IRWXU|S_IRWXO)) && (st.st_uid == *uid)) return obj->sd; user = security_unix_uid_to_sid( st.st_uid ); group = token_get_primary_group( current->process->token ); sd = get_xattr_sd( unix_fd ); if (!sd) sd = get_xattr_acls( unix_fd, user, group ); if (sd) convert_generic_sd( sd ); if (!sd) sd = mode_to_sd( st.st_mode, user, group ); if (!sd) return obj->sd; *mode = st.st_mode; *uid = st.st_uid; free( obj->sd ); obj->sd = sd; return sd; }
static struct security_descriptor *file_get_parent_sd( struct fd *root, const char *child_name, int child_len, int is_dir ) { struct security_descriptor *parent_sd, *sd = NULL; mode_t parent_mode = 0555; char *p, *parent_name; struct fd *parent_fd; struct stat st; int unix_fd; if (!(parent_name = mem_alloc( child_len + 1 ))) return NULL; memcpy( parent_name, child_name, child_len ); parent_name[child_len] = 0; /* skip trailing slashes */ p = parent_name + strlen( parent_name ) - 1; while (p > parent_name && *p == '/') p--; /* remove last path component */ if (p == parent_name && *p == '/') { free(parent_name); return NULL; } while (p > parent_name && *p != '/') p--; while (p > parent_name && *p == '/') p--; p[1] = 0; parent_fd = open_fd( root, parent_name, O_NONBLOCK | O_LARGEFILE, &parent_mode, READ_CONTROL|ACCESS_SYSTEM_SECURITY, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN_FOR_BACKUP_INTENT ); free( parent_name ); if (parent_fd) { unix_fd = get_unix_fd( parent_fd ); if (unix_fd != -1) { parent_sd = get_xattr_sd( unix_fd ); if (!parent_sd && fstat( unix_fd, &st ) != -1) parent_sd = get_xattr_acls( unix_fd, security_unix_uid_to_sid( st.st_uid ), token_get_primary_group( current->process->token ) ); if (parent_sd) { sd = inherit_sd( parent_sd, is_dir ); free( parent_sd ); } } release_object( parent_fd ); } return sd; }
static struct object *create_file( struct fd *root, const char *nameptr, data_size_t len, unsigned int access, unsigned int sharing, int create, unsigned int options, unsigned int attrs, const struct security_descriptor *sd ) { struct security_descriptor *temp_sd = NULL; const SID *owner = NULL, *group = NULL; struct object *obj = NULL; struct fd *fd; int flags; char *name; mode_t mode; if (!len || ((nameptr[0] == '/') ^ !root)) { set_error( STATUS_OBJECT_PATH_SYNTAX_BAD ); return NULL; } if (!(name = mem_alloc( len + 1 ))) return NULL; memcpy( name, nameptr, len ); name[len] = 0; switch(create) { case FILE_CREATE: flags = O_CREAT | O_EXCL; break; case FILE_OVERWRITE_IF: /* FIXME: the difference is whether we trash existing attr or not */ access |= FILE_WRITE_ATTRIBUTES; case FILE_SUPERSEDE: flags = O_CREAT | O_TRUNC; break; case FILE_OPEN: flags = 0; break; case FILE_OPEN_IF: flags = O_CREAT; break; case FILE_OVERWRITE: flags = O_TRUNC; access |= FILE_WRITE_ATTRIBUTES; break; default: set_error( STATUS_INVALID_PARAMETER ); goto done; } /* Note: inheritance of security descriptors only occurs on creation when sd is NULL */ if (!sd && (create == FILE_CREATE || create == FILE_OVERWRITE_IF)) sd = temp_sd = file_get_parent_sd( root, nameptr, len, options & FILE_DIRECTORY_FILE ); if (sd) { owner = sd_get_owner( sd ); if (!owner) owner = token_get_user( current->process->token ); group = sd_get_group( sd ); if (!group) group = token_get_primary_group( current->process->token ); mode = sd_to_mode( sd, owner ); } else if (options & FILE_DIRECTORY_FILE) mode = (attrs & FILE_ATTRIBUTE_READONLY) ? 0555 : 0777; else mode = (attrs & FILE_ATTRIBUTE_READONLY) ? 0444 : 0666; if (len >= 4 && (!strcasecmp( name + len - 4, ".exe" ) || !strcasecmp( name + len - 4, ".com" ))) { if (mode & S_IRUSR) mode |= S_IXUSR; if (mode & S_IRGRP) mode |= S_IXGRP; if (mode & S_IROTH) mode |= S_IXOTH; } access = generic_file_map_access( access ); /* FIXME: should set error to STATUS_OBJECT_NAME_COLLISION if file existed before */ fd = open_fd( root, name, flags | O_NONBLOCK | O_LARGEFILE, &mode, access, sharing, options ); if (!fd) goto done; set_xattr_sd( get_unix_fd( fd ), sd, owner, group ); if (S_ISDIR(mode)) obj = create_dir_obj( fd, access, mode ); else if (S_ISCHR(mode) && is_serial_fd( fd )) obj = create_serial( fd ); else obj = create_file_obj( fd, access, mode ); release_object( fd ); done: free( temp_sd ); free( name ); return obj; }
int set_file_sd( struct object *obj, struct fd *fd, const struct security_descriptor *sd, unsigned int set_info ) { struct security_descriptor *tmp_sd = NULL; int unix_fd = get_unix_fd( fd ); const SID *owner, *group; struct stat st; mode_t mode; int ret = 1; if (unix_fd == -1 || fstat( unix_fd, &st ) == -1) return 1; if (!(set_info & PROTECTED_DACL_SECURITY_INFORMATION)) { char *child_name = fd_get_unix_name( fd ); if (child_name) { struct security_descriptor *parent_sd; parent_sd = file_get_parent_sd( NULL, child_name, strlen(child_name), S_ISDIR(st.st_mode) ); free( child_name ); if (parent_sd) { tmp_sd = file_combine_sds( parent_sd, sd ); if (tmp_sd) sd = tmp_sd; /* only used combined sd if successful */ free( parent_sd ); } } } if (set_info & OWNER_SECURITY_INFORMATION) { owner = sd_get_owner( sd ); if (!owner) { set_error( STATUS_INVALID_SECURITY_DESCR ); ret = 0; goto err; } if (!obj->sd || !security_equal_sid( owner, sd_get_owner( obj->sd ) )) { /* FIXME: get Unix uid and call fchown */ } } else if (obj->sd) owner = sd_get_owner( obj->sd ); else owner = token_get_user( current->process->token ); if (set_info & GROUP_SECURITY_INFORMATION) { group = sd_get_group( sd ); if (!group) { set_error( STATUS_INVALID_SECURITY_DESCR ); ret = 0; goto err; } if (!obj->sd || !security_equal_sid( group, sd_get_group( obj->sd ) )) { /* FIXME: get Unix uid and call fchown */ } } else if (obj->sd) group = sd_get_group( obj->sd ); else group = token_get_primary_group( current->process->token ); /* group and sacl not supported */ if (set_info & DACL_SECURITY_INFORMATION) { /* keep the bits that we don't map to access rights in the ACL */ mode = st.st_mode & (S_ISUID|S_ISGID|S_ISVTX); mode |= sd_to_mode( sd, owner ); set_xattr_sd( unix_fd, sd, owner, group ); if (((st.st_mode ^ mode) & (S_IRWXU|S_IRWXG|S_IRWXO)) && fchmod( unix_fd, mode ) == -1) { file_set_error(); ret = 0; } } err: free( tmp_sd ); return ret; }