| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # SMB-Perl library, Copyright (C) 2014-2018 Mikhael Goikhman, migo@cpan.org | 
| 2 |  |  |  |  |  |  | # | 
| 3 |  |  |  |  |  |  | # This program is free software: you can redistribute it and/or modify | 
| 4 |  |  |  |  |  |  | # it under the terms of the GNU General Public License as published by | 
| 5 |  |  |  |  |  |  | # the Free Software Foundation, either version 3 of the License, or | 
| 6 |  |  |  |  |  |  | # (at your option) any later version. | 
| 7 |  |  |  |  |  |  | # | 
| 8 |  |  |  |  |  |  | # This program is distributed in the hope that it will be useful, | 
| 9 |  |  |  |  |  |  | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| 10 |  |  |  |  |  |  | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
| 11 |  |  |  |  |  |  | # GNU General Public License for more details. | 
| 12 |  |  |  |  |  |  | # | 
| 13 |  |  |  |  |  |  | # You should have received a copy of the GNU General Public License | 
| 14 |  |  |  |  |  |  | # along with this program.  If not, see . | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | package SMB::Auth; | 
| 17 |  |  |  |  |  |  |  | 
| 18 | 2 |  |  | 2 |  | 620 | use strict; | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 43 |  | 
| 19 | 2 |  |  | 2 |  | 20 | use warnings; | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 76 |  | 
| 20 |  |  |  |  |  |  |  | 
| 21 | 2 |  |  | 2 |  | 390 | use parent 'SMB'; | 
|  | 2 |  |  |  |  | 229 |  | 
|  | 2 |  |  |  |  | 23 |  | 
| 22 |  |  |  |  |  |  |  | 
| 23 | 2 |  |  | 2 |  | 549 | use bytes; | 
|  | 2 |  |  |  |  | 13 |  | 
|  | 2 |  |  |  |  | 24 |  | 
| 24 | 2 |  |  | 2 |  | 787 | use Sys::Hostname qw(hostname); | 
|  | 2 |  |  |  |  | 1516 |  | 
|  | 2 |  |  |  |  | 90 |  | 
| 25 | 2 |  |  | 2 |  | 445 | use Encode qw(encode); | 
|  | 2 |  |  |  |  | 7573 |  | 
|  | 2 |  |  |  |  | 85 |  | 
| 26 |  |  |  |  |  |  |  | 
| 27 | 2 |  |  | 2 |  | 705 | use SMB::Crypt qw(des_crypt56 md4 hmac_md5); | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 98 |  | 
| 28 | 2 |  |  | 2 |  | 335 | use SMB::Parser; | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 39 |  | 
| 29 | 2 |  |  | 2 |  | 326 | use SMB::Packer; | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 40 |  | 
| 30 | 2 |  |  | 2 |  | 611 | use SMB::Time qw(to_nttime); | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 95 |  | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | # Abstract Syntax Notation One (small subset) | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | use constant { | 
| 35 | 2 |  |  |  |  | 158 | ASN1_BINARY      => 0x04, | 
| 36 |  |  |  |  |  |  | ASN1_OID         => 0x06, | 
| 37 |  |  |  |  |  |  | ASN1_ENUMERATED  => 0x0a, | 
| 38 |  |  |  |  |  |  | ASN1_SEQUENCE    => 0x30, | 
| 39 |  |  |  |  |  |  | ASN1_SET         => 0x31, | 
| 40 |  |  |  |  |  |  | ASN1_APPLICATION => 0x60, | 
| 41 |  |  |  |  |  |  | ASN1_CONTEXT     => 0xa0, | 
| 42 | 2 |  |  | 2 |  | 10 | }; | 
|  | 2 |  |  |  |  | 2 |  | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | # Generic Security Service API / Simple Protected Negotiation | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | use constant { | 
| 47 | 2 |  |  |  |  | 156 | OID_SPNEGO       => '1.3.6.1.5.5.2', | 
| 48 |  |  |  |  |  |  | OID_MECH_NTLMSSP => '1.3.6.1.4.1.311.2.2.10', | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | SPNEGO_ACCEPT_COMPLETED  => 0, | 
| 51 |  |  |  |  |  |  | SPNEGO_ACCEPT_INCOMPLETE => 1, | 
| 52 | 2 |  |  | 2 |  | 9 | }; | 
|  | 2 |  |  |  |  | 2 |  | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | # NTLMSSP mechanism | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | use constant { | 
| 57 | 2 |  |  |  |  | 6609 | NTLMSSP_ID_STR => "NTLMSSP\0", | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | NTLMSSP_NEGOTIATE => 1, | 
| 60 |  |  |  |  |  |  | NTLMSSP_CHALLENGE => 2, | 
| 61 |  |  |  |  |  |  | NTLMSSP_AUTH      => 3, | 
| 62 |  |  |  |  |  |  | NTLMSSP_SIGNATURE => 4, | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | NTLMSSP_ITEM_TERMINATOR    => 0, | 
| 65 |  |  |  |  |  |  | NTLMSSP_ITEM_NETBIOSHOST   => 1, | 
| 66 |  |  |  |  |  |  | NTLMSSP_ITEM_NETBIOSDOMAIN => 2, | 
| 67 |  |  |  |  |  |  | NTLMSSP_ITEM_DNSHOST       => 3, | 
| 68 |  |  |  |  |  |  | NTLMSSP_ITEM_DNSDOMAIN     => 4, | 
| 69 |  |  |  |  |  |  | NTLMSSP_ITEM_TIMESTAMP     => 7, | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | NTLMSSP_FLAGS_CLIENT => 0x60008215, | 
| 72 |  |  |  |  |  |  | NTLMSSP_FLAGS_SERVER => 0x628a8215, | 
| 73 | 2 |  |  | 2 |  | 10 | }; | 
|  | 2 |  |  |  |  | 2 |  | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | sub new ($) { | 
| 76 | 2 |  |  | 2 | 1 | 436 | my $class = shift; | 
| 77 |  |  |  |  |  |  |  | 
| 78 | 2 |  |  |  |  | 8 | return $class->SUPER::new( | 
| 79 |  |  |  |  |  |  | ntlmssp_supported     => undef, | 
| 80 |  |  |  |  |  |  | client_host           => undef, | 
| 81 |  |  |  |  |  |  | client_domain         => undef, | 
| 82 |  |  |  |  |  |  | server_challenge      => undef, | 
| 83 |  |  |  |  |  |  | server_host           => undef, | 
| 84 |  |  |  |  |  |  | server_netbios_host   => undef, | 
| 85 |  |  |  |  |  |  | server_netbios_domain => undef, | 
| 86 |  |  |  |  |  |  | server_dns_host       => undef, | 
| 87 |  |  |  |  |  |  | server_dns_domain     => undef, | 
| 88 |  |  |  |  |  |  | server_timestamp      => undef, | 
| 89 |  |  |  |  |  |  | client_challenge      => undef, | 
| 90 |  |  |  |  |  |  | lm_response           => undef, | 
| 91 |  |  |  |  |  |  | ntlm_response         => undef, | 
| 92 |  |  |  |  |  |  | domain                => undef, | 
| 93 |  |  |  |  |  |  | host                  => undef, | 
| 94 |  |  |  |  |  |  | username              => undef, | 
| 95 |  |  |  |  |  |  | session_key           => undef, | 
| 96 |  |  |  |  |  |  | auth_completed        => undef, | 
| 97 |  |  |  |  |  |  | user_passwords        => {}, | 
| 98 |  |  |  |  |  |  | parser => SMB::Parser->new, | 
| 99 |  |  |  |  |  |  | packer => SMB::Packer->new, | 
| 100 |  |  |  |  |  |  | ); | 
| 101 |  |  |  |  |  |  | } | 
| 102 |  |  |  |  |  |  |  | 
| 103 |  |  |  |  |  |  | sub set_user_passwords ($$) { | 
| 104 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 105 | 0 |  | 0 |  |  | 0 | my $user_passwords = shift || die "No user passwords to set"; | 
| 106 |  |  |  |  |  |  |  | 
| 107 | 0 | 0 |  |  |  | 0 | die "User passwords should be HASH" | 
| 108 |  |  |  |  |  |  | unless ref($user_passwords) eq 'HASH'; | 
| 109 |  |  |  |  |  |  |  | 
| 110 | 0 |  |  |  |  | 0 | $self->user_passwords($user_passwords); | 
| 111 |  |  |  |  |  |  | } | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | sub create_lm_hash ($) { | 
| 114 | 1 |  | 50 | 1 | 1 | 7 | my $password = substr(encode('ISO-8859-1', uc(shift // "")), 0, 14); | 
| 115 | 1 |  |  |  |  | 37 | $password .= "\0" x (14 - length($password)); | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | return join('', map { | 
| 118 | 1 |  |  |  |  | 6 | des_crypt56('KGS!@#$%', $_) | 
|  | 2 |  |  |  |  | 6 |  | 
| 119 |  |  |  |  |  |  | } $password =~ /^(.{7})(.{7})$/); | 
| 120 |  |  |  |  |  |  | } | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | sub create_ntlm_hash ($) { | 
| 123 | 3 |  | 50 | 3 | 1 | 13 | my $password = encode('UTF-16LE', shift // ""); | 
| 124 |  |  |  |  |  |  |  | 
| 125 | 3 |  |  |  |  | 151 | return md4($password); | 
| 126 |  |  |  |  |  |  | } | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | sub create_lm_response ($$) { | 
| 129 | 0 |  | 0 | 0 | 1 | 0 | my $lm_hash = shift || die; | 
| 130 | 0 |  |  |  |  | 0 | my $server_challenge = shift; | 
| 131 |  |  |  |  |  |  |  | 
| 132 | 0 |  |  |  |  | 0 | $lm_hash .= "\0" x (21 - length($lm_hash)); | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | return join('', map { | 
| 135 | 0 |  |  |  |  | 0 | des_crypt56([ map { ord($_) } split '', $server_challenge ], $_) | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 136 |  |  |  |  |  |  | } $lm_hash =~ /^(.{7})(.{7})(.{7})$/); | 
| 137 |  |  |  |  |  |  | } | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | sub create_ntlmv2_hash ($$$) { | 
| 140 | 3 |  | 50 | 3 | 1 | 6 | my $ntlm_hash = shift || die; | 
| 141 | 3 |  | 50 |  |  | 5 | my $username = shift // ''; | 
| 142 | 3 |  | 50 |  |  | 7 | my $domain = shift // ''; | 
| 143 |  |  |  |  |  |  |  | 
| 144 | 3 |  |  |  |  | 8 | return hmac_md5(encode('UTF-16LE', uc($username . $domain)), $ntlm_hash); | 
| 145 |  |  |  |  |  |  | } | 
| 146 |  |  |  |  |  |  |  | 
| 147 |  |  |  |  |  |  | sub create_lmv2_response ($$$$) { | 
| 148 | 1 |  |  | 1 | 1 | 3 | return create_ntlmv2_response($_[0], $_[1], $_[2], $_[3], 8); | 
| 149 |  |  |  |  |  |  | } | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  | sub create_ntlmv2_response ($$$$;$) { | 
| 152 | 1 |  |  | 1 | 1 | 1 | my $ntlm_hash = shift; | 
| 153 | 1 |  |  |  |  | 1 | my $username = shift; | 
| 154 | 1 |  |  |  |  | 2 | my $domain = shift; | 
| 155 | 1 |  |  |  |  | 1 | my $server_challenge = shift; | 
| 156 | 1 |  | 50 |  |  | 2 | my $client_challenge_len = shift || 24; | 
| 157 |  |  |  |  |  |  |  | 
| 158 | 1 |  |  |  |  | 3 | my $client_challenge = join('', map { chr(rand(0x100)) } 1 .. $client_challenge_len); | 
|  | 8 |  |  |  |  | 12 |  | 
| 159 | 1 |  |  |  |  | 2 | my $ntlmv2_hash = create_ntlmv2_hash($ntlm_hash, $username, $domain); | 
| 160 |  |  |  |  |  |  |  | 
| 161 | 1 |  |  |  |  | 3 | return hmac_md5($server_challenge . $client_challenge, $ntlmv2_hash) . $client_challenge; | 
| 162 |  |  |  |  |  |  | } | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | sub get_user_passwd_line ($$) { | 
| 165 | 0 |  |  | 0 | 1 | 0 | my $username = shift; | 
| 166 | 0 |  |  |  |  | 0 | my $password = shift; | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | return "$username:" . join('', | 
| 169 | 0 |  |  |  |  | 0 | map { map { sprintf "%02x", ord($_) } split '', $_ } | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 170 |  |  |  |  |  |  | create_lm_hash($password), create_ntlm_hash($password) | 
| 171 |  |  |  |  |  |  | ); | 
| 172 |  |  |  |  |  |  | } | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | sub load_user_passwords ($$) { | 
| 175 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 176 | 0 |  | 0 |  |  | 0 | my $filename = shift || return; | 
| 177 |  |  |  |  |  |  |  | 
| 178 | 0 | 0 |  |  |  | 0 | open PASSWD, "<$filename" or return; | 
| 179 | 0 |  |  |  |  | 0 | my @lines = ; | 
| 180 | 0 | 0 |  |  |  | 0 | close PASSWD or return; | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | my %user_passwords = map { | 
| 183 | 0 |  |  |  |  | 0 | s/^\s+//; | 
|  | 0 |  |  |  |  | 0 |  | 
| 184 | 0 |  |  |  |  | 0 | s/\s+$//; | 
| 185 | 0 |  |  |  |  | 0 | my ($username, $hash_str) = split ':', $_; | 
| 186 |  |  |  |  |  |  | my @hash_bytes = ($hash_str || '') =~ /^[0-9a-f]{64}$/ | 
| 187 | 0 | 0 | 0 |  |  | 0 | ? map { chr(hex(substr($hash_str, $_ * 2, 2))) } 0 .. 31 | 
|  | 0 |  |  |  |  | 0 |  | 
| 188 |  |  |  |  |  |  | : (); | 
| 189 | 0 | 0 | 0 |  |  | 0 | $username && $username =~ /^\w[\w.+-]*$/ && @hash_bytes | 
| 190 |  |  |  |  |  |  | ? ($username => [ join('', @hash_bytes[0 .. 15]), join('', @hash_bytes[16 .. 31]) ]) | 
| 191 |  |  |  |  |  |  | : (); | 
| 192 |  |  |  |  |  |  | } grep !/^\s*#/, @lines; | 
| 193 |  |  |  |  |  |  |  | 
| 194 | 0 |  |  |  |  | 0 | $self->user_passwords(\%user_passwords); | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | # in scalar context - number of users loaded | 
| 197 | 0 |  |  |  |  | 0 | return keys %user_passwords; | 
| 198 |  |  |  |  |  |  | } | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | sub is_user_authenticated ($) { | 
| 201 | 1 |  |  | 1 | 1 | 1 | my $self = shift; | 
| 202 |  |  |  |  |  |  |  | 
| 203 |  |  |  |  |  |  | #	my $lm_response   = $self->lm_response   || return $self->err("No lm_response from client"); | 
| 204 | 1 |  | 50 |  |  | 2 | my $ntlm_response = $self->ntlm_response || return $self->err("No ntlm_response from client"); | 
| 205 |  |  |  |  |  |  |  | 
| 206 | 1 |  |  |  |  | 5 | my ($hmac, $client_data) = $ntlm_response =~ /^(.{16})(.+)$/s; | 
| 207 | 1 | 50 |  |  |  | 4 | return $self->err("Invalid short ntlm_response from client") | 
| 208 |  |  |  |  |  |  | unless $hmac; | 
| 209 |  |  |  |  |  |  |  | 
| 210 | 1 |  | 50 |  |  | 2 | my $username = $self->username // return $self->err("No username from client"); | 
| 211 | 1 |  | 50 |  |  | 3 | my $password = $self->user_passwords->{$username} // return $self->err("No user '$username' on server"); | 
| 212 | 1 | 50 |  |  |  | 4 | my ($lm_hash, $ntlm_hash) = ref($password) eq 'ARRAY' ? @$password : (); | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | #	$lm_hash   ||= create_lm_hash($password); | 
| 215 | 1 |  | 33 |  |  | 6 | $ntlm_hash ||= create_ntlm_hash($password); | 
| 216 | 1 |  |  |  |  | 4 | my $ntlmv2_hash = create_ntlmv2_hash($ntlm_hash, $username, $self->client_domain); | 
| 217 |  |  |  |  |  |  |  | 
| 218 | 1 | 50 |  |  |  | 5 | return $self->err("Failed password check for user '$username', client not authenticated") | 
| 219 |  |  |  |  |  |  | unless $hmac eq hmac_md5($self->server_challenge . $client_data, $ntlmv2_hash); | 
| 220 |  |  |  |  |  |  |  | 
| 221 | 1 |  |  |  |  | 5 | return 1; | 
| 222 |  |  |  |  |  |  | } | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | my @parsed_context_values; | 
| 225 |  |  |  |  |  |  |  | 
| 226 |  |  |  |  |  |  | sub parse_asn1 { | 
| 227 | 41 |  |  | 41 | 1 | 38 | my $bytes = shift; | 
| 228 |  |  |  |  |  |  |  | 
| 229 | 41 |  |  |  |  | 38 | my $tag = ord(shift @$bytes); | 
| 230 | 41 |  |  |  |  | 41 | my $len = ord(shift @$bytes); | 
| 231 | 41 | 100 |  |  |  | 54 | if ($len >= 0x80) { | 
| 232 | 6 |  |  |  |  | 8 | my $llen = $len - 0x80; | 
| 233 | 6 |  |  |  |  | 4 | my $factor = 1; | 
| 234 | 6 |  |  |  |  | 7 | $len = 0; | 
| 235 | 6 |  |  |  |  | 8 | for (1 .. $llen) { | 
| 236 | 12 |  |  |  |  | 13 | $len = $len * $factor + ord(shift @$bytes); | 
| 237 | 12 |  |  |  |  | 11 | $factor *= 256; | 
| 238 |  |  |  |  |  |  | } | 
| 239 |  |  |  |  |  |  | } | 
| 240 |  |  |  |  |  |  |  | 
| 241 | 41 |  |  |  |  | 39 | my @contents; | 
| 242 | 41 |  |  |  |  | 98 | my @bytes = splice(@$bytes, 0, $len); | 
| 243 | 41 | 100 | 66 |  |  | 155 | if ($tag == ASN1_BINARY) { | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 | 33 |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 244 | 4 |  |  |  |  | 6 | @contents = (\@bytes); | 
| 245 |  |  |  |  |  |  | } elsif ($tag == ASN1_OID) { | 
| 246 | 7 |  |  |  |  | 6 | my $idx = 0; | 
| 247 | 7 |  |  |  |  | 6 | my $carry = 0; | 
| 248 |  |  |  |  |  |  | @contents = (join('.', map { | 
| 249 | 58 |  |  |  |  | 41 | my @i; | 
| 250 | 58 | 100 |  |  |  | 62 | if (0 == $idx++) { @i = (int($_ / 40), $_ % 40); } | 
|  | 7 | 100 |  |  |  | 18 |  | 
| 251 | 4 |  |  |  |  | 5 | elsif ($_ >= 0x80) { $carry = $carry * 0x80 + $_ - 0x80; } | 
| 252 | 47 |  |  |  |  | 44 | else { @i = ($carry * 0x80 + $_); $carry = 0; } | 
|  | 47 |  |  |  |  | 37 |  | 
| 253 |  |  |  |  |  |  | @i | 
| 254 | 7 |  |  |  |  | 21 | } map { ord($_) } @bytes)); | 
|  | 58 |  |  |  |  | 97 |  | 
|  | 58 |  |  |  |  | 48 |  | 
| 255 |  |  |  |  |  |  | } elsif ($tag == ASN1_ENUMERATED) { | 
| 256 | 2 | 50 |  |  |  | 4 | die "Unsupported len=$len" unless $len == 1; | 
| 257 | 2 |  |  |  |  | 4 | @contents = map { ord($_) } @bytes; | 
|  | 2 |  |  |  |  | 5 |  | 
| 258 |  |  |  |  |  |  | } elsif ($tag == ASN1_SEQUENCE || $tag == ASN1_SET || $tag == ASN1_APPLICATION) { | 
| 259 | 12 |  |  |  |  | 25 | push @contents, parse_asn1(\@bytes) | 
| 260 |  |  |  |  |  |  | while @bytes; | 
| 261 |  |  |  |  |  |  | } elsif ($tag >= ASN1_CONTEXT && $tag <= ASN1_CONTEXT + 3) { | 
| 262 | 16 |  |  |  |  | 10 | @contents = @{parse_asn1(\@bytes)}; | 
|  | 16 |  |  |  |  | 47 |  | 
| 263 | 16 |  | 100 |  |  | 49 | $parsed_context_values[$tag - ASN1_CONTEXT] //= \@contents; | 
| 264 |  |  |  |  |  |  | } else { | 
| 265 | 0 |  |  |  |  | 0 | warn sprintf "Unsupported asn1 tag 0x%x on parse\n", $tag; | 
| 266 |  |  |  |  |  |  | } | 
| 267 |  |  |  |  |  |  |  | 
| 268 | 41 |  |  |  |  | 99 | return [ $tag, @contents ]; | 
| 269 |  |  |  |  |  |  | } | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | sub generate_asn1 { | 
| 272 | 41 |  | 50 | 41 | 1 | 45 | my $tag     = shift // die "No asn1 tag"; | 
| 273 | 41 |  | 50 |  |  | 48 | my $content = shift // die "No asn1 tag content"; | 
| 274 |  |  |  |  |  |  |  | 
| 275 | 41 |  |  |  |  | 33 | my @bytes; | 
| 276 | 41 | 100 | 66 |  |  | 154 | if ($tag == ASN1_BINARY) { | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 | 33 |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 277 | 4 |  |  |  |  | 73 | @bytes = split('', $content); | 
| 278 |  |  |  |  |  |  | } elsif ($tag == ASN1_OID) { | 
| 279 | 7 |  |  |  |  | 7 | my $idx = 0; | 
| 280 | 7 |  |  |  |  | 5 | my $id0; | 
| 281 | 58 |  |  |  |  | 72 | @bytes = map { chr($_) } map { | 
| 282 | 7 | 50 | 33 |  |  | 17 | 0 == $idx++ ? ($id0 = $_) && () : 2 == $idx ? ($id0 * 40 + $_) : ( | 
|  | 61 | 50 |  |  |  | 157 |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 283 |  |  |  |  |  |  | $_ >= 1 << 28 ? (0x80 | (($_ >> 28) & 0x7f)) : (), | 
| 284 |  |  |  |  |  |  | $_ >= 1 << 21 ? (0x80 | (($_ >> 21) & 0x7f)) : (), | 
| 285 |  |  |  |  |  |  | $_ >= 1 << 14 ? (0x80 | (($_ >> 14) & 0x7f)) : (), | 
| 286 |  |  |  |  |  |  | $_ >= 1 <<  7 ? (0x80 | (($_ >>  7) & 0x7f)) : (), | 
| 287 |  |  |  |  |  |  | $_ & 0x7f | 
| 288 |  |  |  |  |  |  | ) | 
| 289 |  |  |  |  |  |  | } split(/\./, $content); | 
| 290 |  |  |  |  |  |  | } elsif ($tag == ASN1_ENUMERATED) { | 
| 291 | 2 |  |  |  |  | 4 | @bytes = (chr($content)); | 
| 292 |  |  |  |  |  |  | } elsif ($tag == ASN1_SEQUENCE || $tag == ASN1_SET || $tag == ASN1_APPLICATION) { | 
| 293 | 12 |  |  |  |  | 11 | do { | 
| 294 | 19 |  |  |  |  | 13 | push @bytes, @{generate_asn1(@$content)}; | 
|  | 19 |  |  |  |  | 28 |  | 
| 295 | 19 |  |  |  |  | 59 | $content = shift; | 
| 296 |  |  |  |  |  |  | } while $content; | 
| 297 |  |  |  |  |  |  | } elsif ($tag >= ASN1_CONTEXT && $tag <= ASN1_CONTEXT + 3) { | 
| 298 | 16 |  |  |  |  | 15 | @bytes = @{generate_asn1($content, @_)}; | 
|  | 16 |  |  |  |  | 24 |  | 
| 299 |  |  |  |  |  |  | } else { | 
| 300 | 0 |  |  |  |  | 0 | warn sprintf "Unsupported asn1 tag 0x%x on generate\n", $tag; | 
| 301 |  |  |  |  |  |  | } | 
| 302 |  |  |  |  |  |  |  | 
| 303 | 41 |  |  |  |  | 82 | my $len = @bytes; | 
| 304 | 41 |  |  |  |  | 30 | my @sub_lens; | 
| 305 | 41 |  |  |  |  | 64 | while ($len >= 0x80) { | 
| 306 | 6 |  |  |  |  | 9 | push @sub_lens, $len % 256; | 
| 307 | 6 |  |  |  |  | 11 | $len /= 256; | 
| 308 |  |  |  |  |  |  | } | 
| 309 | 41 | 100 |  |  |  | 62 | my @len_bytes = @sub_lens ? (0x80 + @sub_lens + 1, $len, @sub_lens) : ($len); | 
| 310 |  |  |  |  |  |  |  | 
| 311 | 41 |  |  |  |  | 43 | return [ (map { chr($_) } $tag, @len_bytes), @bytes ]; | 
|  | 94 |  |  |  |  | 720 |  | 
| 312 |  |  |  |  |  |  | } | 
| 313 |  |  |  |  |  |  |  | 
| 314 |  |  |  |  |  |  | sub process_spnego ($$%) { | 
| 315 | 6 |  |  | 6 | 1 | 1588 | my $self = shift; | 
| 316 | 6 |  | 50 |  |  | 13 | my $buffer = shift // return; | 
| 317 | 6 |  |  |  |  | 9 | my %options = @_; | 
| 318 |  |  |  |  |  |  |  | 
| 319 | 6 |  |  |  |  | 65 | my @bytes = split '', $buffer; | 
| 320 | 6 | 50 |  |  |  | 13 | return unless @bytes > 2; | 
| 321 |  |  |  |  |  |  |  | 
| 322 | 6 |  |  |  |  | 19 | @parsed_context_values = (); | 
| 323 | 6 |  |  |  |  | 11 | my $struct = parse_asn1(\@bytes); | 
| 324 | 6 | 50 |  |  |  | 8 | return unless $struct; | 
| 325 |  |  |  |  |  |  |  | 
| 326 | 6 | 100 | 100 |  |  | 13 | if (!defined $self->ntlmssp_supported || $options{is_initial}) { | 
| 327 | 2 |  |  |  |  | 3 | my $value = $parsed_context_values[0]; | 
| 328 | 2 | 50 | 33 |  |  | 7 | return $self->err("No expected spnego context value") | 
| 329 |  |  |  |  |  |  | unless ref($value) eq 'ARRAY' && shift @$value == ASN1_SEQUENCE; | 
| 330 | 2 |  |  |  |  | 3 | for (@$value) { | 
| 331 | 2 | 50 | 33 |  |  | 9 | return $self->ntlmssp_supported(1) | 
| 332 |  |  |  |  |  |  | if $_->[0] == ASN1_OID && $_->[1] eq OID_MECH_NTLMSSP; | 
| 333 |  |  |  |  |  |  | } | 
| 334 | 0 |  |  |  |  | 0 | return $self->ntlmssp_supported(0); | 
| 335 |  |  |  |  |  |  | } | 
| 336 |  |  |  |  |  |  |  | 
| 337 | 4 |  |  |  |  | 4 | my $value = $parsed_context_values[2]; | 
| 338 | 4 | 100 | 66 |  |  | 14 | my $ntlmssp_bytes = ref($value) eq 'ARRAY' && shift @$value == ASN1_BINARY | 
| 339 |  |  |  |  |  |  | ? shift @$value | 
| 340 |  |  |  |  |  |  | : undef; | 
| 341 | 4 |  |  |  |  | 12 | my $parser = $self->parser; | 
| 342 | 4 | 100 |  |  |  | 9 | unless (defined $self->client_challenge) { | 
| 343 | 3 | 50 |  |  |  | 4 | return $self->err("No expected spnego context+2 value (ntlmssp)") | 
| 344 |  |  |  |  |  |  | unless $ntlmssp_bytes; | 
| 345 | 3 |  |  |  |  | 21 | $parser->set(join('', @$ntlmssp_bytes)); | 
| 346 | 3 | 50 |  |  |  | 18 | return $self->err("No expected NTLMSSP id string") | 
| 347 |  |  |  |  |  |  | unless $parser->bytes(length(NTLMSSP_ID_STR)) eq NTLMSSP_ID_STR; | 
| 348 |  |  |  |  |  |  | } | 
| 349 |  |  |  |  |  |  |  | 
| 350 | 4 | 100 |  |  |  | 7 | if (!defined $self->client_host) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 351 | 1 | 50 |  |  |  | 2 | return $self->err("No expected NTLMSSP_NEGOTIATE") | 
| 352 |  |  |  |  |  |  | unless $parser->uint32 == NTLMSSP_NEGOTIATE; | 
| 353 | 1 |  |  |  |  | 3 | $parser->skip(4);  # skip flags | 
| 354 | 1 |  |  |  |  | 7 | my $len1 = $parser->uint16; | 
| 355 | 1 |  |  |  |  | 2 | my $off1 = $parser->skip(2)->uint32; | 
| 356 | 1 |  |  |  |  | 2 | my $len2 = $parser->uint16; | 
| 357 | 1 |  |  |  |  | 2 | my $off2 = $parser->skip(2)->uint32; | 
| 358 | 1 |  |  |  |  | 2 | $self->client_domain($parser->reset($off1)->bytes($len1)); | 
| 359 | 1 |  |  |  |  | 2 | $self->client_host  ($parser->reset($off2)->bytes($len2)); | 
| 360 |  |  |  |  |  |  | } elsif (!defined $self->server_challenge) { | 
| 361 | 1 | 50 |  |  |  | 2 | return $self->err("No expected NTLMSSP_CHALLENGE") | 
| 362 |  |  |  |  |  |  | unless $parser->uint32 == NTLMSSP_CHALLENGE; | 
| 363 | 1 |  |  |  |  | 2 | my $len1 = $parser->uint16; | 
| 364 | 1 |  |  |  |  | 3 | my $off1 = $parser->skip(2)->uint32; | 
| 365 | 1 |  |  |  |  | 2 | $self->server_challenge($parser->reset(24)->bytes(8)); | 
| 366 | 1 |  |  |  |  | 2 | $self->server_host($parser->reset($off1)->str($len1)); | 
| 367 | 1 |  |  |  |  | 1 | my $itemtype; | 
| 368 | 1 |  |  |  |  | 2 | do {{ | 
| 369 | 6 |  |  |  |  | 7 | $itemtype = $parser->uint16; | 
|  | 6 |  |  |  |  | 7 |  | 
| 370 | 6 | 100 | 33 |  |  | 10 | $parser->uint16 == 8 && $self->server_timestamp($parser->uint64), next | 
| 371 |  |  |  |  |  |  | if $itemtype == NTLMSSP_ITEM_TIMESTAMP; | 
| 372 | 5 |  |  |  |  | 7 | my $str = $parser->str($parser->uint16); | 
| 373 | 5 | 100 |  |  |  | 145 | $self->server_netbios_host($str) | 
| 374 |  |  |  |  |  |  | if $itemtype == NTLMSSP_ITEM_NETBIOSHOST; | 
| 375 | 5 | 100 |  |  |  | 7 | $self->server_netbios_domain($str) | 
| 376 |  |  |  |  |  |  | if $itemtype == NTLMSSP_ITEM_NETBIOSDOMAIN; | 
| 377 | 5 | 100 |  |  |  | 9 | $self->server_dns_host($str) | 
| 378 |  |  |  |  |  |  | if $itemtype == NTLMSSP_ITEM_DNSHOST; | 
| 379 | 5 | 100 |  |  |  | 11 | $self->server_dns_domain($str) | 
| 380 |  |  |  |  |  |  | if $itemtype == NTLMSSP_ITEM_DNSDOMAIN; | 
| 381 |  |  |  |  |  |  | }} while ($itemtype != NTLMSSP_ITEM_TERMINATOR) | 
| 382 |  |  |  |  |  |  | } elsif (!defined $self->client_challenge) { | 
| 383 | 1 | 50 |  |  |  | 2 | return $self->err("No expected NTLMSSP_AUTH") | 
| 384 |  |  |  |  |  |  | unless $parser->uint32 == NTLMSSP_AUTH; | 
| 385 | 1 |  |  |  |  | 2 | my $llen = $parser->uint16; | 
| 386 | 1 |  |  |  |  | 2 | my $loff = $parser->skip(2)->uint32; | 
| 387 | 1 |  |  |  |  | 2 | my $nlen = $parser->uint16; | 
| 388 | 1 |  |  |  |  | 2 | my $noff = $parser->skip(2)->uint32; | 
| 389 | 1 |  |  |  |  | 2 | my $len1 = $parser->uint16; | 
| 390 | 1 |  |  |  |  | 3 | my $off1 = $parser->skip(2)->uint32; | 
| 391 | 1 |  |  |  |  | 2 | my $len2 = $parser->uint16; | 
| 392 | 1 |  |  |  |  | 2 | my $off2 = $parser->skip(2)->uint32; | 
| 393 | 1 |  |  |  |  | 2 | my $len3 = $parser->uint16; | 
| 394 | 1 |  |  |  |  | 2 | my $off3 = $parser->skip(2)->uint32; | 
| 395 | 1 |  |  |  |  | 3 | $self->client_challenge($parser->reset($noff + 28)->bytes(8)); | 
| 396 | 1 |  |  |  |  | 3 | $self->lm_response  ($parser->reset($loff)->bytes($llen)); | 
| 397 | 1 |  |  |  |  | 2 | $self->ntlm_response($parser->reset($noff)->bytes($nlen)); | 
| 398 | 1 |  |  |  |  | 3 | $self->client_domain($parser->reset($off1)->str($len1)); | 
| 399 | 1 |  |  |  |  | 2 | $self->username     ($parser->reset($off2)->str($len2)); | 
| 400 | 1 |  |  |  |  | 3 | $self->client_host  ($parser->reset($off3)->str($len3)); | 
| 401 |  |  |  |  |  |  | } elsif (!defined $self->auth_completed) { | 
| 402 | 1 |  |  |  |  | 2 | my $value = $parsed_context_values[0]; | 
| 403 | 1 | 50 | 33 |  |  | 6 | return $self->err("No expected spnego context value (ACCEPT_COMPLETED)") | 
| 404 |  |  |  |  |  |  | unless ref($value) eq 'ARRAY' && shift @$value == ASN1_ENUMERATED; | 
| 405 | 1 | 50 |  |  |  | 4 | $self->auth_completed(shift @$value == SPNEGO_ACCEPT_COMPLETED ? 1 : 0); | 
| 406 |  |  |  |  |  |  | } else { | 
| 407 | 0 |  |  |  |  | 0 | $self->err("process_spnego called after auth_completed"); | 
| 408 |  |  |  |  |  |  | } | 
| 409 |  |  |  |  |  |  |  | 
| 410 | 4 |  |  |  |  | 26 | return 1; | 
| 411 |  |  |  |  |  |  | } | 
| 412 |  |  |  |  |  |  |  | 
| 413 |  |  |  |  |  |  | sub generate_spnego ($%) { | 
| 414 | 6 |  |  | 6 | 1 | 14 | my $self = shift; | 
| 415 | 6 |  |  |  |  | 10 | my %options = @_; | 
| 416 |  |  |  |  |  |  |  | 
| 417 | 6 |  |  |  |  | 6 | my $struct; | 
| 418 |  |  |  |  |  |  |  | 
| 419 | 6 | 100 | 100 |  |  | 12 | if (!defined $self->ntlmssp_supported || $options{is_initial}) { | 
| 420 | 2 |  |  |  |  | 4 | $self->ntlmssp_supported(1); | 
| 421 | 2 |  |  |  |  | 5 | $struct = [ ASN1_APPLICATION, | 
| 422 |  |  |  |  |  |  | [ ASN1_OID, OID_SPNEGO ], | 
| 423 |  |  |  |  |  |  | [ ASN1_CONTEXT, ASN1_SEQUENCE, | 
| 424 |  |  |  |  |  |  | [ ASN1_CONTEXT, ASN1_SEQUENCE, | 
| 425 |  |  |  |  |  |  | [ ASN1_OID, OID_MECH_NTLMSSP ], | 
| 426 |  |  |  |  |  |  | ], | 
| 427 |  |  |  |  |  |  | ], | 
| 428 |  |  |  |  |  |  | ]; | 
| 429 | 2 |  |  |  |  | 7 | goto RETURN; | 
| 430 |  |  |  |  |  |  | } | 
| 431 |  |  |  |  |  |  |  | 
| 432 | 4 |  |  |  |  | 9 | my @names = hostname =~ /^([^.]*+)\.?+(.*)$/; | 
| 433 | 4 |  | 66 |  |  | 49 | my $host   = $options{host}   || $names[0]; | 
| 434 | 4 |  | 66 |  |  | 11 | my $domain = $options{domain} || $names[1]; | 
| 435 |  |  |  |  |  |  |  | 
| 436 | 4 | 100 |  |  |  | 14 | if (!defined $self->client_host) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 437 | 1 |  |  |  |  | 3 | $self->client_host($host); | 
| 438 | 1 |  |  |  |  | 4 | $self->client_domain($domain); | 
| 439 |  |  |  |  |  |  |  | 
| 440 | 1 |  |  |  |  | 5 | $self->packer->reset | 
| 441 |  |  |  |  |  |  | ->bytes(NTLMSSP_ID_STR) | 
| 442 |  |  |  |  |  |  | ->uint32(NTLMSSP_NEGOTIATE) | 
| 443 |  |  |  |  |  |  | ->uint32(NTLMSSP_FLAGS_CLIENT) | 
| 444 |  |  |  |  |  |  | ->uint16(length($domain)) | 
| 445 |  |  |  |  |  |  | ->uint16(length($domain)) | 
| 446 |  |  |  |  |  |  | ->uint32(32) | 
| 447 |  |  |  |  |  |  | ->uint16(length($host)) | 
| 448 |  |  |  |  |  |  | ->uint16(length($host)) | 
| 449 |  |  |  |  |  |  | ->uint32(32 + length($domain)) | 
| 450 |  |  |  |  |  |  | ->bytes($domain) | 
| 451 |  |  |  |  |  |  | ->bytes($host) | 
| 452 |  |  |  |  |  |  | ; | 
| 453 | 1 |  |  |  |  | 3 | $struct = [ ASN1_APPLICATION, | 
| 454 |  |  |  |  |  |  | [ ASN1_OID, OID_SPNEGO ], | 
| 455 |  |  |  |  |  |  | [ ASN1_CONTEXT, ASN1_SEQUENCE, | 
| 456 |  |  |  |  |  |  | [ ASN1_CONTEXT, ASN1_SEQUENCE, | 
| 457 |  |  |  |  |  |  | [ ASN1_OID, OID_MECH_NTLMSSP ], | 
| 458 |  |  |  |  |  |  | ], | 
| 459 |  |  |  |  |  |  | [ ASN1_CONTEXT + 2, ASN1_BINARY, $self->packer->data ], | 
| 460 |  |  |  |  |  |  | ], | 
| 461 |  |  |  |  |  |  | ]; | 
| 462 |  |  |  |  |  |  | } elsif (!defined $self->server_challenge) { | 
| 463 | 1 |  |  |  |  | 2 | $self->server_challenge(join('', map { chr(rand(0x100)) } 1 .. 8)); | 
|  | 8 |  |  |  |  | 65 |  | 
| 464 | 1 |  |  |  |  | 8 | $self->server_host($host); | 
| 465 | 1 |  |  |  |  | 5 | $self->server_netbios_host($host); | 
| 466 | 1 |  |  |  |  | 5 | $self->server_netbios_domain($domain); | 
| 467 | 1 |  |  |  |  | 4 | $self->server_dns_host($host); | 
| 468 | 1 |  |  |  |  | 7 | $self->server_dns_domain($domain); | 
| 469 | 1 |  |  |  |  | 2 | my $tlen = 32 + length( | 
| 470 |  |  |  |  |  |  | $self->server_netbios_host . | 
| 471 |  |  |  |  |  |  | $self->server_netbios_domain . | 
| 472 |  |  |  |  |  |  | $self->server_dns_host . | 
| 473 |  |  |  |  |  |  | $self->server_dns_domain | 
| 474 |  |  |  |  |  |  | ) * 2; | 
| 475 |  |  |  |  |  |  |  | 
| 476 | 1 |  |  |  |  | 3 | $self->packer->reset | 
| 477 |  |  |  |  |  |  | ->bytes(NTLMSSP_ID_STR) | 
| 478 |  |  |  |  |  |  | ->uint32(NTLMSSP_CHALLENGE) | 
| 479 |  |  |  |  |  |  | ->uint16(length($self->server_host) * 2) | 
| 480 |  |  |  |  |  |  | ->uint16(length($self->server_host) * 2) | 
| 481 |  |  |  |  |  |  | ->uint32(56) | 
| 482 |  |  |  |  |  |  | ->uint32(NTLMSSP_FLAGS_SERVER) | 
| 483 |  |  |  |  |  |  | ->bytes($self->server_challenge) | 
| 484 |  |  |  |  |  |  | ->uint64(0)  # reserved | 
| 485 |  |  |  |  |  |  | ->uint16($tlen) | 
| 486 |  |  |  |  |  |  | ->uint16($tlen) | 
| 487 |  |  |  |  |  |  | ->uint32(56 + length($self->server_host) * 2) | 
| 488 |  |  |  |  |  |  | ->bytes("\x06\x01\xb1\x1d\x00\x00\x00\x0f")  # version | 
| 489 |  |  |  |  |  |  | ->str($self->server_host) | 
| 490 |  |  |  |  |  |  | ->uint16(NTLMSSP_ITEM_NETBIOSDOMAIN) | 
| 491 |  |  |  |  |  |  | ->uint16(length($self->server_netbios_domain) * 2) | 
| 492 |  |  |  |  |  |  | ->str($self->server_netbios_domain) | 
| 493 |  |  |  |  |  |  | ->uint16(NTLMSSP_ITEM_NETBIOSHOST) | 
| 494 |  |  |  |  |  |  | ->uint16(length($self->server_netbios_host) * 2) | 
| 495 |  |  |  |  |  |  | ->str($self->server_netbios_host) | 
| 496 |  |  |  |  |  |  | ->uint16(NTLMSSP_ITEM_DNSDOMAIN) | 
| 497 |  |  |  |  |  |  | ->uint16(length($self->server_dns_domain) * 2) | 
| 498 |  |  |  |  |  |  | ->str($self->server_dns_domain) | 
| 499 |  |  |  |  |  |  | ->uint16(NTLMSSP_ITEM_DNSHOST) | 
| 500 |  |  |  |  |  |  | ->uint16(length($self->server_dns_host) * 2) | 
| 501 |  |  |  |  |  |  | ->str($self->server_dns_host) | 
| 502 |  |  |  |  |  |  | ->uint16(NTLMSSP_ITEM_TIMESTAMP) | 
| 503 |  |  |  |  |  |  | ->uint16(8) | 
| 504 |  |  |  |  |  |  | ->bytes("\0" x 8) | 
| 505 |  |  |  |  |  |  | ->uint16(NTLMSSP_ITEM_TERMINATOR) | 
| 506 |  |  |  |  |  |  | ->uint16(0) | 
| 507 |  |  |  |  |  |  | ; | 
| 508 |  |  |  |  |  |  |  | 
| 509 | 1 |  |  |  |  | 5 | $struct = [ ASN1_CONTEXT + 1, ASN1_SEQUENCE, | 
| 510 |  |  |  |  |  |  | [ ASN1_CONTEXT, ASN1_ENUMERATED, SPNEGO_ACCEPT_INCOMPLETE ], | 
| 511 |  |  |  |  |  |  | [ ASN1_CONTEXT + 1, ASN1_OID, OID_MECH_NTLMSSP ], | 
| 512 |  |  |  |  |  |  | [ ASN1_CONTEXT + 2, ASN1_BINARY, $self->packer->data ], | 
| 513 |  |  |  |  |  |  | ]; | 
| 514 |  |  |  |  |  |  | } elsif (!defined $self->client_challenge) { | 
| 515 | 1 |  | 50 |  |  | 2 | my $username = $options{username} || ''; | 
| 516 | 1 |  | 50 |  |  | 3 | my $password = $options{password} || ''; | 
| 517 | 1 |  | 50 |  |  | 2 | $domain = $options{domain} || 'MYGROUP'; | 
| 518 | 1 |  |  |  |  | 2 | $self->client_challenge(join('', map { chr(rand(0x100)) } 1 .. 8)); | 
|  | 8 |  |  |  |  | 14 |  | 
| 519 | 1 |  |  |  |  | 8 | $self->username($username); | 
| 520 | 1 |  |  |  |  | 5 | $self->domain($domain); | 
| 521 | 1 |  |  |  |  | 2 | $self->session_key([ map { chr(rand(0x100)) } 1 .. 16 ]); | 
|  | 16 |  |  |  |  | 24 |  | 
| 522 |  |  |  |  |  |  |  | 
| 523 |  |  |  |  |  |  | #		my $lm_hash   = $options{lm_password_hash}   || create_lm_hash($password); | 
| 524 | 1 |  | 33 |  |  | 5 | my $ntlm_hash = $options{ntlm_password_hash} || create_ntlm_hash($password); | 
| 525 | 1 |  |  |  |  | 4 | my $ntlmv2_hash = create_ntlmv2_hash($ntlm_hash, $self->username, $self->domain); | 
| 526 |  |  |  |  |  |  |  | 
| 527 | 1 |  | 50 |  |  | 4 | $self->packer->reset | 
| 528 |  |  |  |  |  |  | ->uint32(0x0101)    # header | 
| 529 |  |  |  |  |  |  | ->uint32(0)         # reserved | 
| 530 |  |  |  |  |  |  | ->uint64(to_nttime(time)) | 
| 531 |  |  |  |  |  |  | ->bytes($self->client_challenge) | 
| 532 |  |  |  |  |  |  | ->uint32(0)         # unknown | 
| 533 |  |  |  |  |  |  | ->uint16(NTLMSSP_ITEM_NETBIOSDOMAIN) | 
| 534 |  |  |  |  |  |  | ->uint16(length($self->server_netbios_domain) * 2) | 
| 535 |  |  |  |  |  |  | ->str($self->server_netbios_domain) | 
| 536 |  |  |  |  |  |  | ->uint16(NTLMSSP_ITEM_NETBIOSHOST) | 
| 537 |  |  |  |  |  |  | ->uint16(length($self->server_netbios_host) * 2) | 
| 538 |  |  |  |  |  |  | ->str($self->server_netbios_host) | 
| 539 |  |  |  |  |  |  | ->uint16(NTLMSSP_ITEM_DNSDOMAIN) | 
| 540 |  |  |  |  |  |  | ->uint16(length($self->server_dns_domain) * 2) | 
| 541 |  |  |  |  |  |  | ->str($self->server_dns_domain) | 
| 542 |  |  |  |  |  |  | ->uint16(NTLMSSP_ITEM_DNSHOST) | 
| 543 |  |  |  |  |  |  | ->uint16(length($self->server_dns_host) * 2) | 
| 544 |  |  |  |  |  |  | ->str($self->server_dns_host) | 
| 545 |  |  |  |  |  |  | ->uint16(NTLMSSP_ITEM_TIMESTAMP) | 
| 546 |  |  |  |  |  |  | ->uint16(8) | 
| 547 |  |  |  |  |  |  | ->uint64($self->server_timestamp || 0) | 
| 548 |  |  |  |  |  |  | ->uint16(NTLMSSP_ITEM_TERMINATOR) | 
| 549 |  |  |  |  |  |  | ->uint16(0) | 
| 550 |  |  |  |  |  |  | ; | 
| 551 |  |  |  |  |  |  |  | 
| 552 | 1 |  |  |  |  | 4 | my $client_data = $self->packer->data; | 
| 553 | 1 |  |  |  |  | 3 | my $hmac = hmac_md5($self->server_challenge . $client_data, $ntlmv2_hash); | 
| 554 | 1 |  |  |  |  | 2 | my $ntlm_response = "$hmac$client_data"; | 
| 555 | 1 |  |  |  |  | 9 | my $nlen = 16 + $self->packer->size;  # hmac + client data | 
| 556 |  |  |  |  |  |  |  | 
| 557 | 1 |  |  |  |  | 2 | my $lm_response = create_lmv2_response($ntlm_hash, $username, $domain, $self->server_challenge); | 
| 558 |  |  |  |  |  |  |  | 
| 559 | 1 |  |  |  |  | 8 | $self->lm_response($lm_response); | 
| 560 | 1 |  |  |  |  | 5 | $self->ntlm_response($ntlm_response); | 
| 561 |  |  |  |  |  |  |  | 
| 562 | 1 |  |  |  |  | 3 | $self->packer->reset | 
| 563 |  |  |  |  |  |  | ->bytes(NTLMSSP_ID_STR) | 
| 564 |  |  |  |  |  |  | ->uint32(NTLMSSP_AUTH) | 
| 565 |  |  |  |  |  |  | ->uint16(24) | 
| 566 |  |  |  |  |  |  | ->uint16(24) | 
| 567 |  |  |  |  |  |  | ->uint32(64) | 
| 568 |  |  |  |  |  |  | ->uint16($nlen) | 
| 569 |  |  |  |  |  |  | ->uint16($nlen) | 
| 570 |  |  |  |  |  |  | ->uint32(88) | 
| 571 |  |  |  |  |  |  | ->uint16(length($domain) * 2) | 
| 572 |  |  |  |  |  |  | ->uint16(length($domain) * 2) | 
| 573 |  |  |  |  |  |  | ->uint32(88 + $nlen) | 
| 574 |  |  |  |  |  |  | ->uint16(length($username) * 2) | 
| 575 |  |  |  |  |  |  | ->uint16(length($username) * 2) | 
| 576 |  |  |  |  |  |  | ->uint32(88 + $nlen + length($domain) * 2) | 
| 577 |  |  |  |  |  |  | ->uint16(length($host) * 2) | 
| 578 |  |  |  |  |  |  | ->uint16(length($host) * 2) | 
| 579 |  |  |  |  |  |  | ->uint32(88 + $nlen + length("$domain$username") * 2) | 
| 580 |  |  |  |  |  |  | ->uint16(16) | 
| 581 |  |  |  |  |  |  | ->uint16(16) | 
| 582 |  |  |  |  |  |  | ->uint32(88 + $nlen + length("$domain$username$host") * 2) | 
| 583 |  |  |  |  |  |  | ->uint32(NTLMSSP_FLAGS_CLIENT) | 
| 584 |  |  |  |  |  |  | ->bytes($lm_response) | 
| 585 |  |  |  |  |  |  | ->bytes($ntlm_response) | 
| 586 |  |  |  |  |  |  | ->str($domain) | 
| 587 |  |  |  |  |  |  | ->str($username) | 
| 588 |  |  |  |  |  |  | ->str($host) | 
| 589 |  |  |  |  |  |  | ->bytes($self->session_key) | 
| 590 |  |  |  |  |  |  | ; | 
| 591 |  |  |  |  |  |  |  | 
| 592 | 1 |  |  |  |  | 4 | $struct = [ ASN1_CONTEXT + 1, ASN1_SEQUENCE, | 
| 593 |  |  |  |  |  |  | [ ASN1_CONTEXT + 2, ASN1_BINARY, $self->packer->data ], | 
| 594 |  |  |  |  |  |  | ]; | 
| 595 |  |  |  |  |  |  | } elsif (!defined $self->auth_completed) { | 
| 596 | 1 | 50 |  |  |  | 4 | $self->auth_completed($self->is_user_authenticated ? 1 : 0); | 
| 597 | 1 |  |  |  |  | 2 | my $mechlist_mic = "\x01\x00\x00\x00\x89\x22\x91\x93\xff\xdb\xcb\x26\x00\x00\x00\x00";  #"\x00" x 16; | 
| 598 | 1 | 50 |  |  |  | 2 | $struct = [ ASN1_CONTEXT + 1, ASN1_SEQUENCE, | 
| 599 |  |  |  |  |  |  | [ ASN1_CONTEXT, ASN1_ENUMERATED, SPNEGO_ACCEPT_COMPLETED ], | 
| 600 |  |  |  |  |  |  | [ ASN1_CONTEXT + 3, ASN1_BINARY, $mechlist_mic ], | 
| 601 |  |  |  |  |  |  | ] if $self->auth_completed; | 
| 602 |  |  |  |  |  |  | } else { | 
| 603 | 0 |  |  |  |  | 0 | $self->err("generate_spnego called after auth_completed"); | 
| 604 |  |  |  |  |  |  | } | 
| 605 |  |  |  |  |  |  |  | 
| 606 | 6 | 50 |  |  |  | 12 | RETURN: | 
| 607 |  |  |  |  |  |  | return undef unless $struct; | 
| 608 |  |  |  |  |  |  |  | 
| 609 | 6 |  |  |  |  | 4 | return join '', @{generate_asn1(@$struct)}; | 
|  | 6 |  |  |  |  | 13 |  | 
| 610 |  |  |  |  |  |  | } | 
| 611 |  |  |  |  |  |  |  | 
| 612 |  |  |  |  |  |  | 1; | 
| 613 |  |  |  |  |  |  |  | 
| 614 |  |  |  |  |  |  | __END__ |