demirten-ruby-dbus 0.2.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.rdoc ADDED
@@ -0,0 +1,127 @@
1
+ = Ruby D-Bus -> ffwd
2
+
3
+ Ruby D-Bus provides an implementation of the D-Bus protocol such that the
4
+ D-Bus system can be used in the Ruby programming language.
5
+
6
+ You can switch between REXML and Hpricot as XML parsers!
7
+
8
+ Still a little bumpy with multiple threads, but getting there.
9
+
10
+
11
+ This is my personal dev-fork of mvidners upstream.
12
+
13
+ If you'd like to contrib to this fork, let me know.
14
+
15
+ Most of my work here is actually for another project: http://github.com/pangdudu/robots/tree/master
16
+
17
+ Peace.
18
+
19
+ == Requirements
20
+
21
+ * Ruby 1.8 (>= 1.8.6?)
22
+
23
+ == Installation
24
+
25
+ sudo gem install pangdudu-ruby-dbus --source=http://gems.github.com
26
+
27
+ == Features
28
+
29
+ Ruby D-Bus currently supports the following features:
30
+
31
+ * Connecting to local buses.
32
+ * Accessing remote services, objects and interfaces.
33
+ * Invoking methods on remote objects synchronously and asynchronously.
34
+ * Catch signals on remote objects and handle them via callbacks.
35
+ * Remote object introspection.
36
+ * Walking object trees.
37
+ * Creating services and registering them on the bus.
38
+ * Exporting objects with interfaces on a bus for remote use.
39
+ * Rubyish D-Bus object and interface syntax support that automatically
40
+ allows for introspection.
41
+ * Emitting signals on exported objects.
42
+
43
+ * Connection to a local or remote bus over TCP/IP using 'Dbus::RemoteBus.new(socket_name)'
44
+ * Authentification mechanisms working now: External,CookieSHA1
45
+
46
+ == Usage
47
+
48
+ === Basics:
49
+
50
+ View a tutorial online on http://trac.luon.net/data/ruby-dbus/tutorial/.
51
+
52
+
53
+ === TCP/Remote stuff:
54
+
55
+ Take a look at 'config/remote.session.dbus.conf' and start 'config/start_dbus_session.sh'.
56
+
57
+ You can now try and run one of the tests e.g. 'test/simple_socket_test.rb'.
58
+
59
+
60
+ === Problems you'll have with 'CookieSHA1' when using it remotely:
61
+
62
+ Unless we [including you :)] write a funkier SASL authentification mechanism that
63
+ makes sense when using it over-wire, we're stuck with the cookie auth.
64
+
65
+ Works like this:
66
+
67
+ There is a file in '~/.dbus-keyrings' named 'org_freedesktop_general'.
68
+ It is '-rw-------' so that only you can read it (if you change the permissions,
69
+ dbus-daemon will most likely ignore the file leaving you with 'External', which is
70
+ even worse remotely). It's kind of a secret key you use to auth against yourself
71
+ later on.
72
+
73
+ If you take a look at 'dbus/auth.rb line 40+' you'll see the problem:
74
+
75
+ # Search cookie file for id
76
+ path = File.join(ENV['HOME'], '.dbus-keyrings', context)
77
+
78
+ So if you're starting 'config/start_dbus_session.sh' on one host and 'config/start_dbus_session.sh'
79
+ on another one, you'll need to make sure that the content in '~/.dbus-keyrings/org_freedesktop_general'
80
+ is the same on both machines, in order for the 'CookieSHA1' auth to work.
81
+
82
+ Not cool. I can think of hacks with nfs,smb or fuse:sshfs making it less
83
+ painful to use.
84
+
85
+ The file content also get's updated every 5 minutes (when a client fails to auth etc.).
86
+ Making copy and paste from one shell to another very frolic.
87
+
88
+ === Hackish solution from pangdudu/robots/lib/robots_agent.rb
89
+
90
+ #because dbus remote auth still betrays us, we need to hack it
91
+ def get_remote_cookie
92
+ if @config[:robots_socket_name].include? "tcp:"
93
+ af, port, daemon_name, daemon_addr = (Socket::getaddrinfo(@config[:daemonhost],@config[:daemonhost].to_i)).first
94
+ ilog "Trying to get/hack remote cookie from: #{daemon_name}."
95
+ begin
96
+ cookiepath = "#{ENV['HOME']}/.dbus-keyrings/org_freedesktop_general"
97
+ #oki, the scp magic only works, if you use sshkeys
98
+ `scp #{daemon_addr}:.dbus-keyrings/org_freedesktop_general #{cookiepath}`
99
+ if File.exist? cookiepath
100
+ cookie = File.open(cookiepath)
101
+ dlog "Got cookie: #{cookie.gets}"
102
+ end
103
+ rescue
104
+ elog "Oops, something is wrong."
105
+ end
106
+ else
107
+ dlog "Nothing to hack, boring."
108
+ end
109
+ end
110
+
111
+ in principle, this is ok. we can now do what we want and it is save to exchange cookies over
112
+ a ssh session. However I'm still not feeling comfortable doing this. I guess everything else
113
+ would mean touching the reference C dbus-daemon implementation.
114
+
115
+
116
+ more infos:
117
+ http://dbus.freedesktop.org/doc/dbus-specification.html#auth-mechanisms
118
+ http://lists.freedesktop.org/archives/dbus/2007-June/008067.html
119
+
120
+ Booyaa.
121
+
122
+ == License
123
+
124
+ Ruby D-Bus is free software; you can redistribute it and/or modify it
125
+ under the terms of the GNU Lesser General Public License as published by the
126
+ Free Software Foundation; either version 2.1 of the License, or (at
127
+ your option) any later version.
@@ -0,0 +1,39 @@
1
+ <!-- This configuration file controls the per-user-login-session message bus.
2
+ Add a session-local.conf and edit that rather than changing this
3
+ file directly. -->
4
+
5
+ <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
6
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
7
+ <busconfig>
8
+ <!-- Our well-known bus type, don't change this -->
9
+ <type>session</type>
10
+
11
+ <!-- If we fork, keep the user's original umask to avoid affecting
12
+ the behavior of child processes. -->
13
+ <keep_umask/>
14
+
15
+ <!-- <allow_anonymous/> -->
16
+
17
+ <!-- Listen to everything on tcp! -->
18
+ <listen>tcp:host=0.0.0.0,port=2687,family=ipv4</listen>
19
+ <!-- Listen on socket at this file location -->
20
+ <!-- <listen>unix:path=/tmp/socket_test_session_bus_socket</listen> -->
21
+
22
+ <standard_session_servicedirs />
23
+
24
+ <policy context="default">
25
+ <!-- Allow everything to be sent -->
26
+ <allow send_destination="*" eavesdrop="true"/>
27
+ <!-- Allow everything to be received -->
28
+ <allow eavesdrop="true"/>
29
+ <!-- Allow anyone to own anything -->
30
+ <allow own="*"/>
31
+ </policy>
32
+
33
+ <!-- raise the service start timeout to 40 seconds as it can timeout
34
+ on the live cd on slow machines -->
35
+ <limit name="service_start_timeout">60000</limit>
36
+
37
+ <include if_selinux_enabled="yes" selinux_root_relative="yes">contexts/dbus_contexts</include>
38
+
39
+ </busconfig>
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+ dbus-daemon --print-address --config-file=remote.session.dbus.conf
data/lib/dbus.rb ADDED
@@ -0,0 +1,86 @@
1
+ # dbus.rb - Module containing the low-level D-Bus implementation
2
+ #
3
+ # This file is part of the ruby-dbus project
4
+ # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
5
+ #
6
+ # This library is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU Lesser General Public
8
+ # License, version 2.1 as published by the Free Software Foundation.
9
+ # See the file "COPYING" for the exact licensing terms.
10
+ require 'rubygems'
11
+ require 'rofl' #http://github.com/pangdudu/rofl/tree/master makes the debug/tracing easy
12
+ require 'hpricot' #hope this will speed stuff up a little
13
+ require 'dbus/type'
14
+ require 'dbus/introspect'
15
+ require 'dbus/export'
16
+ require 'dbus/bus'
17
+ require 'dbus/marshall'
18
+ require 'dbus/message'
19
+ require 'dbus/matchrule'
20
+ require 'dbus/auth'
21
+ require 'socket'
22
+ require 'thread'
23
+
24
+ # = D-Bus main module
25
+ #
26
+ # Module containing all the D-Bus modules and classes.
27
+ module DBus
28
+ # Default socket name for the system bus.
29
+ SystemSocketName = "unix:path=/var/run/dbus/system_bus_socket"
30
+
31
+ # Socket name for the session bus, not pretty.
32
+ SessionSocketName = ENV["DBUS_SESSION_BUS_ADDRESS"]
33
+
34
+ # Byte signifying big endianness.
35
+ BIG_END = ?B
36
+ # Byte signifying little endianness.
37
+ LIL_END = ?l
38
+
39
+ # Byte signifying the host's endianness.
40
+ HOST_END = if [0x01020304].pack("L").unpack("V")[0] == 0x01020304
41
+ LIL_END
42
+ else
43
+ BIG_END
44
+ end
45
+
46
+ # General exceptions.
47
+
48
+ # Exception raised when an invalid packet is encountered.
49
+ class InvalidPacketException < Exception
50
+ end
51
+
52
+ # Exception raised when there is a problem with a type (may be unknown or
53
+ # mismatch).
54
+ class TypeException < Exception
55
+ end
56
+
57
+ # Exception raised when an unmarshalled buffer is truncated and
58
+ # incomplete.
59
+ class IncompleteBufferException < Exception
60
+ end
61
+
62
+ # Exception raised when an interface is not implemented.
63
+ class InterfaceNotImplemented < Exception
64
+ end
65
+
66
+ # Exception raised when an method is not found in the interface.
67
+ class MethodNotInInterface < Exception
68
+ end
69
+
70
+ # Exception raised when a method has not been implemented (yet).
71
+ class MethodNotImplemented < Exception
72
+ end
73
+
74
+ # Exception raised when a method is invoked with invalid
75
+ # parameters (wrong number or type).
76
+ class InvalidParameters < Exception
77
+ end
78
+
79
+ # Exception raised when an invalid method name is used.
80
+ class InvalidMethodName < Exception
81
+ end
82
+
83
+ # Exception raised when invalid introspection data is parsed/used.
84
+ class InvalidIntrospectionData < Exception
85
+ end
86
+ end # module DBus
data/lib/dbus/auth.rb ADDED
@@ -0,0 +1,251 @@
1
+ # This file is part of the ruby-dbus project
2
+ # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License, version 2.1 as published by the Free Software Foundation.
7
+ # See the file "COPYING" for the exact licensing terms.
8
+
9
+ $debug = true #it's all over the state machine
10
+
11
+ module DBus
12
+
13
+ # Exception raised when authentication fails somehow.
14
+ class AuthenticationFailed < Exception
15
+ end
16
+
17
+ # = General class for authentication.
18
+ class Authenticator
19
+ # Returns the name of the authenticator.
20
+ def name
21
+ self.class.to_s.upcase.sub(/.*::/, "")
22
+ end
23
+ end
24
+
25
+ # = External authentication class
26
+ #
27
+ # Class for 'external' type authentication.
28
+ class External < Authenticator
29
+ # Performs the authentication.
30
+ def authenticate
31
+ # Take the user id (eg integer 1000) make a string out of it "1000", take
32
+ # each character and determin hex value "1" => 0x31, "0" => 0x30. You
33
+ # obtain for "1000" => 31303030 This is what the server is expecting.
34
+ # Why? I dunno. How did I come to that conclusion? by looking at rbus
35
+ # code. I have no idea how he found that out.
36
+ return Process.uid.to_s.split(//).collect { |a| "%x" % a[0] }.join
37
+ end
38
+ end
39
+
40
+ # = Authentication class using SHA1 crypto algorithm
41
+ #
42
+ # Class for 'CookieSHA1' type authentication.
43
+ # Implements the AUTH DBUS_COOKIE_SHA1 mechanism.
44
+ class DBusCookieSHA1 < Authenticator
45
+
46
+ #the autenticate method (called in stage one of authentification)
47
+ def authenticate
48
+ require 'etc'
49
+ #number of retries we have for auth
50
+ @retries = 1
51
+ return "#{hex_encode(Etc.getlogin)}" #server expects it to be binary
52
+ end
53
+
54
+ #returns the modules name
55
+ def name
56
+ return 'DBUS_COOKIE_SHA1'
57
+ end
58
+
59
+ #handles the interesting crypto stuff, check the rbus-project for more info: http://rbus.rubyforge.org/
60
+ def data(hexdata)
61
+ require 'digest/sha1'
62
+ data = hex_decode(hexdata)
63
+ dlog "context, id, s_challenge: #{data.inspect}"
64
+ # name of cookie file, id of cookie in file, servers random challenge
65
+ context, id, s_challenge = data.split(' ')
66
+ # Random client challenge
67
+ c_challenge = Array.new(s_challenge.length/2).map{|obj|obj=rand(255).to_s}.join
68
+ # Search cookie file for id
69
+ path = File.join(ENV['HOME'], '.dbus-keyrings', context)
70
+ dlog "path: #{path.inspect}" #if $debug
71
+ File.foreach(path) do |line|
72
+ if line.index(id) == 0
73
+ # Right line of file, read cookie
74
+ cookie = line.split(' ')[2].chomp
75
+ dlog "cookie: #{cookie.inspect}" if $debug
76
+ # Concatenate and encrypt
77
+ to_encrypt = [s_challenge, c_challenge, cookie].join(':')
78
+ dlog "s_challenge, c_challenge, cookie: #{to_encrypt.inspect}"
79
+ sha = Digest::SHA1.hexdigest(to_encrypt)
80
+ #the almighty tcp server wants everything hex encoded
81
+ hex_response = hex_encode("#{c_challenge} #{sha}")
82
+ # Return response
83
+ response = [:AuthOk, hex_response]
84
+ return response
85
+ end
86
+ end
87
+ #a little rescue magic
88
+ elog "Could not auth, will now exit." unless @retries <= 0
89
+ elog "Unable to locate cookie, retry in 1 second."
90
+ @retries -= 1
91
+ sleep 1
92
+ data(hexdata)
93
+ end
94
+
95
+ # encode plain to hex
96
+ def hex_encode(plain)
97
+ return nil if plain.nil?
98
+ plain.to_s.unpack('H*')[0]
99
+ end
100
+
101
+ # decode hex to plain
102
+ def hex_decode(encoded)
103
+ encoded.scan(/[[:xdigit:]]{2}/).map{|h|h.hex.chr}.join
104
+ end
105
+ end #DBusCookieSHA1 class ends here
106
+
107
+ # = Authentication client class.
108
+ #
109
+ # Class tha performs the actional authentication.
110
+ class Client
111
+ # Create a new authentication client.
112
+ def initialize(socket)
113
+ @socket = socket
114
+ @state = nil
115
+ @auth_list = [External,DBusCookieSHA1]
116
+ end
117
+
118
+ # Start the authentication process.
119
+ def authenticate
120
+ @socket.write(0.chr)
121
+ next_authenticator
122
+ @state = :Starting
123
+ while @state != :Authenticated
124
+ r = next_state
125
+ return r if not r
126
+ end
127
+ true
128
+ end
129
+
130
+ ##########
131
+ private
132
+ ##########
133
+
134
+ # Send an authentication method _meth_ with arguments _args_ to the
135
+ # server.
136
+ def send(meth, *args)
137
+ o = ([meth] + args).join(" ")
138
+ dlog "#{o.inspect}"
139
+ @socket.write(o + "\r\n")
140
+ end
141
+
142
+ # Try authentication using the next authenticator.
143
+ def next_authenticator
144
+ begin
145
+ raise AuthException if @auth_list.size == 0
146
+ @authenticator = @auth_list.shift.new
147
+ auth_msg = ["AUTH", @authenticator.name, @authenticator.authenticate]
148
+ dlog "auth_msg: #{auth_msg.inspect}" if $debug
149
+ send(auth_msg)
150
+ rescue AuthException
151
+ @socket.close
152
+ raise
153
+ end
154
+ end
155
+
156
+
157
+ # Read data (a buffer) from the bus until CR LF is encountered.
158
+ # Return the buffer without the CR LF characters.
159
+ def next_msg
160
+ data,crlf = "","\r\n"
161
+ left = 1024 #1024 byte, no idea if it's ever getting bigger
162
+ while left > 0
163
+ buf = @socket.read( left > 1 ? 1 : left )
164
+ break if buf.nil?
165
+ left -= buf.size
166
+ data += buf
167
+ break if data.include? crlf #crlf means line finished, the TCP socket keeps on listening, so we break
168
+ end
169
+ readline = data.chomp.split(" ")
170
+ dlog "readline: #{readline.inspect}" if $debug
171
+ return readline
172
+ end
173
+
174
+ # Try to reach the next state based on the current state.
175
+ def next_state
176
+ msg = next_msg
177
+ if @state == :Starting
178
+ dlog ":Starting msg: #{msg[0].inspect}" if $debug
179
+ case msg[0]
180
+ when "OK"
181
+ @state = :WaitingForOk
182
+ when "CONTINUE"
183
+ @state = :WaitingForData
184
+ when "REJECTED" #needed by tcp, unix-path/abstract doesn't get here
185
+ @state = :WaitingForData
186
+ end
187
+ end
188
+ dlog "state: #{@state}" if $debug
189
+ case @state
190
+ when :WaitingForData
191
+ dlog ":WaitingForData msg: #{msg[0].inspect}" if $debug
192
+ case msg[0]
193
+ when "DATA"
194
+ chall = msg[1]
195
+ resp, chall = @authenticator.data(chall)
196
+ dlog ":WaitingForData/DATA resp: #{resp.inspect}" if $debug
197
+ case resp
198
+ when :AuthContinue
199
+ send("DATA", chall)
200
+ @state = :WaitingForData
201
+ when :AuthOk
202
+ send("DATA", chall)
203
+ @state = :WaitingForOk
204
+ when :AuthError
205
+ send("ERROR")
206
+ @state = :WaitingForData
207
+ end
208
+ when "REJECTED"
209
+ next_authenticator
210
+ @state = :WaitingForData
211
+ when "ERROR"
212
+ send("CANCEL")
213
+ @state = :WaitingForReject
214
+ when "OK"
215
+ send("BEGIN")
216
+ @state = :Authenticated
217
+ else
218
+ send("ERROR")
219
+ @state = :WaitingForData
220
+ end
221
+ when :WaitingForOk
222
+ dlog ":WaitingForOk msg: #{msg[0].inspect}" if $debug
223
+ case msg[0]
224
+ when "OK"
225
+ send("BEGIN")
226
+ @state = :Authenticated
227
+ when "REJECT"
228
+ next_authenticator
229
+ @state = :WaitingForData
230
+ when "DATA", "ERROR"
231
+ send("CANCEL")
232
+ @state = :WaitingForReject
233
+ else
234
+ send("ERROR")
235
+ @state = :WaitingForOk
236
+ end
237
+ when :WaitingForReject
238
+ dlog ":WaitingForReject msg: #{msg[0].inspect}" if $debug
239
+ case msg[0]
240
+ when "REJECT"
241
+ next_authenticator
242
+ @state = :WaitingForOk
243
+ else
244
+ @socket.close
245
+ return false
246
+ end
247
+ end
248
+ return true
249
+ end # def next_state
250
+ end # class Client
251
+ end # module D-Bus