-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathclient_connection_authorization.rb
247 lines (204 loc) · 9.79 KB
/
client_connection_authorization.rb
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
module TacacsPlus
class ClientConnection
private
#==============================================================================#
# author_shell_command()
#==============================================================================#
# process authorization when service = shell and cmd != ''
#
def author_shell_command(new_body,username,avpairs,author_request)
# set pass/fail based on default policy. we may change this
# setting below depending whether or not a command_authorization_profile
# is configured
loglevel = :warn # specify this so that whitelist commands may be logged at a higher level
message = ''
whitelisted = false
# get all cmd-arg entries
command = []
avpairs.each do |avpair|
# look for 'cmd-arg' to append to command
command.push(avpair[:value]) if (avpair[:attribute] == 'cmd' || avpair[:attribute] == 'cmd-arg')
end
command = command.join(' ')
# check whitelist
if (@tacacs_daemon.command_authorization_whitelist)
@tacacs_daemon.command_authorization_whitelist.each do |entry|
rule = entry.match?(@peeraddr,command)
if (rule)
whitelisted = true
new_body.status_passadd!
message = "User permitted by whitelisted rule: #{rule}."
loglevel = :info
break
end
end
end
# if no whitelist match
if (!whitelisted)
user = @tacacs_daemon.users(username) if (username)
# fail if user unknown
if (!user)
new_body.status_fail!
message = "Authorization attempt from unknown user."
# fail if user account disabled
elsif (user.disabled?)
new_body.status_fail!
message = "Authorization attempt from disabled account."
else
# get user command auth profile if one exists
command_auth_profile = nil
if (user.command_authorization_profile)
command_auth_profile = user.command_authorization_profile
# if not present, then get group command auth profile if one exists
elsif(user.user_group )
if (user.user_group.command_authorization_profile)
command_auth_profile = user.user_group.command_authorization_profile
end
end
# check command_auth_profile if any
if (command_auth_profile)
match_results = command_auth_profile.matching_entry(command,@peeraddr)
if (match_results)
if (match_results[:permit])
new_body.status_passadd!
if ( match_results.has_key?(:by) )
message = "User permitted by #{match_results[:by]} on rule: #{match_results[:rule]}."
else
message = "User permitted by rule: #{match_results[:rule]}."
end
else
new_body.status_fail!
message = "User denied by #{match_results[:by]} on rule: #{match_results[:rule]}."
end
else
new_body.status_fail!
message = "Authorization denied due to implicit deny."
end
elsif (@tacacs_daemon.default_policy == :deny)
new_body.status_fail!
message = "Authorization denied due to default policy."
else
new_body.status_passadd!
message = "Authorization permitted due to default policy."
end
end
end
# log this attempt
@tacacs_daemon.log(loglevel,['msg_type=Authorization', "message=#{message}", "command=#{command}", "status=#{new_body.xlate_status}"],author_request,@peeraddr)
return(nil)
end
#==============================================================================#
# issue_settings()
#==============================================================================#
# issue avpairs representing shell settings
#
def issue_settings(new_body, username, service, author_request)
# fail if user unknown
user = @tacacs_daemon.users(username) if (username)
if (!user)
new_body.status_fail!
@tacacs_daemon.log(:warn,['msg_type=Authorization', "message=Authorization attempt from unknown user.", "status=#{new_body.xlate_status}"],author_request,@peeraddr)
return(nil)
# fail if user account disabled
elsif (user.disabled?)
new_body.status_fail!
@tacacs_daemon.log(:warn,['msg_type=Authorization', "message=Authorization attempt from disabled account.", "status=#{new_body.xlate_status}"],author_request,@peeraddr)
return(nil)
elsif (user.author_avpair)
author_avpair = user.author_avpair
elsif (user.user_group && user.user_group.author_avpair)
author_avpair = user.user_group.author_avpair
end
args = author_avpair.matching_entry(service,@peeraddr) if (author_avpair)
if (args)
new_body.status_passadd!
new_body.args = args
@tacacs_daemon.log(:info,['msg_type=Authorization', "message=User issued the following AVPairs: #{new_body.args.join(', ')}", "status=#{new_body.xlate_status}"],author_request,@peeraddr)
else
new_body.status_fail!
@tacacs_daemon.log(:debug,['msg_type=Authorization', "message=No AVPairs could be issued for service '#{service}'", "status=#{new_body.xlate_status}"],author_request,@peeraddr)
end
return(nil)
end
#==============================================================================#
# process_authorization()
#==============================================================================#
# the main handler for authorization messages
#
def process_authorization(session)
author_request = session.author_request
new_body = TacacsPlus::AuthorizationResponse.new
username = session.author_request.body.user
# fail if version unsupported
if (author_request.header.version != 0xc0)
msg = "Client version of TACACS+ (0x#{author_request.header.version.to_s(16)}) is unsupported."
@tacacs_daemon.log(:error,['msg_type=TacacsPlus::Server', "message=#{msg}"],author_request,@peeraddr)
author_request.header.version = 0xc0
new_body.status_error!
new_body.server_msg = msg
# else process shell command authorization
elsif (author_request.body.args)
# validate avpairs
av_error = nil
service = nil
cmd = nil
args = []
begin
# some clients wrongly send blank as the first arg, so we need to ignore them.
av = nil
while(1)
av = TacacsPlus.validate_avpair(author_request.body.args.shift)
break if (av[:attribute] != '')
end
# first arg should be 'service'.
if (av[:attribute] != 'service')
raise "Attribute 'service' is required to be the first argument for authorization requests."
else
service = av[:value]
end
# check the remaining args
author_request.body.args.each do |arg|
av = TacacsPlus.validate_avpair(arg)
cmd = av[:value] if (av[:attribute] == 'cmd')
args.push(av)
end
rescue Exception => av_error
msg = "AVPair provided by client raised the following error: #{av_error}"
new_body.status_fail!
new_body.data = msg
@tacacs_daemon.log(:debug,['msg_type=Authorization', "message=#{msg}", "status=#{new_body.xlate_status}"],author_request,@peeraddr)
end
if (!av_error)
if (service == 'shell')
if (cmd)
# if cmd= '' then issue shell settings, otherwise authorize command
if (cmd != '')
author_shell_command(new_body,username,args,author_request)
else
issue_settings(new_body, username, service, author_request)
end
else
msg = "Attribute 'cmd' is required when service is 'shell'."
new_body.status_fail!
new_body.data = msg
@tacacs_daemon.log(:error,['msg_type=Authorization', "message=#{msg}", "status=#{new_body.xlate_status}"],author_request,@peeraddr)
end
else # assume that we need to issue settings for the provided service
issue_settings(new_body, username, service, author_request)
end
end
else
msg = "No arguments were provided with authorization request."
new_body.status_fail!
new_body.data = msg
@tacacs_daemon.log(:error,['msg_type=Authorization', "message=#{msg}", "status=#{new_body.xlate_status}"],author_request,@peeraddr)
end
# finish up
session.reply.header = author_request.header.dup
session.reply.body = new_body
session.terminate = true
return(nil)
end
end # class ClientConnection
end # module TacacsPlus
__END__