File Coverage

blib/lib/Authen/NTLM/HTTP.pm
Criterion Covered Total %
statement 168 181 92.8
branch 20 40 50.0
condition 4 12 33.3
subroutine 45 46 97.8
pod 0 9 0.0
total 237 288 82.2


line stmt bran cond sub pod time code
1             # -*- perl -*-
2             # NTLM.pm - An implementation of NTLM. In this version, I only
3             # implemented the client side functions that calculates the NTLM response.
4             # I will add the corresponding server side functions in the next version.
5             #
6            
7             package Authen::NTLM::HTTP;
8            
9 1     1   1047 use strict;
  1         989  
  1         358  
10 1     1   8 use POSIX;
  1         39  
  1         13  
11 1     1   26839 use Carp;
  1         4  
  1         120  
12 1     1   3527 use MIME::Base64;
  1         1004  
  1         89  
13 1     1   7 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  1         4  
  1         100  
14            
15             require Exporter;
16            
17             *import = \&Exporter::import;
18            
19 1     1   6 use base qw/Authen::NTLM::HTTP::Base/;
  1         2  
  1         273  
20             @EXPORT = qw ();
21             @EXPORT_OK = qw ();
22             $VERSION = '0.33';
23            
24             # Stolen from Crypt::DES.
25             sub usage {
26 0     0 0 0 my ($package, $filename, $line, $subr) = caller (1);
27 0         0 $Carp::CarpLevel = 2;
28 0         0 croak "Usage: $subr (@_)";
29             }
30            
31             # Flags to indicate whether we are talking to web server or proxy
32 1     1   6 use constant NTLMSSP_HTTP_WWW => "WWW";
  1         3  
  1         71  
33 1     1   6 use constant NTLMSSP_HTTP_PROXY => "Proxy";
  1         2  
  1         54  
34            
35             # These constants are stolen from samba-2.2.4 and other sources
36 1     1   6 use constant NTLMSSP_SIGNATURE => 'NTLMSSP';
  1         2  
  1         47  
37            
38             # NTLMSSP Message Types
39 1     1   5 use constant NTLMSSP_NEGOTIATE => 1;
  1         2  
  1         39  
40 1     1   5 use constant NTLMSSP_CHALLENGE => 2;
  1         3  
  1         67  
41 1     1   6 use constant NTLMSSP_AUTH => 3;
  1         2  
  1         43  
42 1     1   5 use constant NTLMSSP_UNKNOWN => 4;
  1         2  
  1         54  
43            
44             # NTLMSSP Flags
45            
46             # Text strings are in unicode
47 1     1   6 use constant NTLMSSP_NEGOTIATE_UNICODE => 0x00000001;
  1         2  
  1         51  
48             # Text strings are in OEM
49 1     1   7 use constant NTLMSSP_NEGOTIATE_OEM => 0x00000002;
  1         2  
  1         45  
50             # Server should return its authentication realm
51 1     1   7 use constant NTLMSSP_REQUEST_TARGET => 0x00000004;
  1         1  
  1         49  
52             # Request signature capability
53 1     1   6 use constant NTLMSSP_NEGOTIATE_SIGN => 0x00000010;
  1         2  
  1         47  
54             # Request confidentiality
55 1     1   6 use constant NTLMSSP_NEGOTIATE_SEAL => 0x00000020;
  1         2  
  1         56  
56             # Use datagram style authentication
57 1     1   5 use constant NTLMSSP_NEGOTIATE_DATAGRAM => 0x00000040;
  1         2  
  1         41  
58             # Use LM session key for sign/seal
59 1     1   6 use constant NTLMSSP_NEGOTIATE_LM_KEY => 0x00000080;
  1         8  
  1         43  
60             # NetWare authentication
61 1     1   6 use constant NTLMSSP_NEGOTIATE_NETWARE => 0x00000100;
  1         2  
  1         51  
62             # NTLM authentication
63 1     1   5 use constant NTLMSSP_NEGOTIATE_NTLM => 0x00000200;
  1         3  
  1         42  
64             # Domain Name supplied on negotiate
65 1     1   6 use constant NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED => 0x00001000;
  1         2  
  1         60  
66             # Workstation Name supplied on negotiate
67 1     1   13 use constant NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED => 0x00002000;
  1         3  
  1         48  
68             # Indicates client/server are same machine
69 1     1   5 use constant NTLMSSP_NEGOTIATE_LOCAL_CALL => 0x00004000;
  1         3  
  1         40  
70             # Sign for all security levels
71 1     1   4 use constant NTLMSSP_NEGOTIATE_ALWAYS_SIGN => 0x00008000;
  1         11  
  1         41  
72             # TargetName is a domain name
73 1     1   4 use constant NTLMSSP_TARGET_TYPE_DOMAIN => 0x00010000;
  1         1  
  1         36  
