Browse Source

New JWT token impl that does not require token verification in Jicofo and uses anonymous authentication method(token goes as BOSH query param). Adds 'allow_empty_token" config option.

master
paweldomas 9 years ago
parent
commit
9d60af1a9d

+ 1
- 1
debian/control View File

35
 
35
 
36
 Package: jitsi-meet-tokens
36
 Package: jitsi-meet-tokens
37
 Architecture: all
37
 Architecture: all
38
-Depends: ${misc:Depends}, prosody | prosody-trunk, jitsi-meet-prosody
38
+Depends: ${misc:Depends}, prosody-trunk (>= 1nightly603), libssl-dev, luarocks, jitsi-meet-prosody
39
 Description: Prosody token authentication plugin for Jitsi Meet
39
 Description: Prosody token authentication plugin for Jitsi Meet
40
 
40
 

+ 11
- 13
debian/jitsi-meet-tokens.postinst View File

62
                 sed -i 's/--plugin_paths/plugin_paths/g' $PROSODY_HOST_CONFIG
62
                 sed -i 's/--plugin_paths/plugin_paths/g' $PROSODY_HOST_CONFIG
63
                 sed -i 's/authentication = "anonymous"/authentication = "token"/g' $PROSODY_HOST_CONFIG
63
                 sed -i 's/authentication = "anonymous"/authentication = "token"/g' $PROSODY_HOST_CONFIG
64
                 sed -i 's/ --allow_unencrypted_plain_auth/ allow_unencrypted_plain_auth/g' $PROSODY_HOST_CONFIG
64
                 sed -i 's/ --allow_unencrypted_plain_auth/ allow_unencrypted_plain_auth/g' $PROSODY_HOST_CONFIG
65
-                sed -i "s/ --app_id=example_app_id/ app_id=$APP_ID/g" $PROSODY_HOST_CONFIG
66
-                sed -i "s/ --app_secret=example_app_secret/ app_secret=$APP_SECRET/g" $PROSODY_HOST_CONFIG
65
+                sed -i "s/ --app_id=\"example_app_id\"/ app_id=\"$APP_ID\"/g" $PROSODY_HOST_CONFIG
66
+                sed -i "s/ --app_secret=\"example_app_secret\"/ app_secret=\"$APP_SECRET\"/g" $PROSODY_HOST_CONFIG
67
                 sed -i 's/ --modules_enabled = { "token_verification" }/ modules_enabled = { "token_verification" }/g' $PROSODY_HOST_CONFIG
67
                 sed -i 's/ --modules_enabled = { "token_verification" }/ modules_enabled = { "token_verification" }/g' $PROSODY_HOST_CONFIG
68
 
68
 
69
-                # Configure Jicofo properties
70
-                JICOFO_CONFIG="/etc/jitsi/jicofo/sip-communicator.properties"
71
-                if ! grep -q "org.jitsi.jicofo.auth.jwt.APP_ID" $JICOFO_CONFIG && \
72
-                   ! grep -q "org.jitsi.jicofo.auth.jwt.APP_SECRET" $JICOFO_CONFIG; then
73
-                   echo "org.jitsi.jicofo.auth.jwt.APP_ID=$APP_ID" >> $JICOFO_CONFIG
74
-                   echo "org.jitsi.jicofo.auth.jwt.APP_SECRET=$APP_SECRET" >> $JICOFO_CONFIG
75
-                   # Restart Jicofo
76
-                   if [ -x "/etc/init.d/jicofo" ]; then
77
-                       invoke-rc.d jicofo restart
78
-                   fi
69
+                # Install luajwt
70
+                if ! luarocks install luajwt; then
71
+                   echo "Failed to install luajwt - try installing it manually"
79
                 fi
72
                 fi
80
 
73
 
81
                 if [ -x "/etc/init.d/prosody" ]; then
74
                 if [ -x "/etc/init.d/prosody" ]; then
82
-                    invoke-rc.d prosody reload
75
+                    invoke-rc.d prosody restart
83
                 fi
76
                 fi
77
+
78
+                echo "This package requires BOSH Prosody module to be patched !"
79
+                echo "Use the following command, after this package has been installed and"
80
+                echo "after every prosody-trunk upgrade:"
81
+                echo "sudo patch -N /usr/lib/prosody/modules/mod_bosh.lua /usr/share/jitsi-meet/prosody-plugins/mod_bosh.lua.patch"
84
             else
