0001-Merge-of-feature-521-into-feature-667.-Resolved-conf.patch

Ted Hesselroth, 07/07/2011 04:22 PM

Download (13.4 KB)

View differences:

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
-