0001-Merge-of-feature-521-into-feature-667.-Resolved-conf.patch
src/authm_mad/oneauth | ||
---|---|---|
103 | 103 |
ssh.login(user, time) |
104 | 104 |
exit_with_code 0 |
105 | 105 |
end |
106 |
|
|
107 |
x509_login_desc = <<-EOT.unindent |
|
108 |
Generates an X509-based authenication proxy based on a user certificate. |
|
109 |
oneauth x509_login <username> [<lifetime in seconds>] |
|
110 |
EOT |
|
106 | 111 | |
112 |
command 'x509_login', x509_login_desc, :userid, :lifetime do |
|
113 |
user=args[0] |
|
114 |
time=args[1] |
|
115 |
|
|
116 |
if time |
|
117 |
time=time.to_i |
|
118 |
else |
|
119 |
time=3600 |
|
120 |
end |
|
121 | ||
122 |
x509=X509Auth.new |
|
123 |
x509.login(user, time) |
|
124 |
exit_with_code 0 |
|
125 |
end |
|
126 | ||
127 |
host_login_desc = <<-EOT.unindent |
|
128 |
Generates an X509-based authenication proxy based on a host certificate. |
|
129 |
oneauth host_login [<username>] [<lifetime in seconds>] [<login_file>] |
|
130 |
EOT |
|
131 | ||
132 |
command 'host_login', host_login_desc, :userid, :lifetime :loginfile do |
|
133 |
user=args[0] |
|
134 |
time=args[1] |
|
135 |
login_file=args[2] |
|
136 |
|
|
137 |
if !user |
|
138 |
user='oneadmin' |
|
139 |
end |
|
140 |
|
|
141 |
if time |
|
142 |
time=time.to_i |
|
143 |
else |
|
144 |
time=0 |
|
145 |
end |
|
146 |
|
|
147 |
if !login_file |
|
148 |
login_file='' |
|
149 |
end |
|
150 |
|
|
151 |
x509=X509Auth.new |
|
152 |
x509.host_login(login_file, time, user) |
|
153 |
exit_with_code 0 |
|
154 |
end |
|
155 |
|
|
107 | 156 |
command 'key', 'Gets public key' do |
108 | 157 |
ssh=SshAuth.new |
109 | 158 |
puts ssh.extract_public_key |
110 | 159 |
exit_with_code 0 |
111 | 160 |
end |
112 |
end |
|
161 |
end |
src/scheduler/src/client/Client.cc | ||
---|---|---|
54 | 54 | |
55 | 55 |
if( rc == 0 ) |
56 | 56 |
{ |
57 |
string sha1_pass = SSLTools::sha1_digest(pass); |
|
58 | ||
59 |
one_auth = user + ":" + sha1_pass; |
|
57 |
string plain_tok = "plain:"; |
|
58 |
if(pass.find(plain_tok) == 0) |
|
59 |
{ |
|
60 |
size_t pt_length = plain_tok.length(); |
|
61 |
if(pass.length() == pt_length) |
|
62 |
{ |
|
63 |
throw runtime_error("Empty password for auth token in the form " |
|
64 |
"<username>:plain:<password>"); |
|
65 |
} |
|
66 |
else |
|
67 |
{ |
|
68 |
string plain_pass = pass.substr(pt_length); |
|
69 |
one_auth = user + ":" + plain_pass; |
|
70 |
} |
|
71 |
} |
|
72 |
else |
|
73 |
{ |
|
74 |
string sha1_pass = SSLTools::sha1_digest(pass); |
|
75 |
one_auth = user + ":" + sha1_pass; |
|
76 |
} |
|
60 | 77 |
} |
61 | 78 |
else |
62 | 79 |
{ |
src/sunstone/models/SunstoneServer.rb | ||
---|---|---|
14 | 14 |
# limitations under the License. # |
15 | 15 |
#--------------------------------------------------------------------------- # |
16 | 16 | |
17 |
require 'OpenNebulaJSON' |
|
17 |
ONE_LOCATION = ENV["ONE_LOCATION"] |
|
18 | ||
19 |
if !ONE_LOCATION |
|
20 |
LOG_LOCATION = "/var/log/one" |
|
21 |
VAR_LOCATION = "/var/lib/one" |
|
22 |
RUBY_LIB_LOCATION = "/usr/lib/one/ruby" |
|
23 |
ETC_LOCATION="/etc/one/" |
|
24 |
else |
|
25 |
VAR_LOCATION = ONE_LOCATION+"/var" |
|
26 |
LOG_LOCATION = ONE_LOCATION+"/var" |
|
27 |
RUBY_LIB_LOCATION = ONE_LOCATION+"/lib/ruby" |
|
28 |
ETC_LOCATION=ONE_LOCATION+"/etc/" |
|
29 |
end |
|
30 | ||
31 |
$: << RUBY_LIB_LOCATION |
|
32 |
$: << File.dirname(__FILE__) |
|
33 | ||
34 |
require 'models/OpenNebulaJSON' |
|
18 | 35 |
include OpenNebulaJSON |
36 |
require 'openssl' |
|
37 |
require 'base64' |
|
19 | 38 | |
20 | 39 |
require 'OneMonitorClient' |
21 | 40 | |
... | ... | |
25 | 44 |
@client = Client.new("dummy:dummy") |
26 | 45 |
@client.one_auth = "#{username}:#{password}" |
27 | 46 |
end |
47 |
|
|
48 |
def self.load_config (config=nil) |
|
49 |
if (config == nil) |
|
50 |
default_hostkey = '/etc/grid-security/hostkey.pem' |
|
51 |
|
|
52 |
auth_conf = ETC_LOCATION+'/auth/auth.conf' |
|
53 |
|
|
54 |
if File.readable?(auth_conf) |
|
55 |
config_data=File.read(auth_conf) |
|
56 |
config=YAML::load(config_data) |
|
57 |
if !config[:hostkey] |
|
58 |
config[:hostkey] = default_hostkey |
|
59 |
end |
|
60 |
else |
|
61 |
default_config = ":hostkey: " + default_hostkey |
|
62 |
config=YAML.load(default_config) |
|
63 |
end |
|
64 |
end |
|
65 |
|
|
66 |
return config |
|
67 |
end |
|
28 | 68 | |
29 | 69 |
############################################################################ |
30 | 70 |
# |
31 | 71 |
############################################################################ |
32 |
def self.authorize(user="", sha1_pass="") |
|
33 |
if user.empty? || sha1_pass.empty? |
|
34 |
return [401, false] |
|
35 |
end |
|
72 |
def self.authorize(user="", sha1_pass="", env="") |
|
73 |
failed = 'Authentication failed. ' |
|
36 | 74 | |
37 | 75 |
# TBD get_user_password(name) from CloudServer |
38 | 76 |
user_pool = UserPool.new(Client.new) |
39 | 77 |
rc = user_pool.info |
78 | ||
40 | 79 |
if OpenNebula.is_error?(rc) |
41 | 80 |
return [500, false] |
42 | 81 |
end |
43 | 82 | |
44 |
user_pass = user_pool["USER[NAME=\"#{user}\"]/PASSWORD"] |
|
45 |
user_id = user_pool["USER[NAME=\"#{user}\"]/ID"] |
|
46 |
user_gid = user_pool["USER[NAME=\"#{user}\"]/GID"] |
|
47 |
user_gname = user_pool["USER[NAME=\"#{user}\"]/GNAME"] |
|
83 |
# For https, the web service should be set to include the user cert in the environment. |
|
84 |
cert_line_in = env['HTTP_SSL_CLIENT_CERT'] |
|
85 |
|
|
86 |
if cert_line_in =="" |
|
87 |
# Use the secret key for authentication. |
|
48 | 88 | |
49 |
if user_pass == sha1_pass |
|
50 |
return [204, [user_id, user_gid, user_gname]] |
|
51 |
else |
|
52 |
return [401, nil] |
|
89 |
if user.empty? || sha1_pass.empty? |
|
90 |
return [401, false] |
|
91 |
end |
|
92 | ||
93 |
user_pass = user_pool["USER[NAME=\"#{user}\"]/PASSWORD"] |
|
94 |
user_id = user_pool["USER[NAME=\"#{user}\"]/ID"] |
|
95 |
user_gid = user_pool["USER[NAME=\"#{user}\"]/GID"] |
|
96 |
user_gname = user_pool["USER[NAME=\"#{user}\"]/GNAME"] |
|
97 | ||
98 |
if user_pass == sha1_pass |
|
99 |
return [204, [user_id, user_gid, user_gname]] |
|
100 |
else |
|
101 |
return [401, nil] |
|
102 |
end |
|
103 | ||
104 |
else |
|
105 |
# Use the https credentials for authentication |
|
106 | ||
107 |
# Get the DN from the certificate |
|
108 |
begin |
|
109 |
cert_array=cert_line_in.scan(/([^\s]*)\s/) |
|
110 |
cert_array = cert_array[2..-3] |
|
111 |
cert_array.unshift('-----BEGIN CERTIFICATE-----').push('-----END CERTIFICATE-----') |
|
112 |
user_cert = cert_array.join("\n") |
|
113 |
user_cert = OpenSSL::X509::Certificate.new(user_cert) |
|
114 |
subjectname = user_cert.subject.to_s |
|
115 |
subjectname_nosp = subjectname.gsub(/\s/, '') |
|
116 |
rescue |
|
117 |
return [401, failed + "Could not create X509 certificate from " + user_cert] |
|
118 |
end |
|
119 | ||
120 | ||
121 |
# Check that the DN corresponds to the password of a user |
|
122 |
begin |
|
123 |
username = user_pool["USER[PASSWORD=\"#{subjectname_nosp}\"]/NAME"] |
|
124 |
if (username == nil) |
|
125 |
|
|
126 |
# Check if the DN is part of a |-separted multi-DN password |
|
127 |
user_elts = Array.new |
|
128 |
user_pool.each {|e| user_elts << e['PASSWORD']} |
|
129 |
multiple_users = user_elts.select {|e| e=~ /\|/ } |
|
130 |
matched = nil |
|
131 |
multiple_users.each do |e| |
|
132 |
e.to_s.split('|').each do |w| |
|
133 |
if (w == subjectname_nosp) |
|
134 |
matched=e |
|
135 |
break |
|
136 |
end |
|
137 |
end |
|
138 |
break if matched |
|
139 |
end |
|
140 |
if matched |
|
141 |
password = matched.to_s |
|
142 |
end |
|
143 |
username = user_pool["USER[PASSWORD=\"#{password}\"]/NAME"] |
|
144 |
end |
|
145 |
rescue |
|
146 |
return [401, failed + "User with DN " + subjectname + " not found."] |
|
147 |
end |
|
148 |
|
|
149 |
config = self.load_config |
|
150 |
hostkey_path = config[:hostkey] |
|
151 | ||
152 |
# Sign the message and compose the special login token |
|
153 |
# Get the host private key |
|
154 |
begin |
|
155 |
host_cert = File.read(hostkey_path) |
|
156 |
rescue |
|
157 |
return [401, failed + "Could not read " + hostkey_path] |
|
158 |
end |
|
159 |
|
|
160 |
begin |
|
161 |
host_cert_array=host_cert.split("\n") |
|
162 |
begin_lines=host_cert_array.select{|l| l.match(/BEGIN RSA PRIVATE KEY/)} |
|
163 |
begin_index=host_cert_array.index(begin_lines[0]) |
|
164 |
begin_line=host_cert_array[begin_index].to_s |
|
165 | ||
166 |
end_lines=host_cert_array.select{|l| l.match(/END RSA PRIVATE KEY/)} |
|
167 |
end_index=host_cert_array.index(end_lines[0]) |
|
168 |
end_line=host_cert_array[end_index].to_s |
|
169 | ||
170 |
host_key_array=host_cert_array[begin_index..end_index] |
|
171 |
private_key=host_key_array.join("\n") |
|
172 |
rescue |
|
173 |
return [401, failed + "Could not get private key from " + hostkey_path] |
|
174 |
end |
|
175 | ||
176 |
begin |
|
177 |
rsa=OpenSSL::PKey::RSA.new(private_key) |
|
178 |
rescue |
|
179 |
return [401, failed + "Could not create RSA key from " + hostkey_path] |
|
180 |
end |
|
181 | ||
182 |
# Sign with timestamp |
|
183 |
time=Time.now.to_i+7*24*3600 |
|
184 |
text_to_sign="#{username}:#{subjectname}:#{time}" |
|
185 |
begin |
|
186 |
special_token=Base64::encode64(rsa.private_encrypt(text_to_sign)).gsub!(/\n/, '').strip |
|
187 |
rescue |
|
188 |
return [401, failed + "Could not create host-signed token for " + subjectname] |
|
189 |
end |
|
190 |
|
|
191 |
user_id = user_pool["USER[NAME=\"#{user}\"]/ID"] |
|
192 |
user_gid = user_pool["USER[NAME=\"#{user}\"]/GID"] |
|
193 |
user_gname = user_pool["USER[NAME=\"#{user}\"]/GNAME"] |
|
194 | ||
195 |
return [204, [user_id, user_gid, user_gname, "#{username}", "host-signed:#{special_token}}"]] |
|
53 | 196 |
end |
54 |
end |
|
197 | ||
198 |
end |
|
55 | 199 | |
56 | 200 |
############################################################################ |
57 | 201 |
# |
src/sunstone/sunstone-server.rb | ||
---|---|---|
69 | 69 |
end |
70 | 70 | |
71 | 71 |
def build_session |
72 | ||
72 | 73 |
auth = Rack::Auth::Basic::Request.new(request.env) |
74 | ||
73 | 75 |
if auth.provided? && auth.basic? && auth.credentials |
74 | 76 |
user = auth.credentials[0] |
75 | 77 |
sha1_pass = Digest::SHA1.hexdigest(auth.credentials[1]) |
76 | 78 | |
77 |
rc = SunstoneServer.authorize(user, sha1_pass) |
|
79 |
rc = SunstoneServer.authorize(user, sha1_pass, request.env) |
|
80 | ||
78 | 81 |
if rc[1] |
79 |
session[:user] = user |
|
80 | 82 |
session[:user_id] = rc[1][0] |
81 | 83 |
session[:user_gid] = rc[1][1] |
82 | 84 |
session[:user_gname] = rc[1][2] |
83 |
session[:password] = sha1_pass |
|
85 |
if rc[1][3] |
|
86 |
session[:user] = rc[1][3] |
|
87 |
else |
|
88 |
session[:user] = user |
|
89 |
end |
|
90 |
if rc[1][4] |
|
91 |
session[:password] = rc[1][4] |
|
92 |
else |
|
93 |
session[:password] = sha1_pass |
|
94 |
end |
|
84 | 95 |
session[:ip] = request.ip |
85 | 96 |
session[:remember] = params[:remember] |
86 | 97 | |
... | ... | |
127 | 138 |
# HTML Requests |
128 | 139 |
############################################################################## |
129 | 140 |
get '/' do |
130 |
return File.read(File.dirname(__FILE__)+ |
|
131 |
'/templates/login.html') unless authorized? |
|
132 | ||
133 |
time = Time.now + 60 |
|
134 |
response.set_cookie("one-user", |
|
141 |
if authorized? |
|
142 |
time = Time.now + 60 |
|
143 |
response.set_cookie("one-user", |
|
135 | 144 |
:value=>"#{session[:user]}", |
136 | 145 |
:expires=>time) |
137 |
response.set_cookie("one-user_id", |
|
146 |
response.set_cookie("one-user_id",
|
|
138 | 147 |
:value=>"#{session[:user_id]}", |
139 | 148 |
:expires=>time) |
140 | 149 | |
141 |
p = SunstonePlugins.new |
|
142 |
@plugins = p.authorized_plugins(session[:user], session[:user_gname]) |
|
150 |
p = SunstonePlugins.new
|
|
151 |
@plugins = p.authorized_plugins(session[:user], session[:user_gname])
|
|
143 | 152 | |
144 |
erb :index |
|
153 |
erb :index |
|
154 |
else |
|
155 |
cert_line_in = env['HTTP_SSL_CLIENT_CERT'] |
|
156 |
if cert_line_in =="" |
|
157 |
redirect '/login' |
|
158 |
else |
|
159 |
encoded_login = ["dummy:dummy"].pack("m*") |
|
160 |
env['HTTP_AUTHORIZATION'] = "Basic #{encoded_login}" |
|
161 |
rc = build_session |
|
162 |
if rc[0] == 204 |
|
163 |
redirect "https://#{request.host}:#{request.port}/" |
|
164 |
else |
|
165 |
redirect "https://#{request.host}:#{request.port}/login" |
|
166 |
end |
|
167 |
end |
|
168 |
end |
|
145 | 169 |
end |
146 | 170 | |
147 | 171 |
get '/login' do |
148 |
- |