82
             else
85
                 echo "Failed apply auto-config to $PROSODY_HOST_CONFIG which most likely comes from not supported version of jitsi-meet"
83
                 echo "Failed apply auto-config to $PROSODY_HOST_CONFIG which most likely comes from not supported version of jitsi-meet"
86
             fi
84
             fi

+ 3
- 18
debian/jitsi-meet-tokens.postrm View File

39
             # Revert prosody config
39
             # Revert prosody config
40
             sed -i 's/plugin_paths/--plugin_paths/g' $PROSODY_HOST_CONFIG
40
             sed -i 's/plugin_paths/--plugin_paths/g' $PROSODY_HOST_CONFIG
41
             sed -i 's/authentication = "token"/authentication = "anonymous"/g' $PROSODY_HOST_CONFIG
41
             sed -i 's/authentication = "token"/authentication = "anonymous"/g' $PROSODY_HOST_CONFIG
42
-            sed -i 's/ allow_unencrypted_plain_auth/ --allow_unencrypted_plain_auth/g' $PROSODY_HOST_CONFIG
43
-            sed -i "s/ app_id=$APP_ID/ --app_id=example_app_id/g" $PROSODY_HOST_CONFIG
44
-            sed -i "s/ app_secret=$APP_SECRET/ --app_secret=example_app_secret/g" $PROSODY_HOST_CONFIG
42
+            sed -i "s/ app_id=\"$APP_ID\"/ --app_id=\"example_app_id\"/g" $PROSODY_HOST_CONFIG
43
+            sed -i "s/ app_secret=\"$APP_SECRET\"/ --app_secret=\"example_app_secret\"/g" $PROSODY_HOST_CONFIG
45
             sed -i 's/ modules_enabled = { "token_verification" }/ --modules_enabled = { "token_verification" }/g' $PROSODY_HOST_CONFIG
44
             sed -i 's/ modules_enabled = { "token_verification" }/ --modules_enabled = { "token_verification" }/g' $PROSODY_HOST_CONFIG
46
 
45
 
47
-            JICOFO_CONFIG="/etc/jitsi/jicofo/sip-communicator.properties"
48
-            if [ -f "$JICOFO_CONFIG" ] ; then
49
-                if grep -q "org.jitsi.jicofo.auth.jwt.APP_ID" $JICOFO_CONFIG || \
50
-                   grep -q "org.jitsi.jicofo.auth.jwt.APP_SECRET" $JICOFO_CONFIG; then
51
-                   sed -i.old "/org.jitsi.jicofo.auth.jwt.APP_ID=$APP_ID/d" $JICOFO_CONFIG
52
-                   sed -i.old "/org.jitsi.jicofo.auth.jwt.APP_SECRET=$APP_SECRET/d" $JICOFO_CONFIG
53
-                   rm "$JICOFO_CONFIG.old"
54
-                   # Restart Jicofo
55
-                   if [ -x "/etc/init.d/jicofo" ]; then
56
-                       invoke-rc.d jicofo restart
57
-                   fi
58
-                fi
59
-            fi
60
-
61
             if [ -x "/etc/init.d/prosody" ]; then
46
             if [ -x "/etc/init.d/prosody" ]; then
62
-                invoke-rc.d prosody reload
47
+                invoke-rc.d prosody restart
63
             fi
48
             fi
64
         fi
49
         fi
65
 
50
 

+ 3
- 4
doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example View File

4
 VirtualHost "jitmeet.example.com"
4
 VirtualHost "jitmeet.example.com"
5
         -- enabled = false -- Remove this line to enable this host
5
         -- enabled = false -- Remove this line to enable this host
6
         authentication = "anonymous"
6
         authentication = "anonymous"
7
-        -- Three properties below get uncommented by jitsi-meet-tokens package config
7
+        -- Properties below are modified by jitsi-meet-tokens package config
8
         -- and authentication above is switched to "token"
8
         -- and authentication above is switched to "token"
9
-        --allow_unencrypted_plain_auth = true;
10
-        --app_id=example_app_id
11
-        --app_secret=example_app_secret
9
+        --app_id="example_app_id"
10
+        --app_secret="example_app_secret"
12
         -- Assign this host a certificate for TLS, otherwise it would use the one
11
         -- Assign this host a certificate for TLS, otherwise it would use the one
13
         -- set in the global section (if any).
12
         -- set in the global section (if any).
