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 21This test program will start ntlm_auth with the given command line switches and 22see if it will get the expected results. 23""" 24 25import os 26import sys 27from optparse import OptionParser 28 29class ReadChildError(Exception): 30 pass 31 32class WriteChildError(Exception): 33 pass 34 35def 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 46def 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 56def 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 106def 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 215if __name__ == "__main__": 216 main() 217 218 219