s4-auth: Make sure error_string is correctly initialized
[Samba.git] / source3 / torture / test_ntlm_auth.py
blobd17af9b0bf90b823637becfc141c8ef2cd1f93c3
1 #!/usr/bin/env python
3 # Unix SMB/CIFS implementation.
4 # A test for the ntlm_auth tool
5 # Copyright (C) Kai Blin <kai@samba.org> 2008
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 """Test ntlm_auth
21 This test program will start ntlm_auth with the given command line switches and
22 see if it will get the expected results.
23 """
25 import os
26 import sys
27 from optparse import OptionParser
29 class ReadChildError(Exception):
30 pass
32 class WriteChildError(Exception):
33 pass
35 def readLine(pipe):
36 """readLine(pipe) -> str
37 Read a line from the child's pipe, returns the string read.
38 Throws ReadChildError if the read fails.
39 """
40 buf = os.read(pipe, 2047)
41 newline = buf.find('\n')
42 if newline == -1:
43 raise ReadChildError()
44 return buf[:newline]
46 def writeLine(pipe, buf):
47 """writeLine(pipe, buf) -> nul
48 Write a line to the child's pipe.
49 Raises WriteChildError if the write fails.
50 """
51 written = os.write(pipe, buf)
52 if written != len(buf):
53 raise WriteChildError()
54 os.write(pipe, "\n")
56 def parseCommandLine():
57 """parseCommandLine() -> (opts, ntlm_auth_path)
58 Parse the command line.
59 Return a tuple consisting of the options and the path to ntlm_auth.
60 """
61 usage = "usage: %prog [options] path/to/ntlm_auth"
62 parser = OptionParser(usage)
64 parser.set_defaults(client_username="foo")
65 parser.set_defaults(client_password="secret")
66 parser.set_defaults(client_domain="FOO")
67 parser.set_defaults(client_helper="ntlmssp-client-1")
69 parser.set_defaults(server_username="foo")
70 parser.set_defaults(server_password="secret")
71 parser.set_defaults(server_domain="FOO")
72 parser.set_defaults(server_helper="squid-2.5-ntlmssp")
73 parser.set_defaults(config_file="/etc/samba/smb.conf")
75 parser.add_option("--client-username", dest="client_username",\
76 help="User name for the client. [default: foo]")
77 parser.add_option("--client-password", dest="client_password",\
78 help="Password the client will send. [default: secret]")
79 parser.add_option("--client-domain", dest="client_domain",\
80 help="Domain the client authenticates for. [default: FOO]")
81 parser.add_option("--client-helper", dest="client_helper",\
82 help="Helper mode for the ntlm_auth client. [default: ntlmssp-client-1]")
84 parser.add_option("--target-hostname", dest="target_hostname",\
85 help="Target hostname for kerberos")
86 parser.add_option("--target-service", dest="target_service",\
87 help="Target service for kerberos")
90 parser.add_option("--server-username", dest="server_username",\
91 help="User name server uses for local auth. [default: foo]")
92 parser.add_option("--server-password", dest="server_password",\
93 help="Password server uses for local auth. [default: secret]")
94 parser.add_option("--server-domain", dest="server_domain",\
95 help="Domain server uses for local auth. [default: FOO]")
96 parser.add_option("--server-helper", dest="server_helper",\
97 help="Helper mode for the ntlm_auth server. [default: squid-2.5-server]")
98 parser.add_option("--server-use-winbindd", dest="server_use_winbindd",\
99 help="Use winbindd to check the password (rather than default username/pw)", action="store_true")
100 parser.add_option("--require-membership-of", dest="sid",\
101 help="Require that the user is a member of this group to authenticate.")
104 parser.add_option("-s", "--configfile", dest="config_file",\
105 help="Path to smb.conf file. [default:/etc/samba/smb.conf")
107 (opts, args) = parser.parse_args()
108 if len(args) != 1:
109 parser.error("Invalid number of arguments.")
111 if not os.access(args[0], os.X_OK):
112 parser.error("%s is not executable." % args[0])
114 return (opts, args[0])
117 def main():
118 """main() -> int
119 Run the test.
120 Returns 0 if test succeeded, <>0 otherwise.
122 (opts, ntlm_auth_path) = parseCommandLine()
124 (client_in_r, client_in_w) = os.pipe()
125 (client_out_r, client_out_w) = os.pipe()
127 client_pid = os.fork()
129 if not client_pid:
130 # We're in the client child
131 os.close(0)
132 os.close(1)
134 os.dup2(client_out_r, 0)
135 os.close(client_out_r)
136 os.close(client_out_w)
138 os.dup2(client_in_w, 1)
139 os.close(client_in_r)
140 os.close(client_in_w)
142 client_args = []
143 client_args.append("--helper-protocol=%s" % opts.client_helper)
144 client_args.append("--username=%s" % opts.client_username)
145 client_args.append("--password=%s" % opts.client_password)
146 client_args.append("--domain=%s" % opts.client_domain)
147 client_args.append("--configfile=%s" % opts.config_file)
148 if opts.target_service:
149 client_args.append("--target-service=%s" % opts.target_service)
150 if opts.target_hostname:
151 client_args.append("--target-hostname=%s" % opts.target_hostname)
153 os.execv(ntlm_auth_path, client_args)
155 client_in = client_in_r
156 os.close(client_in_w)
158 client_out = client_out_w
159 os.close(client_out_r)
161 (server_in_r, server_in_w) = os.pipe()
162 (server_out_r, server_out_w) = os.pipe()
164 server_pid = os.fork()
166 if not server_pid:
167 # We're in the server child
168 os.close(0)
169 os.close(1)
171 os.dup2(server_out_r, 0)
172 os.close(server_out_r)
173 os.close(server_out_w)
175 os.dup2(server_in_w, 1)
176 os.close(server_in_r)
177 os.close(server_in_w)
179 server_args = []
180 server_args.append("--helper-protocol=%s" % opts.server_helper)
181 if not opts.server_use_winbindd:
182 server_args.append("--username=%s" % opts.server_username)
183 server_args.append("--password=%s" % opts.server_password)
184 server_args.append("--domain=%s" % opts.server_domain)
185 if opts.sid:
186 raise Exception("Server must be using winbindd for require-membership-of.")
187 else:
188 if opts.sid:
189 server_args.append("--require-membership-of=%s" % opts.sid)
191 server_args.append("--configfile=%s" % opts.config_file)
193 os.execv(ntlm_auth_path, server_args)
195 server_in = server_in_r
196 os.close(server_in_w)
198 server_out = server_out_w
199 os.close(server_out_r)
201 if opts.client_helper == "ntlmssp-client-1" and opts.server_helper == "squid-2.5-ntlmssp":
203 # We're in the parent
204 writeLine(client_out, "YR")
205 buf = readLine(client_in)
207 if buf.count("YR ", 0, 3) != 1:
208 sys.exit(1)
210 writeLine(server_out, buf)
211 buf = readLine(server_in)
213 if buf.count("TT ", 0, 3) != 1:
214 sys.exit(2)
216 writeLine(client_out, buf)
217 buf = readLine(client_in)
219 if buf.count("AF ", 0, 3) != 1:
220 sys.exit(3)
222 # Client sends 'AF <base64 blob>' but server expects 'KK <abse64 blob>'
223 buf = buf.replace("AF", "KK", 1)
225 writeLine(server_out, buf)
226 buf = readLine(server_in)
228 if buf.count("AF ", 0, 3) != 1:
229 sys.exit(4)
232 elif opts.client_helper == "ntlmssp-client-1" and opts.server_helper == "gss-spnego":
233 # We're in the parent
234 writeLine(client_out, "YR")
235 buf = readLine(client_in)
237 if buf.count("YR ", 0, 3) != 1:
238 sys.exit(1)
240 writeLine(server_out, buf)
241 buf = readLine(server_in)
243 if buf.count("TT ", 0, 3) != 1:
244 sys.exit(2)
246 writeLine(client_out, buf)
247 buf = readLine(client_in)
249 if buf.count("AF ", 0, 3) != 1:
250 sys.exit(3)
252 # Client sends 'AF <base64 blob>' but server expects 'KK <abse64 blob>'
253 buf = buf.replace("AF", "KK", 1)
255 writeLine(server_out, buf)
256 buf = readLine(server_in)
258 if buf.count("AF * ", 0, 5) != 1:
259 sys.exit(4)
262 elif opts.client_helper == "gss-spnego-client" and opts.server_helper == "gss-spnego":
263 # We're in the parent
264 writeLine(server_out, "YR")
265 buf = readLine(server_in)
267 while True:
268 if buf.count("AF ", 0, 3) != 1 and buf.count("TT ", 0, 3) != 1:
269 sys.exit(1)
271 writeLine(client_out, buf)
272 buf = readLine(client_in)
274 if buf.count("AF", 0, 2) == 1:
275 break
277 if buf.count("AF ", 0, 5) != 1 and buf.count("KK ", 0, 3) != 1 and buf.count("TT ", 0, 3) != 1:
278 sys.exit(2)
280 writeLine(server_out, buf)
281 buf = readLine(server_in)
283 if buf.count("AF * ", 0, 5) == 1:
284 break
286 else:
287 sys.exit(5)
289 if opts.client_helper == "ntlmssp-client-1":
290 writeLine(client_out, "GK")
291 buf = readLine(client_in)
293 if buf.count("GK ", 0, 3) != 1:
294 sys.exit(4)
296 writeLine(client_out, "GF")
297 buf = readLine(client_in)
299 if buf.count("GF ", 0, 3) != 1:
300 sys.exit(4)
302 if opts.server_helper == "squid-2.5-ntlmssp":
303 writeLine(server_out, "GK")
304 buf = readLine(server_in)
306 if buf.count("GK ", 0, 3) != 1:
307 sys.exit(4)
309 writeLine(server_out, "GF")
310 buf = readLine(server_in)
312 if buf.count("GF ", 0, 3) != 1:
313 sys.exit(4)
315 os.close(server_in)
316 os.close(server_out)
317 os.close(client_in)
318 os.close(client_out)
319 os.waitpid(server_pid, 0)
320 os.waitpid(client_pid, 0)
321 sys.exit(0)
323 if __name__ == "__main__":
324 main()