CVE-2021-3155 (Go)

snapd 2.54.2 and earlier created ~/snap directories in user home directories without specifying owner-only permissions. This could allow a local attacker to read information that should have been private. Fixed in snapd versions 2.54.3+18.04, 2.54.3+20.04 and 2.54.3+21.10.1

Weakness:
CWE-276: name: Incorrect Default Permissions
description: During installation, installed file permissions are set to allow anyone to modify those files.

Repository: https://github.com/snapcore/snapd

Vulnerable code:
file_path: cmd/snap/cmd_run.go
snippet: func createUserDataDirs(info *snap.Info) error {
	// Adjust umask so that the created directories have the permissions we
	// expect and are unaffected by the initial umask. While go runtime creates
	// threads at will behind the scenes, the setting of umask applies to the
	// entire process so it doesn't need any special handling to lock the
	// executing goroutine to a single thread.
	oldUmask := syscall.Umask(0)
	defer syscall.Umask(oldUmask)

	usr, err := userCurrent()
	if err != nil {
		return fmt.Errorf(i18n.G("cannot get the current user: %v"), err)
	}

	// see snapenv.User
	instanceUserData := info.UserDataDir(usr.HomeDir)
	instanceCommonUserData := info.UserCommonDataDir(usr.HomeDir)
	createDirs := []string{instanceUserData, instanceCommonUserData}
	if info.InstanceKey != "" {
		// parallel instance snaps get additional mapping in their mount
		// namespace, namely /home/joe/snap/foo_bar ->
		// /home/joe/snap/foo, make sure that the mount point exists and
		// is owned by the user
		snapUserDir := snap.UserSnapDir(usr.HomeDir, info.SnapName())
		createDirs = append(createDirs, snapUserDir)
	}
	for _, d := range createDirs {
		if err := os.MkdirAll(d, 0755); err != nil {
			// TRANSLATORS: %q is the directory whose creation failed, %v the error message
			return fmt.Errorf(i18n.G("cannot create %q: %v"), d, err)
		}
	}

	if err := createOrUpdateUserDataSymlink(info, usr); err != nil {
		return err
	}

	return maybeRestoreSecurityContext(usr)
}

Fixed code:
file_path: cmd/snap/cmd_run.go
snippet: func createUserDataDirs(info *snap.Info) error {
	// Adjust umask so that the created directories have the permissions we
	// expect and are unaffected by the initial umask. While go runtime creates
	// threads at will behind the scenes, the setting of umask applies to the
	// entire process so it doesn't need any special handling to lock the
	// executing goroutine to a single thread.
	oldUmask := syscall.Umask(0)
	defer syscall.Umask(oldUmask)

	usr, err := userCurrent()
	if err != nil {
		return fmt.Errorf(i18n.G("cannot get the current user: %v"), err)
	}

	snapDir := filepath.Join(usr.HomeDir, dirs.UserHomeSnapDir)
	if err := os.MkdirAll(snapDir, 0700); err != nil {
		return fmt.Errorf(i18n.G("cannot create snap home dir: %w"), err)
	}
	// see snapenv.User
	instanceUserData := info.UserDataDir(usr.HomeDir)
	instanceCommonUserData := info.UserCommonDataDir(usr.HomeDir)
	createDirs := []string{instanceUserData, instanceCommonUserData}
	if info.InstanceKey != "" {
		// parallel instance snaps get additional mapping in their mount
		// namespace, namely /home/joe/snap/foo_bar ->
		// /home/joe/snap/foo, make sure that the mount point exists and
		// is owned by the user
		snapUserDir := snap.UserSnapDir(usr.HomeDir, info.SnapName())
		createDirs = append(createDirs, snapUserDir)
	}
	for _, d := range createDirs {
		if err := os.MkdirAll(d, 0755); err != nil {
			// TRANSLATORS: %q is the directory whose creation failed, %v the error message
			return fmt.Errorf(i18n.G("cannot create %q: %v"), d, err)
		}
	}

	if err := createOrUpdateUserDataSymlink(info, usr); err != nil {
		return err
	}

	return maybeRestoreSecurityContext(usr)
}