client.pm 6.37 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Radius client
# Copyright (C) 2007-2016, AllWorldIT
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.



package smradius::client;


use strict;
use warnings;

26
27
28
use base qw(AWITPT::Object);

use Getopt::Long qw( GetOptionsFromArray );
29
30
31
32
use IO::Select;
use IO::Socket;

use smradius::version;
Nigel Kukard's avatar
Nigel Kukard committed
33
use smradius::Radius::Packet;
34
35
36
37
38
39
40
41
42

# Check Config::IniFiles is instaslled
if (!eval {require Config::IniFiles; 1;}) {
	print STDERR "You're missing Config::IniFiles, try 'apt-get install libconfig-inifiles-perl'\n";
	exit 1;
}



43
# Run the client
44
45
sub run
{
46
47
48
	my ($self,@methodArgs) = @_;
	# Instantiate if we're not already instantiated
	$self = $self->new() if (!ref($self));
49
50
51
52


	print(STDERR "SMRadClient v".VERSION." - Copyright (c) 2007-2016, AllWorldIT\n");

53
54
	print(STDERR "\n");

55
56
57
58
	# Set defaults
	my $cfg;
	$cfg->{'config_file'} = "/etc/smradiusd.conf";

59
60
	# Grab runtime arguments
	my @runArgs = @methodArgs ? @methodArgs : @ARGV;
61
62
63
64

	# Parse command line params
	my $cmdline;
	%{$cmdline} = ();
65
66
	if (!GetOptionsFromArray(
		\@runArgs,
67
68
69
70
		\%{$cmdline},
		"config:s",
		"raddb:s",
		"help",
71
72
73
74
	)) {
	   print(STDERR "ERROR: Error parsing commandline arguments");
	   return 1;
	}
75
76
77
78

	# Check for some args
	if ($cmdline->{'help'}) {
		displayHelp();
79
		return 0;
80
81
82
	}

	# Make sure we only have 2 additional args
83
84
	if (@runArgs < 3) {
		print(STDERR "ERROR: Invalid number of arguments\n");
85
		displayHelp();
86
		return 1;
87
88
89
	}

	if (!defined($cmdline->{'raddb'}) || $cmdline->{'raddb'} eq "") {
90
		print(STDERR "ERROR: No raddb directory specified!\n");
91
		displayHelp();
92
		return 1;
93
94
95
	}

	# Get variables we need
96
97
98
	my $server = shift(@runArgs);
	my $type = shift(@runArgs);
	$self->{'secret'} = shift(@runArgs);
99
100

	# Validate type
101
102
	if (!defined($type) || ( $type ne "acct" && $type ne "auth" && $type ne "disconnect")) {
		print(STDERR "ERROR: Invalid packet type specified!\n");
103
		displayHelp();
104
		return 1;
105
106
107
108
109
110
111
112
	}


	print(STDERR "\n");


	# Time to start loading the dictionary
	print(STDERR "Loading dictionaries...");
Nigel Kukard's avatar
Nigel Kukard committed
113
	my $raddb = smradius::Radius::Dictionary->new();
114
115

	# Look for files in the dir
116
117
118
119
120
	my $DIR;
	if (!opendir($DIR, $cmdline->{'raddb'})) {
		print(STDERR "ERROR: Cannot open '".$cmdline->{'raddb'}."': $!");
		return 1;
	}
121
122
123
124
	my @raddb_files = readdir($DIR);

	# And load the dictionary
	foreach my $df (@raddb_files) {
125
126
127
128
129
130
		my $df_fn = $cmdline->{'raddb'}."/$df";
		# Load dictionary
		if (!$raddb->readfile($df_fn)) {
			print(STDERR "Failed to load dictionary '$df_fn': $!");
		}
		print(STDERR ".");
131
132
133
134
135
136
137
	}
	print(STDERR "\n");

	# Decide what type of packet this is
	my $port;
	my $pkt_code;
	if ($type eq "acct") {
138
139
		$port = 1813;
		$pkt_code = "Accounting-Request";
140
	} elsif ($type eq "auth") {
141
142
		$port = 1812;
		$pkt_code = "Access-Request";
143
	} elsif ($type eq "disconnect") {
144
145
		$port = 1813;
		$pkt_code = "Disconnect-Request";
146
147
148
149
	}


	print(STDERR "\nRequest:\n");
150
	printf(STDERR " > Secret => '%s'\n",$self->{'secret'});
151
	# Build packet
152
153
	$self->{'pkt'} = smradius::Radius::Packet->new($raddb);
	$self->{'pkt'}->set_code($pkt_code);
154
155
	# Generate identifier
	my $ident = int(rand(32768));
156
	$self->{'pkt'}->set_identifier($ident);
157
158
159
	print(STDERR " > Identifier: $ident\n");
	# Generate authenticator number
	my $authen = int(rand(32768));
160
	$self->{'pkt'}->set_authenticator($authen);
161
162
	print(STDERR " > Authenticator: $ident\n");

163
	# Pull in attributes from STDIN
164
	while (my $line = <STDIN>) {
165
		$self->addAttributesFromString($line);
166
	}
167
168
169
170

	# Pull in attributes from commandline
	while (my $line = shift(@runArgs)) {
		$self->addAttributesFromString($line);
171
172
173
	}

	# Create UDP packet
174
	my $udp_packet = $self->{'pkt'}->pack();
175
176
177
178
179
180
181
182
183
184
185
186

	# Create socket to send packet out on
	my $sockTimeout = "10";  # 10 second timeout
	my $sock = IO::Socket::INET->new(
		PeerAddr => $server,
		PeerPort => $port,
		Type => SOCK_DGRAM,
		Proto => 'udp',
		TimeOut => $sockTimeout,
	);

	if (!$sock) {
187
188
		print(STDERR "ERROR: Failed to create socket\n");
		return 1;
189
190
191
192
	}

	# Check if we sent the packet...
	if (!$sock->send($udp_packet)) {
193
194
		print(STDERR "ERROR: Failed to send data on socket\n");
		return 1;
195
196
197
198
199
200
201
202
203
	}


	# And time for the response
	print(STDERR "\nResponse:\n");

	# Once sent, we need to get a response back
	my $rsock = IO::Select->new($sock);
	if (!$rsock) {
204
205
		print(STDERR "ERROR: Failed to select response data on socket\n");
		return 1;
206
207
208
209
	}

	# Check if we can read a response after the select()
	if (!$rsock->can_read($sockTimeout)) {
210
211
		print(STDERR "ERROR: Failed to receive response data on socket\n");
		return 1;
212
213
214
215
216
	}

	# Read packet
	$sock->recv($udp_packet, 65536);
	if (!$udp_packet) {
217
218
		print(STDERR "ERROR: Receive response data failed: $!\n");
		return 1;
219
220
221
	}

	# Parse packet
222
223
	my $pkt = smradius::Radius::Packet->new($raddb,$udp_packet);
	print(STDERR " > Authenticated: ". (defined(auth_req_verify($udp_packet,$self->{'secret'},$authen)) ? "yes" : "no") ."\n");
224
225
	print(STDERR $pkt->str_dump());

226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271

	if (@methodArgs) {
		warn "CALLED FROM FUNCTION";
	}

	return 0;
}




# Allow adding attribute from a string
sub addAttributesFromString
{
	my ($self,$line) = @_;


	# Remove EOL
	chomp($line);
	# Split on , and newline
	my @rawAttributes = split(/[,\n]+/,$line);
	foreach my $attr (@rawAttributes) {
		# Pull off attribute name & value
		my ($name,$value) = ($attr =~ /\s*(\S+)\s*=\s?(.+)/);
		$self->addAttribute($name,$value);
	}

	return;
}



# Add attribute to packet
sub addAttribute
{
	my ($self,$name,$value) = @_;


	# Add to packet
	print(STDERR " > Adding '$name' => '$value'\n");
	if ($name eq "User-Password") {
		$self->{'pkt'}->set_password($value,$self->{'secret'});
	} else {
		$self->{'pkt'}->set_attr($name,$value);
	}

272
273
274
275
276
277
278
279
280
	return;
}



# Display help
sub displayHelp {
	print(STDERR<<EOF);

281
282
Usage: $0 [args] <server> <acct|auth|disconnect> <secret> [ATTR=VALUE,...]
    --raddb=<DIR>          Directory where the radius dictionary files are
283
284
285
286
287
288
289
290
291

EOF

	return;
}


1;
# vim: ts=4