14
         -- Note that old-style SSL on port 5223 only supports one certificate, and will always
13
         -- Note that old-style SSL on port 5223 only supports one certificate, and will always

+ 81
- 34
prosody-plugins/mod_auth_token.lua View File

1
 -- Token authentication
1
 -- Token authentication
2
 -- Copyright (C) 2015 Atlassian
2
 -- Copyright (C) 2015 Atlassian
3
 
3
 
4
-local usermanager = require "core.usermanager";
4
+local generate_uuid = require "util.uuid".generate;
5
 local new_sasl = require "util.sasl".new;
5
 local new_sasl = require "util.sasl".new;
6
-
7
-local log = module._log;
8
-local host = module.host;
9
-
6
+local sasl = require "util.sasl";
7
+local formdecode = require "util.http".formdecode;
10
 local token_util = module:require "token/util";
8
 local token_util = module:require "token/util";
11
 
9
 
12
 -- define auth provider
10
 -- define auth provider
13
 local provider = {};
11
 local provider = {};
14
 
12
 
15
---do
16
---	local list;
17
---	for mechanism in pairs(new_sasl(module.host):mechanisms()) do
18
---		list = (not(list) and mechanism) or (list..", "..mechanism);
19
---	end
20
---	if not list then
21
---		module:log("error", "No mechanisms");
22
---	else
23
---		module:log("error", "Mechanisms: %s", list);
24
---	end
25
---end
26
-
13
+local host = module.host;
27
 
14
 
28
 local appId = module:get_option_string("app_id");
15
 local appId = module:get_option_string("app_id");
29
 local appSecret = module:get_option_string("app_secret");
16
 local appSecret = module:get_option_string("app_secret");
17
+local allowEmptyToken = module:get_option_boolean("allow_empty_token");
30
 
18
 
31
-function provider.test_password(username, password)
32
-	local result, msg = token_util.verify_password(password, appId, appSecret, nil);
33
-	if result == true then
34
-		return true;
35
-	else
36
-		log("error", "Token auth failed for user %s, reason: %s",username, msg);
37
-		return nil, msg;
19
+if allowEmptyToken == true then
20
+	module:log("warn", "WARNING - empty tokens allowed");
21
+end
22
+
23
+if appId == nil then
24
+	module:log("error", "'app_id' must not be empty");
25
+	return;
26
+end
27
+
28
+if appSecret == nil then
29
+	module:log("error", "'app_secret' must not be empty");
30
+	return;
31
+end
32
+
33
+-- Extract 'token' param from BOSH URL when session is created
34
+module:hook("bosh-session", function(event)
35
+	local session, request = event.session, event.request;
36
+	local query = request.url.query;
37
+	if query ~= nil then
38
+		session.auth_token = query and formdecode(query).token or nil;
38
 	end
39
 	end
40
+end)
41
+
42
+function provider.test_password(username, password)
43
+	return nil, "Password based auth not supported";
39
 end
44
 end
40
 
45
 
41
 function provider.get_password(username)
46
 function provider.get_password(username)
50
 	return nil;
55
 	return nil;
51
 end
56
 end
52
 
57
 
53
-function provider.users()
54
-	return next, hosts[module.host].sessions, nil;
55
-end
56
-
57
 function provider.create_user(username, password)
58
 function provider.create_user(username, password)
58
 	return nil;
59
 	return nil;
59
 end
60
 end
62
 	return nil;
63
 	return nil;
63
 end
64
 end
64
 
65
 
65
-function provider.get_sasl_handler()
66
-	local testpass_authentication_profile = {
67
-		plain_test = function(sasl, username, password, realm)
68
-			return usermanager.test_password(username, realm, password), true;
66
+function provider.get_sasl_handler(session)
67
+	-- JWT token extracted from BOSH URL
68
+	local token = session.auth_token;
69
+
70
+	local function get_username_from_token(self, message)
71
+
72
+		if token == nil then
73
+			if allowEmptyToken == true then
74
+				return true;
75
+			else
76
+				return false, "not-allowed", "token required";
77
+			end
78
+		end
79
+
80
+		-- here we check if 'room' claim exists
81
+		local room, roomErr = token_util.get_room_name(token, appSecret);
82
+		if room == nil then
83
+			return false, "not-allowed", roomErr;
84
+		end
85
+
86
+		-- now verify the whole token
87
+		local result, msg
88
+		= token_util.verify_token(token, appId, appSecret, room);
89
+		if result == true then
90
+			-- Binds room name to the session which is later checked on MUC join
91
+			session.jitsi_meet_room = room;
92
+			return true
93
+		else
94
+			return false, "not-allowed", msg
69
 		end
95
 		end
70
-	};
71
-	return new_sasl(host, testpass_authentication_profile);
96
+	end
97
+
98
+	return new_sasl(host, { anonymous = get_username_from_token });
72
 end
