radius_host_data_source.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. // Copyright (C) 2015-2017 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <config.h>
  7. #include <dhcp/libdhcp++.h>
  8. #include <dhcp/option.h>
  9. #include <dhcp/option_definition.h>
  10. #include <dhcp/option_space.h>
  11. #include <dhcpsrv/cfg_option.h>
  12. #include <dhcpsrv/db_exceptions.h>
  13. #include <dhcpsrv/dhcpsrv_log.h>
  14. #include <dhcpsrv/radius_host_data_source.h>
  15. #include <dhcpsrv/db_exceptions.h>
  16. #include <util/buffer.h>
  17. #include <util/optional_value.h>
  18. #include <boost/algorithm/string/split.hpp>
  19. #include <boost/algorithm/string/classification.hpp>
  20. #include <boost/array.hpp>
  21. #include <boost/pointer_cast.hpp>
  22. #include <boost/static_assert.hpp>
  23. #include <radcli/radcli.h>
  24. #include <stdint.h>
  25. #include <string>
  26. using namespace isc;
  27. using namespace isc::asiolink;
  28. using namespace isc::dhcp;
  29. using namespace isc::util;
  30. using namespace std;
  31. /// @brief Maximum size of an IPv6 address represented as a text string.
  32. ///
  33. /// This is 32 hexadecimal characters written in 8 groups of four, plus seven
  34. /// colon separators.
  35. const size_t ADDRESS6_TEXT_MAX_LEN = 39;
  36. /// @brief Maximum length of classes stored in a dhcp4/6_client_classes
  37. /// columns.
  38. const size_t CLIENT_CLASSES_MAX_LEN = 255;
  39. /// @brief Maximum length of the hostname stored in DNS.
  40. ///
  41. /// This length is restricted by the length of the domain-name carried
  42. /// in the Client FQDN %Option (see RFC4702 and RFC4704).
  43. const size_t HOSTNAME_MAX_LEN = 255;
  44. /// @brief Maximum length of option value.
  45. const size_t OPTION_VALUE_MAX_LEN = 4096;
  46. /// @brief Maximum length of option value specified in textual format.
  47. const size_t OPTION_FORMATTED_VALUE_MAX_LEN = 8192;
  48. /// @brief Maximum length of option space name.
  49. const size_t OPTION_SPACE_MAX_LEN = 128;
  50. /// @brief Maximum length of the server hostname.
  51. const size_t SERVER_HOSTNAME_MAX_LEN = 64;
  52. /// @brief Maximum length of the boot file name.
  53. const size_t BOOT_FILE_NAME_MAX_LEN = 128;
  54. /// @brief Numeric value representing last supported identifier.
  55. ///
  56. /// This value is used to validate whether the identifier type stored in
  57. /// a database is within bounds. of supported identifiers.
  58. const uint8_t MAX_IDENTIFIER_TYPE = static_cast<uint8_t>(Host::LAST_IDENTIFIER_TYPE);
  59. namespace isc {
  60. namespace dhcp {
  61. RadiusHostDataSource::
  62. RadiusHostDataSource(const DatabaseConnection::ParameterMap& parameters) {
  63. int res;
  64. rh = rc_new();
  65. if (rh == NULL) {
  66. isc_throw(isc::Exception, "Failed to initialize Radius client");
  67. }
  68. rh = rc_config_init(rh);
  69. if (rh == NULL) {
  70. isc_throw(isc::Exception, "Failed to initialize Radius client");
  71. }
  72. res = rc_add_config(rh, "auth_order", "radius", NULL, 0);
  73. if (res != 0) {
  74. isc_throw(isc::Exception, "Failed to configure Radius auth_order");
  75. }
  76. /* TODO: just define manually the few attributes we need */
  77. res = rc_add_config(rh, "dictionary", "/usr/share/radcli/dictionary", NULL, 0);
  78. if (res != 0) {
  79. isc_throw(isc::Exception, "Failed to configure Radius dictionary");
  80. }
  81. res = rc_add_config(rh, "radius_timeout", "1", NULL, 0);
  82. if (res != 0) {
  83. isc_throw(isc::Exception, "Failed to configure Radius timeout");
  84. }
  85. res = rc_add_config(rh, "radius_retries", "1", NULL, 0);
  86. if (res != 0) {
  87. isc_throw(isc::Exception, "Failed to configure Radius retries");
  88. }
  89. res = rc_add_config(rh, "authserver", "127.0.0.1:1812:mysecret", NULL, 0);
  90. if (res != 0) {
  91. isc_throw(isc::Exception, "Failed to configure Radius authserver");
  92. }
  93. // Test and apply config (this also setups the necessary structures to
  94. // send requests to the radius server)
  95. res = rc_test_config(rh, "kea");
  96. if (res != 0) {
  97. isc_throw(isc::Exception, "Failed to apply radcli configuration");
  98. }
  99. // Load dictionary
  100. res = rc_read_dictionary(rh, rc_conf_str(rh, "dictionary"));
  101. if (res != 0) {
  102. isc_throw(isc::Exception, "Failed to read Radius dictionary");
  103. }
  104. }
  105. RadiusHostDataSource::~RadiusHostDataSource() {
  106. }
  107. void
  108. RadiusHostDataSource::add(const HostPtr& host) {
  109. // cannot add a host with radius
  110. isc_throw(NotImplemented, "RadiusHostDataSource::add not implemented");
  111. }
  112. bool
  113. RadiusHostDataSource::del(const SubnetID& subnet_id, const asiolink::IOAddress& addr) {
  114. // cannot delete hosts with radius
  115. isc_throw(NotImplemented, "RadiusHostDataSource::del not implemented");
  116. return false;
  117. }
  118. bool
  119. RadiusHostDataSource::del4(const SubnetID& subnet_id,
  120. const Host::IdentifierType& identifier_type,
  121. const uint8_t* identifier_begin, const size_t identifier_len) {
  122. // cannot delete hosts with radius
  123. isc_throw(NotImplemented, "RadiusHostDataSource::del4 not implemented");
  124. return false;
  125. }
  126. bool
  127. RadiusHostDataSource::del6(const SubnetID& subnet_id,
  128. const Host::IdentifierType& identifier_type,
  129. const uint8_t* identifier_begin, const size_t identifier_len) {
  130. // cannot delete hosts with radius
  131. isc_throw(NotImplemented, "RadiusHostDataSource::del6 not implemented");
  132. return false;
  133. }
  134. ConstHostCollection
  135. RadiusHostDataSource::getAll(const HWAddrPtr& hwaddr,
  136. const DuidPtr& duid) const {
  137. if (duid){
  138. return (getAll(Host::IDENT_DUID, &duid->getDuid()[0],
  139. duid->getDuid().size()));
  140. } else if (hwaddr) {
  141. return (getAll(Host::IDENT_HWADDR,
  142. &hwaddr->hwaddr_[0],
  143. hwaddr->hwaddr_.size()));
  144. }
  145. return (ConstHostCollection());
  146. }
  147. ConstHostCollection
  148. RadiusHostDataSource::getAll(const Host::IdentifierType& identifier_type,
  149. const uint8_t* identifier_begin,
  150. const size_t identifier_len) const {
  151. ConstHostCollection result;
  152. HostPtr host;
  153. int res;
  154. VALUE_PAIR *send = NULL, *received;
  155. if (rc_avpair_add(rh, &send, PW_USER_NAME, identifier_begin, identifier_len, 0) == NULL)
  156. isc_throw(isc::Exception, "Failed to set username");
  157. res = rc_auth(rh, 0, send, &received, NULL);
  158. if (res == OK_RC) {
  159. VALUE_PAIR *vp = received;
  160. char name[128];
  161. char value[128];
  162. fprintf(stderr, "\"%s\" RADIUS Authentication OK\n", identifier_begin);
  163. /* print the known attributes in the reply */
  164. while(vp != NULL) {
  165. if (rc_avpair_tostr(rh, vp, name, sizeof(name), value, sizeof(value)) == 0) {
  166. fprintf(stderr, "%s:\t%s\n", name, value);
  167. }
  168. vp = vp->next;
  169. }
  170. host.reset(new Host(identifier_begin, identifier_len,
  171. identifier_type, SubnetID(),
  172. // TODO: set real IP address
  173. SubnetID(), asiolink::IOAddress("10.42.42.42")));
  174. result.push_back(host);
  175. } else {
  176. fprintf(stderr, "\"%s\" RADIUS Authentication failure (RC=%i)\n", identifier_begin, res);
  177. }
  178. return (result);
  179. }
  180. ConstHostCollection
  181. RadiusHostDataSource::getAll4(const asiolink::IOAddress& address) const {
  182. return (ConstHostCollection());
  183. }
  184. ConstHostPtr
  185. RadiusHostDataSource::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
  186. const DuidPtr& duid) const {
  187. if (hwaddr && duid) {
  188. isc_throw(BadValue, "Radius host data source get4() called with both"
  189. " hwaddr and duid, only one of them is allowed");
  190. }
  191. if (!hwaddr && !duid) {
  192. isc_throw(BadValue, "Radius host data source get4() called with "
  193. "neither hwaddr or duid specified, one of them is required");
  194. }
  195. // Choosing one of the identifiers
  196. if (hwaddr) {
  197. return (get4(subnet_id, Host::IDENT_HWADDR, &hwaddr->hwaddr_[0],
  198. hwaddr->hwaddr_.size()));
  199. } else if (duid) {
  200. return (get4(subnet_id, Host::IDENT_DUID, &duid->getDuid()[0],
  201. duid->getDuid().size()));
  202. }
  203. return (ConstHostPtr());
  204. }
  205. ConstHostPtr
  206. RadiusHostDataSource::get4(const SubnetID& subnet_id,
  207. const Host::IdentifierType& identifier_type,
  208. const uint8_t* identifier_begin,
  209. const size_t identifier_len) const {
  210. ConstHostCollection collection = getAll(identifier_type, identifier_begin, identifier_len);
  211. ConstHostPtr result;
  212. if (!collection.empty())
  213. result = *collection.begin();
  214. return (result);
  215. }
  216. ConstHostPtr
  217. RadiusHostDataSource::get4(const SubnetID& subnet_id,
  218. const asiolink::IOAddress& address) const {
  219. // Not relevant for Radius
  220. isc_throw(NotImplemented, "RadiusHostDataSource::get4 not implemented");
  221. return (ConstHostPtr());
  222. }
  223. ConstHostPtr
  224. RadiusHostDataSource::get6(const SubnetID& subnet_id, const DuidPtr& duid,
  225. const HWAddrPtr& hwaddr) const {
  226. // TODO: libradcli call
  227. ConstHostPtr result = NULL;
  228. return (result);
  229. }
  230. ConstHostPtr
  231. RadiusHostDataSource::get6(const SubnetID& subnet_id,
  232. const Host::IdentifierType& identifier_type,
  233. const uint8_t* identifier_begin,
  234. const size_t identifier_len) const {
  235. // TODO: libradcli call
  236. ConstHostPtr result = NULL;
  237. return (result);
  238. }
  239. ConstHostPtr
  240. RadiusHostDataSource::get6(const asiolink::IOAddress& prefix,
  241. const uint8_t prefix_len) const {
  242. // TODO: libradcli call
  243. ConstHostPtr result = NULL;
  244. return (result);
  245. }
  246. ConstHostPtr
  247. RadiusHostDataSource::get6(const SubnetID& subnet_id,
  248. const asiolink::IOAddress& address) const {
  249. // TODO: libradcli call
  250. ConstHostPtr result = NULL;
  251. return (result);
  252. }
  253. // Miscellaneous database methods.
  254. std::string RadiusHostDataSource::getName() const {
  255. std::string name = "";
  256. return (name);
  257. }
  258. std::string RadiusHostDataSource::getDescription() const {
  259. return (std::string("Host data source that retrieves host information"
  260. "in radius server"));
  261. }
  262. std::pair<uint32_t, uint32_t> RadiusHostDataSource::getVersion() const {
  263. // TODO: Not relevant for libradcli ?
  264. return std::make_pair(0,0);
  265. }
  266. void
  267. RadiusHostDataSource::commit() {
  268. // Not relevant for radius.
  269. }
  270. void
  271. RadiusHostDataSource::rollback() {
  272. // Not relevant for radius.
  273. }
  274. }; // end of isc::dhcp namespace
  275. }; // end of isc namespace