a6b3868fa045a31b578b344ace15daab58f1341c
[vlendec/samba-autobuild/.git] / python / samba / tests / auth_log_pass_change.py
1 # Unix SMB/CIFS implementation.
2 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2017
3 #
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 #
17
18 from __future__ import print_function
19 """Tests for the Auth and AuthZ logging of password changes.
20 """
21
22 import samba.tests
23 from samba.samdb import SamDB
24 from samba.auth import system_session
25 import os
26 import samba.tests.auth_log_base
27 from samba.tests import delete_force
28 from samba.net import Net
29 import samba
30 from subprocess import call
31 from ldb import LdbError
32 from samba.tests.password_test import PasswordCommon
33
34 USER_NAME = "authlogtestuser"
35 USER_PASS = samba.generate_random_password(32, 32)
36
37
38 class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):
39
40     def setUp(self):
41         super(AuthLogPassChangeTests, self).setUp()
42
43         self.remoteAddress = os.environ["CLIENT_IP"]
44         self.server_ip = os.environ["SERVER_IP"]
45
46         host = "ldap://%s" % os.environ["SERVER"]
47         self.ldb = SamDB(url=host,
48                          session_info=system_session(),
49                          credentials=self.get_credentials(),
50                          lp=self.get_loadparm())
51
52         print("ldb %s" % type(self.ldb))
53         # Gets back the basedn
54         base_dn = self.ldb.domain_dn()
55         print("base_dn %s" % base_dn)
56
57         # permit password changes during this test
58         PasswordCommon.allow_password_changes(self, self.ldb)
59
60         self.base_dn = self.ldb.domain_dn()
61
62         # (Re)adds the test user USER_NAME with password USER_PASS
63         delete_force(self.ldb, "cn=" + USER_NAME + ",cn=users," + self.base_dn)
64         self.ldb.add({
65             "dn": "cn=" + USER_NAME + ",cn=users," + self.base_dn,
66             "objectclass": "user",
67             "sAMAccountName": USER_NAME,
68             "userPassword": USER_PASS
69         })
70
71         # discard any auth log messages for the password setup
72         self.discardMessages()
73
74     def tearDown(self):
75         super(AuthLogPassChangeTests, self).tearDown()
76
77     def test_admin_change_password(self):
78         def isLastExpectedMessage(msg):
79             return ((msg["type"] == "Authentication") and
80                     (msg["Authentication"]["status"] == "NT_STATUS_OK") and
81                     (msg["Authentication"]["serviceDescription"] ==
82                         "SAMR Password Change") and
83                     (msg["Authentication"]["authDescription"] ==
84                         "samr_ChangePasswordUser3"))
85
86         creds = self.insta_creds(template=self.get_credentials())
87
88         lp = self.get_loadparm()
89         net = Net(creds, lp, server=self.server_ip)
90         password = "newPassword!!42"
91
92         net.change_password(newpassword=password,
93                             username=USER_NAME,
94                             oldpassword=USER_PASS)
95
96         messages = self.waitForMessages(isLastExpectedMessage)
97         print("Received %d messages" % len(messages))
98         self.assertEquals(8,
99                           len(messages),
100                           "Did not receive the expected number of messages")
101
102     def test_admin_change_password_new_password_fails_restriction(self):
103         def isLastExpectedMessage(msg):
104             return ((msg["type"] == "Authentication") and
105                     (msg["Authentication"]["status"] ==
106                         "NT_STATUS_PASSWORD_RESTRICTION") and
107                     (msg["Authentication"]["serviceDescription"] ==
108                         "SAMR Password Change") and
109                     (msg["Authentication"]["authDescription"] ==
110                         "samr_ChangePasswordUser3"))
111
112         creds = self.insta_creds(template=self.get_credentials())
113
114         lp = self.get_loadparm()
115         net = Net(creds, lp, server=self.server_ip)
116         password = "newPassword"
117
118         exception_thrown = False
119         try:
120             net.change_password(newpassword=password,
121                                 oldpassword=USER_PASS,
122                                 username=USER_NAME)
123         except Exception:
124             exception_thrown = True
125         self.assertEquals(True, exception_thrown,
126                           "Expected exception not thrown")
127
128         messages = self.waitForMessages(isLastExpectedMessage)
129         self.assertEquals(8,
130                           len(messages),
131                           "Did not receive the expected number of messages")
132
133     def test_admin_change_password_unknown_user(self):
134         def isLastExpectedMessage(msg):
135             return ((msg["type"] == "Authentication") and
136                     (msg["Authentication"]["status"] ==
137                         "NT_STATUS_NO_SUCH_USER") and
138                     (msg["Authentication"]["serviceDescription"] ==
139                         "SAMR Password Change") and
140                     (msg["Authentication"]["authDescription"] ==
141                         "samr_ChangePasswordUser3"))
142
143         creds = self.insta_creds(template=self.get_credentials())
144
145         lp = self.get_loadparm()
146         net = Net(creds, lp, server=self.server_ip)
147         password = "newPassword!!42"
148
149         exception_thrown = False
150         try:
151             net.change_password(newpassword=password,
152                                 oldpassword=USER_PASS,
153                                 username="badUser")
154         except Exception:
155             exception_thrown = True
156         self.assertEquals(True, exception_thrown,
157                           "Expected exception not thrown")
158
159         messages = self.waitForMessages(isLastExpectedMessage)
160         self.assertEquals(8,
161                           len(messages),
162                           "Did not receive the expected number of messages")
163
164     def test_admin_change_password_bad_original_password(self):
165         def isLastExpectedMessage(msg):
166             return ((msg["type"] == "Authentication") and
167                     (msg["Authentication"]["status"] ==
168                         "NT_STATUS_WRONG_PASSWORD") and
169                     (msg["Authentication"]["serviceDescription"] ==
170                         "SAMR Password Change") and
171                     (msg["Authentication"]["authDescription"] ==
172                         "samr_ChangePasswordUser3"))
173
174         creds = self.insta_creds(template=self.get_credentials())
175
176         lp = self.get_loadparm()
177         net = Net(creds, lp, server=self.server_ip)
178         password = "newPassword!!42"
179
180         exception_thrown = False
181         try:
182             net.change_password(newpassword=password,
183                                 oldpassword="badPassword",
184                                 username=USER_NAME)
185         except Exception:
186             exception_thrown = True
187         self.assertEquals(True, exception_thrown,
188                           "Expected exception not thrown")
189
190         messages = self.waitForMessages(isLastExpectedMessage)
191         self.assertEquals(8,
192                           len(messages),
193                           "Did not receive the expected number of messages")
194
195     # net rap password changes are broken, but they trigger enough of the
196     # server side behaviour to exercise the code paths of interest.
197     # if we used the real password it would be too long and does not hash
198     # correctly, so we just check it triggers the wrong password path.
199     def test_rap_change_password(self):
200         def isLastExpectedMessage(msg):
201             return ((msg["type"] == "Authentication") and
202                     (msg["Authentication"]["serviceDescription"] ==
203                         "SAMR Password Change") and
204                     (msg["Authentication"]["status"] ==
205                         "NT_STATUS_WRONG_PASSWORD") and
206                     (msg["Authentication"]["authDescription"] ==
207                         "OemChangePasswordUser2"))
208
209         username = os.environ["USERNAME"]
210         server = os.environ["SERVER"]
211         password = os.environ["PASSWORD"]
212         server_param = "--server=%s" % server
213         creds = "-U%s%%%s" % (username, password)
214         call(["bin/net", "rap", server_param,
215               "password", USER_NAME, "notMyPassword", "notGoingToBeMyPassword",
216               server, creds, "--option=client ipc max protocol=nt1"])
217
218         messages = self.waitForMessages(isLastExpectedMessage)
219         self.assertEquals(7,
220                           len(messages),
221                           "Did not receive the expected number of messages")
222
223     def test_ldap_change_password(self):
224         def isLastExpectedMessage(msg):
225             return ((msg["type"] == "Authentication") and
226                     (msg["Authentication"]["status"] == "NT_STATUS_OK") and
227                     (msg["Authentication"]["serviceDescription"] ==
228                         "LDAP Password Change") and
229                     (msg["Authentication"]["authDescription"] ==
230                         "LDAP Modify"))
231
232         new_password = samba.generate_random_password(32, 32)
233         self.ldb.modify_ldif(
234             "dn: cn=" + USER_NAME + ",cn=users," + self.base_dn + "\n" +
235             "changetype: modify\n" +
236             "delete: userPassword\n" +
237             "userPassword: " + USER_PASS + "\n" +
238             "add: userPassword\n" +
239             "userPassword: " + new_password + "\n")
240
241         messages = self.waitForMessages(isLastExpectedMessage)
242         print("Received %d messages" % len(messages))
243         self.assertEquals(4,
244                           len(messages),
245                           "Did not receive the expected number of messages")
246
247     #
248     # Currently this does not get logged, so we expect to only see the log
249     # entries for the underlying ldap bind.
250     #
251     def test_ldap_change_password_bad_user(self):
252         def isLastExpectedMessage(msg):
253             return (msg["type"] == "Authorization" and
254                     msg["Authorization"]["serviceDescription"] == "LDAP" and
255                     msg["Authorization"]["authType"] == "krb5")
256
257         new_password = samba.generate_random_password(32, 32)
258         try:
259             self.ldb.modify_ldif(
260                 "dn: cn=" + "badUser" + ",cn=users," + self.base_dn + "\n" +
261                 "changetype: modify\n" +
262                 "delete: userPassword\n" +
263                 "userPassword: " + USER_PASS + "\n" +
264                 "add: userPassword\n" +
265                 "userPassword: " + new_password + "\n")
266             self.fail()
267         except LdbError as e:
268             (num, msg) = e.args
269             pass
270
271         messages = self.waitForMessages(isLastExpectedMessage)
272         print("Received %d messages" % len(messages))
273         self.assertEquals(3,
274                           len(messages),
275                           "Did not receive the expected number of messages")
276
277     def test_ldap_change_password_bad_original_password(self):
278         def isLastExpectedMessage(msg):
279             return ((msg["type"] == "Authentication") and
280                     (msg["Authentication"]["status"] ==
281                         "NT_STATUS_WRONG_PASSWORD") and
282                     (msg["Authentication"]["serviceDescription"] ==
283                         "LDAP Password Change") and
284                     (msg["Authentication"]["authDescription"] ==
285                         "LDAP Modify"))
286
287         new_password = samba.generate_random_password(32, 32)
288         try:
289             self.ldb.modify_ldif(
290                 "dn: cn=" + USER_NAME + ",cn=users," + self.base_dn + "\n" +
291                 "changetype: modify\n" +
292                 "delete: userPassword\n" +
293                 "userPassword: " + "badPassword" + "\n" +
294                 "add: userPassword\n" +
295                 "userPassword: " + new_password + "\n")
296             self.fail()
297         except LdbError as e1:
298             (num, msg) = e1.args
299             pass
300
301         messages = self.waitForMessages(isLastExpectedMessage)
302         print("Received %d messages" % len(messages))
303         self.assertEquals(4,
304                           len(messages),
305                           "Did not receive the expected number of messages")