99
 end
73
 
100
 
74
 module:provides("auth", provider);
101
 module:provides("auth", provider);
102
+
103
+local function anonymous(self, message)
104
+
105
+	local username = generate_uuid();
106
+
107
+	-- This calls the handler created in 'provider.get_sasl_handler(session)'
108
+	local result, err, msg = self.profile.anonymous(self, username, self.realm);
109
+
110
+	self.username = username;
111
+
112
+	if result == true then
113
+		return "success"
114
+	else
115
+
116
+		return "failure", err, msg
117
+	end
118
+end
119
+
120
+sasl.registerMechanism("ANONYMOUS", {"anonymous"}, anonymous);
121
+

+ 12
- 0
prosody-plugins/mod_bosh.lua.patch View File

1
+--- /usr/lib/prosody/modules/mod_bosh.lua	2015-12-16 14:28:34.000000000 -0600
2
++++ /usr/lib/prosody/modules/mod_bosh.lua	2015-12-22 10:45:59.818197967 -0600
3
+@@ -294,6 +294,9 @@
4
+ 
5
+ 		session.log("debug", "BOSH session created for request from %s", session.ip);
6
+ 		log("info", "New BOSH session, assigned it sid '%s'", sid);
7
++		
8
++		hosts[session.host].events.fire_event(
9
++			"bosh-session", { session = session, request = request });
10
+ 
11
+ 		-- Send creation response
12
+ 		local creating_session = true;

+ 41
- 18
prosody-plugins/mod_token_verification.lua View File

4
 local log = module._log;
4
 local log = module._log;
5
 local host = module.host;
5
 local host = module.host;
6
 local st = require "util.stanza";
6
 local st = require "util.stanza";
7
-local token_util = module:require("token/util");
8
 local is_admin = require "core.usermanager".is_admin;
7
 local is_admin = require "core.usermanager".is_admin;
9
 
8
 
10
 
9
 
16
 
15
 
17
 local parentCtx = module:context(parentHostName);
16
 local parentCtx = module:context(parentHostName);
18
 if parentCtx == nil then
17
 if parentCtx == nil then
19
-	log("error", "Failed to start - unable to get parent context for host: %s", tostring(parentHostName));
18
+	log("error",
19
+		"Failed to start - unable to get parent context for host: %s",
20
+		tostring(parentHostName));
20
 	return;
21
 	return;
21
 end
22
 end
22
 
23
 
23
 local appId = parentCtx:get_option_string("app_id");
24
 local appId = parentCtx:get_option_string("app_id");
24
 local appSecret = parentCtx:get_option_string("app_secret");
25
 local appSecret = parentCtx:get_option_string("app_secret");
26
+local allowEmptyToken = parentCtx:get_option_boolean("allow_empty_token");
25
 
27
 
26
-log("debug", "%s - starting MUC token verifier app_id: %s app_secret: %s",
27
-	tostring(host), tostring(appId), tostring(appSecret));
28
+log("debug",
29
+	"%s - starting MUC token verifier app_id: %s app_secret: %s allow empty: %s",
30
+	tostring(host), tostring(appId), tostring(appSecret),
31
+	tostring(allowEmptyToken));
28
 
32
 
29
-local function handle_pre_create(event)
33
+local function verify_user(session, stanza)
34
+	log("debug", "Session token: %s, session room: %s",
35
+		tostring(session.auth_token),
36
+		tostring(session.jitsi_meet_room));
30
 
37
 
31
-	local origin, stanza = event.origin, event.stanza;	
32
-	local token = stanza:get_child("token", "http://jitsi.org/jitmeet/auth-token");
38
+	if allowEmptyToken and session.auth_token == nil then
39
+		module:log(
40
+			"debug",
41
+			"Skipped room token verification - empty tokens are allowed");
42
+		return nil;
43
+	end
33
 
44
 
34
 	-- token not required for admin users
45
 	-- token not required for admin users