74             # TargetName is a server name
75 1     1   5 use constant NTLMSSP_TARGET_TYPE_SERVER => 0x00020000;
  1         2  
  1         40  
76             # TargetName is a share name
77 1     1   5 use constant NTLMSSP_TARGET_TYPE_SHARE => 0x00040000;
  1         2  
  1         42  
78             # TargetName is a share name
79 1     1   5 use constant NTLMSSP_NEGOTIATE_NTLM2 => 0x00080000;
  1         1  
  1         38  
80             # get back session keys
81 1     1   6 use constant NTLMSSP_REQUEST_INIT_RESPONSE => 0x00100000;
  1         2  
  1         51  
82             # get back session key, LUID
83 1     1   4 use constant NTLMSSP_REQUEST_ACCEPT_RESPONSE => 0x00200000;
  1         2  
  1         1587  
84             # request non-ntsession key
85 1     1   6 use constant NTLMSSP_REQUEST_NON_NT_SESSION_KEY => 0x00400000;
  1         2  
  1         47  
86 1     1   6 use constant NTLMSSP_NEGOTIATE_TARGET_INFO => 0x00800000;
  1         1  
  1         505  
87 1     1   6 use constant NTLMSSP_NEGOTIATE_128 => 0x20000000;
  1         2  
  1         44  
88 1     1   5 use constant NTLMSSP_NEGOTIATE_KEY_EXCH => 0x40000000;
  1         2  
  1         38  
89 1     1   5 use constant NTLMSSP_NEGOTIATE_80000000 => 0x80000000;
  1         1  
  1         1044  
