⬆️ ⬇️

YATE as a jabber server





YATE is in many ways unique phone server. He understands SIP-T, is considered the best H323-SIP converter, and also supports most of the SS-7 protocol family (SS7). And all this is available under the GPL. On the other hand, the problem is the lack of documentation of the project.



But I want to talk not about telephony. Rabbits are not only valuable fur, but Yate can also serve as a jabber server. It's funny that Yate is not listed in the xmpp.org/xmpp-software/servers list, although support for the jabber server appeared in it back in 2010.

')

It’s hard to say why you might need to use Yate for a jabber if there is ejabberd, Openfire, Prosody and Tigase. This question is beyond the scope of the article. I just want to introduce you to another option.



So, welcome under cut. (Be careful, there are a lot of letters!) At the same time I will tell you how to fasten authorization in Active Directory.



We will proceed from the fact that the reader will cope with the installation independently . Let's do the setup.



Specify the main parameters of the jabber server: the domain that listens to the sockets, cancel the digest authentication (necessary to transfer the password in its original form for authorization in AD).

Attention! The c2s_plainauthonly option is only available in svn, it is not in version 4.3.


jabberserver.conf

[general] domains=mydomain.org c2s_plainauthonly=yes; force text password for LDAP auth [listener s2s] enable=yes type=s2s port=5269 [listener c2s] enable=yes type=c2s port=5222 




In order not to transmit our password in the clear, you need to enable SSL encryption. For this we will indicate in the settings for which domains you should use the ssl certificate. It should be noted that you should not include the sslcontext = option, which you can see in the default config, as in this case Yate will expect an initially encrypted connection from the client, while jabber clients usually use the two-stage StartTLS procedure.



openssl.conf

 [yate] certificate=yate.pem domains=mydomain.org 


How to generate a certificate, I will not tell.



We configure the module of authorization and registration to the database.



register.conf

 [general] user.auth=yes user.register=yes user.unregister=yes engine.timer=yes [default] account=yate [user.auth] query=SELECT password FROM users WHERE username='${username}' AND password IS NOT NULL AND password<>'' result=password [user.register] query=UPDATE users SET location='${data}', expires=CURRENT_TIMESTAMP + INTERVAL ${expires}+300 second WHERE username='${username}' [user.unregister] query=UPDATE users SET location=NULL,expires=NULL WHERE expires IS NOT NULL AND username='${username}' [engine.timer] query=UPDATE users SET location=NULL,expires=NULL WHERE expires IS NOT NULL AND expires<=CURRENT_TIMESTAMP 




