Browse code

[stable-2.10] Revert "Change default file permissions so they are not world readable (#70221) (#70824)" (#71236)

* [stable-2.10] Revert "Fix warning for new default permissions when mode is not specified (#70976) (#70985)"

This reverts commit 5cb96087e6e8270229d6a801287a01398a9d1bf0.

* [stable-2.10] Revert "Change default file permissions so they are not world readable (#70221) (#70824)"

This reverts commit 7e4cffc5d2b6d4b3d412c2361e4a69d9f229dcbd.

Sam Doran authored on 2020/08/13 04:30:47
Showing 11 changed files
1 1
deleted file mode 100644
... ...
@@ -1,4 +0,0 @@
1
-bugfixes:
2
-  - >
3
-    **security issue** atomic_move - change default permissions when creating
4
-    temporary files so they are not world readable (https://github.com/ansible/ansible/issues/67794) (CVE-2020-1736)
5 1
deleted file mode 100644
... ...
@@ -1,4 +0,0 @@
1
-bugfixes:
2
-  - >
3
-    Fix warning for default permission change when no mode is specified. Follow up
4
-    to https://github.com/ansible/ansible/issues/67794. (CVE-2020-1736)
... ...
@@ -27,7 +27,6 @@ The :ref:`porting_2.10_guide_base` is included in this porting guide. The comple
27 27
   :local:
28 28
   :depth: 2
29 29
 
30
-
31 30
 Playbook
32 31
 ========
33 32
 
... ...
@@ -53,79 +52,51 @@ Deprecated
53 53
 Modules
54 54
 =======
55 55
 
56
-Change to Default File Permissions
56
+.. warning::
57 57
 
58
-To address CVE-2020-1736, the default permissions for certain files created by Ansible using ``atomic_move()`` were changed from ``0o666`` to ``0o600``. The default permissions value was only used for the temporary file before it was moved into its place or newly created files. If the file existed when the new temporary file was moved into place, Ansible would use the permissions of the existing file. If there was no existing file, Ansible would retain the default file permissions, combined with the system ``umask``, of the temporary file.
58
+	Links on this page may not point to the most recent versions of modules. We will update them when we can.
59 59
 
60
-Most modules that call ``atomic_move()`` also call ``set_fs_attributes_if_different()`` or ``set_mode_if_different()``, which will set the permissions of the file to what is specified in the task.
60
+Deprecation notices
61
+-------------------
61 62
 
62
-A new warning will be displayed when all of the following conditions are true:
63
+The following modules will be removed in Ansible 2.14. Please update your playbooks accordingly.
63 64
 
64
-    - The file at the final destination, not the temporary file, does not exist
65
-    - A module supports setting ``mode`` but it was not specified for the task
66
-    - The module calls ``atomic_move()`` but does not later call ``set_fs_attributes_if_different()`` or ``set_mode_if_different()`` with a ``mode`` specified
65
+* ldap_attr use ldap_attrs instead.
66
+* vyos_static_route use vyos_static_routes instead.
67 67
 
68
-The following modules call ``atomic_move()`` but do not call ``set_fs_attributes_if_different()``  or ``set_mode_if_different()`` and do not support setting ``mode``. This means for files they create, the default permissions have changed and there is no indication:
68
+The following functionality will be removed in Ansible 2.14. Please update update your playbooks accordingly.
69 69
 
70
-    - M(known_hosts)
71
-    - M(service)
70
+* :ref:`iam_managed_policy <iam_managed_policy_module>`: the ``fail_on_delete`` option will be removed.  It has always been ignored by the module.
71
+* :ref:`s3_lifecycle <s3_lifecycle_module>`: the ``requester_pays`` option will be removed. It has always been ignored by the module.
72
+* :ref:`s3_sync <s3_sync_module>`: the ``retries`` option will be removed. It has always been ignored by the module.
73
+* :ref:`cloudformation <cloudformation_module>`: the ``template_format`` option will be removed. It has been ignored by the module since Ansible 2.3.
74
+* :ref:`data_pipeline <data_pipeline_module>`: the ``version`` option will be removed. It has always been ignored by the module.
75
+* :ref:`ec2_eip <ec2_eip_module>`: the ``wait_timeout`` option will be removed. It has had no effect since Ansible 2.3.
76
+* :ref:`ec2_key <ec2_key_module>`: the ``wait`` option will be removed. It has had no effect since Ansible 2.5.
77
+* :ref:`ec2_key <ec2_key_module>`: the ``wait_timeout`` option will be removed. It has had no effect since Ansible 2.5.
78
+* :ref:`ec2_lc <ec2_lc_module>`: the ``associate_public_ip_address`` option will be removed. It has always been ignored by the module.
79
+* :ref:`ec2_tag <ec2_tag_module>`: Support for ``list`` as a state has been deprecated.  The ``ec2_tag_info`` can be used to fetch the tags on an EC2 resource.
80
+* :ref:`iam_policy <iam_policy_module>`: the ``policy_document`` option will be removed. To maintain the existing behavior use the ``policy_json`` option and read the file with the ``lookup`` plugin.
81
+* :ref:`win_domain_controller <win_domain_controller_module>`: the ``log_path`` option will be removed. This was undocumented and only related to debugging information for module development.
82
+* :ref:`win_package <win_package_module>`: the ``username`` and ``password`` options will be removed. The same functionality can be done by using ``become: yes`` and ``become_flags: logon_type=new_credentials logon_flags=netcredentials_only`` on the task.
83
+* :ref:`win_package <win_package_module>`: the ``ensure`` alias for the ``state`` option will be removed. Please use ``state`` instead of ``ensure``.
84
+* :ref:`win_package <win_package_module>`: the ``productid`` alias for the ``product_id`` option will be removed. Please use ``product_id`` instead of ``productid``.
72 85
 
73 86
 
74 87
 Code Audit
75 88
 ~~~~~~~~~~
76 89
 
77
-The code was audited for modules that use ``atomic_move()`` but **do not** later call ``set_fs_attributes_if_different()`` or ``set_mode_if_different()``. Modules that provide no means for specifying the ``mode`` will not display a warning message since there is no way for the playbook author to remove the warning. The behavior of each module with regards to the default permissions of temporary files and the permissions of newly created files is explained below.
78
-
79
-known_hosts
80
-^^^^^^^^^^^
81
-
82
-The M(known_hosts) module uses ``atomic_move()`` to operate on the ``known_hosts`` file specified by the ``path`` parameter in the module. It creates a temporary file using ``tempfile.NamedTemporaryFile()`` which creates a temporary file that is readable and writable only by the creating user ID.
83
-
84
-service
85
-^^^^^^^
86
-
87
-The M(service) module uses ``atomic_move()`` to operate on the default rc file, which is the first found of ``/etc/rc.conf``,  ``/etc/rc.conf.local``, and ``/usr/local/etc/rc.conf``. Since these files almost always exist on the target system, they will not be created and the existing permissions of the file will be used.
88
-
89
-**The following modules were included in Ansible <= 2.9. They have moved to collections but are documented here for completeness.**
90
-
91
-authorized_key
92
-^^^^^^^^^^^^^^
93
-
94
-The M(authorized_key) module uses ``atomic_move()`` to operate on the the ``authorized_key`` file. A temporary file is created with ``tempfile.mkstemp()`` before being moved into place. The temporary file is readable and writable only by the creating user ID. The M(authorized_key) module manages the permissions of the the ``.ssh`` direcotry and ``authorized_keys`` files if ``managed_dirs`` is set to ``True``, which is the default. The module sets the ``ssh`` directory owner and group to the ``uid`` and ``gid`` of the user specified in the ``user`` parameter and directory permissions to ``700``. The module sets the ``authorized_key`` file owner and group to the ``uid`` and ``gid`` of the user specified in the ``user`` parameter and file permissions to ``600``. These values cannot be controlled by module parameters.
95
-
96
-interfaces_file
97
-^^^^^^^^^^^^^^^
98
-The M(interfaces_file) module uses ``atomic_move()`` to operate on ``/etc/network/serivces`` or the ``dest`` specified by the module. A temporary file is created with ``tempfile.mkstemp()`` before being moved into place. The temporary file is readable and writable only by the creating user ID. If the file specified by ``path`` does not exist it will retain the permissions of the temporary file once moved into place.
90
+The following functionality will change in Ansible 2.14. Please update update your playbooks accordingly.
99 91
 
100
-pam_limits
101
-^^^^^^^^^^
92
+* :ref:`ec2 <ec2_module>`: the ``group`` and ``group_id`` options will become mutually exclusive.  Currently ``group_id`` is ignored if you pass both.
93
+* :ref:`iam_policy <iam_policy_module>`: the default value for the ``skip_duplicates`` option will change from ``true`` to ``false``.  To maintain the existing behavior explicitly set it to ``true``.
94
+* :ref:`iam_role <iam_role_module>`: the ``purge_policies`` option (also know as ``purge_policy``) default value will change from ``true`` to ``false``
95
+* :ref:`elb_network_lb <elb_network_lb_module>`: the default behaviour for the ``state`` option will change from ``absent`` to ``present``.  To maintain the existing behavior explicitly set state to ``absent``.
96
+* :ref:`vmware_tag_info <vmware_tag_info_module>`: the module will not return ``tag_facts`` since it does not return multiple tags with the same name and different category id. To maintain the existing behavior use ``tag_info`` which is a list of tag metadata.
102 97
 
103
-The M(pam_limits) module uses ``atomic_move()`` to operate on ``/etc/security/limits.conf`` or the value of ``dest``. A temporary file is created using ``tempfile.NamedTemporaryFile()``, which is only readable and writable by the creating user ID. The temporary file will inherit the permissions of the file specified by ``dest``, or it will retain the permissions that only allow the creating user ID to read and write the file.
98
+The following modules will be removed in Ansible 2.14. Please update your playbooks accordingly.
104 99
 
105
-pamd
106
-^^^^
107
-
108
-The M(pamd) module uses ``atomic_move()`` to operate on a file in ``/etc/pam.d``. The path and the file can be specified by setting the ``path`` and ``name`` parameters. A temporary file is created using ``tempfile.NamedTemporaryFile()``, which is only readable and writable by the creating user ID. The temporary file will inherit the permissions of the file located at ``[dest]/[name]``, or it will retain the permissions of the temporary file that only allow the creating user ID to read and write the file.
109
-
110
-redhat_subscription
111
-^^^^^^^^^^^^^^^^^^^
112
-
113
-The M(redhat_subscription) module uses ``atomic_move()`` to operate on ``/etc/yum/pluginconf.d/rhnplugin.conf`` and ``/etc/yum/pluginconf.d/subscription-manager.conf``. A temporary file is created with ``tempfile.mkstemp()`` before being moved into place. The temporary file is readable and writable only by the creating user ID and the temporary file will inherit the permissions of the existing file once it is moved in to place.
114
-
115
-selinux
116
-^^^^^^^
117
-
118
-The M(selinux) module uses ``atomic_move()`` to operate on ``/etc/selinux/config`` on the value specified by ``configfile``. The module will fail if ``configfile`` does not exist before any temporary data is written to disk. A temporary file is created with ``tempfile.mkstemp()`` before being moved into place. The temporary file is readable and writable only by the creating user ID. Since the file specified by ``configfile`` must exist, the temporary file will inherit the permissions of that file once it is moved in to place.
119
-
120
-sysctl
121
-^^^^^^
122
-
123
-The M(sysctl) module uses ``atomic_move()`` to operate on ``/etc/sysctl.conf`` or the value specified by ``sysctl_file``. The module will fail if ``sysctl_file`` does not exist before any temporary data is written to disk. A temporary file is created with ``tempfile.mkstemp()`` before being moved into place. The temporary file is readable and writable only by the creating user ID. Since the file specified by ``sysctl_file`` must exist, the temporary file will inherit the permissions of that file once it is moved in to place.
124
-
125
-.. warning::
126
-
127
-	Links on this page may not point to the most recent versions of modules. We will update them when we can.
100
+* ``vmware_dns_config`` use vmware_host_dns instead.
128 101
 
129 102
 
130 103
 Noteworthy module changes
... ...
@@ -704,10 +704,7 @@ class AnsibleModule(object):
704 704
         self._options_context = list()
705 705
         self._tmpdir = None
706 706
 
707
-        self._created_files = set()
708
-
709 707
         if add_file_common_args:
710
-            self._uses_common_file_args = True
711 708
             for k, v in FILE_COMMON_ARGUMENTS.items():
712 709
                 if k not in self.argument_spec:
713 710
                     self.argument_spec[k] = v
... ...
@@ -1128,13 +1125,6 @@ class AnsibleModule(object):
1128 1128
         if mode is None:
1129 1129
             return changed
1130 1130
 
1131
-        # Remove paths so we do not warn about creating with default permissions
1132
-        # since we are calling this method on the path and setting the specified mode.
1133
-        try:
1134
-            self._created_files.remove(path)
1135
-        except KeyError:
1136
-            pass
1137
-
1138 1131
         b_path = to_bytes(path, errors='surrogate_or_strict')
1139 1132
         if expand:
1140 1133
             b_path = os.path.expanduser(os.path.expandvars(b_path))
... ...
@@ -1430,11 +1420,6 @@ class AnsibleModule(object):
1430 1430
     def set_file_attributes_if_different(self, file_args, changed, diff=None, expand=True):
1431 1431
         return self.set_fs_attributes_if_different(file_args, changed, diff, expand)
1432 1432
 
1433
-    def add_atomic_move_warnings(self):
1434
-        for path in sorted(self._created_files):
1435
-            self.warn("File '{0}' created with default permissions '{1:o}'. The previous default was '666'. "
1436
-                      "Specify 'mode' to avoid this warning.".format(to_native(path), DEFAULT_PERM))
1437
-
1438 1433
     def add_path_info(self, kwargs):
1439 1434
         '''
1440 1435
         for results that are files, supplement the info about the file
... ...
@@ -2145,7 +2130,6 @@ class AnsibleModule(object):
2145 2145
 
2146 2146
     def _return_formatted(self, kwargs):
2147 2147
 
2148
-        self.add_atomic_move_warnings()
2149 2148
         self.add_path_info(kwargs)
2150 2149
 
2151 2150
         if 'invocation' not in kwargs:
... ...
@@ -2441,16 +2425,6 @@ class AnsibleModule(object):
2441 2441
                         self.cleanup(b_tmp_dest_name)
2442 2442
 
2443 2443
         if creating:
2444
-            # Keep track of what files we create here with default permissions so later we can see if the permissions
2445
-            # are explicitly set with a follow up call to set_mode_if_different().
2446
-            #
2447
-            # Only warn if the module accepts 'mode' parameter so the user can take action.
2448
-            # If the module does not allow the user to set 'mode', then the warning is useless to the
2449
-            # user since it provides no actionable information.
2450
-            #
2451
-            if self.argument_spec.get('mode') and self.params.get('mode') is None:
2452
-                self._created_files.add(dest)
2453
-
2454 2444
             # make sure the file has the correct permissions
2455 2445
             # based on the current value of umask
2456 2446
             umask = os.umask(0)
... ...
@@ -59,7 +59,7 @@ PERMS_RE = re.compile(r'[^rwxXstugo]')
59 59
 
60 60
 _PERM_BITS = 0o7777          # file mode permission bits
61 61
 _EXEC_PERM_BITS = 0o0111     # execute permission bits
62
-_DEFAULT_PERM = 0o0600       # default file permission bits
62
+_DEFAULT_PERM = 0o0666       # default file permission bits
63 63
 
64 64
 
65 65
 def is_executable(path):
... ...
@@ -8,7 +8,7 @@
8 8
     test_repo_spec: "deb http://apt.postgresql.org/pub/repos/apt/ {{ ansible_distribution_release }}-pgdg main"
9 9
     test_repo_path: /etc/apt/sources.list.d/apt_postgresql_org_pub_repos_apt.list
10 10
 
11
-- import_tasks: mode_cleanup.yaml
11
+- include: mode_cleanup.yaml
12 12
 
13 13
 - name: Add GPG key to verify signatures
14 14
   apt_key:
... ...
@@ -31,7 +31,7 @@
31 31
   debug:
32 32
     var: mode_given_yaml_literal_0600
33 33
 
34
-- import_tasks: mode_cleanup.yaml
34
+- include: mode_cleanup.yaml
35 35
 
36 36
 - name: Assert mode_given_yaml_literal_0600 is correct
37 37
   assert:
... ...
@@ -52,11 +52,11 @@
52 52
   debug:
53 53
     var: no_mode_stat
54 54
 
55
-- import_tasks: mode_cleanup.yaml
55
+- include: mode_cleanup.yaml
56 56
 
57 57
 - name: Assert no_mode_stat is correct
58 58
   assert:
59
-    that: "no_mode_stat.stat.mode == '0600'"
59
+    that: "no_mode_stat.stat.mode == '0644'"
60 60
 
61 61
 - name: Mode specified as string 0600
62 62
   apt_repository:
... ...
@@ -74,7 +74,7 @@
74 74
   debug:
75 75
     var: mode_given_string_stat
76 76
 
77
-- import_tasks: mode_cleanup.yaml
77
+- include: mode_cleanup.yaml
78 78
 
79 79
 - name: Mode specified as string 600
80 80
   apt_repository:
... ...
@@ -92,7 +92,7 @@
92 92
   debug:
93 93
     var: mode_given_string_600_stat
94 94
 
95
-- import_tasks: mode_cleanup.yaml
95
+- include: mode_cleanup.yaml
96 96
 
97 97
 - name: Assert mode is correct
98 98
   assert:
... ...
@@ -114,7 +114,7 @@
114 114
   debug:
115 115
     var: mode_given_yaml_literal_600
116 116
 
117
-- import_tasks: mode_cleanup.yaml
117
+- include: mode_cleanup.yaml
118 118
 
119 119
 # a literal 600 as the mode will fail currently, in the sense that it
120 120
 # doesn't guess and consider 600 and 0600 to be the same, and will instead
... ...
@@ -127,4 +127,4 @@
127 127
 # See https://github.com/ansible/ansible/issues/16370
128 128
 - name: Assert mode_given_yaml_literal_600 is correct
129 129
   assert:
130
-    that: "mode_given_yaml_literal_600.stat.mode == '1130'"
130
+    that: "mode_given_yaml_literal_600.stat.mode == '1130'"
131 131
\ No newline at end of file
132 132
deleted file mode 100644
... ...
@@ -1 +0,0 @@
1
-shippable/posix/group5
2 1
deleted file mode 100644
... ...
@@ -1,36 +0,0 @@
1
-#!/usr/bin/python
2
-# -*- coding: utf-8 -*-
3
-# Copyright (c) 2020 Ansible Project
4
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
5
-
6
-from __future__ import absolute_import, division, print_function
7
-__metaclass__ = type
8
-
9
-import tempfile
10
-
11
-from ansible.module_utils.basic import AnsibleModule
12
-
13
-
14
-def main():
15
-    module = AnsibleModule(
16
-        argument_spec={
17
-            'dest': {'type': 'path'},
18
-            'call_fs_attributes': {'type': 'bool', 'default': True},
19
-        },
20
-        add_file_common_args=True,
21
-    )
22
-
23
-    results = {}
24
-
25
-    with tempfile.NamedTemporaryFile(delete=False) as tf:
26
-        file_args = module.load_file_common_arguments(module.params)
27
-        module.atomic_move(tf.name, module.params['dest'])
28
-
29
-        if module.params['call_fs_attributes']:
30
-            results['changed'] = module.set_fs_attributes_if_different(file_args, True)
31
-
32
-    module.exit_json(**results)
33
-
34
-
35
-if __name__ == '__main__':
36
-    main()
37 1
deleted file mode 100644
... ...
@@ -1,2 +0,0 @@
1
-dependencies:
2
-  - setup_remote_tmp_dir
3 1
deleted file mode 100644
... ...
@@ -1,33 +0,0 @@
1
-- name: Run task with no mode
2
-  test_perm_warning:
3
-    dest: "{{ remote_tmp_dir }}/endangerdisown"
4
-  register: no_mode_results
5
-
6
-- name: Run task with mode
7
-  test_perm_warning:
8
-    mode: '0644'
9
-    dest: "{{ remote_tmp_dir }}/groveestablish"
10
-  register: with_mode_results
11
-
12
-- name: Run task without calling set_fs_attributes_if_different()
13
-  test_perm_warning:
14
-    call_fs_attributes: no
15
-    dest: "{{ remote_tmp_dir }}/referabletank"
16
-  register: skip_fs_attributes
17
-
18
-- stat:
19
-    path: "{{ remote_tmp_dir }}/{{ item }}"
20
-  loop:
21
-    - endangerdisown
22
-    - groveestablish
23
-  register: files
24
-
25
-- name: Ensure we get a warning when appropriate
26
-  assert:
27
-    that:
28
-      - no_mode_results.warnings | default([], True) | length == 1
29
-      - "'created with default permissions' in no_mode_results.warnings[0]"
30
-      - files.results[0]['stat']['mode'] == '0600'
31
-      - files.results[1]['stat']['mode'] == '0644'
32
-      - with_mode_results.warnings is not defined  # The Jinja version on CentOS 6 does not support default([], True)
33
-      - skip_fs_attributes.warnings | default([], True) | length == 1
... ...
@@ -63,7 +63,7 @@ def atomic_mocks(mocker, monkeypatch):
63 63
 @pytest.fixture
64 64
 def fake_stat(mocker):
65 65
     stat1 = mocker.MagicMock()
66
-    stat1.st_mode = 0o0600
66
+    stat1.st_mode = 0o0644
67 67
     stat1.st_uid = 0
68 68
     stat1.st_gid = 0
69 69
     stat1.st_flags = 0
... ...
@@ -80,7 +80,7 @@ def test_new_file(atomic_am, atomic_mocks, mocker, selinux):
80 80
     atomic_am.atomic_move('/path/to/src', '/path/to/dest')
81 81
 
82 82
     atomic_mocks['rename'].assert_called_with(b'/path/to/src', b'/path/to/dest')
83
-    assert atomic_mocks['chmod'].call_args_list == [mocker.call(b'/path/to/dest', basic.DEFAULT_PERM & ~0o022)]
83
+    assert atomic_mocks['chmod'].call_args_list == [mocker.call(b'/path/to/dest', basic.DEFAULT_PERM & ~18)]
84 84
 
85 85
     if selinux:
86 86
         assert atomic_am.selinux_default_context.call_args_list == [mocker.call('/path/to/dest')]
... ...
@@ -101,7 +101,7 @@ def test_existing_file(atomic_am, atomic_mocks, fake_stat, mocker, selinux):
101 101
     atomic_am.atomic_move('/path/to/src', '/path/to/dest')
102 102
 
103 103
     atomic_mocks['rename'].assert_called_with(b'/path/to/src', b'/path/to/dest')
104
-    assert atomic_mocks['chmod'].call_args_list == [mocker.call(b'/path/to/src', basic.DEFAULT_PERM & ~0o022)]
104
+    assert atomic_mocks['chmod'].call_args_list == [mocker.call(b'/path/to/src', basic.DEFAULT_PERM & ~18)]
105 105
 
106 106
     if selinux:
107 107
         assert atomic_am.set_context_if_different.call_args_list == [mocker.call('/path/to/dest', mock_context, False)]
... ...
@@ -124,7 +124,7 @@ def test_no_tty_fallback(atomic_am, atomic_mocks, fake_stat, mocker):
124 124
     atomic_am.atomic_move('/path/to/src', '/path/to/dest')
125 125
 
126 126
     atomic_mocks['rename'].assert_called_with(b'/path/to/src', b'/path/to/dest')
127
-    assert atomic_mocks['chmod'].call_args_list == [mocker.call(b'/path/to/src', basic.DEFAULT_PERM & ~0o022)]
127
+    assert atomic_mocks['chmod'].call_args_list == [mocker.call(b'/path/to/src', basic.DEFAULT_PERM & ~18)]
128 128
 
129 129
     assert atomic_am.set_context_if_different.call_args_list == [mocker.call('/path/to/dest', mock_context, False)]
130 130
     assert atomic_am.selinux_context.call_args_list == [mocker.call('/path/to/dest')]
... ...
@@ -154,7 +154,7 @@ def test_existing_file_stat_perms_failure(atomic_am, atomic_mocks, mocker):
154 154
     atomic_mocks['rename'].assert_called_with(b'/path/to/src', b'/path/to/dest')
155 155
     # FIXME: Should atomic_move() set a default permission value when it cannot retrieve the
156 156
     # existing file's permissions?  (Right now it's up to the calling code.
157
-    # assert atomic_mocks['chmod'].call_args_list == [mocker.call(b'/path/to/src', basic.DEFAULT_PERM & ~0o022)]
157
+    # assert atomic_mocks['chmod'].call_args_list == [mocker.call(b'/path/to/src', basic.DEFAULT_PERM & ~18)]
158 158
     assert atomic_am.set_context_if_different.call_args_list == [mocker.call('/path/to/dest', mock_context, False)]
159 159
     assert atomic_am.selinux_context.call_args_list == [mocker.call('/path/to/dest')]
160 160
 
... ...
@@ -211,7 +211,7 @@ def test_rename_perms_fail_temp_succeeds(atomic_am, atomic_mocks, fake_stat, moc
211 211
     atomic_am.atomic_move('/path/to/src', '/path/to/dest')
212 212
     assert atomic_mocks['rename'].call_args_list == [mocker.call(b'/path/to/src', b'/path/to/dest'),
213 213
                                                      mocker.call(b'/path/to/tempfile', b'/path/to/dest')]
214
-    assert atomic_mocks['chmod'].call_args_list == [mocker.call(b'/path/to/dest', basic.DEFAULT_PERM & ~0o022)]
214
+    assert atomic_mocks['chmod'].call_args_list == [mocker.call(b'/path/to/dest', basic.DEFAULT_PERM & ~18)]
215 215
 
216 216
     if selinux:
217 217
         assert atomic_am.selinux_default_context.call_args_list == [mocker.call('/path/to/dest')]