90            
91             #########################################################################
92             # Constructor to initialize authentication related information. In this #
93             # version, we assume NTLM as the authentication scheme of choice. #
94             # The constructor takes the class name, LM hash of the client password #
95             # and the LM hash of the client password as arguments. #
96             #########################################################################
97             sub new_client {
98 1 50 33 1 0 14 usage("new_client Authen::NTLM::HTTP(\$lm_hpw, \$nt_hpw\) or\nnew_client Authen::NTLM::HTTP\(\$lm_hpw, \$nt_hpw, \$type, \$user, \$user_domain, \$domain, \$machine\)") unless @_ == 3 or @_ == 4 or @_ == 8;
      33        
99 1         4 my ($package, $lm_hpw, $nt_hpw, $type, $user, $user_domain, $domain, $machine) = @_;
100 1         9 srand time;
101 1 50       4 if (not defined($type)) {$type = NTLMSSP_HTTP_WWW;}
  0         0  
102 1 50       4 if (not defined($user)) {$user = $ENV{'USERNAME'};}
  0         0  
103 1 50       3 if (not defined($user_domain)) {$user_domain = $ENV{'USERDOMAIN'};}
  0         0  
104 1 50       3 if (not defined($domain)) {$domain = Win32::DomainName();}
  0         0  
105 1 50       4 if (not defined($machine)) {$machine = $ENV{'COMPUTERNAME'};}
  0         0  
106 1 50       12 usage("LM hash must be 21-bytes long") unless length($lm_hpw) == 21;
107 1 50       5 usage("NT hash must be 21-bytes long") unless length($nt_hpw) == 21;
108 1 50       12 defined($user) or usage "Undefined User Name!\n";
109 1 50       3 defined($user_domain) or usage "Undefined User Domain!\n";
110 1 50       4 defined($domain) or usage "Undefined Network Domain!\n";
111 1 50       3 defined($machine) or usage "Undefined Computer Name!\n";
112 1         8 my $ctx_id = pack("V", rand 2**32);
113 1         12 bless {
114             'type' => $type,
115             'user' => $user,
116             'user_domain' => $user_domain,
117             'domain' => $domain,
118             'machine' => $machine,
119             'lm_hpw' => $lm_hpw,
120             'nt_hpw' => $nt_hpw
121             }, $package;
122             }
123            
124             ###########################################################################
125             # new_server instantiate a NTLM server that composes an NTLM challenge #
126             # It can take one argument for the server network domain. If the argument #
127             # is not supplied, it will call Win32::DomainName to obtain it. #
128             ###########################################################################
129             sub new_server {
130 1 50 33 1 0 77 usage("new_server Authen::NTLM::HTTP or\nnew_server Authen::NTLM::HTTP(\$type, \$domain\)") unless @_ == 1 or @_ == 2 or @_ == 3;
      33        
131 1         2 my ($package, $type, $domain) = @_;
132 1 50       3 if (not defined($type)) {$type = NTLMSSP_HTTP_WWW;}
  0         0  
133 1 50       2 if (not defined($domain)) {$domain = Win32::DomainName();}
  0         0  
134 1 50       2 defined($domain) or usage "Undefined Network Domain!\n";
135 1         6 bless {
136             'type' => $type,
137             'domain' => $domain,
138             'cChallenge' => 0 # a counter to stir the seed to generate random
139             }, $package; # number for the nonce
140             }
141            
142             ####################################################################
143             # http_negotiate creates a NTLM-over-HTTP tag line for NTLM #
144             # negotiate packet given the domain (from Win32::DomainName()) and #
145             # the workstation name (from $ENV{'COMPUTERNAME'} or #
146             # Win32::NodeName()) and the negotiation flags. #
147             ####################################################################
148             sub http_negotiate($$)
149             {
150 1     1 0 25 my $self = shift;
151 1         2 my $flags = shift;
152 1         11 my $str = encode_base64($self->SUPER::negotiate_msg($flags));
153 1         7 $str =~ s/\s//g;
154 1         3 return "Authorization: NTLM " . $str;
155             }
156            
157             ###########################################################################
158             # http_parse_negotiate parses the NTLM-over-HTTP negotiate tag line and #
159             # return a list of NTLM Negotiation Flags, Server Network Domain and #
160             # Machine name of the client. #
161             ###########################################################################
162             sub http_parse_negotiate($$)
163             {
164 1     1 0 4 my ($self, $pkt) = @_;
165 1         4 $pkt =~ s/Authorization: NTLM //;
166 1         5 my $str = decode_base64($pkt);
167 1         7 return $self->SUPER::parse_negotiate($str);
168             }
169            
170             ####################################################################
171             # http_challenge composes the NTLM-over-HTTP challenge tag line. It#
172             # takes NTLM Negotiation Flags as an argument. #
173             ####################################################################
174             sub http_challenge($$)
175             {
176 1     1 0 164 my $self = $_[0];
177 1         2 my $flags = $_[1];
178 1         2 my $nonce = undef;
179 1         1 my $str;
180 1 50       4 $nonce = $_[2] if @_ == 3;
181 1 50       3 if (defined $nonce) {
182 1         8 $str = encode_base64($self->SUPER::challenge_msg($flags, $nonce));
183             }
184             else {
185 0         0 $str = encode_base64($self->SUPER::challenge_msg($flags));
186             }
187 1         6 $str =~ s/\s//g;
188 1         6 return $self->{'type'} . "-Authenticate: NTLM " . $str;
189             }
190            
191             ###########################################################################
192             # http_parse_challenge parses the NTLM-over-HTTP challenge tag line and #
193             # return a list of server network domain, NTLM Negotiation Flags, Nonce, #
194             # ServerContextHandleUpper and ServerContextHandleLower. #
195             ###########################################################################
196             sub http_parse_challenge
197             {
198 1     1 0 76 my ($self, $pkt) = @_;
199 1         3 my $str = $self->{'type'} . "-Authenticate: NTLM ";
200 1         18 $pkt =~ s/$str//;
201 1         5 $str = decode_base64($pkt);
202 1         9 return $self->SUPER::parse_challenge($str);
203             }
204            
205             ###########################################################################
206             # http_auth creates the NTLM-over-HTTP response to an NTLM challenge from #
207             # the server. It takes 2 arguments: $nonce obtained from parse_challenge #
208             # and NTLM Negotiation Flags. This function ASSUMEs the input of user #
209             # domain, user name and workstation name are in ASCII format and not in #
210             # UNICODE format. #
211             ###########################################################################
212             sub http_auth($$$)
213             {
214 1     1 0 75 my $self = shift;
215 1         2 my $nonce = shift;
216 1         2 my $flags = shift;
217 1         7 my $str = encode_base64($self->SUPER::auth_msg($nonce, $flags));
218 1         10 $str =~ s/\s//g;;
219 1 50       5 if ($self->{'type'} eq NTLMSSP_HTTP_PROXY) {
220 0         0 return "Proxy-Authorization: NTLM " . $str;
221             }
222             else {
223 1         6 return "Authorization: NTLM " . $str;
224             }
225             }
226            
227             ###########################################################################
228             # http_parse_auth parses the NTLM-over-HTTP authentication tag line and #
229             # return a list of NTLM Negotiation Flags, LM response, NT response, User #
230             # Domain, User Name, User Machine Name and Session Key. #
231             ###########################################################################
232             sub http_parse_auth($$)
233             {
234 1     1 0 71 my ($self, $pkt) = @_;
235 1 50       14 if ($self->{'type'} eq NTLMSSP_HTTP_PROXY) {
236 0         0 $pkt =~ s/Proxy-Authorization: NTLM //;
237             }
238             else {
239 1         6 $pkt =~ s/Authorization: NTLM //;
240             }
241 1         5 my $str = decode_base64($pkt);
242 1         9 return $self->SUPER::parse_auth($str);
243             }
244            
245             1;
246            
247             __END__