Merge branch 'v3-2-test' of ssh://git.samba.org/data/git/samba into dmapi-integration
[samba.git] / source3 / torture / test_ntlm_auth.py
1 #!/usr/bin/env python
2
3 # Unix SMB/CIFS implementation.
4 # A test for the ntlm_auth tool
5 # Copyright (C) Kai Blin <kai@samba.org> 2008
6 #
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.
11 #
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.
16 #
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/>.
19 #
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 """
24
25 import os
26 import sys
27 from optparse import OptionParser
28
29 class ReadChildError(Exception):
30         pass
31
32 class WriteChildError(Exception):
33         pass
34
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]
45
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")
55
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)
63
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")
68
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")
74
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]")
83
84         parser.add_option("--server-username", dest="server_username",\
85                                 help="User name server uses for local auth. [default: foo]")
86         parser.add_option("--server-password", dest="server_password",\
87                                 help="Password server uses for local auth. [default: secret]")
88         parser.add_option("--server-domain", dest="server_domain",\
89                                 help="Domain server uses for local auth. [default: FOO]")
90         parser.add_option("--server-helper", dest="server_helper",\
91                                 help="Helper mode for the ntlm_auth server. [default: squid-2.5-server]")
92
93         parser.add_option("-s", "--configfile", dest="config_file",\
94                                 help="Path to smb.conf file. [default:/etc/samba/smb.conf")
95
96         (opts, args) = parser.parse_args()
97         if len(args) != 1:
98                 parser.error("Invalid number of arguments.")
99
100         if not os.access(args[0], os.X_OK):
101                 parser.error("%s is not executable." % args[0])
102
103         return (opts, args[0])
104
105
106 def main():
107         """main() -> int
108         Run the test.
109         Returns 0 if test succeeded, <>0 otherwise.
110         """
111         (opts, ntlm_auth_path) = parseCommandLine()
112
113         (client_in_r,  client_in_w)  = os.pipe()
114         (client_out_r, client_out_w) = os.pipe()
115
116         client_pid = os.fork()
117
118         if not client_pid:
119                 # We're in the client child
120                 os.close(0)
121                 os.close(1)
122
123                 os.dup2(client_out_r, 0)
124                 os.close(client_out_r)
125                 os.close(client_out_w)
126
127                 os.dup2(client_in_w, 1)
128                 os.close(client_in_r)
129                 os.close(client_in_w)
130
131                 client_args = []
132                 client_args.append("--helper-protocol=%s" % opts.client_helper)
133                 client_args.append("--username=%s" % opts.client_username)
134                 client_args.append("--password=%s" % opts.client_password)
135                 client_args.append("--domain=%s" % opts.client_domain)
136                 client_args.append("--configfile=%s" % opts.config_file)
137
138                 os.execv(ntlm_auth_path, client_args)
139
140         client_in = client_in_r
141         os.close(client_in_w)
142
143         client_out = client_out_w
144         os.close(client_out_r)
145
146         (server_in_r,  server_in_w)  = os.pipe()
147         (server_out_r, server_out_w) = os.pipe()
148
149         server_pid = os.fork()
150
151         if not server_pid:
152                 # We're in the server child
153                 os.close(0)
154                 os.close(1)
155
156                 os.dup2(server_out_r, 0)
157                 os.close(server_out_r)
158                 os.close(server_out_w)
159
160                 os.dup2(server_in_w, 1)
161                 os.close(server_in_r)
162                 os.close(server_in_w)
163
164                 server_args = []
165                 server_args.append("--helper-protocol=%s" % opts.server_helper)
166                 server_args.append("--username=%s" % opts.server_username)
167                 server_args.append("--password=%s" % opts.server_password)
168                 server_args.append("--domain=%s" % opts.server_domain)
169                 server_args.append("--configfile=%s" % opts.config_file)
170
171                 os.execv(ntlm_auth_path, server_args)
172
173         server_in = server_in_r
174         os.close(server_in_w)
175
176         server_out = server_out_w
177         os.close(server_out_r)
178
179         # We're in the parent
180         writeLine(client_out, "YR")
181         buf = readLine(client_in)
182
183         if buf.count("YR ", 0, 3) != 1:
184                 sys.exit(1)
185
186         writeLine(server_out, buf)
187         buf = readLine(server_in)
188
189         if buf.count("TT ", 0, 3) != 1:
190                 sys.exit(2)
191
192         writeLine(client_out, buf)
193         buf = readLine(client_in)
194
195         if buf.count("AF ", 0, 3) != 1:
196                 sys.exit(3)
197
198         # Client sends 'AF <base64 blob>' but server expects 'KK <abse64 blob>'
199         buf = buf.replace("AF", "KK", 1)
200
201         writeLine(server_out, buf)
202         buf = readLine(server_in)
203
204         if buf.count("AF ", 0, 3) != 1:
205                 sys.exit(4)
206
207         os.close(server_in)
208         os.close(server_out)
209         os.close(client_in)
210         os.close(client_out)
211         os.waitpid(server_pid, 0)
212         os.waitpid(client_pid, 0)
213         sys.exit(0)
214
215 if __name__ == "__main__":
216         main()
217
218