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 |
- |
|