35
-	local user_jid = stanza.attr.from;	
46
+	local user_jid = stanza.attr.from;
36
 	if is_admin(user_jid) then
47
 	if is_admin(user_jid) then
37
 		log("debug", "Token not required from admin user: %s", user_jid);
48
 		log("debug", "Token not required from admin user: %s", user_jid);
38
 		return nil;
49
 		return nil;
41
 	local room = string.match(stanza.attr.to, "^(%w+)@");
52
 	local room = string.match(stanza.attr.to, "^(%w+)@");
42
 	log("debug", "Will verify token for user: %s, room: %s ", user_jid, room);
53
 	log("debug", "Will verify token for user: %s, room: %s ", user_jid, room);
43
 	if room == nil then
54
 	if room == nil then
44
-		log("error", "Unable to get name of the MUC room ? to: %s", stanza.attr.to);
55
+		log("error",
56
+			"Unable to get name of the MUC room ? to: %s", stanza.attr.to);
45
 		return nil;
57
 		return nil;
46
 	end
58
 	end
47
 
59
 
48
-	if token ~= nil then
49
-		token = token[1];
50
-	end
51
-
52
-	local result, msg = token_util.verify_password(token, appId, appSecret, room);
53
-	if result ~= true then
54
-		log("debug", "Token verification failed: %s", msg);
55
-		origin.send(st.error_reply(stanza, "cancel", "not-allowed", msg));
60
+	local token = session.auth_token;
61
+	local auth_room = session.jitsi_meet_room;
62
+	if room ~= auth_room then
63
+		log("error", "Token %s not allowed to join: %s",
64
+			tostring(token), tostring(auth_room));
65
+		session.send(
66
+			st.error_reply(
67
+				stanza, "cancel", "not-allowed", "Room and token mismatched"));
56
 		return true;
68
 		return true;
57
 	end
69
 	end
70
+	log("debug", "allowed: %s to enter/create room: %s", user_jid, room);
58
 end
71
 end
59
 
72
 
60
-module:hook("muc-room-pre-create", handle_pre_create);
73
+module:hook("muc-room-pre-create", function(event)
74
+	local origin, stanza = event.origin, event.stanza;
75
+	log("debug", "pre create: %s %s", tostring(origin), tostring(stanza));
76
+	return verify_user(origin, stanza);
77
+end);
78
+
79
+module:hook("muc-occupant-pre-join", function(event)
80
+	local origin, room, stanza = event.origin, event.room, event.stanza;
81
+	log("debug", "pre join: %s %s", tostring(room), tostring(stanza));
82
+	return verify_user(origin, stanza);
83
+end);
61
 
84
 

+ 19
- 6
prosody-plugins/token/util.lib.lua View File

5
 
5
 
6
 local _M = {};
6
 local _M = {};
7
 
7
 
8
-local function verify_password_impl(password, appId, appSecret, roomName)
8
+local function _get_room_name(token, appSecret)
9
+	local claims, err = jwt.decode(token, appSecret);
10
+	if claims ~= nil then
11
+		return claims["room"];
12
+	else
13
+		return nil, err;
14
+	end
15
+end
16
+
17
+local function _verify_token(token, appId, appSecret, roomName)
9
 
18
 
10
-	local claims, err = jwt.decode(password, appSecret, true);
19
+	local claims, err = jwt.decode(token, appSecret, true);
11
 	if claims == nil then
20
 	if claims == nil then
12
 		return nil, err;
21
 		return nil, err;
13
 	end
22
 	end
27
 	if roomName ~= nil and roomName ~= roomClaim then
36
 	if roomName ~= nil and roomName ~= roomClaim then
28
 		return nil, "Invalid room name('room' claim)";
37
 		return nil, "Invalid room name('room' claim)";
29
 	end
38
 	end
30
-	
39
+
31
 	return true;
40
 	return true;
32
 end
41
 end
33
 
42
 
34
-function _M.verify_password(password, appId, appSecret, roomName)
35
-	return verify_password_impl(password, appId, appSecret, roomName);
43
+function _M.verify_token(token, appId, appSecret, roomName)
44
+	return _verify_token(token, appId, appSecret, roomName);
45
+end
46
+
47
+function _M.get_room_name(token, appSecret)
48
+	return _get_room_name(token, appSecret);
36
 end
49
 end
37
 
50
 
38
-return _M;
51
+return _M;

Loading…
Cancel
Save