Database structure for MySQL. Postgres can also be used.



 CREATE TABLE `offlinechat` ( `username` varchar(100) DEFAULT NULL, `xml` text, `time` int(11) NOT NULL, KEY `username` (`username`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; CREATE TABLE `roster` ( `username` varchar(100) DEFAULT NULL, `contact` varchar(100) DEFAULT NULL, `name` varchar(100) DEFAULT NULL, `groups` varchar(100) DEFAULT NULL, `subscription` varchar(100) DEFAULT NULL, UNIQUE KEY `uc` (`username`,`contact`), KEY `username` (`username`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; CREATE TABLE `users` ( `username` varchar(100) NOT NULL DEFAULT '', `password` varchar(100) DEFAULT NULL, `vcard` text, `location` varchar(100) DEFAULT NULL, `expires` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`username`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 




Configuring the connector to the database



mysqldb.conf

 [yate] database=yate user=yate password=yatepass 




We configure the vcard function and offline messages.



jbfeatures.conf

 [general] account=yate [vcard] get=SELECT vcard FROM users WHERE username='${username}' set=UPDATE users SET vcard='${vcard}' WHERE username='${username}' [offline_chat] get=SELECT * FROM offlinechat WHERE username='${username}' ORDER BY time add=INSERT INTO offlinechat (username,xml,time) VALUES ('${username}', '${xml}', ${time}) clear_user=DELETE FROM offlinechat WHERE username='${username}' 




Configure the roster.



subscription.conf

 [general] account=yate user_roster_load=SELECT users.username, roster.* FROM users LEFT OUTER JOIN roster ON users.username=roster.username WHERE users.username='${username}' user_roster_delete=DELETE FROM roster WHERE username='${username}' contact_load=SELECT * FROM roster WHERE username='${username}' AND contact='${contact}' contact_subscription_set=INSERT roster (username,contact,subscription) VALUES ('${username}','${contact}','${subscription}') ON DUPLICATE KEY UPDATE subscription='${subscription}' contact_set=INSERT roster (username,contact,name,groups) VALUES ('${username}','${contact}','${name}','${groups}') ON DUPLICATE KEY UPDATE name='${name}',groups='${groups}' contact_set_full=INSERT roster (username,contact,name,groups,subscription) VALUES ('${username}','${contact}','${name}','${groups}','${subscription}') ON DUPLICATE KEY UPDATE name='${name}',groups='${groups}',subscription='${subscription}' contact_delete=DELETE FROM roster WHERE username='${username}' AND contact='${contact}' 


As you can see, most of the setup comes down to prescribing SQL queries. It may seem inconvenient, but it gives flexibility.



Now about authorization through Active Directory.

First, we will correct register.conf so that users are automatically added to the database after successful authorization (and this is where the flexibility came in handy!).

 [user.register] query=INSERT users (username,location,expires) VALUES ('${username}','${data}',CURRENT_TIMESTAMP + INTERVAL ${expires}+300 second) ON DUPLICATE KEY UPDATE location='${data}', expires=CURRENT_TIMESTAMP + INTERVAL ${expires}+300 second 


Secondly, add some Yate magic. The server core is a system message manager that the Engine exchanges with standard and external modules (there are no fundamental differences). To run the external module in PHP, it is enough to register it in the config. Our jabber.php script will intercept the system user.auth message (authorization request), and process it by referring to AD. The message is intercepted due to the priority of the handler = 40, while the other modules have a priority of 50 or higher, which is less priority.

extmodule.conf

 [general] scripts_dir=/etc/yate/ [scripts] jabber.php= 




And finally, the script itself (module) authorization. Please note that it uses the “libyate.php” library included in the Yate distribution. It should be available for the script, so it’s best to copy it to the same directory. If the domain controller uses a self-signed certificate, then a line should be added to /etc/openldap/ldap.conf

 TLS_REQCERT never 




jabber.php

 #!/usr/bin/php -q <?php require_once("libyate.php"); $ad_host = 'ldaps://dc.mydomain.org'; $ad_domain = 'mydomain.org'; /* Always the first action to do */ Yate::Init(); /* Install a handler for the call routing message */ Yate::Install("user.auth",40); function ad_auth($user, $password) { global $ad_host, $ad_domain; $con = ldap_connect($ad_host); return ldap_bind($con, "$user@$ad_domain", $password) & true; } /* The main loop. We pick events and handle them */ for (;;) { $ev=Yate::GetEvent(); /* If Yate disconnected us then exit cleanly */ if ($ev === false) break; /* Empty events are normal in non-blocking operation. This is an opportunity to do idle tasks and check timers */ if ($ev === true) { // Yate::Output("PHP event: empty"); continue; } /* If we reached here we should have a valid object */ switch ($ev->type) { case "incoming": switch ($ev->name) { case "user.auth": if (!isset($ev->params["digest-uri"])) { $username = $ev->params["username"]; $username = substr($username,0,strpos($username,'@')); $password = isset($ev->params["response"]) ? $ev->params["response"] : $ev->params["password"]; $auth = ad_auth($username, $password); if ($auth) { $ev->retval = $password; $ev->handled = true; } } break; } $ev->Acknowledge(); break; case "installed": Yate::Output("PHP Installed: " . $ev->name); break; case "uninstalled": Yate::Output("PHP Uninstalled: " . $ev->name); break; default: Yate::Output("PHP Event: " . $ev->type); } } Yate::Output("PHP: bye!"); /* vi: set ts=8 sw=4 sts=4 noet: */ ?> 


Says Paul Chitescu, Yate's lead developer, must be ready.



And finally, I have two news, good and bad:





If something is not clear, or does not work, ask questions, try to answer.

Source: https://habr.com/ru/post/166235/



All Articles